爲什麼 MySQL 唯一索引會導致死鎖,“有心殺賊,無力迴天”?

唯一性索引unique影響

唯一性索引表創建

DROP TABLE IF EXISTS `sc`;
CREATE TABLE `sc` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `name` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
        `class` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
        `score` int(11) DEFAULT NULL,
        `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
        `create_user_id` bigint(11) DEFAULT NULL COMMENT '創建人id',
        `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最後修改時間',
        `modify_user_id` bigint(11) DEFAULT NULL COMMENT '最後修改人id',
        PRIMARY KEY (`id`),
        UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='學生信息表';

在其中創建了唯一索引name,就是說這個學生表同名的學生只能由一位。

命令添加unique:

alter table sc add unique (name);
alter table sc add unique key `name_score` (`name`,`score`);

刪除:

alter table sc drop index `name`;

唯一性索引作用

先行插入部分數據:

insert into sc (name,class,score) values ('呂布','一年二班',67);
insert into sc (name,class,score) values ('趙雲','一年二班',90);
insert into sc (name,class,score) values ('典韋','一年二班',89);
insert into sc (name,class,score) values ('關羽','一年二班',70);

再次查看錶定義:

show create table sc;
CREATE TABLE `sc` (
                      `id` int(11) NOT NULL AUTO_INCREMENT,
                      `name` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
                      `class` varchar(200) CHARACTER SET utf8 DEFAULT NULL,
                      `score` int(11) DEFAULT NULL,
                      `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
                      `create_user_id` bigint(11) DEFAULT NULL COMMENT '創建人id',
                      `modify_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最後修改時間',
                      `modify_user_id` bigint(11) DEFAULT NULL COMMENT '最後修改人id',
                      PRIMARY KEY (`id`),
                      UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='學生信息表';

這時的Auto_Increment=5 再次執行sql:

insert into sc (name,class,score) values ('呂布','二年二班',77)
> 1062 - Duplicate entry '呂布' for key 'name'
> 時間: 0.01s

此時再次查看錶定義,會發現Auto_Increment=6

unique除了在插入重複數據的時候會報錯,還會使auto_increment自動增長

unique與primary key的區別

簡單的講,primary key=unique+not null

具體的區別:

(1) 唯一性約束所在的列允許空值,但是主鍵約束所在的列不允許空值。

(2) 可以把唯一性約束放在一個或者多個列上,這些列或列的組合必須有唯一的。但是,唯一性約束所在的列並不是表的主鍵列。

(3) 唯一性約束強制在指定的列上創建一個唯一性索引。在默認情況下,創建唯一性的非聚簇索引,但是,也可以指定所創建的索引是聚簇索引。

(4) 建立主鍵的目的是讓外鍵來引用.

(5) 一個表最多隻有一個主鍵,但可以有很多唯一鍵

存在唯一鍵衝突時,避免策略

insert ignore

insert ignore會忽略數據庫中已經存在的數據(根據主鍵或者唯一索引判斷),如果數據庫沒有數據,就插入新的數據,如果有數據的話就跳過這條數據.

insert ignore into sc (name,class,score) values ('呂布','二年二班',77)

執行上面的語句,會發現並沒有報錯,但是主鍵還是自動增長了。

replace into

replace into 首先嚐試插入數據到表中。如果發現表中已經有此行數據(根據主鍵或者唯一索引判斷)則先刪除此行數據,然後插入新的數據,否則,直接插入新數據。使用replace into,你必須具有delete和insert權限

replace into sc (name,class,score) values ('呂布','二年二班',77);

此時會發現呂布的班級跟年齡都改變了,但是id也變成最新的了,所以不是更新,是刪除再新增

insert on duplicate key update

如果在insert into 語句末尾指定了on duplicate key update,並且插入行後會導致在一個UNIQUE索引或PRIMARY KEY中出現重複值,則在出現重複值的行執行UPDATE;如果不會導致重複的問題,則插入新行,跟普通的insert into一樣。使用insert into,你必須具有insert和update權限如果有新記錄被插入,則受影響行的值顯示1;如果原有的記錄被更新,則受影響行的值顯示2;如果記錄被更新前後值是一樣的,則受影響行數的值顯示0

insert into sc (name,class,score) values ('關羽','二年二班',80) on duplicate key update score=100;
> Affected rows: 2
> 時間: 0.008s

舊數據中關羽是一年二班,70分,現在插入,最後發現只有分數變成了100,班級並沒有改變。

4 關羽  一年二班  100 2018-11-16 15:32:18   2018-11-16 15:51:51

id沒有發生變化,數據只更新,但是auto_increment還是增長1了。

死鎖

insert … on duplicate key 在執行時,innodb引擎會先判斷插入的行是否產生重複key錯誤,如果存在,在對該現有的行加上S(共享鎖)鎖,如果返回該行數據給mysql,然後mysql執行完duplicate後的update操作, 然後對該記錄加上X(排他鎖),最後進行update寫入。

如果有兩個事務併發的執行同樣的語句,那麼就會產生death lock,如

解決辦法:

1、儘量對存在多個唯一鍵的table使用該語句2、在有可能有併發事務執行的insert 的內容一樣情況下不使用該語句

結論:

這三種方法都能避免主鍵或者唯一索引重複導致的插入失敗問題。insert ignore能忽略重複數據,只插入不重複的數據。replace into和insert … on duplicate key update,都是替換原有的重複數據,區別在於replace

into是刪除原有的行後,在插入新行,如有自增id,這個會造成自增id的改變;insert … on duplicate key update在遇到重複行時,會直接更新原有的行,具體更新哪些字段怎麼更新,取決於update後的語句。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章