ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【MySQL】事件?隔离级别?锁?详解MySQL并发控制机制 [打印本页]

作者: 九天猎人    时间: 2024-8-31 20:44
标题: 【MySQL】事件?隔离级别?锁?详解MySQL并发控制机制
目录
1.先理清一下概念
2.锁
2.1.分类
2.2.表锁
2.3.行锁(MVCC)
2.4.如何进行事件间数据的欺压同步
2.5.间隙锁
2.6.行锁变表锁
2.7.欺压锁行


1.先理清一下概念

所谓并发控制指的是在对数据库进行并发操作时如何包管数据的一致性和精确性。在数据库中与并发控制相关的概念有如下几个:

这几个概念大家应该都知道,但是我猜许多人没有把它们串在一起搞明白他们之间的关系,导致这三个概念各是各的,造成影象负担,末了对整个数据库并发控制的体系也云里雾里的。
锁与事件的关系:
在计算机科学中,做并发控制都是用的“标志位”来实现的,说直白一点就是锁,我们基本上可以说计算机科学中并发控制的底层都是锁的头脑。在数据库中也不例外,也是通过锁来实现并发控制的。使用上锁之后,整个数据库的读写都会对外呈现出一些特质,这些特质就是事件(ACID),原子性、一致性、隔离性、长期性。
锁与隔离级别的关系:
当然锁有许多,在数据库中有行锁、表锁、读锁、写锁等等......不同级别的锁,ACID这些特质的强弱不同,这个强弱的级别就是“隔离级别”。
所以综合起来说就是用锁来实现并发控制,对外会呈现出事件(ACID),锁的级别的不同,ACID的强弱也不同,隔离级别对应也不同。
三个东西的关系理清楚后,来回顾一下事件和隔离级别的具体内容。
事件:
事件是为了包管SQL之间不产生脏数据。innodb中默认没有被包裹在事件中的一个单条SQL就是一个事件。事件是一类特性的总称,合起来为ACID:

隔离级别:
事件内具有原子性,但是事件间不具有原子性,并发情况下,多事件间仍然会存在脏数据的题目。
1、脏读:事件A读取了事件B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
2、不可重复读:事件 A 多次读取同一数据,事件 B 在事件A多次读取的过程中,对数据作了更新并提交,导致事件A多次读取同一数据时,结果 不一致。
3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时间插入了一条具体分数的记录,当系统管理员A改竣事后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
隔离级别就是为了包管事件之间不产生脏数据。

2.锁

2.1.分类

按照对数据的操作类型分类,分为:读锁、写锁。
按照对数据操作的粒度分类,分为:行锁、表锁、
表锁:
开销小、加锁快,无死锁,锁的粒度大,发生锁辩论的概率最高,并发度最低。
读锁:
又叫共享锁,针对同一份数据,多个读操作可以同时进行而不互相影响。针对被锁表,所有客户端都可以进行读操作,所有客户端都无法进行写操作,加锁方和其他客户端的区别是,加锁方直接不答应进行写操作,而其他客户端的写操作答应进行,只是会被阻塞挂起。锁解开后,所有挂起的操作线程会去重新争抢资源。
写锁:
又叫排它锁,当前写操作没有完成前,它会阻断其他写锁和读锁。针对被锁表,加锁方可以写,其他客户端不可。其他客户端的写操作会直接失败,读操作会被阻塞挂起,解锁以后,被挂起的线程会重新去争抢资源。
保护机制:
读锁、写锁中,加锁方都只能读当前被自己锁定的表,这是MySQL的一个保护机制,为的就是欺压要求加锁方给出一个说法,到底准备锁多久,不给说法不让走。
MySQL中不同引擎支持不同级别的锁,myIsam支持表锁,innodb支持行锁。
2.2.表锁

表锁,我们当然是要基于使用表锁的存储引擎来聊,也就是基于myIsam引擎。
myIsam引擎中的读写锁是主动加的。
myIsam引擎在解锁后的阻塞队列中进行读写锁调理是写优先,这样一旦阻塞队列中大量都是写操作,那么读操作会很难得到锁,变得很慢,从而造成永世堵塞。

