5、MySQL中的锁
无论是Java的并发编程还是数据库的并发操作都会涉及到锁,研发人员引入了悲观锁
跟乐观锁
这样一种锁的设计思想。
悲观锁:
优点
:适合在写多读少的并发环境中使用,虽然无法维持非常高的性能,但是在乐观锁无法提更好的性能前提下,可以做到数据的安全性缺点
:加锁会增加系统开销,虽然能保证数据的安全,但数据处理吞吐量低,不适合在读书写少的场合下使用
乐观锁:
优点
:在读多写少的并发场景下,可以避免数据库加锁的开销,提高DAO层的响应性能,很多情况下ORM工具都有带有乐观锁的实现,所以这些方法不一定需要我们人为的去实现。缺点
:在写多读少的并发场景下,即在写操作竞争激烈的情况下,会导致CAS多次重试,冲突频率过高,导致开销比悲观锁更高。实现
:数据库层面的乐观锁其实跟CAS
思想类似, 通数据版本号
或者时间戳
也可以实现。
数据库并发场景主要有三种:
读-读
:不存在任何问题,也不需要并发控制读-写
:有隔离性问题,可能遇到脏读,幻读,不可重复读写-写
:可能存更新丢失问题,比如第一类更新丢失,第二类更新丢失
两类更新丢失问题:
第一类更新丢失:事务A的事务回滚覆盖了事务B已提交的结果 第二类更新丢失:事务A的提交覆盖了事务B已提交的结果
为了合理贯彻落实锁的思想,MySQL中引入了杂七杂八的各种锁:
锁分类
MySQL支持三种层级的锁定,分别为
表级锁定
MySQL中锁定粒度
最大
的一种锁,最常使用的MYISAM与INNODB都支持表级锁定。页级锁定
是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁,表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了
折衷
的页级,一次锁定相邻的一组记录。行级锁定
Mysql中锁定粒度
最细
的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大行级锁不一定比表级锁要好
:锁的粒度越细,代价越高,相比表级锁在表的头部直接加锁,行级锁还要扫描找到对应的行对其上锁,这样的代价其实是比较高的,所以表锁和行锁各有所长。
MyISAM中的锁
- 虽然MySQL支持表,页,行三级锁定,但MyISAM存储引擎只支持表锁。所以MyISAM的加锁相对比较开销低,但数据操作的并发性能相对就不高。但如果写操作都是尾插入,那还是可以支持一定程度的读写并发
- 从MyISAM所支持的锁中也可以看出,MyISAM是一个支持读读并发,但不支持通用读写并发,写写并发的数据库引擎,所以它更适合用于读多写少的应用场合,一般工程中也用的较少。
InnoDB中的锁
该模式下支持的锁实在是太多了,具体如下:共享锁和排他锁 (Shared and Exclusive Locks) 意向锁(Intention Locks) 记录锁(Record Locks) 间隙锁(Gap Locks) 临键锁 (Next-Key Locks) 插入意向锁(Insert Intention Locks) 主键自增锁 (AUTO-INC Locks) 空间索引断言锁(Predicate Locks for Spatial Indexes)
举个栗子,比如行锁里的共享锁跟排它锁:lock in share modle
共享读锁:
为了确保自己查到的数据没有被其他的事务正在修改,也就是说确保查到的数据是
最新的数据
,并且不允许其他人来修改数据。但是自己不一定能够修改数据,因为有可能其他的事务也对这些数据使用了in share mode
的方式上了S
锁。如果不及时的commit 或者rollback 也可能会造成大量的事务等待。
for update
排它写锁:
为了让自己查到的数据确保是最新数据,并且查到后的数据只允许自己来修改的时候,需要用到
for update
。相当于一个 update 语句。在业务繁忙的情况下,如果事务没有及时的commit或者rollback 可能会造成其他事务长时间的等待,从而影响数据库的并发使用效率。
Gap Lock
间隙锁:
1、行锁只能锁住行,如果