InnoDB锁问题 & DB事务隔离级别

时间:2023-03-08 20:11:48

https://www.cnblogs.com/Katerina/p/11459135.html

数据库事务隔离级别:4 种     (http://www.cnblogs.com/fjdingsd/p/5273008.html)
read uncommitted --> read committed --> repeatable read --> Serializable
脏读             不可重复读           幻读

MySQL的默认隔离级别:Repeatable read。
大多数数据库的默认级别是 Read committed,比如 Sql Server , Oracle。

select @@tx_isolation; -- 查询当前的事务隔离级别
set session transaction isolation level repeatable read; -- 设置当前连接的事务隔离级别(临时更改)

注:
如果要永久更改事务隔离级别,则要在 mysql 的 my.ini 配置中设置
例如:transaction-isolation=Read-Committed

数据库的脏读、不可重复读、幻读以及不可重复读和幻读的区别http://blog.****.net/stu_hsj/article/details/46603681

脏读:

脏读又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。

例如:事务T1修改了一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,之后事务T1因为某种原因Rollback了,那么事务T2读取的数据就是脏的。

解决办法:把数据库的事务隔离级别调整到READ_COMMITTED

不可重复读:

不可重复读是指在同一个事务内,两个相同的查询返回了不同的结果。 
例如:事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。 解决办法:把数据库的事务隔离级别调整到REPEATABLE_READ

幻读:

例如:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样。这就叫幻读。

解决办法:把数据库的事务隔离级别调整到SERIALIZABLE_READ

对 REPEATABLE-READ 的测试:(MySQL)
参考:https://www.liaoxuefeng.com/wiki/1177760294764384/1245268672511968

CREATE TABLE `stu` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

+----+---------+------+
| id | name    | age  |
+----+---------+------+
| 1  | zhansan | 25   |
| 2  | lisi    | 18   |
+----+---------+------+

InnoDB锁问题 & DB事务隔离级别

开两个 dos 窗口来测试:

InnoDB锁问题 & DB事务隔离级别

REPEATABLE_READ 时,产生幻读测试:(MySQL)

InnoDB锁问题 & DB事务隔离级别

<参考:http://www.cnblogs.com/jack204/archive/2012/06/09/2542940.html>
InnoDB行锁实现方式
InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁!

由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。应用设计的时候要注意这一点。

如果出现死锁,可以用SHOW INNODB STATUS命令来确定最后一个死锁产生的原因。

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。
用SELECT ... IN SHARE MODE获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在,并确保没有人对这个记录进行UPDATE或者DELETE操作。但是如果当前事务也需要对该记录进行更新操作,则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用SELECT... FOR UPDATE方式获得排他锁。
给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁;

<href:http://*.com/questions/4034976/difference-between-read-commit-and-repeatable-read>
read commit 和 repeatable read的比较

InnoDB锁问题 & DB事务隔离级别