当然除了主动加锁,表锁可以通过指令来加锁。
查看所有锁:
  
  1. show open tables
复制代码
解锁:
解铃还须系铃人,只有加锁方能解锁。
  
  1. unlock  tables
复制代码
加读锁:
  
  1. lock table 表名 read
复制代码
加写锁
  
  1. lock table 表名 write
复制代码
2.3.行锁(MVCC)

行锁,我们当然是要基于使用行锁的存储引擎来聊,也就是基于innodb引擎。innodb和myIsam最大的不同有两点,一是支持事件,二是接纳了行级锁。innodb下读写都是主动加行锁,这没有什么好说的。但是行锁因为粒度太细了,会影响效率的,innodb没有傻傻的只用了行锁,还给出了行锁的优化方案——MVCC。
MVCC,多版本并发控制,本质上就是使用行锁锁定数据。
对于锁的实现方式来说无非就两种

对于行级锁这种粒度这么细的锁,选用悲观锁性能肯定是不佳的,所以mvcc其实选用的都是乐观锁。
举个例子:
当使用mysql的innodb模式的时间,在mvcc的机制下,当事件A读取了表a的快照,在事件A提交前事件B又去读取了表a并且修改了数据,接下来事件A提交时会有什么效果,直接失败回滚事件还是?
在这种情况下:
事件A提交时,InnoDB会查抄事件A对数据进行更新时的数据版本是否与事件A最初读取数据时的版本一致。如果事件B修改的数据行与事件A无关,那么事件A可以正常提交,因为它们操作的是不同的数据行。
如果事件B修改的数据行恰好是事件A准备更新的那部分数据,由于事件A处于可重复读隔离级别,它看到的是先前的版本,不会看到事件B的修改。当事件A实验提交时,InnoDB的事件处理机制会确保事件的一致性,通常有两种可能的结果:
a. 如果InnoDB检测到事件A试图更新的数据已经被事件B更新过(即版本辩论),事件A的更新操作可能会导致一个死锁错误(Deadlock),在这种情况下,InnoDB会选择回滚此中一个事件以解决死锁题目,通常会回滚较晚开始或者较晚修改数据的事件(在这里可能是事件A)。
b. 如果InnoDB的并发控制机制足够强大(比方使用Next-Key Locks来防止幻读),事件A在更新数据时就已经被制止,也就是说事件A在实验更新数据时就会被阻塞,直到事件B完成并释放锁,事件A才能继承,进而根据实际情况决定是成功提交还是因数据已改变而回滚。
2.4.如何进行事件间数据的欺压同步

MySQL的MVCC是在提交的时间才比对数据是否又修改,那么有没有办法让所有事件间的数据都是欺压同步的喃?有!通过关闭主动提交改为手动提交来包管修改的顺序性。
数据库默认开启主动提交,通过set autocommit=0可以关闭主动提交。
关闭后每次执行sql以后,通过commit命令来手动提交,才会对数据库产生影响,否则只会对当前操作方的数据快照有影响。innodb引擎中,其他客户端想查看到最新的数据情况也必须通过commit指令来做一次同步(因为innodb默认隔离级别为可重复读)。
当一个客户端修改某行数据,未commit前,其他客户端对该行数据的修改会阻塞挂起,直到先改谁人用户commit为止。

2.5.间隙锁

使用范围条件匹配时,innodb会给符合条件的已有数据记录的索引加“范围锁”(范围锁是特别的行锁),对于键值在条件范围内但并不存在的记录,叫做间隙(GAP),innodb也会对这个间隙加锁,这种机制叫做“间隙锁”

2.6.行锁变表锁

任何需要全表扫描的情况时,行锁都会升级为表锁。
因为MySQL不知道到底该锁哪行,所以会将整个表都锁起来,然后再进行全表扫描。

全表扫描的情况无非两种:
2.7.欺压锁行

通过 for update 可以在无update操作下,欺压锁定一行。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4