2

1.MySQL工作流程

  • 客户端发送请求到连接器(授权认证处理)
  • 认证通过后:请求被转发到查询分析器(查询、解析、优化、缓存等等)
  • 查询缓存中如果存在数据直接返回,否则将请求再次转发给优化器(存储过程、触发器、视图等等)
  • 然后将请求转发给执行器调用存储引擎(存储和提取数据)
  • 最终所有数据根据本地文件系统存储在磁盘(内存)上

2.ACID特性

  • 事务:访问和更新数据库的执行单元,包含着一个或多个SQL语句
  • 标准SQL事务需要满足的特性:

    • 原子性:事务是不可分割的最小执行单元,事务中的SQL语句只会全部执行成功或全部不执行
    • 一致性:事务执行前后数据保持一致性,其他事务对同一个数据的访问结果是一致的
    • 隔离性:并发访问数据库时,一个用户的事务不会被其他事务影响,并发事务之间是隔离的
    • 持久性:事务被提交之后,发生任何情况,数据库中数据的改变是持久的

3.事务隔离级别

3-1.脏读/不可重复读/幻读

  • 脏读:A事务能够读取其他事务的修改(update/delete/insert)操作
  • 不可重复读:A事务能够读取其他事务已经提交的update/delete操作(同一条SQL查询读到不同的结果)
  • 幻读: A事务能够读取其他事务已经提交的insert操作(同一条SQL查询读到多余结果)

3-2.标准的事务隔离级别

  • 未提交读:允许读取其他未提交事务的数据变更(存在脏读/不可重复读/幻读问题)
  • 提交读(不可重复读):允许读取其他已提交事务的数据变更(还存在不可重复读/幻读问题)
  • 可重复读:不允许读取其他已提交事务的数据变更(还存在幻读问题)
  • 可串行化:所有操作串行化,读加读锁,写加写锁,读写锁互斥(解决幻读问题)

3-3.实验现象

  • 提交读相关实验
A事务 B事务
begin; begin;
update操作A记录的时候 delete/update操作A记录时会等待锁释放而超时;select操作A记录时看不到更改后的数据
commit(释放锁)
delete/update操作A记录正常;select操作A记录时能看到更改后的数据

在RC级别下:当A事务进行update操作A记录时,为了并发操作过程中的冲突,会给A记录加锁并且如果没有commit时,B事务delete/update操作都会超时,但解决脏读问题;当A事务提交后,select操作存在不可重复读问题;当然如果A事务进行insert操作,B事务很容易通过select操作读到多余记录,没有任何办法`

  • 可重复读相关实验
A事务 B事务 C事务
begin; begin; begin;
select操作1:查询到A和B两条记录
update操作A记录
commit;
insert操作C记录
commit;
select操作1:同一条语句;没有读取到更新记录和插入记录
在INNODB存储引擎中的现象:同一条select查询读不到B事务的更新删除操作(因为读取的记录被加锁,其他事务无法执行更新删除操作),这样意味着解决了不可重读问题;也读取不到C事务添加操作,也解决了幻读问题(但是仅仅通过常规锁机制是无法阻止Insert操作的);所以在非INNODB存储引擎中都是采用串行化去解决幻读问题。`
  • 以上是悲观锁的实现机制,但是为了减少开销,更多情况下会依赖于乐观锁的MVCC(多版本并发控制)来避免以上问题

4.多版本并发控制

4-1.悲观锁和乐观锁

  • 悲观锁:假定大概率会发生并发更新冲突,访问和处理过程中都会加排他锁,整个事务过程中锁定数据,只有当事务提交或者回滚后才释放锁
  • 乐观锁:假定大概率不会发生并发更新冲突,访问和处理数据过程中不加锁,只在更新数据时再根据版本号和时间戳判断是否有冲突(版本号不一致),有则处理,无则提交事务

4-2.MVCC

  • MVCC(多版本并发控制)优点:读不加任何锁,读写不冲突,对于读操作多于写操作的应用,极大的增加了系统的并发性能;
  • MVCC:通过在每一行记录后面添加一个系统Version号(创建标识和删除标识),每开启新事务时系统Version号就自增1,然后将自增后的系统Version号作为当前事务的Version号,用来和查询到的每一行记录的系统Version号进行比较

    • Select操作:只有满足以下2个条件的记录才能返回作为查询结果

      • 记录的创建Version号小于等于事务Version号的数据行;
      • 记录的删除Version号未定义或者大于事务Version号的数据行;
    • Insert操作:保存当前事务Version号作为该数据行的创建Version号
    • Delete操作:保存当前事务Version号作为该数据行的删除Version号
    • Update操作:插入一条新纪录,保存当前事务Version号为行创建Version号,同时保存当前事务Version本号到原来的行作为删除Version号
  • 提交事务:准备更新数据的时候,会将记录的系统Version号加1与数据库的系统Version对比,如果大于给予更新处理,否则认为是过期数据。

5.当前读和快照读

  • 当前读:即加锁读,读取记录的最新版本,会加锁保证其他并发事务不能修改当前记录,直至获取锁的事务释放锁
  • 快照读:即不加锁读,读取记录的快照版本而非最新版本,通过MVCC实现
InnoDB默认的RR事务隔离级别下,不显式加『lock in share mode』与『for update』的『select』操作都属于快照读,保证事务执行过程中只有第一次读之前提交的修改和自己的修改可见,其他的均不可见;但我们读到的数据可能是历史数据,是不及时的数据,不是数据库当前的数据!这在一些对于数据的时效特别敏感的业务中,就很可能出问题。
  • 事务的隔离级别实际上都是定义了当前读的级别,MySQL为了减少锁处理(包括等待其它锁)的时间,提升并发能力,引入了快照读的概念,使得select不用加锁。而update、insert这些“当前读”,就需要另外的模块来解决了

6.锁机制

  • InnoDB主要实现了三种行锁算法:

    • Record Lock:记录锁,锁定一个行记录
    • Gap Lock:间隙锁,锁定一个区间
    • Next-key Lock:Record Lock+Gap Lock,锁定行记录+上下区间
  • 不可重复读情况下:如果A事务select“当前读”操作(会给查询到的数据+记录锁),当B事务Insert新数据后,A事务再次执行相同条件的select“当前读”操作,是可能发现多余的数据,这就是“当前读”的幻读
  • 可重复读情况下:如果A事务select“当前读”操作(会给查询到的记录+记录锁+间隙锁),当B事务Insert新数据后(如果在间隙空间中)会进行Waiting,此时A事务再次执行相同条件的select“当前读”操作, 是不可能发现多余的数据,这就解决了“当前读”的幻读问题。
  • 间隙区间:根据不同的索引类型(数据结构)范围性扫描的索引和记录而确定的,如果没有Gap Lock,RR级别下是可以随意Insert的,从而导致“当前读”的幻读问题......
  • 当前读下:行锁防止update/delete操作,GAP锁防止Insert操作,Next-Key锁解决了在RR级别下写数据时的幻读问题
  • 我可以理解为:Gap锁解决一致性读问题;Next-Key锁解决一致性写问题吗?

参考


英格拉姆浩
40 声望12 粉丝

面对焦虑,认识自我,提升技术