1 什么是事务
什么是事务?
- 事务是数据库执行操作的最小逻辑单元
- 事务可以由一个SQL组成也可以由多个SQL组成
- 组成事务的SQL要么全执行成功要么全执行失败
START TRANSACTION / BEGIN
SELECT ...
UPDATE ...
INSERT ...
DELETE ...
COMMIT / ROLLBACK
事务开启后只能执行DML操作(执行DDL会执行COMMIT操作会把这个事务结束掉)
2 事务的特性
特征 |
说明 |
原子性(A) |
一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。 |
一致性(C) |
在事务开始之前和事务结束以后,数据库的完整性没有被破坏。 |
隔离性(I) |
事务的隔离性要求每个读写事务的对象与其它事务的操作对象能相互分离,即该事务提交前对其它事务都不可见。 |
持久性(D) |
事务一旦提交了,其结果就是永久性的,就算发生了宕机等事故,数据库也能将数据恢复。 |
3 并发带来的问题
3.1 脏读
一个事务读取了另一个事务未提交的数据
3.2 不可重复读
一个事务前后两次读取的同一数据不一致。
3.3 幻读
指一个事务两次查询的结果集记录数不一致。
4 事务的隔离性
INNODB的隔离级别
- 默认可重复读(SQL标准里面不能避免幻读,但是MySQL可以)
隔离级别 |
脏读 | 不可重复读 | 幻读 | 隔离性 | 并发性 |
顺序读(SERIALIZABLE) |
N | N | N | 最高 | 最低 |
可重复读(REPEATABLE READ) |
N | N | N | ||
读以提交(READ COMMITTED) |
N | Y | Y | ||
读未提交(READ UNCOMMITTED) | Y | Y | Y | 最低 | 最高 |
设置事务的隔离级别
SET [PERSIST|GLOBAL|SESSION]
TRANSACTION ISOLATION LEVEL
{
READ UNCOMMITTED
| READ COMMITTED
| REPEATABLE READ
| SERIALIZABLE
}
serializable 级别两个事务并发的情况
# 事务1
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
SHOW VARIABLES LIKE '%iso%';
BEGIN;
SELECT * FROM `employees` WHERE `emp_no` < '10003' ;
# ROLLBACK;
# COMMIT;
# 事务2
# 修改前一个事务里面查询出来的数据
BEGIN;
UPDATE `employees` SET `hire_date` = '1997-06-06' WHERE `emp_no` = '10002';
ROLLBACK;
事务1执行了rollback;后,事务2才能继续执行
repeatable read 级别两个事务并发的情况
# 事务1
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
SHOW VARIABLES LIKE '%iso%';
BEGIN;
SELECT * FROM `employees` WHERE `emp_no` < '10003' ;
# ROLLBACK ;
# 无法读取到第二个事务对数据的修改
# 如果还在这个事务内,这个时候是读取不到事务2修改的值的
SELECT * FROM `employees` WHERE `emp_no` < '10003' ;
# 事务2
# 修改前一个事务里面查询出来的数据
BEGIN;
UPDATE `employees` SET `hire_date` = '1997-06-06' WHERE `emp_no` = '10002';
COMMIT ;
read committed 级别两个事务并发的情况
# 事务1
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SHOW VARIABLES LIKE '%iso%';
# 能够查询出来,另一个事务的修改
BEGIN;
SELECT * FROM `employees` WHERE `emp_no` < '10003' ;
COMMIT;
# 事务2
# 修改前一个事务里面查询出来的数据
BEGIN;
UPDATE `employees` SET `hire_date` = '2007-06-06' WHERE `emp_no` = '10002';
COMMIT;
read uncommitted 级别两个事务并发的情况
# 事务1
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SHOW VARIABLES LIKE '%iso%';
# 能够查询出来,事务1还未提交的数据
BEGIN;
SELECT * FROM `employees` WHERE `emp_no` < '10003' ;
COMMIT;
# 事务2
# 修改前一个事务里面查询出来的数据
BEGIN;
UPDATE `employees` SET `hire_date` = '1996-06-06' WHERE `emp_no` = '10002';
# ROLLBACK第一个事务的查询结果也会跟着变化
ROLLBACK;
repeatable read 级别下的阻塞
# 事务1
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SHOW VARIABLES LIKE '%iso%';
BEGIN;
UPDATE salaries SET salary = salary + 1 WHERE to_date BETWEEN '2001-01-01' AND '2001-01-31' ;
COMMIT;
# 事务2
BEGIN;
UPDATE salaries SET salary = salary - 1 WHERE to_date BETWEEN '2001-01-01' AND '2001-01-31' ;
INNODB中的锁(一个事务不能对另一个事务正在读取或修改的数据进行修改)
- 查询需要对资源加共享锁(S)(被加锁的对象只能被持有锁的事务读取但不能修改,其他事务无法对该对象进行修改,但也可以对该对象加共享锁进行读取)
- 数据修改需要对资源加排它锁(ⅹ)(被加了排他锁的对象只能被持有锁的事务读取和修改,其他事务无法读取修改被加了排他锁的对象)
排它锁 |
共享锁 |
|
排它锁 |
不兼容 |
不兼容 |
共享锁 | 不兼容 | 兼容 |
5 阻塞和死锁
5.1 阻塞
什么是阻塞?
- 由于不同锁之间的兼容关系,造成的一事务需要等待另一个事务释放其所占用的资源的现象
如何发现阻塞?
# 事务1
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SHOW VARIABLES LIKE '%iso%';
# 当前连接的连接id 53
SELECT CONNECTION_ID();
BEGIN;
UPDATE salaries SET salary = salary + 1 WHERE salary >= 150000;
# ROLLBACK;
# 事务2
# 当前连接的连接id 51
SELECT CONNECTION_ID();
BEGIN;
UPDATE salaries SET salary = salary - 1 WHERE salary >= 150000;
# ROLLBACK;
SELECT
waiting_pid AS 'blocked pid',
waiting_query AS 'blocked SQL',
blocking_pid AS 'running pid',
blocking_query AS 'running SQL',
wait_age AS 'blocked time',
sql_kill_blocking_query AS 'info'
FROM
sys.`innodb_lock_waits`
WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(wait_started)) > 30 ;
# kill 53;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
# 查询当前会话等待事务锁超时时间
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout' ;
# 修改当前会话等待事务锁超时时间:
SET innodb_lock_wait_timeout = 10000 ;
如何处理阻塞?
- 终止占用资源的事务
- 优化占用资源事务的SQL,使其尽快释放资源
5.2 死锁
什么是死锁?
- 并行执行的多个事务相互之间占有了对方所需要的资源
如何发现死锁?
- set global innodb_print_all_deadlocks = on ;
# 窗口1
# mysql -u root -p123456 employees
# set global innodb_print_all_deadlocks = on ;
# 退出在登陆,使设置生效
BEGIN;
# 第一个执行
UPDATE `employees` SET `gender` = 'F' WHERE `emp_no` = 10001;
# 第三个执行
UPDATE `employees` SET `gender` = 'F' WHERE `emp_no` = 10002;
# 窗口2
BEGIN;
# 第二个执行
UPDATE `employees` SET `gender` = 'F' WHERE `emp_no` = 10002;
# 第四个执行
UPDATE `employees` SET `gender` = 'F' WHERE `emp_no` = 10001;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
# log_error就是错误日志的位置
SHOW VARIABLES LIKE '%error%';
TRANSACTION 89900, ACTIVE 30 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 63, OS thread handle 26344, query id 7332 localhost ::1 root updating
UPDATE `employees` SET `gender` = 'F' WHERE `emp_no` = 10002
RECORD LOCKS space id 408 page no 5 n bits 408 index PRIMARY of table `employees`.`employees` trx id 89900 lock_mode X locks rec but not gap waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 8; compact format; info bits 0
0: len 4; hex 80002712; asc ' ;;
1: len 6; hex 000000015ef8; asc ^ ;;
2: len 7; hex 0200000130087c; asc 0 |;;
3: len 3; hex 8f58c2; asc X ;;
4: len 7; hex 42657a616c656c; asc Bezalel;;
5: len 6; hex 53696d6d656c; asc Simmel;;
6: len 1; hex 02; asc ;;
7: len 3; hex 8fc856; asc V;;
TRANSACTION 89901, ACTIVE 15 sec starting index read
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 64, OS thread handle 24768, query id 7333 localhost ::1 root updating
UPDATE `employees` SET `gender` = 'F' WHERE `emp_no` = 10001
RECORD LOCKS space id 408 page no 5 n bits 408 index PRIMARY of table `employees`.`employees` trx id 89901 lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 8; compact format; info bits 0
0: len 4; hex 80002712; asc ' ;;
1: len 6; hex 000000015ef8; asc ^ ;;
2: len 7; hex 0200000130087c; asc 0 |;;
3: len 3; hex 8f58c2; asc X ;;
4: len 7; hex 42657a616c656c; asc Bezalel;;
5: len 6; hex 53696d6d656c; asc Simmel;;
6: len 1; hex 02; asc ;;
7: len 3; hex 8fc856; asc V;;
RECORD LOCKS space id 408 page no 5 n bits 408 index PRIMARY of table `employees`.`employees` trx id 89901 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 8; compact format; info bits 0
0: len 4; hex 80002711; asc ' ;;
1: len 6; hex 000000015f2c; asc _,;;
2: len 7; hex 010000009d284a; asc (J;;
3: len 3; hex 8f4322; asc C";;
4: len 6; hex 47656f726769; asc Georgi;;
5: len 7; hex 466163656c6c6f; asc Facello;;
6: len 1; hex 02; asc ;;
7: len 3; hex 8fa022; asc ";;
如何处理死锁?
- 数据库自行回滚占用资源少的事务
- 并发事务按相同顺序占有资源 (1->2->3->4 改为1->4->2->3)