暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

读书笔记之MySQL的InnoDB存储引擎

徘徊笔记 2021-05-07
378

来源:《MySQL技术内幕:InnoDB存储引擎》


表空间可以看做是innodb存储引擎逻辑结构的最高层,所有的数据都是存储在表空间中,表空间又由段,区,页(块),行组成。


innodb存储引擎必须要有主键,如果没有主键的话,选择设置非空唯一索引的列为主键,否则自动创建一个6字节大小的指针


Mysql支持对表的水平分区,表的数据存储的地方分为了多个,使用上也和没分区一样,但是分区总要根据一种模式,比如根据id进行hash分区,这个时候根据id进行查询时,会先根据分区算法选出存储的区域,然后在该分区中查找即可,然而如果根据该表的其他key查询时,则该查询语句的执行就需要在每个分区中查找,io查找次数会增多,因为每个分区都需要遍历一遍。感觉这个分区和表的分表类似,分表时每个表名不同,没有数据库中间件的话操作起来比较麻烦,好在是单独存储,并发量会提高,但是类似的查找问题也是存在的。


分区模式有RANGE,LIST,HASH,KEY,COLUMNS,前四种的分区条件必须是整数,不是整数的话需要用mysql自带的函数转化成整数,HASH使用用户自定义的函数进行分区,KEY需要用mysql自带的函数进行分区


多版本并发控制MVCC,在RC和RR事务隔离级别下使用非锁定的一致性读,也就是读的快照文件,RC下读取锁定行的最新一份快照数据,RR下总读取事务开始时的行数据版本,这样可以达到可重复读,当本次事务更新时,这个违背了可重复的前提

条件,所以快照也会进行更新,当别的事务更新数据时,本事务读取的快照不会更新,依然保存本次事务最开始的快照信息。


SELECT ... FROR UPDATE 对读取的行记录加一个X锁,其他事务想在这些行上加任何锁都会被阻塞,SELECT ... LOCK IN SHARE MODE 对记录的行记录加一个S锁,其他事务可以向被锁定的记录加S锁,但是对于加X锁,则会被阻塞。这两个语句在使用时必须在一个事务中,锁定语句时,务必加上begin,START TRANSACTION,或者SET autocommit=0禁用自动提交


Record Lock单个行记录上的锁。Gap Lock间隙锁,锁定一个范围,但不包含记录本身。Next-Key Lock是前两者之和,锁定一个范围,并且锁定记录本身。


在RR模式下,Next-Key Lock算法是默认的行记录锁定算法。


脏读是指一个事务可以读到另一个事务中未提交的数据。不可重复读是指在一个事务内多次读同一数据,中间有别的事务的修改操作,导致两次读取结果不一致。这两个的区别是:脏读是读到未提交的数据,而不可重复读读到的确实是已经提交的数据,但是其违反了数据库事务一致性的要求。


在InnoDB存储引擎中,通过Next-Key Lock算法来避免不可重复读的问题,在MySQL官方文档中,将不可重复读定义为Phantom Problem幻想问题,在Next-Key Lock算法下,对于索引的扫描,不仅仅是锁住扫描的索引,而且还锁住这些索引覆盖的范围gap,因此对于这个范围内的插入都是不允许的,这样就避免了另外的事务在这个范围内插入数据导致的不可重复的问题,因此,InnoDB存储引擎的默认事务隔离级别是READ REPEATABLE,采用Next-Key Lock算法就可以避免幻读的现象。


原子性(atomicity)一致性(consistency)隔离性(isolation)持久性(durability),隔离性由锁来实现,其他特性通过数据库的redo和undo日志来完成


从同步的二进制日志复制方式为ROW(每行记录的改变日志)和STATEMENT(SQL命令形式)和MIX(两者混合方式,分语句执行不同的方式)


数据库应用分类:OLTP(Online Transaction Processing)在线事务处理,多用在日常的事务处理应用中,如银行交易、在线商品交易、blog、网络游戏等应用,数据库容量较小。OLAP(Online Analytical Processing)在线分析处理,多用于数据仓库中,一般需要执行复杂的SQL语句来进行查询。


磁盘组合方式:RAID0一个磁盘的内容分为多个磁盘分别存储部分。RAID1一个磁盘的内容分为多个磁盘分别存储全部。RAID5采用硬盘分区技术,把数据和相对应的奇偶校验信息存储到组成的各个磁盘上,两者分别存储到不同的磁盘上,所以其中一个磁盘损坏可以恢复。RAID10为RAID0和RAID1两者的结合,理论上最优,就是耗费磁盘比较多。


Innodb的默认数据结构是聚簇索引,而MyIsam是非聚簇索引,两者内部都是B+树,即高度平衡的,叶子节点存放着所有的数据,不同在于聚索引的叶节点存放的是一整行的信息。


