初遇事务
在MySQL5.5之前, 默认的存储引擎是MyISAM, 在5.5版本之后默认存储引擎是InnoDB, 而这两个存储引擎的最大区别就在于InnoDB是支持事务的, 这也是InnoDB取代MyISAM的重要原因. 什么是事务呢? 事务的英文是transaction, 也就是进行一次处理的基本单元, 要么完全执行, 要么全都不执行.
事务的特性: ACID
深入理解事务, 就需要知道事务的四个特性, 那就是ACID:
- A, 也就会说原子性(Aomicity). 原子的概念就是不可分割, 可以理解为组成物质的基本单元, 也就是数据处理操作的基本单位.
- C, 就是一致性(Consistency), 一致性指的是数据库在进行事务操作后, 会有原来的一致状态, 编程另一种一致状态. 也就是事务提交或回滚后, 数据库的完成性约束不能破坏.
- I, 就是隔离性(Isolocation), 指的是每个事务都是彼此独立的, 不会受到其他事务的执行影响. 简单说就是一个事务在提交之前, 对其他事务都是不可见的.
- D, 就是持久性(Durability), 事务提交之后对数据的修改是持久性的, 及时在系统出故障的情况下(崩溃或存储介质发生故障), 数据的修改依然是有效地. 因为当事务完成之后, 数据库的日志就会被更新, 这个通过日志, 可以让系统恢复到最后一次成功的更新状态.
这四个特性中, 原子性是基础, 隔离性是手段, 一致性是约束条件, 持久性是目的. 原子性和隔离性好理解, 至于一致性相对难, 笔者开始总是不知道这个一致性所说的状态是什么意思, 直到现在看了这个文档, 才发现.
一致性本身是由具体的业务定义的, 任何写入数据库中的数据都需要满足我们事先定义的约束规则, 到这里就可以理解一致性具体的含义了, 一致性其实说的是不违反约束规则, 比如主键约束, 唯一约束, 不为NULL约束等等.
另一个持久性, 持久性是通过事务日志来保证的. 日志包括了回滚日志和重做日志. 通过事务对数据进行修改的时候, 首先会将数据库的变化信息记录到重做日志中, 然后再对数据库中对应的行进行修改, 这样做的好处是, 即使数据库崩溃, 数据库重启后也能找到没有更新到数据库系统中的重做日志, 重新执行, 从而使事务具有持久性.
事务的控制
Oracle是支持事务的, 而在MySQL中, 需要选择合适的存储引擎才可以支持事务, 使用MySQL, 可以使用SHOW ENGINES
来查看当前MySQL支持的存储引擎, 以及该引擎是否支持事务.
事务常用的控制语句:
- START TRANSACTION或者BEGIN, 作用是显式开启一个事务.
- COMMIT: 提交事务, 提交事务后, 对数据库的修改是永久性的
- ROLLBACK或者ROLLBACK TO [SAVEPOINT], 回滚事务, 意思是撤销正在进行的所有没有提交的修改, 或者将事务回滚到某个保存点.
- SAVEPOINT: 在事务中创建保存点, 方便后续针对保存点进行回滚, 一个事务中可以存在多个保存点.
- RELEASE SAVEPOINT: 删除某个保存点
- SET TRANSACTION, 设置事务的隔离级别.
使用事务有两种方式, 一种是隐式事务, 一种是显示事务. 隐式事务其实就是自动提交. Oracle默认不自动提交, 需要手写COMMIT命令, 而MySQL默认自动提交, 可以使用MySQL的命令来配置参数关闭自动提交:set autocommit = 0;是关闭自动提交, set autocommit = 1;是开启自动提交
.
MySQL的默认状态下:
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
BEGIN;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
现在执行完的情况下, 数据库中默认保存了一条.
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
现在数据库中应该是两条记录, 因为默认会隐式提交, 也就是在执行第一个INSERT INTO test SELECT '张飞';
的时候, 执行完成就会提交, 只有在执行第二个的时候会产生主键冲突而回滚.
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
SET @@completion_type = 1;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
结果是一条数据, 很明显, 原因是SET @@completion_type = 1;
, 这个参数有3种可能:
- completion=0, 这是默认情况, 也就是执行COMMIT会提交事务, 执行下一个事务前, 需要使用START TRANSACTION或者BEGIN来开启.
- completion=1, 这种情况下, 提交事务后, 相当于执行了COMMIT AND CHAIN, 也就是开启一个链式事务, 即当我们提交事务之后会开启一个相同隔离级别的事务.
- completion=2, 这种情况下COMMIT=COMMIT AND RELEASE, 也就是当提交之后, 会自动与服务器断开连接.
这里使用了completion=1, 就是提交之后, 相当于在下一行写了START TRANSACTION 或 BEGIN, 这是插入两次张飞会被认为是在同一个事务之内的操作, 所以回滚之后, 数据库中就只有一条关羽的数据.
这个是事务的基本认识, 当然事务的隔离级别也很重要, 下一个随笔说.