日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

on duplicate mysql_mysql 避免重复写入数据的三种方式 和insert ...on duplicate updt... 死锁...

發(fā)布時(shí)間:2023/12/18 数据库 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 on duplicate mysql_mysql 避免重复写入数据的三种方式 和insert ...on duplicate updt... 死锁... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

mysql在存在主鍵沖突或者唯一鍵沖突的情況下,根據(jù)插入策略不同,一般有以下三種避免方法。

1、insert ignore

2、replace into

3、insert on duplicate key update

注意,除非表有一個(gè)PRIMARY KEY或UNIQUE索引,否則,使用以上三個(gè)語(yǔ)句沒有意義,與使用單純的INSERT INTO相同。

一、insert ignore

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

Case:

表結(jié)構(gòu)如下:

復(fù)制代碼

root:test> show create table t3\G

*************************** 1. row ***************************

Table: t3

Create Table: CREATE TABLE t3 (

id int(11) NOT NULL AUTO_INCREMENT,

c1 int(11) DEFAULT NULL,

c2 varchar(20) DEFAULT NULL,

c3 int(11) DEFAULT NULL,

PRIMARY KEY (id),

UNIQUE KEY uidx_c1 (c1)

) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

root:test> select * from t3;

±—±-----±-----±-----+

| id | c1 | c2 | c3 |

±—±-----±-----±-----+

| 1 | 1 | a | 1 |

| 2 | 2 | a | 1 |

| 8 | NULL | NULL | 1 |

| 14 | 4 | bb | NULL |

| 17 | 5 | cc | 4 |

±—±-----±-----±-----+

5 rows in set (0.00 sec)

復(fù)制代碼

測(cè)試插入唯一鍵沖突的數(shù)據(jù)

root:test> insert ignore into t3 (c1,c2,c3) values(5,‘cc’,4),(6,‘dd’,5); Query OK, 1 row affected, 1 warning (0.01 sec)

Records: 2 Duplicates: 1 Warnings: 1

如下,可以看到只插入了(6,‘dd’,5)這條,同時(shí)有一條warning提示有重復(fù)的值。

復(fù)制代碼

root:test> show warnings;

±--------±-----±--------------------------------------+

| Level | Code | Message |

±--------±-----±--------------------------------------+

| Warning | 1062 | Duplicate entry ‘5’ for key ‘uidx_c1’ |

±--------±-----±--------------------------------------+

1 row in set (0.00 sec)

root:test> select * from t3;

±—±-----±-----±-----+

| id | c1 | c2 | c3 |

±—±-----±-----±-----+

| 1 | 1 | a | 1 |

| 2 | 2 | a | 1 |

| 8 | NULL | NULL | 1 |

| 14 | 4 | bb | NULL |

| 17 | 5 | cc | 4 |

| 18 | 6 | dd | 5 |

±—±-----±-----±-----+

6 rows in set (0.00 sec)

復(fù)制代碼

重新查詢表結(jié)構(gòu),發(fā)現(xiàn)雖然只增加了一條記錄,但是AUTO_INCREMENT還是增加了2個(gè)(18變成20)

復(fù)制代碼

root:test> show create table t3\G

*************************** 1. row ***************************

Table: t3

Create Table: CREATE TABLE `t3` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`c1` int(11) DEFAULT NULL,

`c2` varchar(20) DEFAULT NULL,

`c3` int(11) DEFAULT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `uidx_c1` (`c1`)

) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

復(fù)制代碼

二、replace into

replace into 首先嘗試插入數(shù)據(jù)到表中。 如果發(fā)現(xiàn)表中已經(jīng)有此行數(shù)據(jù)(根據(jù)主鍵或者唯一索引判斷)則先刪除此行數(shù)據(jù),然后插入新的數(shù)據(jù),否則,直接插入新數(shù)據(jù)。

使用replace into,你必須具有delete和insert權(quán)限

Case:

復(fù)制代碼

root:test> show create table t3\G

*************************** 1. row ***************************

Table: t3

Create Table: CREATE TABLE t3 (

id int(11) NOT NULL AUTO_INCREMENT,

c1 int(11) DEFAULT NULL,

c2 varchar(20) DEFAULT NULL,

c3 int(11) DEFAULT NULL,

PRIMARY KEY (id),

UNIQUE KEY uidx_c1 (c1)

) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

