MariaDB [test]> desc leouser_inno; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(100) | NO | | NULL | | +-------+--------------+------+-----+---------+-------+ MariaDB [test]> select * from leouser_inno; +----+-------------+ | id | name | +----+-------------+ | 1 | changefrom2 | | 5 | leo2 | | 7 | leo7 | +----+-------------+ MariaDB [test]> desc leouser2_inno; +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | bigint(20) | NO | PRI | NULL | auto_increment | | name | varchar(30) | YES | | NULL | | +-------+-------------+------+-----+---------+----------------+ MariaDB [test]> select * from leouser2_inno; +----+------+ | id | name | +----+------+ | 1 | leo | | 4 | leo4 | +----+------+
事務隔離級別為Repeatable Read
test1: begin;
select * from leouser_inno straight_join leouser2_inno on leouser_inno.id = leouser2_inno.id for update;
--鎖住兩個表的所有record和gap
test2: begin; -- 加入主鍵過濾
select * from leouser_inno straight_join leouser2_inno on leouser_inno.id = leouser2_inno.id where leouser2_inno.id=1 for update;
--各自lock id=1的record, 沒有lock gap
test2.1:begin; -- change join table order
-- exactly the same result with test2
test3:begin; -- 主鍵范圍過濾
MariaDB [test]> select * from leouser_inno straight_join leouser2_inno on leouser_inno.id = leouser2_inno.id where leouser2_inno.id<3 for update;
-- leouser2_inno的id=1的記錄lock,id=4右邊gaplock,id=2,3的gap沒有lock,但是沒有選中的記錄不會lock。由於先select leouser_inno的所有記錄,對leouser2_inno 的select很名確(where id=1 or id=5),id=5在leouser2_inno 的最大id=4的右邊,如果不對id=4的右側gap加鎖,可能導致幻讀,而對id=2,3的insert在leouser_inno
-- leouser_inno的所有record都lock,所有gap都lock。
insert into leouser2_inno(id) values(0);的時候有個小插曲,不能插入,以為id=1的左側也是gap lock,想不通。其實是因為leouser2_inno.id是autoincrement的,當id=0或者null的時候自動增加到mysql維護的下一個id,也就是id=5,而id=5是gaplock狀態導致不能insert。
SET sql_mode='NO_AUTO_VALUE_ON_ZERO';
當前session設置后,可以insert id=0的record,因此,不矛盾。
test3.1 -- reorder join tables;
MariaDB [test]> begin; Query OK, 0 rows affected (0.000 sec) MariaDB [test]> select * from leouser2_inno straight_join leouser_inno on leouser_inno.id = leouser2_inno.id where leouser2_inno.id<3 for update; +----+------+----+-------------+ | id | name | id | name | +----+------+----+-------------+ | 1 | leo | 1 | changefrom2 | +----+------+----+-------------+ 1 row in set (0.004 sec)
leouser2_inno 發現所有record被加鎖;相關gap被lock。why all records locked but not all gaps locked??——是因為RR事務隔離級別下,唯一索引上查詢時使用的lock類型next-key lock,除非等號過濾條件(可以是or連接的多個相等條件),並且對應的等號條件查詢到了一條記錄,MySQL做了優化此時退化為record lock,本測試中不適用於優化情形,仍是next-key lock。leouser2_inno中的鎖有3個:(-infinite,1],(1,4],(4,+infinite)。
leouser_inno 只有符合條件的id=1一條record被lock,只有record lock,因此其他的record沒有被加鎖。
=============
總結,join的時候mysql straight_join阻止優化select表的順序,按照從左到右結合where條件查詢,對第一個表加相應的鎖,得到記錄后作為條件查詢第二個表,對第二個表加鎖。多表join時屬於嵌套情況。join情況的加鎖就是多個表依次查詢——加鎖,和分析單表查詢相同,其結果通過mysql server層連接返回客戶端。