何为数据库事务?
事务是一个业务,一个逻辑工作单元,这个工作单元中的所有的操作要么都执行,要么都不执行.
事务有哪些特性?
- 原子性:同一个事务内部的所有操作,要么都执行,要么都不执行.
- 一致性:事务前和事务后,数据的最终结果应该是一样的(例如转账操作,转入和转出的金额应该一致的)
- 隔离性:事务与事务之间应该是相互隔离的,互不影响(例如存钱业务,多个存钱的并发事务应该是相互隔离的)
- 持久性:事务一旦提交,对数据库数据的更新是要写到数据库的.
MySQL中事务特性是如何实现的?
- 原子性:通过undolog(回滚日志)实现-执行回滚;
- 隔离性:通过锁(Lock)以及MVCC(多版本并发控制);
- 一致性:通过undolog(回滚日志),redolog(重做日志),binlog(归档日志);
- 持久性:通过redolog日志实现;(commit了不一定会马上更新后的数据写入到磁盘)
MySQL中的事务隔离级别有哪些?
为了解决事务并发执行时,可能会带来的一些问题(脏读,不可重复读,幻读),MySQL内部提供了如下几种事务隔离级别:
- Read-UnCommitted (读未提交) : 这个隔离级别可能会出现脏读现象(一个事务读取了其它事务未提交的数据)
- Read-Committed (读已提交):这个隔离级别解决了脏读问题,但是会出现不可重复读问题.
- Repeatable-read(可重复读):解决了事务的可重复读问题,但会是会出现幻读问题(读到的数据可能已经被删除)
- Seriablizable 可串行化:所有事务对表的操作是顺序执行的. 如何查看事务的隔离级别呢?
select @ @tx _isolation; #mysql5.7
select @@transaction_isolation; #mysql8.0
如何设置的事务的隔离级别呢?
set tx_isolation ='read-committed'; #mysql5.7
set session transaction isolation level read committed; #mysql8.0 此隔离级别对当前会话有效
set global transaction isolation level read committed; #mysql8.0设置全局事务隔离级别,新会话有效.
多事务并发执行时可能会有什么问题?
- 脏读 (一个事务读取了其它事务未提交的数据)
- 不可重复读(一个事务内部多次对相同sql的查询结果是不一致)
- 幻影读(对表中数据进行统计时,统计出的结果和实际表中的结果不一致)
脏读问题实验分析(mysql5.7) 打开两个会话窗口,分别启动事务A和事务B
| 事务A | 事务B |
|---|---|
| 设置事务隔离级别set tx_isolation='read-uncommitted' | 设置事务隔离级别set tx_isolation='read-uncommitted' |
| 设置事务手动提交set autocommit=0 | 设置事务手动提交set autocommit=0 |
| 开启事务begin | 开启事务begin |
| 执行查询select * from regions; | 执行写入操作insert into regions values (10,'RA'); |
| 执行查询select * from regions;这里会发现事务B中的写入的RA记录,这条记录在事务B中的还没有提交,我们通常称之为脏数据. | |
| commit; | 执行回滚操作rollback; |
脏读问题的解决方案:修改事务隔离级别为read-committed;
不可重复读实验分析 mysql 5.7 (10.5.17-MariaDB 隔离级别不生效)
| 事务A | 事务B |
|---|---|
| 设置事务隔离级别set tx_isolation='read-committed' | 设置事务隔离级别set tx_isolation='read-committed' |
| 设置事务手动提交set autocommit=0 | 设置事务手动提交set autocommit=0 |
| 开启事务begin | 开启事务begin |
| 执行查询select * from regions; | 执行写入操作insert into regions values (10,'RA');commit; |
| 执行查询select * from regions;commit; |
MySQL 8.0 (查看隔离级别使用 select @@transaction_isolation;)
| 事务A | 事务B |
|---|---|
| 设置事务隔离级别set session transaction isolation level read commit; | 设置事务隔离级别set session transaction isolation level read commit; |
| 设置事务手动提交set autocommit=0 | 设置事务手动提交set autocommit=0 |
| 开启事务begin | 开启事务begin |
| 执行查询select * from regions; | 执行写入操作insert into regions values (10,'RA');commit; |
| 执行查询select * from regions;commit; |
假如希望同一个事务内部,基于同一个sql进行的数据读取,始终是一致的,可以将事务的隔离级别修改repeatable read;
幻影读实验分析(mysql8.0 )
| 事务A | 事务B |
|---|---|
| 设置事务隔离级别set session transaction isolation level repeatable read; | 设置事务隔离级别set session transaction isolation level repeatable read; |
| 设置事务手动提交set autocommit=0 | 设置事务手动提交set autocommit=0 |
| 开启事务begin | 开启事务begin |
| 执行查询select * from regions; | 执行写入操作delete from regions where region_id=5;commit; |
| 执行查询select * from regions;在这里依旧可以读取到事务B删除的记录,那对这个数据的读取就是幻读.commit; | |
| 对幻影读问题,可以修改事务的隔离级别为serializable.但是,隔离级别越高,事务的并发就会越低. |
当事务的隔离级别设置为serializable时,多个更新(insert,update,delete)事务的执行会串行化.