MySQL的事务与事务隔离

MySQL中自从引入InnoDB引擎后,在MySQL中就支持事务,事务就是一组原子性的查询语句,也即将多个查询当作一个独立的工作单元,平时通过提交工作单元来完成在事务中的相应的查询或修改,在能支持事务的数据库中必须要满足ACID测试,即事务的四个特性:

A:Atomicity,原子性(都执行或者都不执行)

C:Consistency,一致性(从一个一致性状态转到另外一个一致性状态)

I:Isolaction,隔离性(一个事务的所有修改操作在提交前对其他事务时不可见的)

D: Durability,持久性(旦事务得到提交,其所做的修改会永久有效)

而在早期默认的引擎MyISAM是不支持事务的,所以如果是在MyISAM表中是不支持事物的,想要知道数据中具体支持哪些表引擎可以通过”SHOW ENGINES;”查看在所使用版本中MySQL所支持的所有引擎。在这里先创建一张表transaction_tbl用于测试:

CREATE TABLE transaction_tbl (id int(4)) ENGINE=InnoDB; 
INSERT INTO transaction_tbl VALUE (1);
INSERT INTO transaction_tbl VALUE (2);
INSERT INTO transaction_tbl VALUE (3);
INSERT INTO transaction_tbl VALUE (4);
INSERT INTO transaction_tbl VALUE (5);

在正常的使用过程中需要关闭自动提交的功能默认系统下是开启的

mysql> SHOW GLOBAL VARIABLES LIKE ‘autocommit‘;                       
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

在使用事务之前需要关闭,在平时使用事务之前需要先检查是否有开启autocommit,当然如果对事务的依赖比较大建议可以永久关闭全局的自动提交,但是在平时使用的过程中只要在使用时关闭自动提交,而使用手动启动事务,这样在事务中需要回滚时才能根据相关的事务隔离级别得到想要的效果,可以在使用事务时关闭autocommit,在所有的事务结束后再开启autocommit

mysql> SET GLOBAL AUTOCOMMIT=off;#当然这里也可以使用布尔值的0和1
Query OK, 0 rows affected (0.00 sec)

其中事务的控制语句也很简单,如下:

BEGIN; 或 START TRANSACTION;
显式地开启一个事务

COMMIT;
提交事务即结束事务,并使已对数据库进行的所有修改为持久性的

ROLLBACK;
事务回滚,会结束用户的事务并撤销正在进行的所有未提交的修改

SAVEPOINT identifier;
允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT

RELEASE SAVEPOINT identifier;
删除一个事务的保存点,当没有指定的保存点时,执行该SQL语句会报异常

ROLLBACK TO identifier;
把事务回滚到标记点

而在事务中的隔离级别不同,则事物的安全性就不同,但是事物得安全性越高,并发性越低,当然需要根据实际情况选择,在MySQL中事务的隔离级别有四种,安全级分别由低至高:

READ UNCOMMITTEND:读未提交

READ COMMITTEND:读提交

REPEATABLE READ :可重读

SERIALIZABLE:可串行化

查看当前使用的默认的事务隔离级别:

mysql> SHOW GLOBAL VARIABLES LIKE ‘tx_isolation‘;
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)

而在使用四个隔离级别中,所带来效果都是不相同的,此时测试需要开启2个session更为直观,在这里就用A、B来代表两个session中开启事务A、B:

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.01 sec)

一、READ UNCOMMITTEND:读未提交,顾名思义即所有的事务都可以读取到其他事务中未提交的内容,该隔离模式在平时一般都不使用,因为使用READ UNCOMMITTEND会带来脏读问题,下面就用transaction_tbl举一个简单例子说明下:

mysql> SET GLOBAL tx_isolation=‘READ-UNCOMMITTED‘;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW GLOBAL VARIABLES LIKE ‘tx_isolation‘;
+---------------+------------------+
| Variable_name | Value            |
+---------------+------------------+
| tx_isolation  | READ-UNCOMMITTED |
+---------------+------------------+
1 row in set (0.00 sec)

事务A中做了操作,但不提交:

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE transaction_tbl SET id = ‘6‘ WHERE id=‘1‘;
Query OK, 1 row affected (0.02 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  6 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

但是此时事务B是可以看见事务A中数据

事务B:

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  6 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.01 sec)

此时事务A回滚:

mysql> ROLLBACK;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

事务B中查出来的是事务A中未提交的数据:

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

这样就是由READ UNCOMMITTEND所带来的脏读,一般数据库生产环境中都不用这种事务隔离级别。

