事务就是一组原子性的 SQL 查询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。
我们转账的一般都是:从你的账户扣除 100 元,然后再到其他人的账户中增加 100 元。但是如果在这中间,因为某些原因(网络问题),导致在你的账户中扣除了,但是没有在你朋友的账户中增加,那我们是不是直接损失了一百元,是不是找银行了。
所以,一般这种情况,就可以把这两个步骤放到一个事务里面。要么全部成功,自己的账户扣除一百,朋友的账户新增一百;要么全部失败,已经进行的操作退回去,恢复到最开始的状态,我的账户没有扣100,朋友的账户也没有加100。
注意: 事务是由存储引擎实现的,以下没有特殊说明默认innodb
mysql> start transaction;#手动开启事务
mysql> update t21 set b = 77 where b = 7;
Query OK, 3 rows affected (0.00 sec)
mysql> commit;#commit之后即可改变底层数据库数据
mysql> select * from t_user;
| id | a | b |
+----+---+----+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 33 |
| 4 | 4 | 4 |
| 6 | 5 | 5 |
| 7 | 7 | 7 |
| 8 | 8 | 8 |
| 9 | 8 | 7 |
| 10 | 2 | 7 |
+----+---+----+
9 rows in set (0.00 sec)
mysql> start transaction;
mysql> insert into t21(a,b) values(3,3);
mysql> rollback;
mysql> select * from t_user;
| id | a | b |
+----+---+----+
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 33 |
| 4 | 4 | 4 |
| 6 | 5 | 5 |
| 7 | 7 | 7 |
| 8 | 8 | 8 |
| 9 | 8 | 7 |
| 10 | 2 | 7 |
+----+---+----+
9 rows in set (0.00 sec)
在mysql中默认支持自动提交,在自动提交模式下,如果没有start transaction显式地开始一个事务,那么每个sql语句都会被当做一个事务执行提交操作。
原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做;如果事务中一个sql语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。
mysql的原子性是通过undo日志实现的,接下来我们简单说下undo日志。
Undo log 是逻辑日志,将数据库逻辑地恢复到原来的样子,所有修改都被逻辑的取消了。
也就是如果是 insert 操作,其对应的回滚操作就是 delete;
如果是 delete,则对应的回滚操作是 insert;
如果是 update,则对应的回滚操作是一个反向的 update 操作。
持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。
事务的持久性是通过redo日志实现的,我们下面简单介绍下redo日志。
与undo日志相反,redo日志是对新数据的备份。在事务提交前,只要将redo日志进行持久化,不需要将数据持久化。当系统崩溃时,只要根据redo日志的内容,将数据恢复到最新的状态。redo日志具体内容可以参考:mysql日志文件总结,这里我就不再说了
与原子性、持久性侧重于研究事务本身不同,隔离性研究的是不同事务之间的相互影响。隔离性是指,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。严格的隔离性,对应了事务隔离级别中的Serializable (可串行化),但实际应用中出于性能方面的考虑很少会使用可串行化。
锁机制的基本原理可以概括为:事务在修改数据之前,需要先获得相应的锁;获得锁之后,事务便可以修改数据;该事务操作期间,这部分数据是锁定的,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁。
行锁与表锁
按照粒度,锁可以分为表锁、行锁以及其他位于二者之间的锁。表锁在操作数据时会锁定整张表,并发性能较差;行锁则只锁定需要操作的数据,并发性能好。但是由于加锁本身需要消耗资源(获得锁、检查锁、释放锁等都需要消耗资源),因此在锁定数据较多情况下使用表锁可以节省大量资源。MySQL中不同的存储引擎支持的锁是不一样的,例如MyIsam只支持表锁,而InnoDB同时支持表锁和行锁,且出于性能考虑,绝大多数情况下使用的都是行锁。
如何查看锁信息
有多种方法可以查看InnoDB中锁的情况,例如:
select * from information_schema.innodb_locks; #锁的概况
show engine innodb status; #InnoDB整体状态,其中包括锁的情况
下面举个例子:
# 第一个客户端
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update t21 set b = 77 where b = 7;
Query OK, 3 rows affected (0.00 sec)
Rows matched: 3 Changed: 3 Warnings: 0
# 第二个客户端
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update t21 set b = 77 where b = 7;
# 查询锁的情况
mysql> select * from information_schema.innodb_locks;
+--------------+-------------+-----------+-----------+--------------+------------+------------+-----------+----------+-----------+
| lock_id | lock_trx_id | lock_mode | lock_type | lock_table | lock_index | lock_space | lock_page | lock_rec | lock_data |
+--------------+-------------+-----------+-----------+--------------+------------+------------+-----------+----------+-----------+
| 33048:41:3:2 | 33048 | X | RECORD | `muke`.`t21` | PRIMARY | 41 | 3 | 2 | 1 |
| 33047:41:3:2 | 33047 | X | RECORD | `muke`.`t21` | PRIMARY | 41 | 3 | 2 | 1 |
+--------------+-------------+-----------+-----------+--------------+------------+------------+-----------+----------+-----------+
2 rows in set, 1 warning (0.00 sec)
通过上述命令可以查看事务33048和33047占用锁的情况;其中lock_type为RECORD,代表锁为行锁(记录锁);lock_mode为X,代表排它锁(写锁)。
除了排它锁(写锁)之外,MySQL中还有共享锁(读锁)的概念
一致性是指事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。数据库的完整性约束包括但不限于:实体完整性(如行的主键存在且唯一)、列完整性(如字段的类型、大小、长度要符合要求)、外键约束、用户自定义完整性(如转账前后,两个账户余额的和应该不变)
可以说,一致性是事务追求的最终目标:前面提到的原子性、持久性和隔离性,都是为了保证数据库状态的一致性。此外,除了数据库层面的保障,一致性的实现也需要应用层面进行保障。
实现一致性的措施包括:
mysql日志可以看下这个地址:mysql日志文件总结
mysql事务隔离级别可以看下这个地址: mysql事务-----四种隔离级别
书籍是在时代波涛中航行的思想之船,它小心翼翼地把珍贵的货物运送给一代又一代。 ----培根