root:test> select * from t3;

±—±-----±-------±-----+

| id | c1 | c2 | c3 |

±—±-----±-------±-----+

| 1 | 1 | cc | 4 |

| 2 | 2 | dd | 5 |

| 3 | 3 | qwewqe | 3 |

±—±-----±-------±-----+

3 rows in set (0.00 sec)

復(fù)制代碼

插入一條與記錄id=3存在唯一鍵(列c1)沖突的數(shù)據(jù)

復(fù)制代碼

root:test> replace into t3 (c1,c2,c3) values(3,‘new’,8);

Query OK, 2 rows affected (0.02 sec)

root:test> select * from t3;

±—±-----±-----±-----+

| id | c1 | c2 | c3 |

±—±-----±-----±-----+

| 1 | 1 | cc | 4 |

| 2 | 2 | dd | 5 |

| 4 | 3 | new | 8 |

±—±-----±-----±-----+

3 rows in set (0.00 sec)

復(fù)制代碼

可以看到原有id=3,c1=3的記錄不見了,新增了一條id=4,c1=3的記錄.

replace into語(yǔ)句執(zhí)行完會(huì)返回一個(gè)數(shù),來(lái)指示受影響的行的數(shù)目。該數(shù)是被刪除和被插入的行數(shù)的和,上面的例子中2 rows affected .

三、insert on duplicate key update

如果在insert into 語(yǔ)句末尾指定了on duplicate key update,并且插入行后會(huì)導(dǎo)致在一個(gè)UNIQUE索引或PRIMARY KEY中出現(xiàn)重復(fù)值,則在出現(xiàn)重復(fù)值的行執(zhí)行UPDATE;如果不會(huì)導(dǎo)致重復(fù)的問題,則插入新行,跟普通的insert into一樣。

使用insert into,你必須具有insert和update權(quán)限

如果有新記錄被插入,則受影響行的值顯示1;如果原有的記錄被更新,則受影響行的值顯示2;如果記錄被更新前后值是一樣的,則受影響行數(shù)的值顯示0

Case:

復(fù)制代碼

root:test> show create table t3\G

*************************** 1. row ***************************

Table: t3

Create Table: CREATE TABLE t3 (

id int(11) NOT NULL AUTO_INCREMENT,

c1 int(11) DEFAULT NULL,

c2 varchar(20) DEFAULT NULL,

c3 int(11) DEFAULT NULL,

PRIMARY KEY (id),

UNIQUE KEY uidx_c1 (c1)

) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

root:test> select * from t3;

±—±-----±-----±-----+

| id | c1 | c2 | c3 |

±—±-----±-----±-----+

| 1 | 1 | fds | 4 |

| 2 | 2 | ytu | 3 |

| 3 | 3 | czx | 5 |

±—±-----±-----±-----+

3 rows in set (0.00 sec)

復(fù)制代碼

插入一條與記錄id=3存在唯一鍵(列c1)沖突的數(shù)據(jù)

復(fù)制代碼

root:test> insert into t3(c1,c2,c3) values (3,‘new’,5) on duplicate key update c1=c1+3;

Query OK, 2 rows affected (0.01 sec)

root:test> select * from t3;

±—±-----±-----±-----+

| id | c1 | c2 | c3 |

±—±-----±-----±-----+

| 1 | 1 | fds | 4 |

| 2 | 2 | ytu | 3 |

| 3 | 6 | czx | 5 |

±—±-----±-----±-----+

3 rows in set (0.00 sec)

復(fù)制代碼

可以看到,id=3的記錄發(fā)生了改變,c1=原有的c1+3,其他列沒有改變。

結(jié)論:

這三種方法都能避免主鍵或者唯一索引重復(fù)導(dǎo)致的插入失敗問題。

insert ignore能忽略重復(fù)數(shù)據(jù),只插入不重復(fù)的數(shù)據(jù)。

replace into和insert … on duplicate key update,都是替換原有的重復(fù)數(shù)據(jù),區(qū)別在于replace into是刪除原有的行后,在插入新行,如有自增id,這個(gè)會(huì)造成自增id的改變;insert … on duplicate key update在遇到重復(fù)行時(shí),會(huì)直接更新原有的行,具體更新哪些字段怎么更新,取決于update后的語(yǔ)句。

分割線分割線分割線分割線分割線分割線分割線分割線

