MySQL 允許在相同欄位上重複建立索引 (Oracle 不允許),導致寫入效能下降、空間浪費、優化器混淆。Percona Toolkit `pt-duplicate-key-checker` 可自動掃描並產生清理 DDL。
# RHEL/CentOS
yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm
yum install percona-toolkit -y
# Ubuntu/Debian
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
dpkg -i percona-release_latest.generic_all.deb
apt-get update
apt-get install percona-toolkit -y
pt-duplicate-key-checker \
--host=10.10.11.208 \
--user=root \
--password=root \
--port=3316 \
--charset=gbk \
--databases=test # 可選,指定資料庫
########################################################################
# test.app_chat_message
########################################################################
# idx_app_chat_message_to_user_from_user is a left-prefix of Index 3
# Key definitions:
# KEY `idx_app_chat_message_to_user_from_user` (`to_user`,`from_user`),
# KEY `Index 3` (`to_user`,`from_user`,`message_id`),
# To remove this duplicate index, execute:
ALTER TABLE `test`.`app_chat_message` DROP INDEX `idx_app_chat_message_to_user_from_user`;
# Index 2 is a left-prefix of Index 3
# KEY `Index 2` (`to_user`,`from_user`),
# KEY `Index 3` (`to_user`,`from_user`,`message_id`),
ALTER TABLE `test`.`app_chat_message` DROP INDEX `Index 2`;
########################################################################
# test.pub_user
########################################################################
# phone is a duplicate of phone_idx
# KEY `phone` (`phone`), KEY `phone_idx` (`phone`)
ALTER TABLE `test`.`pub_user` DROP INDEX `phone`;
# email is a duplicate of email_idx
ALTER TABLE `test`.`pub_user` DROP INDEX `email`;
# account is a duplicate of account_idx
ALTER TABLE `test`.`pub_user` DROP INDEX `account`;
| 類型 | 定義 | 範例 | 處理 |
|---|---|---|---|
| 完全重複 | 欄位順序、數量完全相同 | KEY `a`(c1), KEY `b`(c1) | 刪除任一 |
| 左前綴重複 | 索引 A 欄位是索引 B 的左側前綴 | KEY `a`(c1,c2), KEY `b`(c1,c2,c3) | 刪除較短索引 A |
| 主鍵/唯一鍵重複 | 主鍵/唯一鍵包含於普通索引 | PRIMARY KEY(c1), KEY `a`(c1) | 刪除普通索引 |
pt-online-schema-change \
--alter "DROP INDEX \`idx_dup\`" \
D=test,t=app_chat_message \
--execute \
--max-load Threads_running=50 \
--critical-load Threads_running=100 \
--chunk-time 0.5
#!/bin/bash
# 每月掃描一次
pt-duplicate-key-checker \
--user=root --password=xxx --host=127.0.0.1 \
--no-summary \
--tables-regex='^(?!mysql\.)' \
> /var/log/dup_idx_$(date +%F).log 2>&1
# 郵件通知有輸出時
if [ -s /var/log/dup_idx_$(date +%F).log ]; then
mail -s "MySQL 重複索引檢測報告" dba@company.com < /var/log/dup_idx_$(date +%F).log
fi
# Size Duplicate Indexes 20358902596 -- 重複索引佔用空間 ~20GB
# Total Duplicate Indexes 17 -- 重複索引總數
# Total Indexes 664 -- 總索引數
此例中 2.5% 的索引為重複,佔用 20GB 空間,清理後可顯著降低寫入放大與備份時間。