大白话讲解MySQL 索引,页分裂,行溢出,事务

目录

一、索引

索引是什么,页分裂

页的结构

mysql怎么查询数据

二、页合并

三、行溢出

四、事务

事务四大特性

1、原子性

2、一致性

3、隔离性

4、持久性

事务隔离级别

隔离级别导致的问题

隔离级别与锁的关系

锁与快照读、当前读的关系

RR快照读与RC快照读

MVCC机制——多版本并发控制机制

undo log

 read view

举例


一、索引

图1
图1

索引是什么,页分裂

MySQL b树与b+树,mysql索引等与数据的关系、命中多个索引走哪一个、为什么不建议is_del建索引_qq_41369135的博客-CSDN博客

页的结构

图2

mysql怎么查询数据

如在图1中查询id为10的数据,首先会找到页1,通过二分查找,定位到10在1-18之间。此时就找id=1的指针p1指向所在的第二层页2(为什么找1而不找18,是因为第一层的所有id指向的各自第二层页中都是最小的id,所以10必定在p1指针指向的页2,而不是p2指向的页3),再根据二分查找定位到10在页2的8-14之间,根据刚才的定理,10一定在8指向的页中。所以找到页6,注意。此时并不是直接去遍历user Record里的记录,而是去page directory二分查找,找到对应的槽位,遍历槽位之间的数据找到10(如user Record存放了1-100的数据,page directory存放的是[1,20,50,80,100],通过二分查找,得到10在1-20这个槽位。再遍历1-20找到10。)

二、页合并

InnoDB中的页合并与分裂 - 知乎

三、行溢出

Mysql之大字段溢出问题_jannals的博客-CSDN博客

四、事务

事务四大特性

1、原子性

事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程中,只允许出现两种状态之一,要么都成功,要么都失败

任何一项操作都会导致整个事务的失败,同时其它已经被执行的操作都将被撤销并回滚,只有所有的操作全部成功,整个事务才算是成功完成

2、一致性

事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处以一致性状态。

比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱

3、隔离性

事务的隔离性是指在并发环境中,并发的事务是互相隔离的,一个事务的执行不能被其它事务干扰。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。

一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的

4、持久性

事务的持久性是指事务一旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态

