本文目录导读:

我来详细介绍如何编写优化数据库索引的脚本,涵盖MySQL、PostgreSQL等主流数据库。
索引检查脚本
MySQL索引分析脚本
-- 查找未使用的索引
SELECT
OBJECT_SCHEMA AS '数据库',
OBJECT_NAME AS '表名',
INDEX_NAME AS '索引名',
COUNT_STAR AS '使用次数',
LAST_USED AS '最后使用时间'
FROM performance_schema.table_index_usage_statistics
WHERE INDEX_NAME IS NOT NULL
AND COUNT_STAR = 0
ORDER BY OBJECT_SCHEMA, OBJECT_NAME;
-- 查找重复索引
SELECT
t.TABLE_SCHEMA,
t.TABLE_NAME,
GROUP_CONCAT(DISTINCT i.INDEX_NAME SEPARATOR ', ') AS '重复索引'
FROM information_schema.TABLES t
JOIN information_schema.STATISTICS i ON t.TABLE_SCHEMA = i.TABLE_SCHEMA
AND t.TABLE_NAME = i.TABLE_NAME
WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema')
GROUP BY t.TABLE_SCHEMA, t.TABLE_NAME, i.COLUMN_NAME
HAVING COUNT(DISTINCT i.INDEX_NAME) > 1;
-- 查找低效索引(索引基数低)
SELECT
TABLE_SCHEMA,
TABLE_NAME,
INDEX_NAME,
COLUMN_NAME,
CARDINALITY,
(SELECT COUNT(*) FROM information_schema.COLUMNS c
WHERE c.TABLE_SCHEMA = s.TABLE_SCHEMA
AND c.TABLE_NAME = s.TABLE_NAME) AS '总行数'
FROM information_schema.STATISTICS s
WHERE TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema')
AND CARDINALITY < 10
ORDER BY CARDINALITY;
PostgreSQL索引分析脚本
-- 查找未使用的索引
SELECT
schemaname || '.' || tablename AS "表名",
indexrelname AS "索引名",
idx_scan AS "扫描次数",
idx_tup_read AS "读取元组数",
idx_tup_fetch AS "获取元组数"
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY schemaname, tablename;
-- 查找重复索引
SELECT
a.indrelid::regclass AS "表名",
array_agg(a.indexrelid::regclass) AS "重复索引"
FROM pg_index a
JOIN pg_index b ON a.indrelid = b.indrelid
AND a.indexrelid != b.indexrelid
AND a.indkey = b.indkey
GROUP BY a.indrelid;
-- 查找碎片化索引
SELECT
schemaname || '.' || tablename AS "表名",
indexrelname AS "索引名",
pg_size_pretty(pg_relation_size(indexrelid)) AS "索引大小",
avg_leaf_density AS "叶密度",
leaf_fragmentation AS "碎片率"
FROM pgstatindex('索引名');
索引优化脚本
MySQL索引优化
-- 创建索引优化建议存储过程
DELIMITER //
CREATE PROCEDURE optimize_indexes(IN db_name VARCHAR(64))
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE tbl_name VARCHAR(64);
DECLARE cur CURSOR FOR
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = db_name
AND TABLE_TYPE = 'BASE TABLE';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO tbl_name;
IF done THEN
LEAVE read_loop;
END IF;
-- 重建表索引
SET @sql = CONCAT('ALTER TABLE ', db_name, '.', tbl_name, ' ENGINE=InnoDB');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 分析表更新统计信息
SET @sql = CONCAT('ANALYZE TABLE ', db_name, '.', tbl_name);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP;
CLOSE cur;
END//
DELIMITER ;
-- 添加必要索引的脚本生成
SELECT CONCAT(
'CREATE INDEX idx_',
TABLE_NAME, '_',
GROUP_CONCAT(COLUMN_NAME ORDER BY ORDINAL_POSITION SEPARATOR '_'),
' ON ', TABLE_SCHEMA, '.', TABLE_NAME,
' (', GROUP_CONCAT(COLUMN_NAME ORDER BY ORDINAL_POSITION), ');'
) AS create_index_sql
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = 'your_database'
AND COLUMN_KEY = '' -- 没有索引的列
AND (DATA_TYPE IN ('int', 'bigint', 'varchar', 'char')
OR COLUMN_NAME LIKE '%id%'
OR COLUMN_NAME LIKE '%fk%')
GROUP BY TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME;
PostgreSQL索引优化
-- 重建索引函数
CREATE OR REPLACE FUNCTION rebuild_indexes(schema_name TEXT)
RETURNS TABLE(index_name TEXT, status TEXT) AS $$
DECLARE
idx RECORD;
BEGIN
FOR idx IN
SELECT indexrelid::regclass::text AS idx_name
FROM pg_stat_user_indexes
WHERE schemaname = schema_name
AND idx_scan > 0 -- 只重建使用的索引
LOOP
BEGIN
EXECUTE format('REINDEX INDEX %I.%I', schema_name, idx.idx_name);
RETURN QUERY SELECT idx.idx_name, 'success'::TEXT;
EXCEPTION WHEN OTHERS THEN
RETURN QUERY SELECT idx.idx_name, SQLERRM::TEXT;
END;
END LOOP;
END;
$$ LANGUAGE plpgsql;
-- 创建覆盖索引建议
SELECT
schemaname,
tablename,
'CREATE INDEX CONCURRENTLY idx_' || tablename || '_covering ON ' ||
schemaname || '.' || tablename || ' (' ||
string_agg(column_name, ', ' ORDER BY column_position) ||
') INCLUDE (' ||
string_agg(include_column, ', ') || ');' AS create_index_sql
FROM (
SELECT
schemaname,
tablename,
column_name,
column_position,
include_column
FROM (
SELECT
schemaname,
tablename,
column_name,
ordinal_position AS column_position,
NULL AS include_column,
COUNT(*) OVER (PARTITION BY schemaname, tablename) AS col_count
FROM pg_catalog.pg_indexes i
JOIN information_schema.columns c
ON c.table_schema = i.schemaname
AND c.table_name = i.tablename
WHERE i.indexdef LIKE '%WHERE%' -- 只处理条件索引
) sub
) sub2
GROUP BY schemaname, tablename;
自动化监控脚本
Python自动化索引优化脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import mysql.connector
from datetime import datetime
import json
class IndexOptimizer:
def __init__(self, host, user, password, database):
self.connection = mysql.connector.connect(
host=host,
user=user,
password=password,
database=database
)
self.cursor = self.connection.cursor(dictionary=True)
def analyze_index_usage(self):
"""分析索引使用情况"""
query = """
SELECT
OBJECT_SCHEMA,
OBJECT_NAME,
INDEX_NAME,
COUNT_STAR,
SUM_TIMER_WAIT/1000000000 AS total_wait_seconds
FROM performance_schema.table_index_usage_statistics
WHERE INDEX_NAME IS NOT NULL
ORDER BY COUNT_STAR ASC
LIMIT 20
"""
self.cursor.execute(query)
return self.cursor.fetchall()
def find_unused_indexes(self, days_threshold=30):
"""查找未使用的索引"""
query = """
SELECT
t.TABLE_SCHEMA,
t.TABLE_NAME,
s.INDEX_NAME,
GROUP_CONCAT(s.COLUMN_NAME ORDER BY s.SEQ_IN_INDEX) AS columns
FROM information_schema.TABLES t
JOIN information_schema.STATISTICS s
ON t.TABLE_SCHEMA = s.TABLE_SCHEMA
AND t.TABLE_NAME = s.TABLE_NAME
LEFT JOIN performance_schema.table_index_usage_statistics u
ON u.OBJECT_SCHEMA = t.TABLE_SCHEMA
AND u.OBJECT_NAME = t.TABLE_NAME
AND u.INDEX_NAME = s.INDEX_NAME
WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema')
AND t.TABLE_TYPE = 'BASE TABLE'
AND (u.COUNT_STAR = 0 OR u.COUNT_STAR IS NULL)
GROUP BY t.TABLE_SCHEMA, t.TABLE_NAME, s.INDEX_NAME
HAVING COUNT(*) > 1
"""
self.cursor.execute(query)
return self.cursor.fetchall()
def suggest_index_improvements(self):
"""建议索引改进"""
query = """
SELECT
t.TABLE_SCHEMA,
t.TABLE_NAME,
GROUP_CONCAT(DISTINCT c.COLUMN_NAME ORDER BY c.ORDINAL_POSITION) AS foreign_keys,
COUNT(DISTINCT i.INDEX_NAME) AS existing_indexes
FROM information_schema.TABLES t
JOIN information_schema.COLUMNS c
ON t.TABLE_SCHEMA = c.TABLE_SCHEMA
AND t.TABLE_NAME = c.TABLE_NAME
LEFT JOIN information_schema.STATISTICS i
ON i.TABLE_SCHEMA = t.TABLE_SCHEMA
AND i.TABLE_NAME = t.TABLE_NAME
AND i.COLUMN_NAME = c.COLUMN_NAME
WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'performance_schema', 'information_schema')
AND c.COLUMN_NAME LIKE '%_id'
AND t.TABLE_TYPE = 'BASE TABLE'
GROUP BY t.TABLE_SCHEMA, t.TABLE_NAME
HAVING COUNT(DISTINCT i.INDEX_NAME) < COUNT(DISTINCT c.COLUMN_NAME)
"""
self.cursor.execute(query)
return self.cursor.fetchall()
def generate_optimization_script(self, output_file='index_optimization.sql'):
"""生成优化脚本"""
unused_indexes = self.find_unused_indexes()
suggestions = self.suggest_index_improvements()
with open(output_file, 'w') as f:
f.write(f"-- Index Optimization Script\n")
f.write(f"-- Generated: {datetime.now()}\n\n")
# 删除未使用的索引
if unused_indexes:
f.write("-- Drop unused indexes\n")
for idx in unused_indexes:
f.write(f"DROP INDEX {idx['INDEX_NAME']} ON {idx['TABLE_SCHEMA']}.{idx['TABLE_NAME']};\n")
f.write("\n")
# 添加建议的索引
if suggestions:
f.write("-- Add suggested indexes\n")
for sug in suggestions:
# 简化的索引创建,实际应根据具体列创建
f.write(f"CREATE INDEX idx_{sug['TABLE_NAME']}_fk ON {sug['TABLE_SCHEMA']}.{sug['TABLE_NAME']} ({sug['foreign_keys']});\n")
def close(self):
self.cursor.close()
self.connection.close()
# 使用示例
if __name__ == "__main__":
optimizer = IndexOptimizer(
host='localhost',
user='root',
password='password',
database='your_database'
)
# 分析索引使用情况
usage = optimizer.analyze_index_usage()
print("Index Usage Analysis:")
for row in usage:
print(f" {row['OBJECT_NAME']}.{row['INDEX_NAME']}: {row['COUNT_STAR']} uses")
# 生成优化脚本
optimizer.generate_optimization_script()
optimizer.close()
索引维护脚本
定期维护脚本
#!/bin/bash
# index_maintenance.sh
# MySQL索引维护
optimize_mysql_indexes() {
local DB_HOST=$1
local DB_USER=$2
local DB_PASS=$3
local DB_NAME=$4
echo "Starting MySQL index optimization..."
# 获取需要优化的表
TABLES=$(mysql -h $DB_HOST -u $DB_USER -p$DB_PASS -N -e "
SELECT CONCAT(TABLE_SCHEMA, '.', TABLE_NAME)
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = '$DB_NAME'
AND TABLE_TYPE = 'BASE TABLE'
AND DATA_FREE > 10000000 # 碎片大于10MB
")
for table in $TABLES; do
echo "Optimizing table: $table"
mysql -h $DB_HOST -u $DB_USER -p$DB_PASS -e "
ALTER TABLE $table ENGINE=InnoDB;
ANALYZE TABLE $table;
OPTIMIZE TABLE $table;
"
done
echo "MySQL index optimization completed"
}
# PostgreSQL索引维护
optimize_postgres_indexes() {
local DB_HOST=$1
local DB_PORT=$2
local DB_NAME=$3
echo "Starting PostgreSQL index optimization..."
# 重建碎片化索引
psql -h $DB_HOST -p $DB_PORT -d $DB_NAME -c "
SELECT schemaname || '.' || tablename AS table_name,
indexrelname AS index_name,
pg_size_pretty(pg_relation_size(indexrelid)) AS size,
ROUND(100 * (1 - avg_leaf_density)) AS fragmentation_percent
FROM pgstatindex('all')
WHERE avg_leaf_density < 0.8
ORDER BY fragmentation_percent DESC;
" | while read line; do
if [[ $line != *"table_name"* ]] && [[ ! -z "$line" ]]; then
echo "Rebuilding index: $line"
psql -h $DB_HOST -p $DB_PORT -d $DB_NAME -c "REINDEX INDEX $line;"
fi
done
echo "PostgreSQL index optimization completed"
}
# 定时任务配置
setup_cron_job() {
# 添加到crontab,每周日凌晨3点执行
(crontab -l 2>/dev/null; echo "0 3 * * 0 /path/to/index_maintenance.sh mysql") | crontab -
echo "Cron job configured for weekly index maintenance"
}
# 主函数
main() {
case "$1" in
mysql)
optimize_mysql_indexes "localhost" "root" "password" "your_database"
;;
postgres)
optimize_postgres_indexes "localhost" "5432" "your_database"
;;
setup_cron)
setup_cron_job
;;
*)
echo "Usage: $0 {mysql|postgres|setup_cron}"
exit 1
;;
esac
}
main "$@"
最佳实践建议
索引优化原则
- 定期检查:每周检查索引使用情况
- 按需创建:只创建必要的索引,避免过度索引
- 监控性能:监控慢查询,根据查询模式调整索引
- 及时清理:定期删除未使用或低效索引
注意事项
- 在生产环境操作前,先在测试环境验证
- 创建或删除索引时注意锁定影响
- 对大表的索引操作选择低峰期
- 备份重要数据和索引定义
这些脚本可以帮助你系统化地管理和优化数据库索引,提高查询性能和数据库整体效率。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播