[mysql 详解]myqsl 锁机制
mysql锁概念
锁是计算机协调多个进程或纯线程并发访问某一资源的机制。
保证数据并发访问的一致性、有效性
锁的运行
事务T在对某个数据对象(如表、记录等)操作之前,先向系统发出请求,对其加锁,加锁后事务T就对数据库对象有一定的控制,在事务T释放它的锁之前,其他事务不能更新此数据对象。
锁分类
按封锁类型
- 排他锁(写锁、X锁)会阻塞其他事务读和写。
- 共享锁(读锁、S锁)阻塞其他事务修改表数据。
- 意向锁(innoDb特有,本身是表级锁,是为了解决表级锁和行级锁之间的冲突)
(意向锁为了方便检测表级锁和行级锁之间的冲突,故在给一行记录加锁前,首先给该表加意向锁。也就是同时加意向锁和行级锁。) - 间隙锁(innoDb特有)
按封锁数据粒度
- 行级锁(开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
- 表级锁(开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。事务中查询非索引字段会锁表
- 页级锁(销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。事务中查询索引字段会锁该行数据
活锁、死锁
采用封锁的方法可以有效防止数据的不一致性,单封锁本身会引起麻烦,就是死锁和活锁。
活锁
- 定义:如果事务T1封锁了数据对象R后,事务T2也请求封锁R,于是T2等待,接着T3也请求封锁R。当T1释放了加载R上的锁后,系统首先批准T3的请求,T2只能继续等待。接着T4也请求封锁R,T3释放R上的锁后,系统又批转了T4的请求。这样的一直循环下去,事务T2就只能永远等待了,这样情况叫活锁。
- 解决方案:采用先来先服务的队列策略。队列式申请。
死锁
- 定义:当两个事务分别锁定了两个单独的对象,这时每一个事务都要求在另一个事务锁定的对象上获得一个锁,因此每一个事务都必须等待另一个事务释放占有的锁。这就发生了死锁了。
- 预防解决方案:一次封锁法、顺序封锁法
- 诊断死锁的方法:超时法、事务等待图法。
myisam 引擎 锁机制
- 读锁
- 写锁
详解:
(一)MyISAM表的读操作,不会阻塞其他用户对同一个表的读请求,但会阻塞对同一个表的写请求。
(二)MyISAM表的写操作,会阻塞其他用户对同一个表的读和写操作。
(三)MyISAM表的读、写操作之间、以及写操作之间是串行的。
(四)当一个线程获得对一个表的写锁后,只有持有锁线程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。
注意:
1. MySQL认为写请求一般比读请求重要。
2. MyISAM在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,在执行更新操作(UPDATE、DELETE、INSERT等)前,会自动给涉及的表加写锁,这个过程并不需要用户干预,因此用户一般不需要直接用LOCK TABLE命令给MyISAM表显式加锁。
InnoDB引擎的锁机制
Innodb和myisam不同
两个引擎的死锁对比
MyISAM表锁是无死锁的,这是因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁。但是在InnoDB中,除单个SQL组成的事务外,锁是逐步获得的,这就决定了InnoDB发生死锁是可能的。
for update 锁级别
- 开启事务后,使用for update 会锁表,按照索引字段查询除外。
- 开启事务后,按照索引索引字段会锁住该行数据,其他不受影响。
- FOR UPDATE 是写锁,读操作不会锁住。
- 不开启事务,FOR UPDATE 不会锁任何数据。
乐观锁、悲观锁
乐观锁:
- 使用数据版本(Version)记录机制实现
- 使用时间戳(timestamp)
悲观锁:
认为本次操作会发生并发冲突,所以一开始就对商品加上锁(SELECT … FOR UPDATE),然后就可以安心的做判断和更新,因为这时候不会有别人更新这条商品库存。
参考
本作品采用《CC 协议》,转载必须注明作者和本文链接