在事物进行过程中,未结束之前,DML语句是不会更改底层数据,只是将历史操作记录一下,在内存中完成记录。只有在事物结束的时候,而且是成功的结束的时候,才会修改底层硬盘文件中的数据(这里就是指mysql刷盘机制了

事务隔离级别

读未提交:事务A能看到事务B修改但还没提交的数据。可能会导致脏读、不可重复度、幻读

读已提交:事务A只能看见事务B修改并提交事务后数据(解决了脏读,因为当事务B在对id=1的数据修改时,会对这行数据加排他锁,在事务B没提交之前,其他数据都不能读取该数据)。可能会导致不可重复读、幻读

可重复读:再事务A里多次读取到id=1的数据是一样的,不管其他事务这条数据进行了怎样的修改。可能会导致幻读

串行化:对于同一行记录,写会加“写锁”,读会加“读锁”,当出现锁冲突时,后访问的事务需要等前一个事务执行完成,才能继续执行。

隔离级别导致的问题

脏读:主要针对已存在的数据。举例场景(读未提交):事务A得到id=1的数据,并进行了修改。此时进来一个事务B,得到A修改后确没有提交事务的d=1的数据,并进行了其他操作,事务A报错回滚。那此时事务B读到的数据就是脏数据

不可重复读:主要针对已存在的数据。举例场景(读已提交):事务A得到id=1的数据,此时进来一个事务B,也得到id=1的数据,并进行修改,提交事务。此时数据库的真实数据已经被修改了。事务A再次查询id=1的数据,发现在同一个事务里(事务A)两次查询得到的数据不一致,导致该条数据不可重复读。同理:读未提交场景也是一样。

幻读:主要针对新增数据。举例场景(可重复读):幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:事务A查询某条记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在(因为事务B悄悄的插入了),无法插入,此时就发生了幻读。

隔离级别与锁的关系

mysql隔离级别与锁_像你这样的的博客-CSDN博客_mysql隔离级别与锁

锁与快照读、当前读的关系

S 锁与 X 锁,当前读与快照读! - 简书

RR快照读与RC快照读

因为我们在平时在写select查询语句时都是普通读,基本很少加锁(for update )。普通读在不同隔离级别就引出了两种读的方式:在RU与串行化是都是当前读(读取最新的),在RR与RC是快照读。但在RC中的快照读的效果确是相当于当前读(原因听下面的分解)

RR快照:业务层方法中第一次普通读(select查询)就会对当前查询记录生成快照,也只会生成一次快照。(注:update/insert/delete等语句虽然都有查询,但都是当前读不会生成readView)

RC快照:每此执行普通读都会生成新的快照

MVCC机制——多版本并发控制机制

  1. RC、RR里用到了MVCC机制,MVCC主要由undo log和read view实现
  2. 首先我们要清楚数据库的每条记录都有三个主要隐藏字段。row_id、trx_id、db_roll_ptr

        row_id:当我们没有显示指定主键,那row_id就是数据库默认主键

        trx_id:插入或更新这条记录时的事务id

        db_roll_ptr:回滚时更新前的那条记录的地址,插入时,该条记录的这个字段为null,因为没得历史记录

undo log

undo log分成insert undolog和update undoLog(delete也算update undolog)

insert undolog:只有在回滚时需要。在事务提交完就删除(这个很好理解:比如事务A执行select普通查询,生成readview(快照),此时进来个事务B,事务B执行insert操作,并且commit提交,立马删除事务 B 该条记录的undolog。事务A再次执行select普通查询,如果是mysql是RC隔离级别,就会重新生成readView会把新插入的记录读取出来,如果是RR隔离级别,)

(原因下面readView讲完举例)

update undolog:回滚时和快照读都需要,事务提交后不能立马删,等待mysql的purge线程删。

(原因下面readView讲完举例)

历史数据再udno log里是什么样的?

图3

 read view

我们先看看 ReadView 的几个重要属性

  • trx_ids: 当前系统中那些活跃(未提交)的读写事务ID, 它数据结构为一个List。(重点注意:这里的trx_ids中的活跃事务,不包括当前事务自己和已提交的事务,这点非常重要)

  • low_limit_id: 目前出现过的最大的事务ID+1,即下一个将被分配的事务ID。

  • up_limit_id: 活跃事务列表trx_ids中最小的事务ID,如果trx_ids为空,则up_limit_id 为 low_limit_id。

  • creator_trx_id: 表示生成该 ReadView 的事务的 事务id

访问某条记录的时候如何判断该记录是否可见,具体规则如下:

  • 如果被访问版本的 事务ID = creator_trx_id,那么表示当前事务访问的是自己修改过的记录,那么该版本对当前事务可见;
  • 如果被访问版本的 事务ID < up_limit_id,那么表示生成该版本的事务在当前事务生成 ReadView 前已经提交,所以该版本可以被当前事务访问。
  • 如果被访问版本的 事务ID >= low_limit_id 值,那么表示生成该版本的事务在当前事务生成 ReadView 后才开启,所以该版本不可以被当前事务访问。
  • 如果被访问版本的 事务ID在 up_limit_id和m_low_limit_id 之间,那就需要判断一下版本的事务ID是不是在 trx_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;
    如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。

举例

1、insert undo log为什么commit之后就可以删?(假如此时数据库维护的待分配的最大事务id=457)

RC下:事务A执行select,生成read view(trx_ids=null,low_limit_id=457,up_limit_id=457)。事务B 进场,执行insert并commit(由于执行了增删改操作,mysql给事务事务id=457,同时mysql自身维护全局事务id+1=458),事务A再次select,生成新的read view(此时属性发变化,trx_ids=null,low_limit_id=458,up_limit_id=458)。事务B刚才新插入的那条记录的trx_id=457。根据mvcc可见性机制,事务ID < up_limit_id(457<458),该条记录可见。这证明了两个事,一:事务A查看事务B 插入的记录时,根本没去undo  log里找数据,所以undo log可删,二:正时因为RC下每次select都重新生成read view机制,实现读已提交

RR下:事务A执行select,生成read view(trx_ids=null,low_limit_id=457,up_limit_id=457)。事务B 进场,执行insert并commit(由于执行了增删改操作,mysql给事务事务id=457,同时mysql自身维护全局事务id+1=458),事务A再次select,不会生成新的read view(此时属性还是第一次select时样子,trx_ids=null,low_limit_id=457,up_limit_id=457)。事务B刚才新插入的那条记录的trx_id=457。根据mvcc可见性机制,事务ID >= low_limit_id (457>=457),该条记录不可见。这证明了两个事,一:事务A查看事务B 插入的记录时,根本没去undo  log里找数据,所以undo log可删,二:正时因为RR下的只有第一次select才生成read view机制,实现可重复读

总结:通过上面两个例子发现,在RC,RR中,针对insert操作产生undo log,RC,RR都快照读都没用上,所以MySQL索性就直接删了。

2、update/delete undo log为什么commit之后就不可以删?(假如此时数据库维护的待分配的最大事务id=457)

RR下:事务A执行select,生成read view(trx_ids=null,low_limit_id=457,up_limit_id=457)。事务B 进场,对某条记录执行update并commit(由于执行了增删改操作,mysql给事务事务id=457,同时mysql自身维护全局事务id+1=458),事务A再次select,不会生成新的read view(属性还是老样子,trx_ids=null,low_limit_id=457,up_limit_id=457)。事务B刚才更新的那条记录的trx_id=457。根据mvcc可见性机制,事务ID >= low_limit_id (457>=457),该条记录不可见。此时情况就来了,如果这条记录不可见,事务A就会根据这条记录上的roll_ptr去undo log找对应的记录。针对update、insert操作。如果事务B  commit就删undo log,此时事务A去undo log中时找不到历史数据的,这不满足RR的可重复读条件

猜你喜欢

转载自blog.csdn.net/qq_41369135/article/details/127752608