//展示事务隔离级别
show global variables like '%isolation%';
//修改事务隔离级别
set global transaction_isolation ='read-committed';
//事务是否自动提交
show GLOBAL variables LIKE 'autocommit' ;
You need to make sure about few points to make the row locking work.
在真实数据行的锁
Any lock held with the LOCK IN SHARE MODE clause will allow other transactions to read(using the clause LOCK IN SHARE MODE) the locked row but will not allow other transactions to write on the row until the transaction get committed or rolled back and the lock is released. This is basically a shared/read lock.
SELECT … LOCK IN SHARE MODE适用于两张表存在业务关系时的一致性要求
比如parent、child表,一个parent可以有多个孩子,则会在设计时将child端加外键。从业务角度讲,直接插入child记录是有风险的,因为此child关联的parent可能被其他事务所删除,从而造成数据不一致的风险。正确的做法是select * from parent where c_child_id=100 lock in share mode,锁定了parent表的这条记录,然后执行insert into child(child_id) values (100)就ok了。
Any lock held with the FOR UPDATE clause will not allow other transactions to read (using the clause FOR UPDATE), update, or delete the rows until the transaction gets committed or rolled back, releasing the lock. This is basically an exclusive/write lock.
适用于操作同一张表时的一致性要求。
电商系统中计算一种商品的剩余数量,在产生订单之前需要确认商品数量>=1,产生订单之后应该将商品数量减1。
①select amount from product where product_name=‘XX’;
②update product set amount=amount-1 where product_name=‘XX’;
如果用LOCK IN SHARE MODE就会出现问题,若此时两个购物订单,则T1、T2会读到相同的amount,若更新,还会造成死锁(在均未超时的情况下)。
此时就应该用for update,若此时两个购物订单,其中T1查到amount,而另一个事务再查同条记录会阻塞;当T1提交后,T2查到结果,再操作提交。
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
幻读:同一事务,在不同时间,执行相同的查询语句,产生不同的结果行(或多或少)。
举例:
在id列有索引,想要读,然后锁住id>100的所有行。
SELECT * FROM child WHERE id > 100 FOR UPDATE;
假设表中包含id=90、id=102,如果不对间隙(gap)进行锁定,则另一个事务可以插入id=101的行。其他事务会看到新插入的行。
模拟:
在read commit 级别下,并没有解决幻读问题,则可以在此级别下模拟
加粗样式
gap lock:锁定一个范围,但不包括记录本身。GAP锁的目的,是为了防止同一事务的两次当前读,出现幻读的情况。
gap:A place in an InnoDB index data structure where new values could be inserted.
也就是说,gap是索引树中插入新记录的空隙。
A gap lock is a lock on a gap between index records, or a lock on the gap before the first or after the last index record.
将范围内的空隙锁住
SELECT * FROM child WHERE id between 1 and 10 FOR UPDATE; //锁住[1-10]
SELECT * FROM child WHERE id < 10 FOR UPDATE; //锁住>10
SELECT * FROM child WHERE id > 10 FOR UPDATE; //锁住<10
Gap locking is not needed for statements that lock rows using a unique index to search for a unique row. (This does not include the case that the search condition includes only some columns of a multiple-column unique index; in that case, gap locking does occur.) For example, if the id column has a unique index, the following statement uses only an index-record lock for the row having id value 100 and it does not matter whether other sessions insert rows in the preceding gap:
对于使用唯一索引来锁定唯一行来锁定行的语句,不需要间隙锁定。 (这不包括搜索条件是联合索引的某些列;在这种情况下,会发生间隙锁定。)例如,如果id列具有唯一索引,则以下语句仅使用 ID值为100的行的索引记录锁,其他会话是否在前面的间隙中插入行都没有关系:
SELECT * FROM child WHERE id = 100;
If id is not indexed or has a nonunique index, the statement does lock the preceding gap.
若搜索条件不是索引或唯一索引,则会锁住preceding gap.
A next-key lock is a combination of a record lock on the index record and a gap lock on the gap before the index record.
next-key lock:Row-level locking + gap lock
repeatable-read用next-key lock避免幻读
InnoDB是用乐观锁
MySQL InnoDB MVCC实现:https://blog.csdn.net/jiangshangchunjiezi/article/details/105802025)
【将MVCC理解才能较深入理解Isolation levels】
以下是普通查询,也就是select … from… 是快照读read view
避免了脏读(dirty read):是用read view解决的
避免了不可重复读和幻读:不可重复读是MVCC解决的,幻读是next-key lock解决的
参考:https://blog.toadworld.com/2018/01/11/explaining-innodb-explicit-locking-mechanisms
https://blog.csdn.net/cug_jiang126com/article/details/50544728
https://tech.meituan.com/2014/08/20/innodb-lock.html