一、前言
二、事务隔离
三、并发事务的影响
四、数据库隔离级别
五、查看数据库隔离级别
一、前言
在转账的时候,钱从一张银行卡扣除了,但是又没有转入另一张银行卡,怎么办?
假设A给B发送过年红包666元。
转账涉及的步骤有:
1、读取A数据库余额 > 将数据库余额减去666元 > 将减去666元后的余额更新到数据库
2、读取B数据库余额 > 将数据库余额加上666元 > 将加上666元后的余额更新到数据库
要是只完整执行了第1步骤,神奇的是服务器宕机了,那这666元岂不是飞了?
为了不让这666元飞了,需要保证转账第1步骤和第2步骤要么都执行成功,要么都执行失败,不允许出现只执行第1步骤的中间状态。
这就靠数据库中的事务来保证了。
二、事务隔离
1、事务
指用户定义的一个数据库操作序列,这些操作要么全成功,要么全失败,没有中间状态,是一个不可分割的工作单位。
2、事务的隔离性
多个并发的事务同时访问一个数据库时,一个事务不应该被另一个事务所干扰,每个并发的事务间要相互进行隔离。
3、四个特性
任何支持事务的数据库,都必须具备四个特性,分别是:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),也就是的事务ACID,这样才能保证事务(Transaction)中数据的正确性。
原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部失败回滚。
一致性(Consistency):指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。,例如表中有个余额字段,约束为不小于0,假设A余额有100元,但是向B发起转账666元,这时候应该校验支付后A的余额,如果小于0则进行事务的回滚,从而保证一致性。
隔离性(Isolation):多个并发的事务的操作,事务不会查看到中间状态的数据。
持久性(Durability):事务正确执行后,事务中对数据的操作不会回滚,即对数据的修改是永久的。
三、并发事务的影响
多客户端连接MySQL服务端,MySQL服务端就要同时处理多个请求,那么就可能会出现脏读,不可重复读,幻读问题。
什么是脏读,不可重复读,幻读问题?
1、脏读(dirty read)
一个事务的没提交之前的修改被另外一个事务可以看到。即读到其他事务未提交的数据。
为什么会出现“脏读”?因为“select”操作不恰当。
2、不可重复读(non-repeatable read)
(同一个事务中)同一select语句,两次读取到已提交数据,数据内容(数据信息)不一致。即前后读取的数据不一致。
为什么会出现“不可重复读”?因为“update”操作不恰当。
3、幻读(phantom read)
(可以不是同一事务)同一select语句,两次读取到已提交数据,数据内容(数据条数)不一致。即前后读取的记录数量不一致。
为什么会出现“幻读”?因为“insert”和“delete”操作不恰当。
三、数据库隔离级别
如何解决脏读,不可重复读,幻读问题?
数据库四个隔离级别为读未提交(read uncommitted),读已提交(read committed),可重复读(repeatable read),序列化(serializable),依次解决脏读,不可重复读,幻读问题。
1、读未提交(read uncommitted)
指一个事务还没提交时,它做的变更就能被其他事务看到。
会出现脏读、不可重复读和幻读问题。
2、读已提交(read committed)
指一个事务提交之后,它做的变更才能被其他事务看到。
可以避免脏读,仍会出现不可重复读和幻读问题。
3、可重复读(repeatable read)
指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB引擎的默认隔离级别。
可以避免脏读和不可重复读,仍会出现幻读问题。
4、序列化(serializable)
指事务一个一个顺序执行,无法并发执行。
可以解决幻读问题。确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,可避免所有并发问题,但性能非常低。
四、查看数据库隔离级别
可以开两个窗口来测试一下这四个隔离级别,数据库demo可参考【数据库系列】你想要的sql全都有plus。
-- 窗口A
-- 设置隔离级别
set session transaction isolation level read committed;
select @@tx_isolation;
-- 开始事务
start transaction;
select * from dbname.t_table_info where id =1;
update dbname.t_table_info set name = '窗口A1' where id =1;
select * from dbname.t_table_info where id =1;
select count(*) from dbname.t_table_info;
insert into dbname.t_table_info (id, name, age, sex, job, json_set, create_time, update_time) values(ceiling(rand()*9000+100), '呱呱大王', 6, '女', '教师', '{"uuid":"456"}', now(), now());
-- 回滚
rollback;
-- 提交
commit;
-- 查看当前运行的事务
select * from information_schema.innodb_trx;
-- 查询线程
select * from information_schema.processlist;
show full processlist;
-- 杀掉当前运行的事务 kill trx_mysql_thread_id
-- mysql提示Lock wait timeout exceeded解决办法
kill 666;