mysql的锁和并发控制(MVCC)

mysql在两个层面实现了并发控制:服务器层和存储引擎层
大多数数据库包括mysql为了提升并发性能都实现了MVCC,不是简单地行级锁。

理解:
1,无论是否加锁都可以读到资源,如果加锁只阻塞修改资源,原因就是 innodb 引擎实现了MVCC机制,在读资源的时候读取小于等于当前版本号的数据,在update 和显式加写锁的时候 会给资源加写锁,但是其他事务读取该资源时会根据版本号读取最新的数据不会读到事务中修改的数据。
2,MVCC只在 可重复读和提交读两个隔离级别下工作,其他两个级别不兼容因为 read uncommitted 级别下总是读取最新的数据行而不是符合当前事务版本的数据行,而 serializable 则会对所有的行加锁
3,当给一个资源加上了排他锁其他事务就不能获取对这个资源的排它锁了,例如 update,delete,for update未提交时,那其他事务的update, delete, select…for update 都需要等待
4,数据的当前版本号是6,同时开启了三个事务分别是 7,8,9。 事务7是update,事务8是update ,事务9是两次select,第一次查询时三个事务读取的都是 版本6 的数据,事务7先得到写锁,事务8只能等待,等7结束才能执行,事务9可以继续执行读取 版本6的数据但是此时事务7提交了,如果此时隔离级别是 提交读那么 事务9第二次查询读取的是版本7 的数据,如果隔离级别是可重复读那么读取的数据就是 版本6的数据。
锁的分类
mysql 中锁分为 共享锁(S锁,读锁)和排它锁(X锁,写锁);
锁的总结:
读锁: 读锁是共享的,相互不阻塞的多个可户可以同一时刻读取同一资源而互不干扰,
写锁:是排他的一个写锁会阻塞其他的读锁和写锁。其他事务不能读取和修改该资源
但是:因为innodb 在 提交读和可重复读级别下实现了MVCC所以对于加锁行为进行了优化。最终就是 读操作不加锁,显示加锁操作和写操作还是会给资源加锁,但是当其他线程读取该资源的时候可以根据版本号读取最新的数据而不会读到正在被事务修改的数据,这样对其他事务的读操作没有任何阻塞。
innodb 下 sql语句的对存在数据默认加锁规则(提交读和可重复读级别):
select :
a,普通的查询不加锁,
b, select…for update 显式加写锁,
c, select … ock in share mode 显式加读锁
update : 写锁
delete: 写锁,
其实加了写锁只不允许其他事务对资源修改,不影响其他事务的读取。
锁策略
存储引擎会自己管理锁,服务器也可以管理锁
提高并发性的方式就是让锁定方式更具有选择性,就是只锁定需要修改的部分数据而不是所有资源理想方式是只锁定需要的数据片只要不冲突这样并发性最好。但实际上管理锁也需要耗费资源(获得锁,检查锁,释放锁)这样系统耗费时间来管理锁那么系统性能也会受到影响
锁策略就是在锁的开销和数据安全性之间寻求平衡,mysql 中每种不同的引擎可以实现自己的锁策略,锁策略有很多种,目前常用的就是 表锁和行锁,顾名思义就是对表或行加共享锁或排它锁,
表锁:锁定整张表,,例如alter table 等操作就是服务器层面加锁,忽略掉存储引擎的锁策略,并发性比较差
行锁:锁定某一行数据,例如 update 等操作,这个加锁操作时在存储引擎管理的,服务器层面没有实现,并发性最好但系统开销也最大,目前是 innodb 等引擎才实现了行级锁。

MVCC是什么
MVCC可以认为是行级锁的一个变种,它可以在很多情况下避免加锁操作,因此开销更低。MVCC的实现大都都实现了非阻塞的读操作,写操作也只锁定必要的行。InnoDB的MVCC实现,是通过保存数据在某个时间点的快照来实现的。也就是说不管什么时间所有事务看到的数据都是一样的。看到的是同一个快照 也就是事务在执行的过程中不会相互影响。通俗理解就是 ,读时候不加锁,写的时候虽然加锁但是还是可以根据版本号读到资源的最新版本而不会读到事务中修改的版本(事务中此时有可能在修改数据但他们没有提交,提交后就会更新版本号)
不同的存储引擎MVCC的实现也是不同,典型的有乐观并发控制和悲观并发控制。

MVCC在InnoDB中的实现。
  InnoDB的MVCC,通过在每行记录后面保存两个隐藏的列来实现:一个保存了行的创建时间,一个保存行的过期时间(删除时间),当然,这里的时间并不是时间戳,而是系统版本号,每开始一个新的事务,系统版本号就会递增。在RR隔离级别下,MVCC的操作如下:
select操作。(这就是为什么事务内update了某个资源在事务内在查询这个数据读出来的是更新后的,在事务外读数据读的是更新前的数据)
InnoDB只查找版本早于(包含等于)当前事务版本的数据行。可以确保事务读取的行,要么是事务开始前就已存在,或者事务自身插入或修改的记录。
行的删除版本要么未定义,要么大于当前事务版本号。可以确保事务读取的行,在事务开始之前未删除。
insert操作。为新插入的行保存当前版本号为行版本号。
delete操作。为删除的行保存当前版本号为删除标识。
update操作。变为insert和delete操作的组合,insert的行保存当前版本号为行版本号,同时保存当前版本号到原来的行作为删除标识。
  由于旧数据并不真正的删除,所以必须对这些数据进行清理,innodb会开启一个后台线程执行清理工作,具体的规则是将删除版本号小于当前系统版本的行删除,这个过程叫做purge。
保存这两个版本号是大多数读操作都不用加锁,加写锁的时候不在阻塞读操作,提高了性能,缺点是需要更多的存储空间和需要做更多的操作。

加锁的各种测试例子
// 提交读
p d o = n e w P D O ( " m y s q l : h o s t = 127.0.0.1 ; d b n a m e =

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值