事务是指一组业务操作,要么全部成功,要么全部失败。
比如银行转账业务,步骤一:从 A 账户减少 300 元;步骤二:向 B 账户增加 300 元。为了确保总的金额不变,就要维持数据的一致性,那么步骤一和步骤二两个操作必须全确认或者全取消。这里的每个步骤就可以理解为每个 SQL 语句。
autocommit 默认为 on(打开),即我们每执行一条 SQL 都相当于一个事务并自动提交。
Session 1 | Session 2 |
start transaction; -- 显式开启一个事务 |
|
delete from emp where ename=‘wang‘; |
|
select * from emp; -- 数据"wang"已删除 |
|
select * from emp; -- 数据"wang"未删除 |
|
commit; -- 提交事务 |
|
select * from emp; -- 数据"wang"已删除 |
|
select * from emp; -- 数据"wang"已删除 |
Session 1 | Session 2 |
begin; -- 显式开启一个事务 |
|
insert into emp values(‘wang‘, now(), 4000, 2); |
|
select * from emp; -- 新增了"wang"数据 |
|
insert into emp values(‘liu‘, now(), 90000, 3); |
|
select * from emp; -- 新增了"liu"数据 |
|
select * from emp; -- 未新增两条数据 | |
rollback; -- 回滚事务 | |
select * from emp; -- 未新增两条数据 |
|
select * from emp; -- 未新增两条数据 |
隔离问题:
隔离级别:
锁是一种使各种共享资源在被并发访问变得有序的机制,目的是为了保证数据的一致性。
根据加锁范围大致划分:
根据加锁功能大致划分:
共享锁/排他锁都只是行锁。
死锁发生在当两个事务均尝试获取对方已经持有的排他锁时。
在 innodb 中,select 不会对数据加锁,而 update/delete 会加行级别的独占锁。
当数据库的隔离级别为 Repeatable Read 或 Serializable 时,我们来看以下会发生死锁的并发事务场景。
表结构示例:
mysql> desc user; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | id | int | YES | MUL | NULL | | | age | int | YES | MUL | NULL | | | name | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.12 sec)
当 update 数据未作用于索引时,会发生表锁:
Session 1 | Session 2 |
begin; | begin; |
select * from user; | select * from user; |
update user set name=‘test1‘ where name=‘xiaoming‘; | |
(表)锁等待解除 | update user set name=‘test1‘ where name=‘xiaodan‘; |
死锁,事务被回滚 |
当 update 数据作用于索引时,会发生行锁:
Session 1 | Session 2 |
begin; | begin; |
select * from user; | select * from user; |
update user set name=‘test1‘ where id=1; | |
(行)锁等待解除 | update user set name=‘test1‘ where id=2; |
死锁,事务被回滚 |
当 InnoDB 检测到死锁时,会回滚其中一个事务,让另一个事务得以完成。
Session 1 | Session 2 |
begin; | begin; |
select * from user; | select * from user; |
update user set name=‘test2‘ where id=1; | |
(行)锁等待解除 | update user set name=‘test2‘ where id=1; |
死锁,事务被回滚 |
mysql> update user set name=‘test2‘ where id=1; -- Session 2 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
Session 1 | Session 2 |
begin; | begin; |
select * from user; | select * from user; |
update user set name=‘test3‘ where id=1; | |
commit; | update user set name=‘test4‘ where id=1; |
commit; |
在这个并发场景下,两个事务均能成功提交,而不会有死锁。
select * from emp; -- 未新增两条数据
原文:https://www.cnblogs.com/juno3550/p/14898926.html