菜鸟的mysql进阶

说明

一直想学习一下mysql进阶的相关知识,刚好趁着前段时间redis学完,可以学习一下mysql的进阶,简单了解了一下进阶的相关知识,还挺多的,感觉之前的mysql学的好像只是简单入门而已。本文会持续更新,不断地扩充

本文仅为记录学习轨迹,如有侵权,联系删除

一、mysql的存储引擎

引擎
MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,你能够获得额外的速度或者功能,从而改善你的应用的整体功能。

查看mysql支持的存储引擎
命令show engines;
菜鸟的mysql进阶
我这边的mysql版本是8.0以上的,可以看到mysql支持的存储引擎还不少,它默认的存储引擎是InnoDB,当然,这个跟我的mysql的配置文件有关
菜鸟的mysql进阶
我这边设置了默认的引擎和字符串,所以不同配置的mysql可能跟我这个有些不同。

MyISAM与InnoDB区别
这里重点提出来学习一下这个两个引擎,因为这两个好像是最常见的两个。

MyISAM InnoDB
文件构成 每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。.frm文件存储表定义。数据文件的扩展名为.MYD (MYData)。 索引文件的扩展名是.MYI (MYIndex)。 基于磁盘的资源是InnoDB表空间数据文件和它的日志文件,InnoDB 表的大小只受限于操作系统文件的大小,一般为 2GB
事务 不提供事务支持 InnoDB提供事务支持事务
执行速度 MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快 InnoDB相较速度慢了一点
CURD操作 如果执行大量的SELECT,MyISAM是更好的选择 如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表
外键 不支持外键 支持外键
主键 允许没有主键的表存在。 如果没有设定主键,就会自动生成一个 6 字节的主键(用户不可见)。
MyISAM 只支持表级锁 InnoDB 支持行级锁和表级锁,默认是行级锁,行锁大幅度提高了多用户并发操作的性能。
全文索引 MyISAM 支持全文索引。 InnoDB 不支持全文索引 ,但innodb 从 mysql5.6 版本开始提供对全文索引的支持。

经过本人查询之后,发现它们之间的区别还不止这些,这些只是相对比较重要的区别。

MyISAM与InnoDB选择
关于这两种存储引擎的选择,网上有很多说法,我个人比较认可的是要根据需求和存储引擎来决定。比如像一些简单的需求,没有复杂的数据表的关系,数据大多以查询和插入为主,并且对安全性没有特别高的要求,这个时候就建议用MyISAM引擎,如果是涉及到的表有很多,而且各种表还有一些复杂的关联,平时除了查询之外,更新数据也挺频繁的,更重要的是安全性要求特别高,这个时候出于各种综合考虑,建议用InnoDB引擎,因为该引擎支持事务等高级操作。

总之,对于存储引擎的选择,需要结合各种引擎的特点以及需求来进行选择。

二、事务的ACID原则

这里给大家推荐篇两篇博客,这一部分的内容都是基于这两篇博客的整合
博客一博客二

什么是ACID

ACID 说明
原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
一致性(Consistency) 事务前后数据的完整性必须保持一致。
隔离性(Isolation) 事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

有一个非常经典的银行转账的例子就可以很好的体现了这4个原则。银行转账主要分两个步骤,假设有A和B账户,A账户现有800元,B账户现有200元
菜鸟的mysql进阶
转账过程:A减200元,B加200元两个步骤要么都成功,要么都失败,不会出现A减200后,B却不加200的情况,这就是原子性,转账前A加B总额1000元,转账后也是1000元,这就是一致性,如果有多个用户并发的在执行转账,那么它们之前的事务应该是互补干扰的,这就是隔离性,事务执行成功后,即转账成功后,数据就被持久化到数据库了,这就是持久性

