Percona MySQL 5.5 唯一鍵重複
我完全沒有想法,所以也許其他人可以回答我的問題。我們有一台來自 Percona 的 MySQL 5.5 伺服器,流量很大。該應用程序使用 PHP 編寫,並且始終向 master 寫入。我們同時有 4 個奴隸,我們只能從中讀取。基本上它是一個標準的主從配置。上週發生了所有從伺服器上的複制都被破壞了,所以我檢查了數據庫出了什麼問題。我發現基本上是我的問題,這怎麼會發生:其中一個表的唯一鍵列(不是主鍵)在 2 行中具有相同的值。我試圖找出這種情況是否發生了不止一次,但沒有。它只發生過一次,但我會理解為什麼或如何會發生這種情況。為了更好地理解,這裡有一些來自我們數據庫的真實數據:
show create table registeredUsers; | registeredUsers | CREATE TABLE `registeredUsers` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, `userId` varchar(32) NOT NULL, `client` varchar(200) NOT NULL, `osVersion` varchar(10) NOT NULL, `deviceGroup` varchar(50) NOT NULL, `registrationDate` datetime NOT NULL, `lastAction` datetime NOT NULL, `cultureLanguage` varchar(2) NOT NULL, `cultureRegion` varchar(2) NOT NULL, `cultureCode` varchar(5) NOT NULL DEFAULT 'de-de', `lastPush` datetime DEFAULT NULL, `pushToken` mediumtext, `permaToken` varchar(74) DEFAULT NULL, `accessCount` int(11) NOT NULL DEFAULT '0', `access` varchar(1) NOT NULL DEFAULT '1', `provider` varchar(255) NOT NULL, `providerTld` varchar(5) NOT NULL, `environment` tinyint(1) DEFAULT '0', `udidMd5` varchar(32) NOT NULL, `development` tinyint(1) DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `userId_2` (`userId`), UNIQUE KEY `permaAccessToken_2` (`permaToken`), KEY `client` (`client`), KEY `lastAction` (`lastAction`), KEY `deviceGroup` (`deviceGroup`), KEY `osVersion` (`osVersion`), KEY `cultureCode` (`cultureCode`), KEY `udidMd5` (`udidMd5`) ) ENGINE=InnoDB AUTO_INCREMENT=38466378 DEFAULT CHARSET=utf8 |
有問題的列是 userId,它是唯一的。這是一個查詢,它顯示我們有 2 行具有相同的值:
mysql> select userId, count(userId) as ct from registeredUsers group by userId having ct; +----------------------------------+----+ | userId | ct | +----------------------------------+----+ | 748ec561dbc733452bfd697076787ef9 | 2 | +----------------------------------+----+ 1 row in set (3.53 sec)
我什至無法重現,所以如果有人對這種情況有解釋,那就太好了。
提前謝謝你,塔馬斯
更新 這裡要求的是排序規則的結果:
mysql> SHOW FULL COLUMNS FROM registeredUsers; +------------------+---------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+ | Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment | +------------------+---------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+ | id | bigint(20) unsigned | NULL | NO | PRI | NULL | auto_increment | select,insert,update,references | | | userId | varchar(32) | utf8_general_ci | NO | UNI | NULL | | select,insert,update,references | | | client | varchar(200) | utf8_general_ci | NO | MUL | NULL | | select,insert,update,references | | | osVersion | varchar(10) | utf8_general_ci | NO | MUL | NULL | | select,insert,update,references | | | deviceGroup | varchar(50) | utf8_general_ci | NO | MUL | NULL | | select,insert,update,references | | | registrationDate | datetime | NULL | NO | | NULL | | select,insert,update,references | | | lastAction | datetime | NULL | NO | MUL | NULL | | select,insert,update,references | | | cultureLanguage | varchar(2) | utf8_general_ci | NO | | NULL | | select,insert,update,references | | | cultureRegion | varchar(2) | utf8_general_ci | NO | | NULL | | select,insert,update,references | | | cultureCode | varchar(5) | utf8_general_ci | NO | MUL | de-de | | select,insert,update,references | | | lastPush | datetime | NULL | YES | | NULL | | select,insert,update,references | | | pushToken | mediumtext | utf8_general_ci | YES | | NULL | | select,insert,update,references | | | permaToken | varchar(74) | utf8_general_ci | YES | UNI | NULL | | select,insert,update,references | | | accessCount | int(11) | NULL | NO | | 0 | | select,insert,update,references | | | access | varchar(1) | utf8_general_ci | NO | | 1 | | select,insert,update,references | | | provider | varchar(255) | utf8_general_ci | NO | | NULL | | select,insert,update,references | | | providerTld | varchar(5) | utf8_general_ci | NO | | NULL | | select,insert,update,references | | | environment | tinyint(1) | NULL | YES | | 0 | | select,insert,update,references | | | udidMd5 | varchar(32) | utf8_general_ci | NO | MUL | NULL | | select,insert,update,references | | | development | tinyint(1) | NULL | YES | | 0 | | select,insert,update,references | | +------------------+---------------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+ 20 rows in set (0.00 sec)
更新 2
mysql> SELECT id FROM registeredUsers WHERE userid = '748ec561dbc733452bfd697076787ef9' ORDER BY id DESC ; +----------+ | id | +----------+ | 38245456 | +----------+ 1 row in set (0.00 sec) mysql> select userId from registeredUsers where id in (38245456, 38245457); +----------------------------------+ | userId | +----------------------------------+ | 748ec561dbc733452bfd697076787ef9 | | 748ec561dbc733452bfd697076787ef9 | +----------------------------------+ 2 rows in set (0.00 sec) mysql> select id, userId from registeredUsers where id in (38245456, 38245457); +----------+----------------------------------+ | id | userId | +----------+----------------------------------+ | 38245456 | 748ec561dbc733452bfd697076787ef9 | | 38245457 | 748ec561dbc733452bfd697076787ef9 | +----------+----------------------------------+ 2 rows in set (0.00 sec) mysql> select id, userId from registeredUsers where userId = '748ec561dbc733452bfd697076787ef9'; +----------+----------------------------------+ | id | userId | +----------+----------------------------------+ | 38245456 | 748ec561dbc733452bfd697076787ef9 | +----------+----------------------------------+ 1 row in set (0.00 sec)
更新 3 可以肯定的是,這兩個字元串是相同的,這是一個返回所有 2 行的查詢。(感謝它的建議)
SELECT id FROM registeredUsers WHERE userid >= '748ec561dbc733452bfd697076787ef9' LIMIT 2; +----------+ | id | +----------+ | 38245456 | | 38245457 | +----------+ 2 rows in set (0.00 sec)
這看起來您可能遇到了針對 Percona Server 5.5 記錄的錯誤:
這個錯誤還沒有修復,也沒有可重現的測試案例。它僅在生產環境中觀察到。
描述的模式是:
- 在具有唯一約束的列中插入一個值。
- 刪除該行。
- 兩個並發會話插入與已刪除行具有相同值的新行。
- 兩個會話都送出,並且它們的兩個 INSERT 都成功。
根本原因可能與已刪除行的未完成清除有關。在 InnoDB 中,從索引中刪除條目是一個多步驟的過程。首先,條目是“刪除標記的”,它將條目留在索引中,以便推遲從索引中物理刪除。然後稍後,清除執行緒執行最終刪除,其中可能包括 B 樹的一些重新平衡。
如果您嘗試插入與帶有刪除標記的值相同的值,它只會刪除其刪除標記,並將該值與您插入的新行相關聯。
根據錯誤報告,似乎雖然刪除的條目只是被刪除標記但尚未清除,但兩個並發會話可以插入相同的值。這可能一直發生在非唯一索引上,這沒問題。但是,如果索引是唯一索引,這當然是一個問題。
抱歉,這個錯誤還沒有解決方案。我鼓勵您登錄啟動板並註冊此錯誤會影響您。如果您可以發布有關錯誤如何在您的環境中發生的其他資訊,那也會很有幫助。最重要的是,如果您可以幫助創建可重現的測試案例!
此外,這可能與針對庫存 MySQL 的錯誤有關:錯誤 #69979 具有唯一鍵的列會出現間歇性重複值!雖然有些細節不同。該 MySQL 錯誤被關閉為“不是錯誤”,因為開發人員顯然得出結論,在 InnoDB 的 MVCC 架構中,發生某些衝突並根據競爭條件產生無效結果是可以接受的。恕我直言,這應該為他們贏得響亮的“WTF?!”