事务
一个事务是一个完整的逻辑单元 ,不可以再分。
比如 银行用户转账,A账号给B账号转账1000元,A账户里的余额需要减少一千元,
B账户余额需要增加一千元,这是两条更新语句,这两条更新语句只能有两种结果,
要么同时成功,要么同时失败。那么在这个过程中需要用到数据库的“事务机制”
事务的特性
事务包括四大特性
原子性:事务是工作的最小单元,一个事务不能再分割
一致性:事务必须保证多条DML语句同时成功,或者同时失败
隔离性:事务和事务之间不能相互干扰。
永久性:最终的数据必须持久化到硬盘文件中。
事务的隔离性
- 第一级别:读未提交
事务读取的是另一个事务未提交的数据,,容易产生脏读 - 第二级别:读已提交
事务读取的是另一个事务已经提交的数据,解决了脏读,但是会产生不可重复读 - 第三级别:可重复读 (MySQL数据库默认的隔离级别)
不会产生脏读、不可重复读,但是会产生幻读。 - 第四级别:序列化读/串行化读
将事务进行了排队,一个事务没有结束,另一个事务不能开始
解决了脏读,不可重复读,幻读。
但是这样执行效率会变低。
脏读 不可重复读 幻读
- 脏读:脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
例如:
张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。
与此同时,
事务B正在读取张三的工资,读取到张三的工资为8000。
随后,
事务A发生异常,而回滚了事务。张三的工资又回滚为5000。
最后,
事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。 - 不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
例如:
在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。
与此同时,
事务B把张三的工资改为8000,并提交了事务。
随后,
在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。 - 幻读:
具体看以下实例
(1) 事务A
以上结果可以发现在A事务中没有查询到A等于1的数据;现在打开另一个窗口,开启事务B# 设置事务的隔离级别是可重复读,并开启事务A; mysql> set session transaction isolation level repeatable read; mysql>start transaction; mysql> select * from jmeter_contacts where id = 1; Empty set (0.00 sec)
(2)事务B
(3) 接下来事务A再次查询id为1的数据# 设置事务的隔离级别是可重复读,并开启事务A; mysql> set session transaction isolation level repeatable read; Query OK, 0 rows affected (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) #向表中插入id为1的数据,并提交事务 mysql> insert into jmeter_contacts (id, user_id ,dingding_token,is_deleted) values (1, 123, 'adadas', 0 ); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec)
这时,问题就暴露出来了,明明我在本事务中没有mysql> select * from jmeter_contacts where id = 1; Empty set (0.00 sec) # 因为是可重复读,所以还是没有查询到数据; # 那接下来让我们同样在数据库中插入id为1的数据, mysql> insert into jmeter_contacts (id, user_id ,dingding_token,is_deleted) values (1, 123, 'adadas', 0 ); ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
查询到id为1的数据,为啥还是不能插入id为1的数据呢?
难道是我的查询产生幻觉了?