我們?cè)趯?shí)際業(yè)務(wù)場(chǎng)景中,經(jīng)常會(huì)有一個(gè)這樣的需求,插入某條記錄,如果已經(jīng)存在了則更新它如果更新日期或者某些列上的累加操作等,我們肯定會(huì)想到使用INSERT … ON DUPLICATE KEY UPDATE語(yǔ)句,一條語(yǔ)句就搞定了查詢是否存在和插入或者更新這幾個(gè)步驟,但是使用這條語(yǔ)句在msyql的innodb5.0以上版本有很多的陷阱,即有可能導(dǎo)致death lock死鎖也有可能導(dǎo)致主從模式下的replication產(chǎn)生數(shù)據(jù)不一致。

正文

正如前言說(shuō)的那樣,在實(shí)際業(yè)務(wù)中,曾經(jīng)有過一個(gè)需求就是插入一條業(yè)務(wù)數(shù)據(jù),如果不存在則新增,存在則累加更新某一個(gè)字段的值,于是乎就想到了使用insert… on duplicate key update這個(gè)語(yǔ)句,但是有一天去測(cè)試環(huán)境查看錯(cuò)誤日志時(shí),卻發(fā)現(xiàn)了在多個(gè)事務(wù)并發(fā)執(zhí)行同一條insert…on duplicate key update 語(yǔ)句時(shí),也就是insert的內(nèi)容相同時(shí),發(fā)生 了死鎖。

對(duì)于insert…on duplicate key update這個(gè)語(yǔ)句會(huì)引發(fā)dealth lock問題,官方文檔也沒有相關(guān)描述,只是進(jìn)行如下描述:

An INSERT … ON DUPLICATE KEY UPDATE statement against a table having more than one unique or primary key is also marked as unsafe. (Bug #11765650, Bug #58637)

也就是如果一個(gè)表定義有多個(gè)唯一鍵或者主鍵時(shí),是不安全的,這又引發(fā)了以一個(gè)問題,見https://bugs.mysql.com/bug.php?id=58637

也就是

當(dāng)mysql執(zhí)行INSERT ON DUPLICATE KEY的 INSERT時(shí),存儲(chǔ)引擎會(huì)檢查插入的行是否會(huì)產(chǎn)生重復(fù)鍵錯(cuò)誤。如果是的話,它會(huì)將現(xiàn)有的

行返回給mysql,mysql會(huì)更新它并將其發(fā)送回存儲(chǔ)引擎。當(dāng)表具有多個(gè)唯一或主鍵時(shí),此語(yǔ)句對(duì)存儲(chǔ)引擎檢查密鑰的順序非常敏感。根據(jù)這個(gè)順序,

存儲(chǔ)引擎可以確定不同的行數(shù)據(jù)給到mysql,因此mysql可以更新不同的行。存儲(chǔ)引擎檢查key的順序不是確定性的。例如,InnoDB按照索引添加到

表的順序檢查鍵。首先檢查第一個(gè)添加的索引。

所以,如果主站和從站按不同的順序添加索引,那么如果主從復(fù)制是基于語(yǔ)句的復(fù)制,那么可能最后同一個(gè)語(yǔ)句在master上執(zhí)行和slaver上執(zhí)行的

結(jié)果不一致.

回到死鎖的問題

insert … on duplicate key 在執(zhí)行時(shí),innodb引擎會(huì)先判斷插入的行是否產(chǎn)生重復(fù)key錯(cuò)誤,如果存在,在對(duì)該現(xiàn)有的行加上S(共享鎖)鎖,如果返回該行數(shù)據(jù)給mysql,然后mysql執(zhí)行完duplicate后的update操作,然后對(duì)該記錄加上X(排他鎖),最后進(jìn)行update寫入。

如果有兩個(gè)事務(wù)并發(fā)的執(zhí)行同樣的語(yǔ)句,那么就會(huì)產(chǎn)生death lock,如:

解決辦法:

1、盡量對(duì)存在多個(gè)唯一鍵的table使用該語(yǔ)句

2、在有可能有并發(fā)事務(wù)執(zhí)行的insert 的內(nèi)容一樣情況下不使用該語(yǔ)句

總結(jié)

以上是生活随笔為你收集整理的on duplicate mysql_mysql 避免重复写入数据的三种方式 和insert ...on duplicate updt... 死锁...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。