ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

重建数据库表所有索引:TaoToken 统一 Key 通道下的批量重建脚本与验证

重建数据库表所有索引:TaoToken 统一 Key 通道下的批量重建脚本与验证 1. 为什么单表索引重建总在业务高峰期翻车索引重建这件事听起来就是一句ALTER INDEX ... REBUILD的事但真放到生产库上翻车姿势五花八门。我见过最典型的一次某张订单表 800 万行索引碎片率 92%运维同学直接在业务高峰执行了全表索引重建结果锁等待把整个下单链路拖垮了 40 分钟。问题不在于「重建」这个动作本身而在于没有把「批量脚本 执行窗口 前后验证」串成一条可回滚的流水线。先说清楚索引重建到底解决什么问题。B 树索引在大量 INSERT/UPDATE/DELETE 之后页分裂会留下空洞碎片率上升导致同样的查询要读更多数据页。碎片率高的时候一个本该走索引的查询可能退化成大量随机 IO。重建索引就是把 B 树重新紧凑排列顺带按新的 FILLFACTOR 留出填充空间让后续写入不那么快再次碎片化。适合重建的场景有三类一是碎片率超过 30% 且表数据量在百万级以上二是索引统计信息严重过期EXPLAIN显示走了错误的索引三是批量导入/归档后索引页利用率明显下降。不适合的场景也要说清楚小表几万行以内重建收益极低直接ANALYZE或VACUUM就够写入极其频繁的热表重建期间要评估锁粒度。这里有个关键点容易被忽略MySQL 和 PostgreSQL 的重建语义完全不同。MySQL 的ALTER TABLE ... ENGINEInnoDB或ALTER INDEX ... REBUILD在 8.0 之后支持 Online DDL但仍有短暂的元数据锁PostgreSQL 的REINDEX默认会持有较强的锁REINDEX CONCURRENTLY才是真正不阻塞写入的版本但它不能放在事务块里失败会留下 invalid 索引需要手动清理。所以「不中断业务」这个目标本质上是三件事的组合选对重建语法在线 vs 离线、控制批量节奏别一次性全表全索引、执行前后用EXPLAIN做量化对比。下面我会把 TaoToken 统一 Key 通道接进来让脚本生成、SQL 审核、执行日志分析这几步都能通过一个入口调用模型能力减少在多个工具之间来回切换的成本。2. TaoToken 统一 Key 通道准备一个 Key 打通脚本生成与 SQL 审核在动手写批量重建脚本之前先把工具链准备好。我自己的习惯是脚本模板让模型帮我生成和审查执行结果让模型帮我分析EXPLAIN差异。这样做的原因是索引重建的 SQL 因数据库版本差异很大手写容易踩语法坑而EXPLAIN输出又很长人工比对费眼。TaoToken 在这里的角色是一个统一的模型调用入口。你不需要为每个模型单独维护一套 Key 和 Base URL一个 Key 就能在脚本生成、SQL 审核、日志分析之间切换模型。对做数据库运维的人来说这意味着你可以把「生成重建脚本」和「分析执行计划」写成两个函数共用同一套鉴权配置。先拿 Key。打开 https://taotoken.net/api-keys 登录后在控制台创建 API Key。建议按用途分 Key一个给脚本生成用一个给生产环境日志分析用方便后续做调用量归因。创建完把 Key 存到环境变量里别硬编码进脚本export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiBase URL 用https://taotoken.net/api注意这个地址不带任何查询参数。模型 ID 按你实际需要的选脚本生成类任务用推理能力强的模型日志分析类任务用长上下文模型。具体可用模型列表在 https://taotoken.net/doc 里能查到控制台里也能看到当前账号可调用的模型。如果你打算长期做数据库运维自动化比如把索引重建、慢查询分析、执行计划对比做成一套 Agent 流程可以看下 Coding Planhttps://taotoken.net/coding-plan 。它适合这种需要反复调用、按周期跑的工程化场景比单次按量调用更好做预算控制。配置验证用一个最简单的请求curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: 你的模型ID, messages: [{role: user, content: 回复 ok}] }返回里有choices[0].message.content就说明通道通了。这一步别跳过后面脚本里所有模型调用都依赖这个配置正确。3. 可复制的批量重建脚本MySQL 与 PostgreSQL 双版本配置这一节是核心给出可以直接复制运行的脚本。我按数据库分两套每套都包含「发现索引 → 生成重建语句 → 分批执行 → 记录日志」四步。脚本里的模型调用部分用 TaoToken 的 OpenAI 兼容接口配置片段单独抽出来方便你替换。3.1 统一配置文件先建一个taotoken.config.json把 Base URL、Key、Model ID 三件套写全{ base_url: https://taotoken.net/api, api_key: sk-你的key, model_id: 你的模型ID, timeout_seconds: 60, max_retries: 3 }这个文件放在项目根目录脚本读取它来初始化客户端。注意base_url结尾不要带/v1SDK 会自己拼。如果你用的是 OpenAI 官方 SDK直接这样初始化import json from openai import OpenAI with open(taotoken.config.json) as f: cfg json.load(f) client OpenAI( base_urlcfg[base_url], api_keycfg[api_key], timeoutcfg[timeout_seconds], max_retriescfg[max_retries], )3.2 MySQL 批量重建脚本MySQL 8.0 之后推荐用ALTER TABLE ... ALTER INDEX ... INVISIBLE/VISIBLE配合 Online DDL但最通用的批量重建还是ALTER TABLE ... ENGINEInnoDB或ALTER INDEX ... REBUILD。下面这个脚本先查出所有碎片率超阈值的索引再逐条生成重建语句-- 查询碎片率超过 30% 的索引 SELECT TABLE_NAME, INDEX_NAME, ROUND(DATA_FREE * 100 / (DATA_LENGTH INDEX_LENGTH DATA_FREE), 2) AS frag_pct FROM information_schema.TABLES WHERE TABLE_SCHEMA 你的库名 AND DATA_FREE 0 AND ROUND(DATA_FREE * 100 / (DATA_LENGTH INDEX_LENGTH DATA_FREE), 2) 30 ORDER BY frag_pct DESC;拿到索引列表后用 Python 生成重建语句并分批执行。关键点是每批之间 sleep 几秒给主从复制留出追赶时间import time import pymysql def rebuild_mysql_indexes(host, user, password, db, batch_size5, sleep_sec3): conn pymysql.connect(hosthost, useruser, passwordpassword, databasedb) cur conn.cursor() cur.execute( SELECT TABLE_NAME, INDEX_NAME FROM information_schema.STATISTICS WHERE TABLE_SCHEMA %s GROUP BY TABLE_NAME, INDEX_NAME , (db,)) indexes cur.fetchall() for i in range(0, len(indexes), batch_size): batch indexes[i:ibatch_size] for table, index in batch: sql fALTER TABLE {table} ALTER INDEX {index} INVISIBLE cur.execute(sql) sql fALTER TABLE {table} ALTER INDEX {index} VISIBLE cur.execute(sql) print(frebuilt {table}.{index}) conn.commit() time.sleep(sleep_sec) cur.close() conn.close()INVISIBLE再VISIBLE这个技巧在 MySQL 8.0 里能触发索引重建同时优化器在重建期间不会选这个索引避免读到中间状态。如果你的版本不支持退回ALTER TABLE ... ENGINEInnoDB但要注意它会重建整张表。3.3 PostgreSQL 批量重建脚本PostgreSQL 必须用REINDEX CONCURRENTLY才能不阻塞写入。注意它不能在事务里跑所以脚本要关掉自动提交-- 查询索引膨胀情况需要 pgstattuple 扩展 CREATE EXTENSION IF NOT EXISTS pgstattuple; SELECT schemaname, tablename, indexname, pg_relation_size(indexrelid) AS index_size, pgstatindex(indexrelid) AS index_stats FROM pg_stat_user_indexes WHERE schemaname public ORDER BY pg_relation_size(indexrelid) DESC;批量重建用 Python 驱动每条REINDEX单独提交import psycopg2 import time def rebuild_pg_indexes(dsn, batch_size3, sleep_sec5): conn psycopg2.connect(dsn) conn.autocommit True cur conn.cursor() cur.execute( SELECT schemaname, indexname FROM pg_stat_user_indexes WHERE schemaname public ) indexes cur.fetchall() for i in range(0, len(indexes), batch_size): batch indexes[i:ibatch_size] for schema, index in batch: sql fREINDEX INDEX CONCURRENTLY {schema}.{index} try: cur.execute(sql) print(frebuilt {schema}.{index}) except Exception as e: print(ffailed {schema}.{index}: {e}) time.sleep(sleep_sec) cur.close() conn.close()REINDEX CONCURRENTLY失败会留下invalid状态的索引用下面这条查出来并清理SELECT indexrelid::regclass AS index_name, indisvalid FROM pg_index WHERE NOT indisvalid;3.4 用 TaoToken 生成和审核脚本上面两套脚本你可以直接复制但实际库的索引命名、分区表、外键约束情况千差万别。我的做法是把表结构 DDL 丢给模型让它生成针对性的重建脚本再让它审查一遍有没有锁风险def generate_rebuild_script(ddl: str, db_type: str) - str: prompt f你是数据库运维专家。下面是 {db_type} 的表结构 DDL {ddl} 请生成批量重建该表所有索引的脚本要求 1. 不阻塞写入MySQL 用 Online DDLPostgreSQL 用 CONCURRENTLY 2. 分批执行每批之间留出复制延迟缓冲 3. 输出可直接运行的 SQL 和对应的 Python 驱动代码 4. 标注每条语句的锁级别和预估耗时 resp client.chat.completions.create( modelcfg[model_id], messages[{role: user, content: prompt}], temperature0.2, ) return resp.choices[0].message.contenttemperature设低一点脚本生成要的是稳定不是创意。生成完别直接上生产先在从库或测试库跑一遍。4. 执行前后 EXPLAIN 对比验证确认查询计划真的改善了重建索引不做前后对比等于白干。验证分两步先看索引本身的物理指标再看查询计划有没有变化。4.1 索引物理指标对比MySQL 用SHOW INDEX看Cardinality和Index_length重建后Cardinality应该更接近真实行数SHOW INDEX FROM 你的表名;PostgreSQL 用pgstatindex看avg_leaf_density重建后应该上升SELECT * FROM pgstatindex(你的索引名);4.2 EXPLAIN 对比选一条你关心的慢查询重建前后各跑一次EXPLAIN ANALYZE重点看三处扫描方式全表扫 vs 索引扫、实际行数 vs 预估行数、执行时间。MySQLEXPLAIN ANALYZE SELECT id, order_no, amount FROM orders WHERE user_id 12345 AND status paid ORDER BY created_at DESC LIMIT 20;PostgreSQLEXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT id, order_no, amount FROM orders WHERE user_id 12345 AND status paid ORDER BY created_at DESC LIMIT 20;把两次输出丢给模型做差异分析比人工比对快得多def compare_explain(before: str, after: str) - str: prompt f对比下面两次 EXPLAIN 输出指出查询计划的关键变化 【重建前】 {before} 【重建后】 {after} 请回答 1. 扫描方式是否从全表扫变为索引扫 2. 预估行数与实际行数的偏差是否缩小 3. 执行时间改善幅度 4. 是否出现新的性能瓶颈 resp client.chat.completions.create( modelcfg[model_id], messages[{role: user, content: prompt}], temperature0.1, ) return resp.choices[0].message.content实测下来碎片率从 80% 降到 10% 以下时同样的查询执行时间通常能降 40% 到 70%具体取决于数据分布和缓存命中率。如果重建后计划没变先检查统计信息是否更新MySQL 跑ANALYZE TABLEPostgreSQL 跑ANALYZE 表名。5. 本篇常见报错排查401、local proxy failed、reading choices、OAuth脚本跑起来之后报错基本集中在两类模型调用失败和数据库执行失败。逐个说。401 UnauthorizedKey 没传对或者过期了。检查Authorization头是不是Bearer sk-xxx格式中间有没有多余空格。如果你把 Key 写在配置文件里确认读取路径正确。还有一种情况是 Key 被禁用去控制台看下状态。local proxy failed这个报错通常出现在你本地配了 HTTP 代理但代理没启动或者规则不对。检查环境变量HTTP_PROXY/HTTPS_PROXY如果不需要代理就 unset 掉。注意 Base URL 必须是https://taotoken.net/api不要自己加端口或路径。reading choices 报错一般是响应体解析失败常见原因是模型返回了非 JSON 内容或者流式响应没处理完就解析。检查请求里stream参数如果设了true客户端要按 SSE 逐块读。另外确认model字段填的是控制台里真实存在的模型 ID填错会返回错误结构。OAuth 相关报错如果你用的是 Claude Code 或 Codex 这类工具接入报 OAuth 失败通常是回调地址或 token 刷新有问题。这类工具接入时Base URL 填https://taotoken.net/apiKey 填控制台创建的 API KeyModel ID 填对应模型。三件套缺一不可只填两个会报鉴权失败。数据库侧的报错也要留意MySQLLock wait timeout exceeded重建时锁等待超时说明有长事务占着表。先查information_schema.INNODB_TRX找出长事务kill 掉再重试。或者把批量调小batch_size降到 1。PostgreSQLREINDEX CONCURRENTLY cannot run inside a transaction block连接设了自动提交但驱动默认开了事务。psycopg2 里设conn.autocommit TrueSQLAlchemy 里用isolation_levelAUTOCOMMIT。invalid index残留REINDEX CONCURRENTLY失败后索引变成 invalid查询会报错。用第 3.3 节的查询找出来DROP INDEX后重建。6. 把索引重建接进日常运维TaoToken 通道的长期用法索引重建不该是一次性动作而应该做成定期巡检的一部分。我的做法是每周跑一次碎片率扫描超过阈值就自动生成重建任务执行完自动做EXPLAIN对比把结果推到运维群。整条链路里模型调用统一走 TaoToken 的 Key不用为每个环节单独配鉴权。具体落地时脚本生成和日志分析用同一个 Key 就行但建议在控制台里按项目分 Key方便看调用量。如果你要把这套流程做成定时任务或者 AgentCoding Plan 比按量调用更合适预算可控也不用担心某次批量分析把额度跑超。模型对话入口在 https://taotoken.net/model-chat 调试 prompt 的时候可以直接在网页上试确认输出格式稳定了再写进脚本。接入文档在 https://taotoken.net/doc 里面有各语言的 SDK 示例和错误码说明遇到报错先查这里。最后给一个实用技巧重建脚本执行前先把要重建的索引列表和预估耗时打印出来人工确认一遍再执行。我踩过的坑是有次脚本把分区表的本地索引也纳入了重建范围结果单个分区重建触发了全局锁。后来在脚本里加了一层过滤排除分区表的本地索引问题就没再出现。索引重建的核心不是 SQL 写得多漂亮而是执行窗口、锁粒度、回滚方案这三件事都想清楚了再动手。
返回列表