二、READ COMMITTEND,读提交,同理根据名字可知事物的隔离级别会比读未提交高一个事务隔离级别更高,从而解决了脏读的问题,这也是大多数数据库中所用的默认事务隔离级别,但并不是MySQL的默认事务隔离级别,该事务隔离级别虽然解决了脏读问题,但是带来新的问题是不可重读,如果此时恰好有2个事务对相同的一张表做操作时,在一个事务中执行相同的查询时会查出不同的结果:

mysql> SET GLOBAL tx_isolation=‘READ-COMMITTED‘;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL VARIABLES LIKE ‘tx_isolation‘;       
+---------------+----------------+
| Variable_name | Value          |
+---------------+----------------+
| tx_isolation  | READ-COMMITTED |
+---------------+----------------+
1 row in set (0.00 sec)

在事务A中:

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

此时事务B中也开启事务做了一个操作:

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> UPDATE transaction_tbl SET id = ‘6‘ WHERE id=‘1‘;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  6 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.01 sec)

此时看下事务A中的查询:

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)
#但此时事务B中COMMIT提交后,事务A中
mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  6 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

这样就是由READ COMMITTEND所带来的不可重读问题,所以在一般数据库生产环境中也不建议采用这种事务隔离级别。

三、REPEATABLE READ,可重读,同理该事务隔离级别解决了不可重读的问题,在REPEATABLE READ中使用MVCC(多版本并发控制)在每个事务启动时,InnoDB会为每个启动的事务提供一个当下时刻的快照,为实现此功能,InnoDB会为每个表提供两隐藏的字段,一个用于保存行的创建时间,一个用于保存行的失效时间,里面存储的系统版本号,MVCC旨在READ COMMITTEND和REPEATABLE READ两个事务隔离中生效,但是在REPEATABLE READ同以上事务隔离级别一样,在解决了不可重读的问题同时也带来新的问题幻读,此时恰好有2个事务对相同的一张表做操作时,在一个事务中提交之前其中一个事务在另一事务提交前后查询的结果不一样:

mysql> SET GLOBAL tx_isolation=‘REPEATABLE-READ‘; 
Query OK, 0 rows affected (0.01 sec)

mysql> SHOW GLOBAL VARIABLES LIKE ‘tx_isolation‘;
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.01 sec)

事务A:

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  6 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.02 sec)

事务B中做相关操作并提交:

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  6 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

mysql> UPDATE transaction_tbl SET id = ‘1‘ WHERE id=‘6‘;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.01 sec)

mysql> COMMIT;
Query OK, 0 rows affected (0.02 sec)

此时再来看下事务A中:

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.02 sec)

mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  6 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

这样就是REPEATABLE READ带来的幻读问题,当然在实际生产中这么恰好的事比较少,所以一般都做为MySQL的默认事务隔离级别。

四、SERIALIZABLE,可串行化,强事务排序也是最高级别的事务隔离,所有的事务都有使用共享锁,这样就解决相应的幻读问题,但是因为共享锁的原因从而使写入的性能降低,从而降低了MySQL的性能:

mysql> SET GLOBAL tx_isolation=‘SERIALIZABLE‘;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL VARIABLES LIKE ‘tx_isolation‘;
+---------------+--------------+
| Variable_name | Value        |
+---------------+--------------+
| tx_isolation  | SERIALIZABLE |
+---------------+--------------+
1 row in set (0.00 sec)

在事务A中插入一条数据不提交:

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO transaction_tbl VALUE (‘7‘);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
|  7 |
+----+
6 rows in set (0.00 sec)

此时在事务B中,在事务A未提交前是无法写入提交的

mysql> SHOW GLOBAL VARIABLES LIKE ‘tx_isolation‘;
+---------------+--------------+
| Variable_name | Value        |
+---------------+--------------+
| tx_isolation  | SERIALIZABLE |
+---------------+--------------+
1 row in set (0.00 sec)

mysql> BEGIN;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM transaction_tbl;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
+----+
5 rows in set (0.00 sec)

mysql> UPDATE transaction_tbl SET id = ‘6‘ WHERE id=‘1‘;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

只有在事务A中COMMIT提交后才能在事务B中提交,但是在此需要注意的是在MySQL 5.7开始经过大量的代码重构优化后事务A、B在使用前三种隔离级别都是默认为REPEATABLE READ的事务隔离级别,而在平时利用事务时多用于存储过程中大量使用,而不同数据库(包括MySQL的不同版本都存在差异),语法差别很大,移植困难,换了数据库,需要重新编写,所以把过多业务逻辑写在存储过程不好维护,不利于分层管理,容易混乱,一般存储过程适用于个别对性能要求较高的业务,其它的必要性不是很大,在平时使用需要根据实际情况而定。

时间: 2024-10-22 15:04:47

MySQL的事务与事务隔离的相关文章

MySQL InnoDB中的事务隔离级别和锁的关系

