Db2中的Next-key locking

Db2中的Next-key locking

Next-key locking的含义是指,当有RR scan时,如果往索引里面插入一条记录,那么需要获得这条记录的下一个记录的锁,比如索引中有两条连续的记录100、105,那么要在105之前插入大于100的数据,必须先获得105这条记录的锁。Next-key locking可以用来实现RR隔离级别,防止幻象读(Phantom read)

https://www.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.admin.perf.doc/doc/c0009699.html

在不使用索引访问的情况下,如果应用A使用RR隔离级别访问(SELECT)某张表,则会整个表上面加一个S锁。如果使用了索引,那么不需要在整个表上面加锁

下面的测试中,创建一个含有一个字段year的表test1,并在year列上创建了一个索引,通过索引访问时,返回的结果是有序的:

$ db2 "create table test1 (year int)"

$ db2 "insert into test1 values(2010),(2007),(2005),(2012),(2000),(2017)"

$ db2 "select year from test1"

YEAR       
-----------
       2010
       2007
       2005
       2012
       2000
       2017

  6 record(s) selected.
  
$ db2 "create index yearidx on test1(year)"

$ db2 "runstats on table test1 and indexes all"

$ db2 "select year from test1"

YEAR       
-----------
       2000
       2005
       2007
       2010
       2012
       2017

  6 record(s) selected.


现在使用RR隔离级别访问表test1,看一下它需要哪些锁:

$ db2 +c "select * from test1 where year between 2007 and 2010 with RR"

YEAR       
-----------
       2007
       2010

  2 record(s) selected.

$ db2pd -db sample -locks showlock

Database Member 0 -- Database SAMPLE -- Active -- Up 0 days 01:05:41 -- Date 2017-09-15-20.37.34.581396

Locks:
Address            TranHdl    Lockname                   Type           Mode Sts Owner      Dur HoldCount  Att        ReleaseFlg rrIID TableNm            SchemaNm 
0x00007F219FA8AA00 3          03001B00040000000000000052 RowLock        ..S  G   3          1   0          0x00000010 0x00000001 1     TEST1              INST105     03001B00040000000000000052 SQLP_RECORD (obj={3;27}, rid=d(0;0;4), x0400000000000000)
0x00007F219FA8A500 3          03001B00070000000000000052 RowLock        ..S  G   3          1   0          0x00000010 0x00000001 1     TEST1              INST105     03001B00070000000000000052 SQLP_RECORD (obj={3;27}, rid=d(0;0;7), x0700000000000000)
0x00007F219FA8A180 3          4141414141664164FE8BC714C1 PlanLock       ..S  G   3          1   0          0x00000000 0x40000000 0     N/A                N/A         4141414141664164FE8BC714C1 SQLP_PLAN ({41414141 64416641 14C78BFE}, loading=0)
0x00007F219FA8A980 3          03001B00050000000000000052 RowLock        ..S  G   3          1   0          0x00000010 0x00000001 1     TEST1              INST105     03001B00050000000000000052 SQLP_RECORD (obj={3;27}, rid=d(0;0;5), x0500000000000000)
0x00007F219FA8A300 3          01000000010000000100C034D6 VarLock        ..S  G   3          1   0          0x00000000 0x40000000 0     N/A                N/A         01000000010000000100C034D6 SQLP_VARIATION (anchor,stmt,env,var={422,1,1,1}, loading = 0, )
0x00007F219FA7D580 3          03001B00000000000000000054 TableLock      .IS  G   3          1   0          0x00002000 0x00000001 0     TEST1              INST105     03001B00000000000000000054 SQLP_TABLE (obj={3;27})

$ db2 "select * from test1 where hex(rid(test1)) in ('0400000000000000','0700000000000000','0500000000000000')"

YEAR       
-----------
       2007
       2010
       2012

  3 record(s) selected.

从上面的测试中可以看到,虽然结果集中只有两行2007和2010,但却要在2010后的记录2012上额外加一个行锁,这个锁即是Next Key lock,可以认为是行锁和gap锁的结合。在上面的基础上,进行锁超时的测试,结果如下:
Session 1:
$ db2 +c "select * from test1 where year between 2007 and 2010 with RR"

Session 2:
$ db2 +c "delete from test1 where year=2005" => 成功
$ db2 rollback
$ db2 +c "insert into test1 values(2005)" => 成功
$ db2 rollback
$ db2 +c "insert into test1 values(2006)" => 锁超时
$ db2 rollback
$ db2 +c "delete from test1 where year=2007" => 锁超时
$ db2 rollback
$ db2 +c "delete from test1 where year=2010" => 锁超时
$ db2 rollback
$ db2 +c "insert into test1 values(2011)" => 锁超时
$ db2 rollback
$ db2 +c "delete from test1 where year=2012" => 锁超时
$ db2 rollback
$ db2 +c "insert into test1 values(2012)" => 成功
$ db2 rollback

测试结果表明,加锁的范围是(2005,2012],如下图所示,除了在2007,2010和2012上加行锁之外,在2005~2007之间、2007~2010之间、2010~2012之间也插入不了任何值,不防称之为gap lock:

Db2中的Next-key locking_第1张图片


下面以浮点数为例子,再次验证一下:

$ db2 "create table test2(num FLOAT)"

$ db2 "insert into test2 values(3.1415926),(2.71828),(9.8),(5.55),(6.66)"

$ db2 "create index numidx on test2(num)"

$ db2 "runstats on table test2 and indexes all"

$ db2 "select * from test2"

NUM                     
------------------------
  +2.71828000000000E+000
  +3.14159260000000E+000
  +5.55000000000000E+000
  +6.66000000000000E+000
  +9.80000000000000E+000

  5 record(s) selected.

按照原理,下面的SQL应该加锁的范围是(5.55,9.8]  

Session1:  
inst105@db2a:~$ db2 +c "select * from test2 where num=6.66 with RR" 

NUM                     
------------------------
  +6.66000000000000E+000

  1 record(s) selected.
  
在另一个session里,测试结果表明,加锁范围确实是(5.55,9.8]  
Session2:
$ db2 +c "delete from test2 where num=5.55" =>成功
$ db2 rollback
$ db2 +c "insert into test2 values(5.56)" => 锁超时
$ db2 rollback
$ db2 +c "delete from test2 where num=9.8" => 锁超时 
$ db2 rollback
$ db2 +c "insert into test2 values(9.8)" =>成功 
$ db2 rollback


1. 
Next key lock 可以这么理解:要想在A记录之后插入一条数据,那么必须要先获得第A+1条记录的锁。第一个例子中,要在2007到2010之间插入数据,必须先获得2010的锁;要在2010和2012之间插入数据(比如2010),必须先获得2012的锁;要在2005到2007之间插入数据(比如2007),必须先获得2007的锁。这里不必过于关注边界是开区间还是闭区间,最重要的是中间这个gap, MySQL中也有类似的概念和相似的效果。

2. 在第2个例子中,如果索引numidx改为唯一索引,那么session 1里的select语句只会在6.66上加一个行锁,而不在9.8上加锁,实际上的锁定范围是(5.55,6.66],插入6.00会报错超时,插入7.00不会,但MySQL这种情况下不需要Gap lock,插入6.00也可以成功。也就是说,在RR隔离级别和唯一索引的情况下,如果第N条记录上有锁,那么db2不允许插入N-1~N之间的记录,但MySQL允许。

参考资料:
http://hedengcheng.com/?p=771

你可能感兴趣的:(DB2,Db2,Next,key,lock)