数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。
MySQL?数据库由于其自身架构的特点,存在多种数据存储引擎,每种存储引擎的锁定机制都是为各自所面对的特定场景而优化设计,所以各存储引擎的锁定机制也有较大区别。
按照锁的粒度:
总的来说,MySQL 这 3 种锁的特性可大致归纳如下:
MyISAM
,MEMORY
,CSV
?等一些非事务性存储引擎。InnoDB
?存储引擎。BerkeleyDB
?存储引擎。按数据库管理角度:
# 共享锁
SELECT ... LOCK IN SHARE MODE;
# 排他锁
SELECT ... FOR UPDATE;
按程序员的角度:
write_condition
?机制,Java API 并发工具包下面的原子变量类就是使用了乐观锁的?CAS(Compare and Swap,比较并交换)
?来实现的。synchronized
?和?ReentrantLock
?等独占锁都是悲观锁思想的实现。举一个版本号的例子,
数据表中除了数据还有一个version字段,更新数据时version字段会加一,
假设线程A在读取数据和version(version = 1)的期间,有另一个线程B也读取了version(version = 1),
线程A修改数据,更新version(version = 2),提交更新时,在更新version前读取的version(version = 1)和当前数据表中的version(version = 1)相同,则更新成功
线程B也修改数据,更新version(version = 2)提交更新时,由于读取时version = 1 而当前数据表version = 2 不相等,则更新失败,
根据前面对两种锁的介绍,总结一下两种锁的应用场景:
思考一刻:你认为秒杀场景下,并发下单和支付应该使用什么锁?
MySQL InnoDB 支持三种行锁定方式:
InnoDB 的默认隔离级别 RR(可重读)是可以解决幻读问题发生的,主要有下面两种情况:
这里对 记录锁、间隙锁、临键锁 做一个总结:
CAS(compare and swap) 比较并交换,依赖CPU的CAS指令在更新数据前先判断数据是否被其他线程修改过,如果没有修改则更新,整个操作是原子操作。
CAS指令执行时,比较内存值与期望值是否相等,若相等则用目标值替换当前值,(不相等则会循环比较直到相等)整个比较赋值操作是一个原子操作;
过程:
1、取出内存值,比如求a++,首先取a此时的内存值0
2、计算目标值,计算a+1=1,此值是基于a=0得到
3、调用CAS指令
4、取出当前a的内存值,与期望值比较,如果相等,则用目标值替换。
5、如果不一致,则说明其他线程已经更改,转到第一步重新执行。
CAS有三个缺点:
1,CAS自旋操作:当内存地址V与预期值B不相等时会一直循环比较直到相等,
2,只能保证一个共享变量的原子操作,
3,出现ABA问题:如果内存值初次读取的时候为A,在将要赋值的时候再次检查还是A,能说明V没有改变过吗?
有一种可能时当读取内存值V的时候是A,有一个线程将A改为B,后有改为A,CAS会误认为内存值V没有改变,这称为CAS操作的ABA问题;
如果需要用到表锁的话,如何判断表中的记录没有行锁呢?一行一行遍历肯定是不行,性能太差。我们需要用到意向锁来快速判断是否可以对某个表使用表锁。
意向锁是表级锁,共有两种:
意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致的恶性循环现象。
常用解决方法: