共享锁和排它锁(Shared and Exclusive Locks)
并发控制
提到共享锁和排它锁就不得不提并发控制(Concurrency Control),并发控制可以解决临界资源操作时不一致的情况产生,保证数据一致性常见的手段就是锁和数据多版本(Multi Version)
直接加锁
这种方式会导致被加锁的资源都被锁住,读取任务也无法执行直到锁释放,所有执行的任务相当于串行化方式,简单粗暴,不能并发
共享锁(Shared Locks)简称为 S 锁
读取数据时候可以加 S 锁
共享 (S) 锁允许并发事务读取 (SELECT) 一个资源。资源上存在共享 (S) 锁时,任何其它事务都不能修改数据。一旦已经读取数据,便立即释放资源上的共享 (S) 锁,除非将事务隔离级别设置为可重复读或更高级别,或者在事务生存周期内用锁定提示保留共享 (S) 锁
排它锁 (Exclusive Locks)简称为 X 锁
修改数据时候加 X 锁
排它 (X) 锁可以防止并发事务对资源进行访问。其它事务不能读取或修改排它 (X) 锁锁定的数据
共享锁之间可以并行,排它锁和共享锁之间互斥,也就是说只要共享锁开启没有释放掉的时候,更新锁是不能抢占的,此时其他读取同资源的操作可以进行读取不受限制;同理排它锁开启时候只要没有释放其他不管是排它锁还是共享锁都不可以抢占资源直到锁释放
死锁
死锁是指两个或两个以上的进程(线程)在运行过程中因争夺资源而造成的一种僵局(Deadly-Embrace) ) ,若无外力作用,这些进程(线程)都将无法向前推进,列举一些可能会造成死锁的诱因
- 如上所述并发修改同一记录
- 事务之间对资源访问(表)顺序的交替进行,与第一条大同小异,上升到锁表的级别
- 数据量庞大时候索引建立机制不行,经常扫全表操作,也会造成资源阻塞死锁
- 愿意在代码里开大事务然后对数据库一顿操作互相等待,容易引发死锁,所以代码里要节俭对事务的开销,以及事务开销时候尽可能有效合理利用资源
数据多版本
上面讲到 X 锁占领后其他 S 锁没法占用导致只要写没完成读就不能进行并发查询,InnoDB 引入了数据多版本概念去解决这一问题
核心原理简单讲就是 clone 了一个版本数据进行修改,比如原有的数据版本号是 Version0,这个时候进行写操作 clone 了一份版本号 V1,这个时候对 V1 版本数据进行修改写入操作;与此同时其他查询读任务并发进来一样可以读取 V0 版本数据不受任何影响,这样就解决了在数据更新回写之前不能读取的问题,进一步提高了数据库引擎处理并发的能力
InnoDB 如何操作多版本控制
这个需要简单讲一下 redo,undo 和回滚段
- redo:事务提交后需要把数据刷盘,原有的机械磁盘情况下效率很低(固盘另说),随机写性能低,这样就先放到 redo 日志中,定期把一批数据刷盘,巧妙的利用 redo 日志把随机写变成了顺序写
- undo:事务没有提交之前可能会 rollback,undo 就是把就得数据镜像放到 undo 日志里,回滚时恢复数据
- 存储 undo 日志地方是回滚段,事务提交回滚段日志可以删除,回滚时查找回滚段记录复原,也可以理解为旧的数据存储在回滚段中
InnoDB 本身就是 MVCC 多版本并发控制引擎,通过读取旧版本数据来降低并发事务的锁冲突,提高任务的并发度,与 MyISam 比不单单只是支持事务那么简单