事务简言之就是一组SQL执行要么所有成功,要么所有失败。MYSQL的事务在存储引擎层实现。mysql
事务都有ACID特性:web
原子性(Atomicity):一个事务必须被视为一个不可分割的单元;sql
一致性(Consistency):数据库老是从一种状态切换到另外一种状态;数据库
隔离性(Isolation):一般来讲,事务在提交前对于其余事务不可见;并发
持久性(Durablity):一旦事务提交,所作修改永久保存数据库;app
事务最经常使用的例子就是银行转帐。假设polo需给lynn转帐1000元,以下步骤:ui
确认polo帐户余额高于1000元;spa
从polo的帐户余额减去1000元;code
将lynn的帐户余额增长1000元;orm
SQL语句以下:
mysql> BEGIN; mysql> SELECT balance FROM bank_account WHERE uid=10001; mysql> UPDATE bank_account SET balance=balance-1000 WHERE uid=10001; mysql> UPDATE bank_account SET balance=balance+1000 WHERE uid=10002; mysql> COMMIT;
注:mysql启动事务可以使用BEGIN或者START TRANSACTION;
上述三个步骤执行在一个事务中就可以保证数据的完整性,要么所有成功,要么所有失败。
MYSQL提供两种事务型引擎:Innodb和NDBCluster。默认采用自动提交模式,执行一条语句自动COMMIT。经过AUTOCOMMIT变量可启用或者禁用自动提交模式:
mysql> SHOW VARIABLES LIKE "AUTOCOMMIT"; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> SET AUTOCOMMIT=1
AUTOCOMMIT=1表示开启默认提交,0表示关闭默认提交须要手动提交。
事务隔离性的解释:一般状况下,事务在提交以前对于其余事务不可见。
数据库有四种隔离级别,固然MYSQL也是如此。分别为:
READ UNCOMMITED(未提交读)
READ COMMITED(已提交读)
EPEATABLE READ(可重复读)
SEAIALIZABLE(可串行化)
本人理解 : 隔离级别就是决定一个事务的修改另外一个事务什么状况下可见。
书本解释 : 隔离级别都规定了一个事务中所作修改,哪些在事务内和事务间是可见的。
上面两段理解的区别在因而否存在事务内可见性的规定。我在各个级别彷佛没有看到
下面开始说明MYSQL的四种隔离级别,先准备一张学生表:
mysql> CREATE TABLE `student` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
只有id(主键自增)与name字段
READ UNCOMMITTED(未提交读)
事务中修改没有提交对其余事务也是可见的,俗称脏读。很是不建议使用。
实例演示
客户端A和B设置隔离级别为未提交读
mysql> SET SESSION TX_ISOLATION='READ-UNCOMMITTED';
客户端A与B开启事务并查询student
mysql> BEGIN; mysql> SELECT * FROM student; Empty set (0.00 sec)
客户端A和B都是空数据
客服端B插入一条新的数据
mysql> INSERT INTO student(name) VALUES("polo"); Query OK, 1 row affected (0.00 sec)
此时事务未提交,客服端A查看student表
$ SELECT * FROM student; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 row in set (0.00 sec)
客户端A看到B未提交的修改
客户端B执行回滚操做
mysql> ROLLBACK
成功以后,客户端A查看student表
mysql> SELECT * FROM student; Empty set (0.00 sec)
客户端A查看数据为空
以上能够看出未提交读隔离级别的危险性,对于一个没有提交事务所作修改对另外一个事务是可见状态,容易形成脏读。非特殊状况不得使用此级别
READ COMMITTED(提交读)
多数数据库系统默认为此级别(MYSQL不是)。已提交读级别即为一个事务只能已提交事务所作的修改,也就解决了未提交读的问题,即脏读的问题。
实例演示
客户端A和B设置隔离级别为已提交读
mysql> SET SESSION TX_ISOLATION='READ-COMMITTED';
客户端A与B开启事务并查询student
mysql> BEGIN; mysql> SELECT * FROM student; Empty set (0.00 sec)
客户端A和B都为空
客服端B插入一条新的数据,不提交
mysql> INSERT INTO student (name) VALUES('polo');
客户端A查看student
mysql> SELECT * FROM student; Empty set (0.00 sec)
注意这里与上面不一样了,在客户端B没有提交事务状况下无数据
下面客户端B提交事务
mysql> COMMIT;
客户端A再次查看student表。
mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 row in set (0.00 sec)
成功读取到客户
从上面的示例能够看出,提交读没有了未提交读的问题,但咱们能够看到在客户端A的一个事务中执行两次一样的SELECT语句获得不一样结果,所以已提交读又被称为不可重复读。一样筛选条件可能获得不一样的结果。
REPEATABLE READ(可重复读)
如其名也,解决已提交读不可重复读取的问题。
示例演示
客户端A和B设置隔离级别为可重复读
mysql> SET SESSION tx_isolation='REPEATABLE-READ'
客户端A与B开启事务并查看
mysql> BEGIN; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 rows in set (0.00 sec)
客服端B更新polo为jeff,并提交事务
mysql> UPDATE student SET name='jeff' WHERE id=1; mysql> COMMIT
客户端A查看student表
mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | jeff | +----+------+ 1 rows in set (0.00 sec)
注意客户端A查看数据未变,没有不可重复读问题
客户端A提交事务,并查看student表
mysql> COMMIT; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 rows in set (0.00 sec)
上面实例可知,可重复读两次读取内容同样。数据库这级别并无解决幻读的问题。可是MYSQL在可重复读基础上增长了MVCC机制解决了此问题,实例没法演示幻读效果。
那什么是幻读?首先,可重复读锁定范围为当前查询到的内容,如执行
mysql> SELECT * FROM student WHERE id>=1
锁定的即id>=1查到的行,为行级锁。如另外一事务执行并默认提交如下语句
mysql> INSERT INTO student (name) VALUES ('peter');
新增的这行并无被锁定,此时读取student
mysql> SELECT * FROM student WHERE id>=1; +----+---------+ | id | name | +----+---------+ | 1 | polo | | 2 | peter | +----+---------+ 2 rows in set (0.00 sec)
便出现了幻读
除了使用MYSQL的MVCC机制,还可使用可串行化隔离级别解决此问题。
SEAIALIZABLE(可串行化)
可串行化是最高隔离级别,强制事务串行执行。执行串行了也就解决了一切的问题,这个级别只有在对数据一致性要求很是严格且没用并发的状况下使用
实例演示
客户端A和B设置隔离级别为可串行化
mysql> SET SESSION tx_isolation='SERIALIZABLE';
客户端A执行查询
mysql> SELECT * FROM student WHERE id<4; +----+---------+ | id | name | +----+---------+ | 1 | polo | | 2 | peter | +----+---------+ 2 rows in set (0.00 sec)
客户端B执行新增
mysql> INSERT INTO student (name) VALUES('tiger');
好的!效果出现了,此时咱们会发现INSERT语句被阻塞执行,缘由就是A执行了查询表student同时知足id<4,已被锁定。若是查询表student条件为id<3,则新增语句可正常执行。