重新认识Mysql之事务那些事
(一)什么是事务?
是一组原子性的SQL查询,或者说是一个独立的工作单元。事务内的语句,要么就全部执行,要么就全都不执行。
场景模拟:
用户A给用户B转账1000元
用户A - 1000元 用户B + 1000元
以上操作,要么全部执行成功,要么全部执行失败,不会出现用户A扣钱,用户B加钱这种现象发生.
(二、)事务的四大特性
- 原子性(Atomicity)
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
- 一致性(Consistency)
表示一个事务内有一个操作失败了,所有更改过的数据都必须会滚到修改前的状态
- 隔离性(Isolation)
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。 即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
4.持久性(Durability)
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
(三、)事务的隔离级别
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted(未提交读)、Read committed(读提交)、Repeatable read(重复读)、Serializable(序列化)。
但是在事务的并发操作中可能会出现脏读,不可重复读,幻读
1. Read uncommitted(未提交读)
引发现象:==脏读==
事务B可以读取到事务A未提交的数据.
场景模拟:
职员小A的工资是3000,但是发工资时,老板按错了数字,按成了30000元,但是事务还没有提交,就在这个时候,小A去查询了工资,以为涨工资了非常开心,但是老板发现不对,及时回滚数据,再重新改成3000进行提交:
场景分析:
这个情况就是脏读,小A看到的工资是30000万,但实际还是3000元,因为他看到的是老板还没提交的数据.
解决方案:
Read committed(读提交),能解决掉脏读的问题
2. Read committed(读提交)
事务B必须要等待事务A提交后才能读取数据,一个事务从开始到提交结束之前,所做的任何修改对其他事务都是不可见的.所以也称为
不可重复读
场景模拟:
职员小A准备去商场购物(卡余额3000元),当他准备进行买单时(小A事务开启),收费系统检测到小A的卡主有3000元可以支付,就在这时,小A的女朋友需要把钱全部转出去充当家用,并提交(女朋友事务在小A前),当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了,结果小A购物失败....
场景分析:
这就是读提交,事务A在对数据进行更新操作时,事务B的读操作需要等待事务A更新操作提交完成后,才能读取数据,可以解决脏读问题。
但是在本场景中,收费系统中一个事务范围,两次相同的查询,却获取到了不同的结果,这就是==不可重复读(两次执行一样查询,结果却不同)==
解决方案:
Repeatable read(重复读) 来解决.
3.Repeatable read(重复读)
重复读:在事务开启进行读数据时,不允许修改操作。
补充:Mysql默认事务隔离级别为:Repeatable read
场景模拟:
第二个月,职员小A又出去购物(卡里余额3000元),当他准备买单时(事务开启,不允许其他事务更新操作),收费系统检测他卡中余额3000元,他的女朋友又想转出这3000元作为家用,但是转出失败(无法更新修改),收费系统扣款成功,小A顺利完成了购物!
场景分析:
重复读,虽然可以解决不可重读问题,但是重复读对应的是修改(Update)操作,还是可能中途会出现插入(Insert)操作,会产生出幻读问题.
幻读场景模拟:
职员小A,去查询消费记录(事务开启),发现消费了1000元,卡中还剩2000元,开始打印消费清单(事务提交),但是在打印清单的刚刚,老板发工资了,新增了3000元(insert了一条记录),发现清单上的余额是5000元,跟之前的查询的记录不对,这就是幻读.
幻读:指的是一个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(phantom row)
重点:Mysql InnoDB存储引擎通过Next-Key Locking解决了幻读的问题
解决方案:
非 InnoDB引擎 幻读解决方案 Serializable(序列化)
Serializable(序列化)
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。Serializable 在读取的每一行数据上都进行了加锁.
(四、事务操作命令及实战)
1. 查看数据库引擎是否支持事务(Innodb支持?)
show engines
2. 查看Mysql默认储存引擎
show variables like '%storage_engine%';
3.创建事务
//开启事务 start transaction //开启事务 begin //二者等价
4.提交事务
COMMIT 会提交事务,并使已对数据库进行的所有修改成为永久性的
//提交 commit //提交 commit work //二者等价 //提交后开启一个新事务 commit and chain
5.回滚事务
回滚会结束用户的事务,并撤销正在进行的所有未提交的修改
//回滚 rollback //回滚 rollback work //二者等价
6.Mysql自动提交模式
//查看当前自动提交 show variables like 'autocommit' //禁止自动提交 SET AUTOCOMMIT=0 //开启自动提交 SET AUTOCOMMIT=1
7.事务还原点
//在事务中创建一个保存点 savepoint identifier //将事务回滚到标记点 rollback to identifier
实战案例:
//开启事务 begin; //更新 update users set name = '我是第一次的名字' where id = 2; //保存点ts1 savepoint ts1; //更新 update users set name = '我是第二次的名字' where id = 2; //保存点ts2 savepoint ts2; //查询 select * from users where id =2; //回滚到ts1 rollback to ts1;
8.InnoDB锁的问题
在InnoDB储存引擎中,start transaction 开启事务会造成一个隐式的unlock tables 执行
实战案例:
ClientA: //写锁 lock table users write; ClientB: //查询,卡住等待锁释放 select name from users where id = 2 ; ClientA: //更新数据 update users set name = '17ns' where id = 2; ClientB: //查询卡住中 ClientA: //开启事务,隐式执行了unlock tables begin; ClientB: //获得数据
实战图
尾:
以上就是事务操作的基本性质主要特征就是ACID(原子性,一致性,隔离性,持久性),在一些重要的场景使用事务会提高程序的可靠性,使数据能够正确地提交到数据库当中。