大きなテーブルに_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'
_
しかし、なぜ? このレコードは存在しません!MAX
は1少ないこれより、クエリの前から変更されていません。だから私はそれを証明します...
_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;
_
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`);