mysql事务的含义是什么
时间:2024-10-26 21:19 文章来源于网友投稿,仅供参考!
什么是事务?数据库中的事务是指对数据库执行一批操作,在同一个事务当中,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况。 事务是一个原子操作。是一个最小执行单元。可以由一个或多个SQL语句组成 在同一个事务当中,所有的SQL语句都成功执行时,整 个事务成功,有一个SQL语句执行失败,整个事务都执行失败。 举个例子: 比如A用户给B用户转账100操作,过程如下: 从A账户扣100 给B账户加100 如果在事务的支持下,上面最终只有2种结果: 操作成功:A账户减少100;B账户增加100 操作失败:A、B两个账户都没有发生变化 如果没有事务的支持,可能出现错:A账户减少了100,此时系统挂了,导致B账户没有加上100,而A账户凭空少了100。 事务的几个特性(ACID) -重点 |
| 隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 |
|---|---|---|---|
| READ-UNCOMMITTED | 有 | 有 | 有 |
| READ-COMMITTED | 无 | 有 | 有 |
| REPEATABLE-READ | 无 | 无 | 有 |
| SERIALIZABLE | 无 | 无 | 无 |
下面我们来演示一下,各种隔离级别中可见性的问题,开启两个窗口,叫做A、B窗口,两个窗口中登录mysql。
READ-UNCOMMITTED:读未提交
将隔离级别置为READ-UNCOMMITTED:
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=READ-UNCOMMITTED
重启mysql:
C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
查看隔离级别:
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-UNCOMMITTED |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
先清空test1表数据:
delete from test1;select * from test1;
按时间顺序在2个窗口中执行下面操作:
| 时间 | 窗口A | 窗口B |
|---|---|---|
| T1 | start transaction; | |
| T2 | select * from test1; | |
| T3 | start transaction; | |
| T4 | insert into test1 values (1); | |
| T5 | select * from test1; | |
| T6 | select * from test1; | |
| T7 | commit; | |
| T8 | commit; |
A窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;+------+| a|+------+|1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
B窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a|+------+|1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
看一下:
T2-A:无数据,T6-A:有数据,T6时刻B还未提交,此时A已经看到了B插入的数据,说明出现了脏读。
T2-A:无数据,T6-A:有数据,查询到的结果不一样,说明不可重复读。
结论:读未提交情况下,可以读取到其他事务还未提交的数据,多次读取结果不一样,出现了脏读、不可重复读、幻读
READ-COMMITTED:读已提交
将隔离级别置为READ-COMMITTED
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=READ-COMMITTED
重启mysql:
C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
查看隔离级别:
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
先清空test1表数据:
delete from test1;select * from test1;
按时间顺序在2个窗口中执行下面操作:
| 时间 | 窗口A | 窗口B |
|---|---|---|
| T1 | start transaction; | |
| T2 | select * from test1; | |
| T3 | start transaction; | |
| T4 | insert into test1 values (1); | |
| T5 | select * from test1; | |
| T6 | select * from test1; | |
| T7 | commit; | |
| T8 | select * from test1; | |
| T9 | commit; |
A窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;+------+| a|+------+|1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
B窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a|+------+|1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
看一下:
T5-B:有数据,T6-A窗口:无数据,A看不到B的数据,说明没有脏读。
T6-A窗口:无数据,T8-A:看到了B插入的数据,此时B已经提交了,A看到了B已提交的数据,说明可以读取到已提交的数据。
T2-A、T6-A:无数据,T8-A:有数据,多次读取结果不一样,说明不可重复读。
结论:读已提交情况下,无法读取到其他事务还未提交的数据,可以读取到其他事务已经提交的数据,多次读取结果不一样,未出现脏读,出现了读已提交、不可重复读、幻读
REPEATABLE-READ:可重复读
将隔离级别置为REPEATABLE-READ
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=REPEATABLE-READ
重启mysql:
C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
查看隔离级别:
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
先清空test1表数据:
delete from test1;select * from test1;
按时间顺序在2个窗口中执行下面操作:
| 时间 | 窗口A | 窗口B |
|---|---|---|
| T1 | start transaction; | |
| T2 | select * from test1; | |
| T3 | start transaction; | |
| T4 | insert into test1 values (1); | |
| T5 | select * from test1; | |
| T6 | select * from test1; | |
| T7 | commit; | |
| T8 | select * from test1; | |
| T9 | commit; | |
| T10 | select * from test1; |
A窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a|+------+|1 ||1 |+------+2 rows in set (0.00 sec)
B窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a|+------+|1 ||1 |+------+2 rows in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
看一下:
T2-A、T6-A窗口:无数据,T5-B:有数据,A看不到B的数据,说明没有脏读。
T8-A:无数据,此时B已经提交了,A看不到B已提交的数据,A中3次读的结果一样都是没有数据的,说明可重复读。
结论:可重复读情况下,未出现脏读,未读取到其他事务已提交的数据,多次读取结果一致,即可重复读。
幻读演示
将隔离级别置为REPEATABLE-READ
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=REPEATABLE-READ
重启mysql:
C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
查看隔离级别:
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
准备数据:
mysql> create table t_user(id int primary key,name varchar(16) unique key);Query OK, 0 rows affected (0.01 sec)mysql> insert into t_user values (1,'路人甲Java'),(2,'路人甲Java');ERROR 1062 (23000): Duplicate entry '路人甲Java' ***\*for\**** key 'name'mysql> select * from t_user;Empty set (0.00 sec)
上面我们创建t_user表,name添加了唯一约束,表示name不能重复,否则报错。
按时间顺序在2个窗口中执行下面操作:
| 时间 | 窗口A | 窗口B |
|---|---|---|
| T1 | start transaction; | |
| T2 | start transaction; | |
| T3 | – 插入路人甲Java insert into t_user values (1,‘路人甲Java’); | |
| T4 | select * from t_user; | |
| T5 | – 查看路人甲Java是否存在 select * from t_user where name=‘路人甲Java’; | |
| T6 | commit; | |
| T7 | – 插入路人甲Java insert into t_user values (2,‘路人甲Java’); | |
| T8 | – 查看路人甲Java是否存在 select * from t_user where name=‘路人甲Java’; | |
| T9 | commit; |
A窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from t_user where name='路人甲Java';Empty set (0.00 sec)mysql> insert into t_user values (2,'路人甲Java');ERROR 1062 (23000): Duplicate entry '路人甲Java' ***\*for\**** key 'name'mysql> select * from t_user where name='路人甲Java';Empty set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
B窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into t_user values (1,'路人甲Java');Query OK, 1 row affected (0.00 sec)mysql> select * from t_user;+----+---------------+| id | name |+----+---------------+| 1 | 路人甲Java|+----+---------------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
看一下:
A想插入数据路人甲Java,插入之前先查询了一下(T5时刻)该用户是否存在,发现不存在,然后在T7时刻执行插入,报错了,报数据已经存在了,因为T6时刻B已经插入了路人甲Java。
然后A有点郁闷,刚才查的时候不存在的,然后A不相信自己的眼睛,又去查一次(T8时刻),发现路人甲Java还是不存在的。
此时A心里想:数据明明不存在啊,为什么无法插入呢?这不是懵逼了么,A觉得如同发生了幻觉一样。
SERIALIZABLE:串行
SERIALIZABLE会让并发的事务串行执行(多个事务之间读写、写读、写写会产生互斥,效果就是串行执行,多个事务之间的读读不会产生互斥)。
读写互斥:事务A中先读取操作,事务B发起写入操作,事务A中的读取会导致事务B中的写入处于等待状态,直到A事务完成为止。
表示我开启一个事务,为了保证事务中不会出现上面说的问题(脏读、不可重复读、读已提交、幻读),那么我读取的时候,其他事务有修改数据的操作需要排队等待,等待我读取完成之后,他们才可以继续。
写读、写写也是互斥的,读写互斥类似。
这个类似于java中的java.util.concurrent.lock.ReentrantReadWriteLock类产生的效果。
下面演示读写互斥的效果。
将隔离级别置为SERIALIZABLE
# 隔离级别设置,READ-UNCOMMITTED读未提交,READ-COMMITTED读已提交,REPEATABLE-READ可重复读,SERIALIZABLE串行transaction-isolation=SERIALIZABLE
重启mysql:
C:\Windows\system32>net stop mysql
mysql 服务正在停止..mysql 服务已成功停止。
C:\Windows\system32>net start mysql
mysql 服务正在启动 .mysql 服务已经启动成功。
查看隔离级别:
mysql> show variables like 'transaction_isolation';+-----------------------+--------------+| Variable_name | Value |+-----------------------+--------------+| transaction_isolation | SERIALIZABLE |+-----------------------+--------------+1 row in set, 1 warning (0.00 sec)
先清空test1表数据:
delete from test1;select * from test1;
按时间顺序在2个窗口中执行下面操作:
| 时间 | 窗口A | 窗口B |
|---|---|---|
| T1 | start transaction; | |
| T2 | select * from test1; | |
| T3 | start transaction; | |
| T4 | insert into test1 values (1); | |
| T5 | commit; | |
| T6 | commit; |
按时间顺序运行上面的命令,会发现T4-B这样会被阻塞,直到T5-A执行完毕。
上面这个演示的是读写互斥产生的效果,大家可以自己去写一下写读、写写互斥的效果。
可以看出来,事务只能串行执行了。串行情况下不存在脏读、不可重复读、幻读的问题了。
小结读未提交( Read Uncommitted )
读未提交是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。
读已提交( Read Committed )
在 Read Committed 隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
可重复读( Repeatable Read )
在Repeatable Read隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。幻读就是没有读到的记录,以为不存在,但其实是可以更新成功的,并且,更新成功后,再次读取,就出现了。
可串行化( Serializable )
Serializable 是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。
虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。
默认隔离级别:如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用 InnoDB,默认的隔离级别是Repeatable Read。
关于隔离级别的选择需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
隔离级别越高,并发性也低,比如最高级别SERIALIZABLE会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。
具体选择哪种需要结合具体的业务来选择。
读已提交(READ-COMMITTED)通常用的比较多。
读未提交( Read Uncommitted )
读未提交是隔离级别最低的一种事务级别。在这种隔离级别下,一个事务会读到另一个事务更新后但未提交的数据,如果另一个事务回滚,那么当前事务读到的数据就是脏数据,这就是脏读(Dirty Read)。
读已提交( Read Committed )
在 Read Committed 隔离级别下,一个事务可能会遇到不可重复读(Non Repeatable Read)的问题。不可重复读是指,在一个事务内,多次读同一数据,在这个事务还没有结束时,如果另一个事务恰好修改了这个数据,那么,在第一个事务中,两次读取的数据就可能不一致。
可重复读( Repeatable Read )
在Repeatable Read隔离级别下,一个事务可能会遇到幻读(Phantom Read)的问题。幻读是指,在一个事务中,第一次查询某条记录,发现没有,但是,当试图更新这条不存在的记录时,竟然能成功,并且,再次读取同一条记录,它就神奇地出现了。幻读就是没有读到的记录,以为不存在,但其实是可以更新成功的,并且,更新成功后,再次读取,就出现了。
可串行化( Serializable )
Serializable 是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。
虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。
默认隔离级别:如果没有指定隔离级别,数据库就会使用默认的隔离级别。在MySQL中,如果使用 InnoDB,默认的隔离级别是Repeatable Read。
关于隔离级别的选择需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
隔离级别越高,并发性也低,比如最高级别SERIALIZABLE会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。
具体选择哪种需要结合具体的业务来选择。
读已提交(READ-COMMITTED)通常用的比较多。
需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余
隔离级别越高,并发性也低,比如最高级别SERIALIZABLE会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。
具体选择哪种需要结合具体的业务来选择。
读已提交(READ-COMMITTED)通常用的比较多。
上一篇:MySQL分布式恢复的方法是什么
下一篇:如何为MySQL创建高性能索引