Innodb存储引擎表是索引组织表,即表中数据按照主键顺序存放,而聚集索引就是按照每张表的主键构造一颗B+树,并且叶节点中存放着整张表的行记录数据,因此也让聚集索引的叶节点成为数据页,聚集索引的这个特性决定了索引组织表中数据也是索引的一部分,同B+数数据结构一样,每个数据页都通过一个双向链表来进行连接。


由于实际的数据页只能按照一颗B+树进行排序,因此每张表只能拥有一个聚集索引,在许多情况下,查询优化器非常倾向于采用聚集索引,因为聚集索引能够让我们在索引的叶节点上直接找到数据,此外,由于定义了数据的逻辑顺序,聚集索引能够特别快地访问针对范围值的查询,查询优化器能够快速发现某一段范围的数据页扫描。


聚集索引的存储并不是物理上的连续,相反是逻辑上连续的,其中有两点,一是页通过双向链表链接,页按照主键的顺序排列,另一点是每个页中的记录也是通过双向链表进行维护,物理存储上可以同样不按照主键存储


聚集索引对于主键的排序查找和范围查找速度非常快。


对于辅助索引,也就是普通索引,叶节点不包括行的全部数据,除了包括键值以外,每个叶级别中的索引行中还包含了一个书签,该书签用来告诉innodb引擎,哪里可以找到与索引对应的行数据,因为innodb存储引擎表是索引组织表,因此书签就是相应行数据的聚集索引键。


还有一种表类型是堆表,它的索引是非聚集的,书签是一个行标识符,在某些只读情况下,书签为行标识符的非聚集索引可能会比书签为主键方式的非聚集索引快,但是在表可能还需要插入,更新,删除等DML操作时,书签为行标识符的非聚集索引可能需要不断更新行标识符所指向的数据页的位置,这时的开销可能就会大于书签为主键方式的非聚集索引了


Innodb是通过主键来聚集数据的,就是被索引的列就是主键。如果一张表没有主键,那就会通过某一唯一列来聚集数据,没有唯一列的时候,就会隐式的生成一个id,通过这个id来聚集数据。在Myisam引擎索引和数据是分开存储的,而Innodb是索引和数据是一起以idb文件的形式进行存储的。在访问速度上,聚簇索引比非聚簇索引快。非聚簇索引需要先查询一遍索引文件,得到索引,跟据索引获取数据。而聚簇索引的索引树的叶子节点的直接指向要查找的数据行。在使用二级索引进行查询的时候,Innodb首先通过二级索引B+Tree得到数据行的主键索引,然后再通过主键索引树查询数据。所以在二级索引,Innodb的性能消耗比较大。 但是,这种情况在innodb中有一定的优化,不是人为控制的,而是引擎实现的,通过二级索引查询多了,innodb会生成自适应的哈希索引。


基于statement格式的二进制日志文件保存的是sql语句,对于复制是有一定要求的如rand、uuid等函数,或者有使用触发器等可能会导致主从服务器上表的数据不一致,这可能是的复制变得没有意义,另一个影响是,你会发现innodb存储引擎的默认事务隔离级别是repeatable read,这其实也是因为二进制日志文件格式的关系,如果使用read committed的事务隔离级别会出现类似丢失更新的现象,从而出现主从数据库上的数据不一致row格式记录标的行变更情况可以设置事务隔离级别为rc,获取更好的并发性。


compact行格式的首部是一个非null变长字段长度列表,而且是按照列的顺序逆序放置的,当列的长度小于255字节,用1字节标识,若大于255字节,用2个字节表示,变长字段的长度最大不可以超过2个字节,这也很好地解释了为什么mysql的varchar的最大长度为65535,因为2个字节为16位,第二个部分是null标志位,改为指示了该行数据中是否有null值,用1表示,该部分所占的字节应该为bytes,接下去的部分是为记录头信息,固定占5个字节,最后部分就是实际存储的每个列的数据了,需要特别注意的是,null不占该部分任何数据,即null除了占用null标志位,实际存储不占用任何空间,另外有一点需要特别注意的是,每行数据除了用户定义的例外,还有两个隐藏列,事务id列和回滚指针列,分别为6个字节和7个字节的大小,若innodb表没有定义primary key,每行还会增加一个6字节的rowid列。


innodb存储引擎对于主键索引的创建和删除需要先创建一张新的临时表,然后把数据导入临时表,删除原表,再把临时表重命名为原来的表名,因此对于一张大表,添加和删除索引需要很长时间,针对辅助索引的创建会对表加上一个s锁,在创建过程中,不需要重建表,因此速度很快,但是在创建的过程中,由于加上了s锁,这个过程中只能进行读操作,删除辅助索引操作就更简单了,只需在内部视图的更新下,将辅助索引的空间标记为可用,并删除内部视图上对于该表的索引定义即可


联合索引不仅可以适用两种查询情况,而且他的第二个键值已经是排序情况


