web-dev-qa-db-ja.com

MySQLが存在しないのに重複キーにフラグを立てるのはなぜですか?

大きなテーブルに_auto-increment_フィールドを追加する作業をしています。

フィールドは既に入力されており(約1〜500M)、新しい挿入は手動で正しく増加しています。確認するために、重複を表示するクエリを実行しました。

_mysql> SELECT new_id, COUNT(*) AS count FROM my_table GROUP BY new_id HAVING count > 1;
#      No Results
_

そして、手動で最新のレコードを見ると、自動インクリメントするBIGINT(20)フィールドのように見えます。そして、その列にはNULLはありません。

それでも、インデックスを_UNIQUE INDEX_(一意でないインデックスから、一意の値を持つ)に変更しようとすると、_DUPLICATE KEY_エラーがトリガーされます。また、重複キーは常にMAX(new_id)+1であるため、問題の重複のSELECTは常に結果を返しません。

これが私が実行しているものです:

_mysql> SELECT MAX(new_id) FROM my_table;
+-------------+
| MAX(new_id) |
|   512345678 |
+-------------+
1 row in set (0.00 sec)

mysql> ALTER TABLE my_table
        DROP INDEX `idx_new_id`,
        ADD UNIQUE INDEX `idx_new_id ` USING BTREE (`new_id `);

ERROR 1062 (23000): Duplicate entry '512345679' for key 'idx_new_id'
_

しかし、なぜ? このレコードは存在しません!MAX1少ないこれより、クエリの前から変更されていません。だから私はそれを証明します...

_mysql> SELECT MAX(new_id) FROM my_table;

+-------------+
| MAX(new_id) |
|   512345678 |
+-------------+
1 row in set (0.00 sec)

mysql> SELECT * FROM my_table WHERE new_id = 512345679;
# No results
_

レコード_512345679_が存在しない場合、MySQLは_512345679_を重複としてフラグ付けしたのはなぜですか?

私はこれまでのところ不成功です問題の複製 ゼロから。 ( db-fiddle

次にどこを見ればいいですか?

最終的に私は主キーを別のフィールドからこのフィールドに変更することに向かっています。しかし、一意のIDを事前に入力することでダウンタイムを防止しようとしています(すでに完了しています)。この手順で問題がなければ、この_new_id_フィールドを_PRIMARY KEY_およびAUTOINCREMENTに切り替えます。


_SHOW CREATE_の_my_table_は次のとおりです。

_CREATE TABLE `my_table` (
  `new_id` bigint(20) NOT NULL,
  `id` bigint(20) NOT NULL,
  `username` varchar(80) NOT NULL,
  `name` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`),
  KEY `idx_username` (`username `),
  KEY `idx_new_id` (`new_id`) USING BTREE,
  KEY `idx_id` (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
_
3
Ryan

MacOS用のMySQL 5.7.12でコードを実行してみました。これを複製することはできませんが、何か面白いものを見つけました。

SQL-FiddleにはMySQL 5.7.17があります。 MySQL 5.7.13でエラーが発生しました。これらのバージョン間で修正されたセカンダリインデックスの削除と追加に関して何かありましたか?そうだったと思います。 5.7.14のリリースノート、小見出しBugs Fixed の下の箇条書き21に注意してください。

InnoDB:セカンダリインデックスを削除および追加するALTER TABLE操作のロールバック中にアサーションが発生しました。バグ#22005726)

質問:セカンダリインデックスとは何ですか? MySQL Documentation for Clustered and Secondary Indexes]の見出しの下によると、セカンダリインデックスとクラスタ化インデックスの関係

クラスタ化インデックス以外のすべてのインデックスは、セカンダリインデックスと呼ばれます。 InnoDBでは、セカンダリインデックスの各レコードには、行のプライマリキー列と、セカンダリインデックスに指定された列が含まれます。 InnoDBはこの主キー値を使用して、クラスター化インデックス内の行を検索します。

これは、UNIQUE INDEXが単なるセカンダリインデックスであることを示しています。セカンダリインデックスでは、すべてのインデックスエントリにPRIMARY KEYのコピーが添付されます。

私の主張では、同じ列にインデックスを同時に削除および作成することに関しては、5.7.12には存在しないバグが5.7.13に導入されました。

ALTER TABLEコマンドを振り返ってください

mysql> ALTER TABLE my_table
        DROP INDEX `idx_new_id`,
        ADD UNIQUE INDEX `idx_new_id` USING BTREE (`new_id `);

特定のインデックス名(idx_new_id)と特定の列リスト(1列のみnew_id)のインデックスを削除し、同じ名前で同じ列リストの新しいインデックスを追加するとしましたが、インデックスUNIQUEにします。正味の効果は、インデックスが実際に削除されることはなく、UNIQUE属性が既存の一意でないインデックスに単にアタッチされたことです。次に、内部でmysqldが、すでに入力されているテーブルとまだ入力されているインデックスにデータをロードしようとしたため、重複キーエラーが発生しました。私がこれを主張しているだけであることに注意してください。これが本当に発生しているかどうかを確認するには、5.7.13のソースコードを確認する必要があります。

それにもかかわらず、5.7.12および5.7.14ではこの問題を再現できません。これは、この奇妙な状態が5.7.14の作成時に対処されたことを示しています。

私のすべての推測に照らして、ここに私の提案があります:

提案

提案#1:5.7.14以降にアップグレードする

提案#2:ALTER TABLEを変更して別のインデックス名を使用する

mysql> ALTER TABLE my_table
       DROP INDEX `idx_new_id`,
       ADD UNIQUE INDEX `uniq_idx_new_id` USING BTREE (`new_id`);

提案#3:2 ALTER TABLEコマンドを実行する

mysql> ALTER TABLE my_table DROP INDEX `idx_new_id`;
mysql> ALTER TABLE my_table ADD UNIQUE INDEX `uniq_idx_new_id` USING BTREE (`new_id`);

試してみる !!!

1
RolandoMySQLDBA