前言: 我们都知道事务的几种性质,数据库为了维护这些性质,尤其是一致性和隔离性,一般使用加锁这种方式.同时数据库又是个高并发的应用,同一时间会有大量的并发访问,如果加锁过度,会极大的降低并发处理能力.所以对于加锁的处理,可以说就是数据库对于事务处理的精髓所在.这里通过分析MySQL中InnoDB引擎的加锁机制,来抛砖引玉,让读者更好的理解,在事务处理中数据库到底做了什么. 一.一次封锁or两段锁因为有大量的并发访问,为了预防死锁,一般应用中推荐使用一次封锁法,就是在方法的开始阶段,已经预先知道会

MySQL事务四个隔离级别

MySQL事务隔离级别详解             MySQL数据结构SQL SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的.低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销. Read Uncommitted (读取未提交的内容)  在该隔离级别,所有事务都可以看到其他未提交事务的执行结果.本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少.读取未提交的数据,也被称之为脏读(Dirty Read).Read Commit

MySQL/MariaDB中的事务和事务隔离级别

本文目录:1.事务特性2.事务分类 2.1 扁平事务 2.2 带保存点的扁平事务 2.3 链式事务 2.4 嵌套事务 2.5 分布式事务3.事务控制语句4.显式事务的次数统计5.一致性非锁定读(快照查询)6.一致性锁定读7.事务隔离级别 7.1 设置和查看事务隔离级别 7.2 read uncommitted 7.3 read committed 7.4 repeatable read 7.5 serializable 1.事务特性 事务具有ACID特性:原子性(A,atomicity).一致性

MySQL数据库引擎、事务隔离级别、锁

MySQL数据库引擎.事务隔离级别.锁 数据库引擎InnoDB和MyISAM有什么区别 大体区别为: MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持.MyISAM类型的表强调的是性能,其执行效率比InnoDB类型更快,但是不支持事务,而InnoDB提供事务支持以及外键等高级数据库功能. 具体实现的区别: InnoDB不支持FULLTEXT类型的索引 InnoDB中不保存表的具体行数,也就是说,执行查询SQL时,InnoDB要扫描一遍整个表来计算有多少行,而MyISAM只要简单的

重新理解mysql的锁、事务隔离级别及事务传播行为

数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行.ACID,是指在可靠数据库管理系统(DBMS)中,事务(Transaction)所应该具有的四个特性:原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持久性(Durability). 原子性原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生.如,A向B转钱,在事务中的扣款和加款两条语句,要么

深入解析Mysql中事务的四大隔离级别及其所解决的读现象

本文详细介绍四种事务隔离级别,并通过举例的方式说明不同的级别能解决什么样的读现象.并且介绍了在关系型数据库中不同的隔离级别的实现原理. 在DBMS中,事务保证了一个操作序列可以全部都执行或者全部都不执行(原子性),从一个状态转变到另外一个状态(一致性).由于事务满足久性.所以一旦事务被提交之后,数据就能够被持久化下来,又因为事务是满足隔离性的,所以,当多个事务同时处理同一个数据的时候,多个事务直接是互不影响的,所以,在多个事务并发操作的过程中,如果控制不好隔离级别,就有可能产生脏读.不可重复读.

MySql数据库并发和事务资料汇总

InnoDB多版本并发控制-MVCC http://my.oschina.net/xinxingegeya/blog/208821 MySql并发控制 http://my.oschina.net/xinxingegeya/blog/215417 MySQL之事务 http://my.oschina.net/xinxingegeya/blog/215419 MySql命令行控制事务 http://my.oschina.net/xinxingegeya/blog/296459 MySQL事务隔离级别

事务并发之隔离级别

事务 事务是作为单个逻辑工作单元执行的一系列操作.一个逻辑工作单元必须有四个属性,称为原子性.一致性.隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务. 事务并发 数据库是多个用户(事务)共享的,当多个用户同时访问数据时,那么在这种情况下就叫做并发. 事务并发下可能出现的问题 更新丢失 两个事务都同时更新一行数据,一个事务对数据的更新把另一个事务对数据的更新覆盖了.这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来. 脏读 一个事务读取到了另一个事务未提交的数据操作结果.

事务库事务隔离级别

为了快速同步数据的需要,我分段执行了两次python脚本,即开启了两个进程同步数据,结果服务器不时报出数据库死锁异常,通过排查代码和数据库日志发现,是由长事务并发引起的.代码中有入账和出账两个方法,里面涉及操作较多,都为其加了事务,抛出异常时可自动回滚,采用数据库(mysql)默认的隔离级别(Repeatable read).提到并发,一般就会想到用同步代码块的方法的处理,但是由于项目是分布式的,共用一个主库,单单在代码加锁是不能保证数据的准确的,那就只能在数据库层面去考虑加锁了.由于数据量暂时