Val*_*mak 5 mysql concurrency innodb locking
我们有桌子
CREATE TABLE TEST_SUBSCRIBERS (
SUBSCRIPTION_ID varchar(255) NOT NULL COMMENT 'Subscriber id in format MSISDN-SERVICE_ID-TIMESTAMP',
MSISDN varchar(12) NOT NULL COMMENT 'Subscriber phone',
STATE enum ('ACTIVE', 'INACTIVE', 'UNSUBSCRIBED_SMS', 'UNSUBSCRIBED_PARTNER', 'UNSUBSCRIBED_ADMIN', 'UNSUBSCRIBED_REBILLING') NOT NULL,
SERVICE_ID varchar(255) NOT NULL COMMENT 'Id of service',
PRIMARY KEY (SUBSCRIPTION_ID)
)
ENGINE = INNODB
CHARACTER SET utf8
COLLATE utf8_general_ci;
Run Code Online (Sandbox Code Playgroud)
在并行线程中,我们执行如下操作(在java中)
1. Select active subscribers
SELECT *
FROM TEST_SUBSCRIBERS
WHERE SERVICE_ID='web-sub-1'
and MSISDN='000000002'
AND STATE IN ('ACTIVE', 'INACTIVE');
2. If there are no such subscribers, I can insert it
INSERT INTO TEST_SUBSCRIBERS
(SUBSCRIPTION_ID, MSISDN, STATE, SERVICE_ID)
VALUES ('web-sub-1-000000002-1504624819', '000000002', 'ACTIVE', 'web-sub-1');
Run Code Online (Sandbox Code Playgroud)
在并发模式下,2个线程可以尝试插入msisdn =“000000002”和service-id =“web-sub-1”以及不同subscriptionId的行,因为当前时间戳可能不同。两个线程都执行第一个选择,得到零结果,然后都插入。因此,我们尝试将这两个查询连接到事务中,但是当我们需要插入锁或类似操作时,锁定不存在的行会出现问题。我们不想在这两个操作期间锁定所有表,因为我们认为在这种情况下我们的系统会运行得太慢。我们无法为这种情况创建 uniq key,因为对于一个 abonent,可能有多行具有相同的取消订阅状态。如果我们尝试插入同一服务的 2 个订阅者,主键可以包含具有不同秒数的时间戳。我们尝试使用 SELECT ... FOR UPDATE 和 SELECT ... LOCK IN SHARE MODE,但是我们遇到了死锁,并且这对数据库服务器来说是一个繁重的操作。
为了进行测试,我们打开了 2 个终端并逐步进行:
# Window 1
mysql> start transaction;
mysql> SELECT SUBSCRIPTION_ID FROM TEST_SUBSCRIBERS s
WHERE s.SERVICE_ID="web-sub-1" AND s.MSISDN="000000002" FOR UPDATE;
# Window 2
start transaction;
mysql> SELECT SUBSCRIPTION_ID FROM TEST_SUBSCRIBERS s
WHERE s.SERVICE_ID="web-sub-1" AND s.MSISDN="000000002" FOR UPDATE;
# Window 1
mysql> INSERT INTO TEST_SUBSCRIBERS
(SUBSCRIPTION_ID, MSISDN, STATE, SERVICE_ID)
VALUES('web-sub-1-000000002-1504624818', '000000002', 'ACTIVE', 'web-sub-1');
# Window 2
mysql> INSERT INTO TEST_SUBSCRIBERS
(SUBSCRIPTION_ID, MSISDN, STATE, SERVICE_ID)
VALUES('web-sub-1-000000002-1504624819', '000000002', 'ACTIVE', 'web-sub-1');
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction
Run Code Online (Sandbox Code Playgroud)
有没有办法做到这一点而不会出现死锁并且不会锁定整个表?我们分析的其他变体是: 1. 单独的表 2. 插入和删除不需要的行。
计划 A. 这将插入(如果需要)或默默地不执行任何操作:
INSERT IGNORE ...;
Run Code Online (Sandbox Code Playgroud)
B 计划。这可能有点矫枉过正,因为没有什么需要“更新”:
INSERT INTO ...
(...)
ON DUPLICATE KEY UPDATE
...;
Run Code Online (Sandbox Code Playgroud)
Plan C。该语句大部分被IODKU替代:
REPLACE ... (same syntax as INSERT, but it does a silent DELETE first)
Run Code Online (Sandbox Code Playgroud)
A 和 B(可能还有 C)是“原子的”,因此不会出现死锁。