image.png
image.png
image.png image.png image.png
image.png
image.png
image.png image.png
image.png
image.png image.png
总结一下
首先注意:
1.S锁和X锁不能同一事务的同一操作共存。即一条数据被加了S锁就不能加X锁,加X锁就不能加S锁。
2.读锁和写锁分离,并不是说A事务对一条数据加了写锁独占(X锁),B事务就不能读了,依然可以读,因为写读锁和写锁不是一回事。就相当于对write()加了同步不影响read()的使用一样。
无锁导致数据覆盖
如果不加任何锁,那就出现AB线程数据覆盖的情况。主要出现在AB线程同时对一个数据“写”的情况。
A事务和B事务同时读到一条数据,如100,A事务做加法,变成120,B事务做减法变成70,按照操作者的逻辑最终结果为90是正确的,但由于无锁导致A先提交后B在提交导致最终结果为70。出现数据覆盖
为此需要在写的过程中加入行级独占锁(X锁)(写锁)(行锁),如果一个线程中有写的操作,那么这个线程将对这条被写的数据独占到“事务结束”。
这种对应的事务隔离级别是Read Uncommitted,其实现是读数据时候不加锁(读锁无),写数据时候加行级别的独占锁(行锁)(X锁)(写锁),事务提交时释放锁。
当存在事务后,又发生回滚时,会出现脏读。
脏读的问题针对的是:隔离级别为Read Uncommitted时的是A事务写加回滚。B事务在A事务写完之后读。由于A的回滚对B不可见,所以B用了错误的数据进行事务。
Read Uncommitted解决方案不生效了,为此事务读取数加行级共享锁(行锁)(S锁)(读锁),读完释放。
事务写数据时候加行级独占锁(行锁)(X锁)(写锁),事务结束释放。(同Read Uncommitted)
由于事务读操作加上共享锁(S锁)(读锁),因此B事务读操作时,由于A事务对该条数据加了(X锁),根据一条数据不能同时加(X锁)和(S锁)。导致B事务获取读权限失败,因此,不能读到事务的未提交数据,避免了脏读的问题。对应事务的read committed隔离级别。
当存在事务后,两次读操作中间被其他事务写了一次,导致不可重复读。
不可重复读的问题针对的是:在read committed隔离级别,A事务进行了两次读(select)中间被B事务改了一次(update)。
A事务第一次读(Select)前获得该条数据的共享锁(S锁),读完释放;B事务在A事务的两次读中进行一次写(update),获得该条数据的独占锁,写完之后B事务即结束释放独占锁。A事务为了读再次获得该条数据的共享锁(S锁)成功,但发现和事务中第一次读的数据不一样。
究其原因在于,读操作的锁加在读上面,而不是加在事务之上,所以,在同一事务的两次读操作之间可以插入其他事务的写操作,所以可能发生不可重复读的问题。
为此应将读的共享锁(S锁)延长至事务结束,即事务隔离级别的repeatable read。
如果对整表的部分数据加锁后修改,但在加锁后又插入一条新数据,导致新数据未能被修改,导致幻读。
幻读的问题针对的是:在repeatable read隔离级别下,A事务对整表的部分合适的数据加锁并修改(update)(表锁)(写锁)(独占锁)。但B事务插入了一条合适的数据。(之前所有的操作都是对读写即select和update加锁,未出现增锁(Insert)所以在A事务锁表的情况下,新增数据依然是被允许的)
为了解决这个问题只能使用最严格的隔离级别Serializable,在A事务未完成的情况下,不允许B事务进行任何操作。即所有事务串行进行,相当于Thread.join();
网友评论