锁的认识
1.1 锁的解释
|
1.2 锁的重要性
|
1.3 锁的缺点
|
1.4 简单的例子
|
锁的类型
2.1 表锁
种类
|
|
存储引擎默认锁
|
特点
|
结论
|
建议
|
2.2 行锁
种类
|
|
特点存储引擎默认锁
|
|
事务并发带来的问题
事务并发带来的问题 脏读 最容易理解。另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据。 假如,中午去食堂打饭吃,看到一个座位被同学小Q占上了,就认为这个座位被占去了,就转身去找其他的座位。不料,这个同学小Q起身走了。事实:该同学小Q只是临时坐了一小下,并未“提交”。 不重复读 同样是两个事务在操作同一数据,如果在事务开始时读了某数据,这时候另一个事务修改了这条数据,等事务再去读这条数据的时候发现已经变了,先后两次读到的数据结果不一致,这就是没办法重复读一条数据。 假如,中午去食堂打饭吃,看到一个座位是空的,便屁颠屁颠的去打饭,回来后却发现这个座位却被同学小Q占去了。 幻读 事务一开始按某个查询条件没查出任何数据,结果因为另一个事务的影响,再去查时却查到了数据,这种就像产生幻觉了一样,被称作幻读。 假如,中午去食堂打饭吃,看到一个座位是空的,便屁颠屁颠的去打饭,回来后,发现这些座位都还是空的(重复读),窃喜。走到跟前刚准备坐下时,却惊现一个恐龙妹,严重影响食欲。仿佛之前看到的空座位是“幻影”一样。 |
2.3 事务的四种隔离级别
MySQL InnoDB事务的隔离级别有四级,默认是“可重复读”(REPEATABLE READ)。
未提交读(READ UNCOMMITTED)
读未提交其实就是事务没提交就可以读,很显然这种隔离级别会导致读到别的还没提交的数据,一旦基于读到的数据做了进一步处理,而另一个事务最终回滚了操作,那么数据就会错乱,而且很难追踪。总的来说说,读未提交级别会导致脏读。
另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据(脏读)。
提交读(READ COMMITTED)
务提交后才能读,假设你拿着银行卡去消费,付钱之前你看到卡里有2000元,这个时候你老婆在淘宝购物,赶在你前面完成了支付,这个时候你再支付的时候就提示余额不足,但是分明你看到卡里的钱是够的啊。
这就是两个事务在执行时,事务A一开始读取了卡里有2000元,这个时候事务B把卡里的钱花完了,事务A最终再确认余额的时候发现卡里已经没有钱了。很显然,读提交能解决脏读问题,但是解决不了不可重复读。
本事务读取到的是最新的数据(其他事务提交后的)。问题是,在同一个事务里,前后两次相同的SELECT会读到不同的结果(不重复读)。
(默认)可重复读(REPEATABLE READ)
解决不可重复读问题,事务A一旦开始执行,无论事务B怎么改数据,事务A永远读到的就是它刚开始读的值。那么问题就来了,假设事务B把id为1的数据改成了2,事务A并不知道id发生了变化,当事务A新增数据的时候却发现为2的id已经存在了,这就是幻读。
在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象(幻读)。
串行化(SERIALIZABLE)
所有的事务串起来一个个执行,因为没有并发的场景出现了,什么幻读、脏读、不可重复读统统都不存在的。但是同样的,基本并发能力会非常差。
读操作会隐式获取共享锁,可以保证不同事务间的互斥。
如何上锁?
3.1 表锁
隐式上锁(默认,自动加锁自动释放)
|
|
显式上锁(手动)
|
解锁(手动)
|
session01 | session02 |
---|---|
lock table teacher read;//上读锁 | |
select * from teacher; //可以正常读取 | select * from teacher;//可以正常读取 |
update teacher set name = 3 where id =2;//报错因被上读锁不能写操作 | update teacher set name = 3 where id =2;//被阻塞 |
unlock tables;//解锁 | |
update teacher set name = 3 where id =2;//更新操作成功 |
session01 | session02 |
---|---|
lock table teacher write;//上写锁 | |
select * from teacher; //可以正常读取 | select * from teacher;//被阻塞 |
update teacher set name = 3 where id =2;//可以正常更新操作 | update teacher set name = 4 where id =2;//被阻塞 |
unlock tables;//解锁 | |
select * from teacher;//读取成功 | |
update teacher set name = 4 where id =2;//更新操作成功 |
3.2 行锁
隐式上锁(默认,自动加锁自动释放)
|
|
显式上锁(手动)
|
解锁(手动)
|
session01 | session02 |
---|---|
begin; | |
select * from teacher where id = 2 lock in share mode;//上读锁 | |
select * from teacher where id = 2;//可以正常读取 | |
update teacher set name = 3 where id =2;// 可以更新操作 | update teacher set name = 5 where id =2;//被阻塞 |
commit; | |
update teacher set name = 5 where id =2;//更新操作成功 |
session01 | session02 |
---|---|
begin; | |
select * from teacher where id = 2 for update;//上写锁 | |
select * from teacher where id = 2;//可以正常读取 | |
update teacher set name = 3 where id =2;// 可以更新操作 | update teacher set name = 5 where id =2;//被阻塞 |
rollback; | |
update teacher set name = 5 where id =2;//更新操作成功 |
|
4 行锁的注意点
|
如何排查锁?
5.1 表锁
查看表锁情况
1 |
|
表锁分析
1 |
|
|
5.2 行锁
行锁分析
1 |
|
|
死锁
6.1 解释
|
6.2 产生的条件
|
6.1 解决
|
6.1 如何避免
|
乐观锁与悲观锁
7.1 悲观锁
解释
|
实现机制
|
实现层面
|
适用场景
|
7.2 乐观锁
解释
|
实现机制
|
实现层面
|
适用场景
|
8.select … for update查询
select查询是不加锁的,select…for update是会加锁的,而且是悲观锁,但是在不同查询条件时候加的锁的类型(行锁,表锁)是不同的。
select * from t_user where id = 1 for update;
原由:
在where 后面查询条件是主键索引,唯一索引时候是行锁
查询条件是普通字段时候加的是表锁
9.自己容易忘的命令
- 开启事务:start transaction ;
- 悲观锁:select user_id ,user_name from huixin where user_id=1002 for update ;
- 提交事务: commit ;
- 查看当前锁:show open tables where In_use >0;
- 清空所有锁:unlock tables;
- 设置事务的提交:set autocommit = (off/0) 或 set autocommit =( on/1)
- 查看事务是否自动提交: show variables like ‘autocommit’
- 回滚事务:rollback
文章评论