一致性的非锁定读是指innodb存储引擎通过行多版本控制的方式来读取当前执行时间数据库中行的数据,如果读取的行正在执行delete,update操作,这是读取操作不会等待行上锁的释放,相反,会去读取行的一个快照数据,之所以称其为非锁定读,因为不需要等待访问的行上x锁的释放,快照数据是指行之前版本的数据,该实现是通过undo段来实现的,而undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销,此外,读取快照数据是不需要上锁的,因为没有必要对历史数据进行修改,可以看到,非锁定读的机制大大提高了数据读取的并发性,这是innodb默认的读取方式,但是在不同事务隔离级别下读取的方式不同,并不是每个事务隔离级别下读取的都是一致性读,同样,即使都是使用一致性读,但是对于快照数据的定义也不相同。快照数据其实是当前行数剧之前的历史版本,可能有多个版本,一个行可能有不止一个快照数据,成为行多版本技术,由此带来的并发控制,称之为多版本并发控制mvcc,在read committed和repeatable read(innodb存储引擎默认事务隔离级别)下,使用非锁定一致性读,然而,对于快照数据的定义却不相同,在ec下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照,在rr下,总是读取事务开始时的行数据版本


多版本的并发控制协议MVCC读不加锁,读写不冲突,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。

快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。

当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录,查询加共享锁排它锁,或者执行更新添加删除操作

针对当前读,RC隔离级别保证对读取到的记录加锁 (记录锁)。RR隔离级别保证对读取到的记录以及扫描的范围加next-key锁,不存在幻读现象。


RC:针对字段加锁时需要判断该字段是否有索引,如果是主键索引的话,直接在对应的主键索引上加锁即可。如果是普通索引的话,不光要在针对的普通索引上加锁,

还要在对应的主键索引上加记录锁,因为数据记录都是在主键索引上绑定的,普通索引上绑定的只有主键索引。如果没有索引的话,SQL会走聚簇索引的全扫描进行过滤,由于过滤是由MySQL Server层面进行的。因此每条记录,无论是否满足条件,都会被加上X锁。但是,为了效率考量,MySQL做了优化,对于不满足条件的记录,

会在判断后放锁,最终持有的,是满足条件的记录上的锁,但是不满足条件的记录上的加锁/放锁动作不会省略。同时,优化也违背了2PL的约束。


RR:加锁字段是主键索引或者唯一索引的话和RC一致。如果是普通索引的话,这个时候为了防止幻读的产生,会加next-key锁。如果没有索引的话,会对表中的所有记录加next-key锁,也意味着会锁住表,拒绝所有的更新删除添加操作


不可重复读侧重表达读读,幻读则是说读写操作


当根据查询条件查不到任何数据时,RR锁住的是整个表,不管该条件是否有无索引,RC和能查到数据的时候保持一致


意向锁为了实现多粒度锁机制即为了表锁和行锁都能用,因为在RR有些情况下需要获取所有数据的next-key,即代表表锁,如果这个时候针对每条数据进行检验并加锁的话,消耗太大,因为这个时候需要获取表锁,可以直接对该表设置意向锁,如果之前有事务已经对该表的部分数据加了行锁的话,就可以快速返回等待,而不用遍历一遍数据。


MVCC本身不支持read uncommitted等级,如果设置该级别的话,有可能导致binlog中记录的sql语句不能正确的串行化执行,主从数据库的数据可能不能保持一致,而且基于binlog的增量备份也不再有效.所以除非不需要记录binlog,否则别这么做.当然我们可以这样做来优化从库的性能,因为从库不需要记录binlog.


当查询结果和查询条件都包含在一个索引中,那么该索引可以被称为覆盖索引


尽量设置主键递增,数据行写入可以提高插入性能,可以避免页分裂,减少空间碎片化,提升空间和内存的使用效率,主键可以选择较短的数据类型,减少存储空间。


mysql检索的时候优化器会自己判断是否走索引,如果索引扫描行数太多的话就直接全表扫描,当然也可以自己force index(age)强制走一下索引


ACID:原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability)


CAP:一致性(Consistency)可用性(Availability)分区容错性(Partition tolerance)


BASE:基本可用,弱状态,最终一致性


二阶段2PC提交:将一个事务的处理过程分为了投票和执行两个阶段,其核心是对每个事务都采用先尝试后提交的处理方式。

优点是:原理简单,实现方便,缺点是:二阶段提交的执行过程中同步阻塞,协调者单点问题,部分收到commit小猴导致数据不一致,

任何一个节点的失败都会导致整个事务的失败太过保守


三阶段3PC:事务询问,预提交,提交,当进入阶段三时,协调者出现问提或者协调者和参与者之间的网络出现故障都会导致参与者

无法及时接收到来自协调者的提交或者回滚请求,针对这样的异常情况,参与者都会在等待超时之后,继续进行实物提交,

优点是:降低了暗语者的阻塞范围,并且能够在出现单点故障后继续达成一致,缺点是:在参与者接收到提交消息后,如果网络出现

分区,此时协调者所在的节点和参与者无法进行正常的网络通信,在这种情况下,该参与者依然会进行实物的提交,这必然出现数据

的不一致性。



文章转载自徘徊笔记,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论