脏读、不可重复读和幻读
在进行web系统的开发的时候,有一个必须要考虑的重点,那就是并发处理,虽然自己平时学校里面的一些实训很少会做并发的处理,但是学到现在,自己会有意识的考虑并发的一些情况。额,扯远了,就是说在高并发的情况,可能会出现脏读、不可重复读和幻读的情况,下面给出相应的概述。

  • 脏读
    所谓脏读,就是指事务A读到了事务B还没有提交的数据,比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务–>取走100元,此时切换回事务A,事务A读取的肯定是数据库里面的原始数据,因为事务B取走了100块钱,并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读。

  • 不可重复读
    所谓不可重复读,就是指在一个事务里面读取了两次某个数据,读出来的数据不一致。还是以银行取钱为例,事务A开启事务–>查出银行卡余额为1000元,此时切换到事务B事务B开启事务–>事务B取走100元–>提交,数据库里面余额变为900元,此时切换回事务A,事务A再查一次查出账户余额为900元,这样对事务A而言,在同一个事务内两次读取账户余额数据不一致,这就是不可重复读。

  • 幻读
    所谓幻读,就是指在一个事务里面的操作中发现了未被操作的数据。比如学生信息,事务A开启事务–>修改所有学生当天签到状况为false,此时切换到事务B,事务B开启事务–>事务B插入了一条学生数据,此时切换回事务A,事务A提交的时候发现了一条自己没有修改过的数据,这就是幻读,就好像发生了幻觉一样。幻读出现的前提是并发的事务中有事务发生了插入、删除操作。

事务隔离级别
其实在应对上面的并发问题,我第一个想到的是用锁。。。,为了应对上面的出现的并发问题,就有了事务隔离级别,因为事务隔离级别越高,在并发下会产生的问题就越少,但同时付出的性能消耗也将越大,因此很多时候必须在并发性和性能之间做一个权衡。
事务隔离级别有4种,但是像Spring会提供给用户5种

事务隔离级别 说明
DEFAULT 默认隔离级别,每种数据库支持的事务隔离级别不一样,如果Spring配置事务时将isolation设置为这个值的话,那么将使用底层数据库的默认事务隔离级别。顺便说一句,如果使用的MySQL,可以使用"select @@tx_isolation"来查看默认的事务隔离级别
READ_UNCOMMITTED 读未提交,即能够读取到没有被提交的数据,所以很明显这个级别的隔离机制无法解决脏读、不可重复读、幻读中的任何一种,因此很少使用
READ_COMMITED 读已提交,即能够读到那些已经提交的数据,自然能够防止脏读,但是无法限制不可重复读和幻读
REPEATABLE_READ 重复读取,即在数据读出来之后加锁,类似"select * from XXX for update",明确数据读取出来就是为了更新用的,所以要加一把锁,防止别人修改它。REPEATABLE_READ的意思也类似,读取了一条数据,这个事务不结束,别的事务就不可以改这条记录,这样就解决了脏读、不可重复读的问题,但是幻读的问题还是无法解决
SERLALIZABLE 串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了

给出网上整理的图
菜鸟的mysql进阶

级别越高效率越低,一般情况下会设置为READ_COMMITED,此时避免了脏读,并发性也还不错,之后再通过一些别的手段去解决不可重复读和幻读的问题就好了。

三、数据库设计的三大范式

关于数据库设计的三大范式,首推这篇博客

第一范式(1NF)
要求数据库表的每一列都是不可分割的原子数据项。
例子:
菜鸟的mysql进阶
像这种情况是不行的,因为家庭信息和学校信息的数据是可以再分的,比如家庭地址,家里人口数等,是属于可分割的原子数据项,所以上面这张表是不符合第一范式的,最好的做法是再拆分出家庭信息表和学校信息表

第二范式(2NF)
在满足第一范式的前提下,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖),第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。

一句话总结就是,一张表只能存放对应的一件事情
例子
菜鸟的mysql进阶
很明显,这种设计是不符合第二范式的,单从字段上看就知道,这张表描述了两件事情,产品和订单,所以,这种设计违反了第二范式。

第三范式(3NF)
在满足第一和第二范式的前提下,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖),第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

一句话总结就是,表的每一个字段都应该直接与主键相关,间接相关是不行的
例子
菜鸟的mysql进阶
很明显,后面班主任相关的字段好像跟主键学号不是直接相关的,而像是间接相关的,所以这也是不符合第三范式的,应该通过外键,将班主任与学生关联起来,两张表相关联。

规范与性能
既然三大范式是规范的话,那数据库设计是不是都要符合三大范式才行呢,显然不是的,说到规范就必须要说到性能了,很多情况下,规范和项目的性能往往不能兼得,为了规范化,得牺牲一部分性能,尤其是在海量数据的情况下,所以,在规范和性能之间要做一个权衡。

不过就个人而已,我会优先考虑性能,然后再考虑规范化,我宁愿查询的性能高一点,哪怕是不符合三大范式,最常见的就是给表增加一些冗余字段,将多表查询变为单表查询,虽然可能不符合三大范式,但在大量数据的情况下,这能提高效率。