数据库中的锁机制
为什么要加锁
数据库是多用户共享资源的,在并发环境下会破坏数据库的一致性,所以需要锁来控制它们。
从程序员角度分
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
乐观锁适用于多读的应用类型,这样可以提高吞吐量
实现方式:
版本号
执行操作时先读取版本号,完成一次操作,需要对比版本号与读取到的是否相同,相同则版本号加1,数据更新;不同,则操作被驳回。
CAS算法
即compare and swap(比较和互换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步。CAS算法涉及到3个操作数。
1、需要读写的内存值V
2、进行比较的值A
3、拟写入的新值B
当且仅当V的值等于A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。
悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等
Java中synchronized
和ReentrantLock
等独占锁就是悲观锁思想的实现
更详细:https://blog.csdn.net/qq_34337272/article/details/81072874
按*的数据粒度分类
行级锁
只针对当前操作的行进行加锁,能大大减少数据库操作的冲突。
锁粒度最小,开销大,加锁慢,易出现死锁,锁冲突的概率最低,并发度也最高。
页级锁
粒度介于行级锁和表级锁中间,并发度一般
表级锁
对当前操作的整张表加锁。
锁粒度最大,开销小,加锁快,不会出现死锁,锁冲突的概率最高,并发度最低。
常用引擎的锁机制
MyISAM和MEMORY采用表级锁(table-level locking)
BDB采用页面锁(page-level locking)或表级锁,默认为页面锁
InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
从数据库系统角度分为三种
共享锁(Shared Lock)
共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改
排他锁(Exclusive Lock)
排他锁又称写锁,如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的*。获准排他锁的事务既能读数据,又能修改数据。
更新锁(Update Lock)
更新锁是为了防止死锁而设立的。当SQL Server 准备更新数据时,它首先对数据对象作更新锁锁定,这样数据将不能被修改,但可以读取。等到SQL Server 确定要进行更新数据操作时,它会自动将更新锁换为独占锁。但当对象上有其它锁存在时,无法对其作更新锁锁定。
死锁
在多用户或多进程下,为使用同一资源而产生的相互等待状行级锁态
锁监视器会自动定期对死锁进行检测
如何查看数据库中的锁
可使用存储过程查看锁
EXEC sp_lock
隔离级别
未提交读(READ_UNCOMMITTED)
允许读取脏数据
提交读(READ_COMMITTED)
Oracle默认级别,该级别通过指定语句不能读取其它事务已改动可是尚未提交的数据值。禁止运行脏读
可重复度(REPEATABLE_READ)
MySQL默认级别,其它事务均不能够改动或删除(可插入)当前事务已读取的数据。
序列化读(SERIALIZABLE)
将共享锁保持到事务完成,事物之间完全隔离。读用读锁,写用写锁,读锁和写锁相互排斥,这么做能够有效的避免幻读、不可反复读、脏读等问题,但会极大的减少数据库的并发能力。
并发产生的问题
脏读
即为事务1第二次读取时,读到了事务2未提交的数据。
例如:事务T1修改了某个表中的一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,之后事务T1因为某种原因回滚(Rollback)了,那么事务T2读取的数据就是脏数据,是无效的。
不可重复读
主要在于事务2在事务1第二次读取时,提交了数据。导致事务1前后两次读取的数据不一致。数据的值不同
例如:事务T1会读取两次数据,在第一次读取某一条数据后,事务T2修改了该数据并提交了事务,T1此时再次读取该数据,两次读取便得到了不同的结果。
虚读
事务1第二次查询时,读到了事务2提交的数据。数据的条目不同
例如:系统事务A将数据库中所有数据都删除的时候,但是事务B就在这个时候新插入了一条记录,当事务A删除结束后发现还有一条数据,就好像发生了幻觉一样。这就叫幻读。
隔离级别 | 脏读 | 不可重复读 | 虚读 |
---|---|---|---|
READ_UNCOMMITTED | 出现 | 出现 | 出现 |
READ_COMMITTED | 不会出现 | 出现 | 出现 |
REPEATABLE_READ | 不会出现 | 不会出现 | 出现 |
SERIALIZABLE | 不会出现 | 不会出现 | 不会出现 |