2016-09-22 69 views
-1

我們有2個表Mysql的併發插入和鎖定

CREATE TABLE `Queue_token` (
    `token_id` int(11) NOT NULL AUTO_INCREMENT, 
    `token_queue_id` int(11) NOT NULL, 
    `total_process_time` smallint(6) NOT NULL, 
    `token_user` int(11) DEFAULT NULL, 
    `created_on` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `join_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `join_time` time NOT NULL, 
    `app_type` tinyint(1) NOT NULL DEFAULT '1', 
    `is_advance` tinyint(1) NOT NULL DEFAULT '0', 
    `is_confirmed` tinyint(1) NOT NULL DEFAULT '1', 
    `token_user_group` int(11) DEFAULT NULL, 
    `uuid` binary(16) DEFAULT NULL, 
    PRIMARY KEY (`token_id`), 
    KEY `join_date_idx` (`join_date`), 
    KEY `queue_join_date` (`token_queue_id`,`join_date`), 
    KEY `token_user` (`token_user`), 
    KEY `fk_token_user_group` (`token_user_group`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

CREATE TABLE `Live_token_sequence` (
    `token_id` int(11) NOT NULL, 
    `queue_id` int(11) NOT NULL, 
    `sequence` int(11) NOT NULL, 
    `time_slot_id` mediumint(9) NOT NULL, 
    `time_slot_sequence` tinyint(4) NOT NULL, 
    `created_on` datetime DEFAULT CURRENT_TIMESTAMP, 
    PRIMARY KEY (`token_id`), 
    KEY `queue_sequence` (`queue_id`,`sequence`), 
    KEY `queue_time_slot` (`time_slot_id`), 
    CONSTRAINT `token_id_seq_fk` FOREIGN KEY (`token_id`) REFERENCES  `Queue_token` (`token_id`) ON DELETE CASCADE ON UPDATE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

基於我們生成用於Live_token_sequence表中的每個令牌的獨特序列中的Queue_token表產生一個日期的令牌的數量。

要生成序列,我們首先從Queue_token表中獲取標記的計數,並且生成的下一個標記獲取count + 1序列。

我們遇到了併發插入令牌變得相同的問題。如果我們嘗試使用SELECT FOR UPDATE,我們將面臨死鎖,因爲上面的count查詢也會與其他表進行連接。

我們如何去做這件事?

更新------ 計數查詢

```

select count(`sminq`.`Queue_token`.`token_id`) 
from `sminq`.`Queue_token` 
join `sminq`.`Live_token_sequence` 
on `sminq`.`Queue_token`.`token_id` = `sminq`.`Live_token_sequence`.`token_id` 
join `sminq`.`Calendar_time_slot` 
on `sminq`.`Live_token_sequence`.`time_slot_id` = `sminq`.`Calendar_time_slot`.`slot_id` 
join `sminq`.`Live_token_status` on `sminq`.`Queue_token`.`token_id` = `sminq`.`Live_token_status`.`token_id` 

left outer join `sminq`.`Status_code` 
on (`sminq`.`Live_token_status`.`token_status_id` = `sminq`.`Status_code`.`status_id` 
and `sminq`.`Status_code`.`status_type` not in (?)) 

where (`sminq`.`Queue_token`.`join_date` >= ? and `sminq`.`Queue_token`.`join_date` < ? 
and `sminq`.`Live_token_sequence`.`queue_id` = ? and `sminq`.`Calendar_time_slot`.`group_id` = ?) for update 

包括新指標後,explin輸出

+------+-------------+---------------------+--------+---------------- ----------------------------------------------+------------+---------+--- -------------------------------------+------+-------------+ 
| id | select_type | table    | type | possible_keys             | key  | key_len | ref         | rows | Extra  | 
+------+-------------+---------------------+--------+---------------- ----------------------------------------------+------------+---------+--- -------------------------------------+------+-------------+ 
| 1 | SIMPLE  | Calendar_time_slot | ref | slot_group,group_slot          | group_slot | 4  | const         | 6 | Using index | 
| 1 | SIMPLE  | Live_token_sequence | ref | PRIMARY,queue_sequence,queue_time_slot,queue_slot,slot_queue | queue_slot | 7  | const,sminq.Calendar_time_slot.slot_id | 1 | Using index | 
| 1 | SIMPLE  | Queue_token   | eq_ref | PRIMARY,join_date_idx          | PRIMARY  | 4  | sminq.Live_token_sequence.token_id  | 1 | Using where | 
+------+-------------+---------------------+--------+---------------- ----------------------------------------------+------------+---------+--- -------------------------------------+------+-------------+ 
+1

你指的是「作爲上面的計數查詢」? – Drew

+0

http://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.html – GhostGambler

+0

包含更新的查詢 – user160108

回答

0

小更易於閱讀:

select count(qt.`token_id`) 
    from `Queue_token` AS qt 
    join `Live_token_sequence` AS seq ON qt.`token_id` = seq.`token_id` 
    join `Calendar_time_slot` AS cts ON seq.`time_slot_id` = cts.`slot_id` 
    join `Live_token_status` AS stat ON qt.`token_id` = stat.`token_id` 
    left outer join `Status_code` AS code 
       ON (stat.`token_status_id` = code.`status_id` 
       and code.`status_type` not in (?)) 
    where (qt.`join_date` >= ? 
     and qt.`join_date` < ? 
     and seq.`queue_id` = ? 
     and cts.`group_id` = ? 
      ) 
    for update 

當心not in (?);如果綁定給出'1,2,3',則可能導致not in ('1,2,3'),即而不是not in (1,2,3)not in ('1','2','3')相同。

而不是COUNT(token_id),你可能想COUNT(DISTINCT token_id)

沒有交易的其餘部分,這是很難討論的鎖定。

同時,讓我們看看可能的加速。

兩個或三個表格似乎在token_id上處於1:1的關係。是不是合併表的一些原因?

建議的額外索引:

seq: INDEX(queue_id, time_slot_id), INDEX(time_slot_id, queue_id) 
(In doing so, KEY `queue_time_slot` (`time_slot_id`) can be removed.) 
cts: INDEX(group_id, slot_id), INDEX(slot_id, group_id) 

取出LEFT JOIN Status_code ... - 什麼都不做,但膨脹計數。請提供EXPLAIN SELECT ...;

+0

解釋包括 – user160108

+0

'EXPLAIN'顯示非常有效的查詢。 (我不記得我爲什麼要求它。) –

0

「要生成序列,我們首先從Queue_token表中獲取標記的計數,並且生成的下一個標記獲取count + 1序列。我們面臨併發插入問題,即令牌獲得相同的序列。 「

這是一個非常糟糕的設計。

如果一行被刪除,您保證會生成一個副本。

如果您有兩個線程同時運行並執行相同的操作,它們幾乎肯定會生成重複的值。這是因爲transaction_isolation_mode,可能無法通過FOR UPDATE修復。

將其替換爲使用AUTO_INCREMENT列。 token_id就是這樣,我不知道爲什麼它不夠用。無論併發性和刪除如何,AUTO_INCREMENT都能保證生成不同的值。 (它沒有說明兩個「併發插入」中的哪一個將具有較小的ID)。

+0

自動增量可以生成非常大的值,token_id用於此目的。但是與tokenId一起,我們需要一個分配給令牌的號碼,每天都可以重複。例如,診所每天獲得100位患者,因此對於每個令牌生成,用戶將得到1-100的數字,並且每天重複。 – user160108

+0

@ user160108 - KEY'queue_join_date'的用途是什麼?或'序列'?或者是其他東西? (對不起,我迷路了。)你可能需要這個鍵是'UNIQUE'。而且您可能需要一個交易來爲兩個患者分配相同的隊列/序列。 –