事务的隔离级别
一个事务具有ACID
特性,也就是(Atomicity、Consistency、Isolation、Durability,即原子性
、一致性
、隔离性
、持久性
),本文主要讲解一下其中的Isolation
,也就是事务的隔离性
。
四种隔离级别分别是:
读未提交(read uncommitted)
一个事务还没提交时,它修改的数据都可以被别的事物看到。
读已提交(read committed)
一个事务提交之后,它修改的数据才会被别的事物看到。
可重复读(repeatable read)
一个事务执行过程中看到的数据,总是和这个事务开启时看到的数据是一致的。在可重复读的隔离级别下,未提交的事务对其他事务也是不可见的。
串行化(serializable)
数据的
读
和写
都会加锁
,读
会加读锁
,写
会加写锁
。当遇到读写锁冲突时,后访问的事务必须等前一个事务执行完成后,再继续执行。以上四种隔离级别,由上往下隔离强度越来越大,但是执行效率会随之降低。在设置隔离级别时候,需要在隔离级别
和执行效率
两者做平衡取舍。程序测试这四个隔离级别
事务1
$cc = $this->db->query("select @@session.tx_isolation;")->row_array(); var_dump($cc); echo '第一个事务<br/>'; echo date('Y-m-d H:i:s')."start0<br/>"; $this->db->trans_begin(); echo date('Y-m-d H:i:s')."end0<br/>"; $num = rand(0, 1000); var_dump($num); //查询id=1记录对应的password的值 echo date('Y-m-d H:i:s')."select-start1<br/>"; $info = $this->db->select('*')->from($this->table_name)->where('id', 1)->get()->row_array(); echo date('Y-m-d H:i:s')."select-end1<br/>"; var_dump($info); //把id=1的记录的password字段值改成$num对应的值 echo date('Y-m-d H:i:s')."update-start1<br/>"; $this->db->update($this->table_name, ['password' => $num], ['id' => 1]); echo date('Y-m-d H:i:s')."update-end2<br/>"; //update后,再次查询id=1记录对应的password的值 echo date('Y-m-d H:i:s')."select-start2<br/>"; $info = $this->db->select('*')->from($this->table_name)->where('id', 1)->get()->row_array(); echo date('Y-m-d H:i:s')."select-end2<br/>"; var_dump($info); //睡上8秒 sleep(8); echo date('Y-m-d H:i:s')."事务提交的时间<br/>"; $this->db->trans_commit();
事务2
$cc = $this->db->query("select @@session.tx_isolation;")->row_array(); var_dump($cc); echo '第二个事务<br/>'; echo date('Y-m-d H:i:s')."start0<br/>"; $this->db->trans_begin(); echo date('Y-m-d H:i:s')."end0<br/>"; //查询id=1记录对应的password的值 echo date('Y-m-d H:i:s')."start1<br/>"; $info = $this->db->select('*')->from($this->table_name)->where('id', 1)->get()->row_array(); echo date('Y-m-d H:i:s')."end1<br/>"; var_dump($info); //睡上12秒(等待第一个事务提交后再执行) sleep(12); //再次查询id=1记录对应的password的值 echo date('Y-m-d H:i:s')."start2<br/>"; $info = $this->db->select('*')->from($this->table_name)->where('id', 1)->get()->row_array(); echo date('Y-m-d H:i:s')."end2<br/>"; var_dump($info); $this->db->trans_commit();
一 读未提交
先把mysql的隔离级别改成:读未提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;#修改全局的事务隔离级别SQL语句 select @@session.tx_isolation;#查看mysql的隔离级别
同时运行上面2个事务
可以看到第一个事务的update操作没提交前(事务提交时间:2023-02-20 01:23:18),事务2(读取的时间点:2023-02-20 01:23:11)已经读取到了事务1修改后的值。
二 读已提交
设置当前隔离级别为:读已提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED; select @@session.tx_isolation;
可以看到第一个事务修改后的值提交hou,第二个事务也能查到第一个事务修改后的值.
三 可重复读
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; select @@session.tx_isolation;
从图可以看到,两个事务第一查询的时候都是:888,第一个事务把值改成484,提交后,第二个事务第2次查询到的值还是原来的值:888,而不是第一个事务修改后的值:484.
四 串行化(serializable)
2个事务,如果都是读操作,2个事务不冲突,就不用等另外一个事务执行完后再执行.如果其中有一个事务有写操作的.只有读操作的事务必须等待有写操作的事务执行完后再执行。如果有多个事务进行写操作,那只会有一个事务的写操作成功.其它写操作的事务会报 Deadlock found when trying to get lock; try restarting transaction