【MySQL事务和锁】
学习原文:https://blog.csdn.net/zly03/article/details/127170995
事务四大特性:原子性、一致性、隔离性、持久性,简称ACID
MySQL中支持3种不同的存储引擎:
MyISAM存储引擎、Memory存储引擎、和InnoDB存储引擎
注:只有InnoDB才支持事务。
事务的控制语句
begin或者start transaction 开启一个事务
commit 或者 commit work 提交事务,进行持久性修改
rollback 或者 rollback work 回滚事务,撤销已经进行修改但未提交的操作
savepoint [保存点] 在事务中创建一个保存点,一个事务可以有多个保存点
releasavepoint [保存点] 回滚到指定的保存点
set transaction 设置事务的隔离级别
事务隔离级别设置
读未提交(READ-UNCOMMITTED)
读已提交(READ-COMMITTED)
可重复读(RE-PEATABLE-READ)
可序列化读(SERIALIZABLE)
【脏读】
两个用户的账号都为100元。
分别打开两个连接mysql的会话窗口:
a会话隔离级别READ-UNCOMMITED
b会话隔离级别采用默认,为可重复读。开启事务,并执行lisi给zhangsan转账100元。
在a会话查看zhangsan已经收到钱,但实际上b会话中lisi的事务还没提交
假如这个时候,lisi不想转账回滚事务,那a会话的zhangsan就读到脏数据。
个人理解:
事务期间的操作被其他会话看见。
【不可重复读】
将a会话中zhangsan的事务隔离级别设置成READ-COMMITTED
在两个窗口都开启事务
假设a会话中zhangsan想统计全部人的钱有多少,假定为200;
这时b会话的lisi往账户里面存了100元,并提交事务。
这时,zhangsan再次查询总和,查询总金额300,但这次查询对于a会话来说是处于同一事务,查询到两次不一样的结果。
属于不可重复读。
个人理解:
一个事务中查询两次,两次的结果不一致。
【幻读】
为了解决不可重复读的问题,将事务的隔离等级设置为默认值REPEATABLE-READ
两边都开启事务。
先在一个窗口插入一条数据并提交,然后在另外一个窗口查看,此时查询不到该记录,
这时新插入一条记录,主键和刚插入的记录的主键相同。
插入不进去,报错为:主键重复
算是一种幻读,如果要解决幻读,可将事务隔离等级设置为SERIALIZABLE
【锁机制】
InnoDB的行锁
InnoDB默认采用行锁,分为以下这两种,分别为共享锁和排他锁。
这两个概念我在这篇文章中也有介绍。
共享锁(S锁):该事务可以读取但不能修改数据。其他事务可以在该对象上加共享锁,但不能修改数据。
排他锁(X锁):一个数据对象只有一把排他锁,获取到该锁的事务可以读取数据和修改数据。
一般的查询语句不会加任何的锁。
当然也可以为数据加锁
select * from … for update,为数据添加排他锁,
select … lock in share mode 为数据添加共享锁。
【锁实战】
首先关闭事务自动提交
set autocommit=0;
先在一个窗口输入一条获取到排他锁
(虽然操作的是一条数据,但是锁的是整张表,因为没有添加索引)
select * from user where user=‘zly1’ for update;
对user这一列添加索引,就可以看到对其进行加锁就不会出现阻塞的情况。
alter table user add index(user);
注:
在MySQL的行级锁是针对索引中的行加锁,而不是针对表中的行加锁。
访问不同行如果不存在对应的索引,或者使用相同的索引,就会造成锁冲突而锁住整表。
【死锁】
死锁是指两个或者两个以上的额事务在执行过程中,因为互相的等待或者因为争抢相同的资源而造成的互相等待现象。
将事务的自动提交关闭掉。
首先先在会话1中,更新id为1的记录,然后在会话2中更新id为2的记录。
这时再回来在会话1更新id为2的记录,在会话2中更新id为1的记录。
产生死锁。