Mysql

Percona MySQL 5.5 唯一鍵重複

  • July 15, 2014

我完全沒有想法,所以也許其他人可以回答我的問題。我們有一台來自 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 記錄的錯誤:

並發重複插入可以違反 InnoDB 表中的唯一鍵約束

這個錯誤還沒有修復,也沒有可重現的測試案例。它僅在生產環境中觀察到。

描述的模式是:

  1. 在具有唯一約束的列中插入一個值。
  2. 刪除該行。
  3. 兩個並發會話插入與已刪除行具有相同值的新行。
  4. 兩個會話都送出,並且它們的兩個 INSERT 都成功。

根本原因可能與已刪除行的未完成清除有關。在 InnoDB 中,從索引中刪除條目是一個多步驟的過程。首先,條目是“刪除標記的”,它將條目留在索引中,以便推遲從索引中物理刪除。然後稍後,清除執行緒執行最終刪除,其中可能包括 B 樹的一些重新平衡。

如果您嘗試插入與帶有刪除標記的值相同的值,它只會刪除其刪除標記,並將該值與您插入的新行相關聯。

根據錯誤報告,似乎雖然刪除的條目只是被刪除標記但尚未清除,但兩個並發會話可以插入相同的值。這可能一直發生在非唯一索引上,這沒問題。但是,如果索引是唯一索引,這當然是一個問題。

抱歉,這個錯誤還沒有解決方案。我鼓勵您登錄啟動板並註冊此錯誤會影響您。如果您可以發布有關錯誤如何在您的環境中發生的其他資訊,那也會很有幫助。最重要的是,如果您可以幫助創建可重現的測試案例!

此外,這可能與針對庫存 MySQL 的錯誤有關:錯誤 #69979 具有唯一鍵的列會出現間歇性重複值!雖然有些細節不同。該 MySQL 錯誤被關閉為“不是錯誤”,因為開發人員顯然得出結論,在 InnoDB 的 MVCC 架構中,發生某些衝突並根據競爭條件產生無效結果是可以接受的。恕我直言,這應該為他們贏得響亮的“WTF?!”

引用自:https://dba.stackexchange.com/questions/71522