MySQL 运维提效与 8.0 新特性实战:最快复制表、分区表、instant DDL 与 EXPLAIN ANALYZE
MySQL 运维提效与 8.0 新特性实战最快复制表、分区表、instant DDL 与 EXPLAIN ANALYZE系列第 9 篇 · 对标《MySQL 实战 45 讲》第 41/42/43 讲 8.0 增量延伸实验环境云服务器 Ubuntu 24.04 / 8C14G / MySQL 8.0.46b2 库 t 表 10 万行引言给你 10 分钟把这张表复制到另一个库运维同学大概率都遇到过这种需求“帮我把 b2 库的 t 表原样复制一份到 b2copy 库数据不能丢越快越好”。听起来像一句简单的CREATE TABLE ... SELECT但真到了 10 万行、100 万行甚至上亿行的量级复制的姿势直接决定了你今晚几点下班。是mysqldump一把梭是SELECT ... INTO OUTFILE加LOAD DATA还是干脆把.ibd物理文件拷走本篇不灌鸡汤全部基于我在真实云服务器Ubuntu 24.04 / 8C14G / MySQL 8.0.46上跑出来的实机回显。前半段复盘 45 讲里最贴近日常的三个话题——最快复制表41、grant 之后要不要 flush privileges42、要不要使用分区表43后半段带你把 MySQL 8.0 的几个让 DBA 失业的新特性真正跑一遍invisible index 灰度下线索引、instant DDL 秒加列、EXPLAIN ANALYZE 真实执行剖析、直方图、窗口函数。所有命令的输出均为实机回显宁缺毋假。实验环境开始之前先交代清楚实验底座避免有人拿着结论在自己的 5.5 版本上跑不出来。项目配置操作系统Ubuntu 24.04 (x86_64)规格8 核 / 14G 内存实验机云服务器公网 IP124.70.***.***MySQL 版本8.0.46目标库 / 表b2 库t 表10 万行账号root本地执行一次性实验账号 ua密码 ExpPass_2026先 sanity 一把确认表结构和数据量。t 表结构如下后面所有实验都建立在这张表上$ mysql -uroot b2 -e select count(*) from t; show create table t\G 21 count(*) 100000 *************************** 1. row *************************** Table: t Create Table: CREATE TABLE t ( id int NOT NULL, a int DEFAULT NULL, b int DEFAULT NULL, c varchar(20) DEFAULT x, PRIMARY KEY (id), KEY a (a), KEY ab (a,b) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci10 万行主键id两个二级索引a和联合索引ab(a,b)。这就是我们的实验白鼠。第一部分最快复制表三种姿势到底差多少姿势一mysqldump 逻辑导出 导入最经典、最通用、跨版本最稳的姿势。导出$ time mysqldump -uroot b2 t --single-transaction --set-gtid-purgedOFF /tmp/t_dump.sql 2/dev/null; ls -l --block-sizeK /tmp/t_dump.sql real 0m0.042s user 0m0.015s sys 0m0.006s -rw-r--r-- 1 root root 2314K Jul 29 12:58 /tmp/t_dump.sql10 万行导出只用了real 0.042s生成的 SQL 文件2314K约 2.3MB。--single-transaction保证了一致性快照不会锁表--set-gtid-purgedOFF是我们单机实验避免 GTID 相关噪声。导入到新库 b2copy$ mysql -uroot -e create database if not exists b2copy; time mysql -uroot b2copy /tmp/t_dump.sql real 0m1.206s user 0m0.017s sys 0m0.004s导入耗时1.206s。注意 mysqldump 默认会带上建表、加索引、插数据的全流程索引是在数据导入后再建的所以导入比导出慢一个数量级是正常的。姿势二SELECT INTO OUTFILE LOAD DATA INFILE如果你只需要裸数据搬运CSV 文本路线更快。先确认安全目录限制——这是 8.0 的硬约束绕不开$ mysql -uroot b2 -e show variables like secure_file_priv; 21 Variable_name Value secure_file_priv /var/lib/mysql-files/secure_file_priv被设置为/var/lib/mysql-files/意味着INTO OUTFILE/LOAD DATA INFILE只能读写这个目录这是 8.0 的安全默认值切忌用 root 家目录或/tmp去赌权限。导出 CSV$ time mysql -uroot b2 -e select * from t into outfile /var/lib/mysql-files/t.csv 21; ls -l --block-sizeK /var/lib/mysql-files/t.csv real 0m0.036s user 0m0.004s sys 0m0.001s -rw-r----- 1 mysql mysql 1921K Jul 29 12:58 /var/lib/mysql-files/t.csv导出real 0.036s比 mysqldump 还快一点CSV 文件1921K比 SQL 文件小因为没有INSERT语句开销。导入前先建好同构表OUTFILE 不含 DDL$ mysql -uroot b2 -e drop table if exists t_load; create table t_load like t; 21导入$ time mysql -uroot b2 -e load data infile /var/lib/mysql-files/t.csv into table t_load 21; mysql -uroot b2 -e select count(*) from t_load real 0m0.892s user 0m0.002s sys 0m0.003s count(*) 100000LOAD DATA导入real 0.892s比 dump 的 1.206s 快约 26%且导入后校验count(*)100000数据一条不少。文本协议少了解析 SQL 的开销这是它更快的根本原因。姿势三物理拷贝思路本篇不展开还有第三条路直接拷.ibd文件 DISCARD/IMPORT TABLESPACE。它最快但限制也最多表空间自包含、版本/页大小一致、需要FLUSH TABLES ... FOR EXPORT拿.cfg元数据。日常跨库复制逻辑路线足矣物理拷贝更适合同版本整机迁移这种场景本篇焦点在前面两种。三种姿势真实耗时对比姿势导出耗时(real)产物大小导入耗时(real)含 DDL跨版本mysqldump0.042s2314K SQL1.206s是自带友好OUTFILELOAD0.036s1921K CSV0.892s否需手动建表需注意字符集物理 .ibd 拷贝极快文件级原表.ibd需 IMPORT否严格一致结论10 万行这个量级下OUTFILELOAD 综合最快如果追求省心、要连带索引和表结构一次带走mysqldump 依然是最稳的默认值。第二部分grant 之后到底要不要 flush privileges“我刚GRANT完是不是得FLUSH PRIVILEGES让它生效”——这是面试和群里最高频的迷思之一。45 讲第 42 讲把这件事讲透了我们直接上实机验证。先建一个一次性实验账号ua只给 b2 库的 SELECT 权限$ mysql -uroot mysql -e create user if not exists ua% identified by ExpPass_2026; grant select on b2.* to ua%; show grants for ua%; 21 Grants for ua% GRANT USAGE ON *.* TO ua% GRANT SELECT ON b2.* TO ua%权限已确认ua只对b2.*有 SELECT。关键验证grant 之后立刻用 ua 新开一个连接去查要不要 flush直接试$ mysql -uua -pExpPass_2026 b2 -e select count(*) from t 21 | grep -v Warning count(*) 100000新连接立即生效查到了 100000 行全程没有执行任何FLUSH PRIVILEGES。原因很简单5.7 之后8.0 更是如此GRANT/REVOKE/CREATE USER这些账户管理语句本身就会同时更新内存中的权限缓存和mysql 系统库如mysql.user、mysql.db。既然内存已经是最新的新连接建立时直接读内存就行根本不需要你手动 flush。那FLUSH PRIVILEGES什么时候才需要只有当你绕过 SQL 语句、直接手改mysql系统库表比如UPDATE mysql.user SET ...时内存和磁盘不一致了才需要用FLUSH PRIVILEGES把磁盘刷回内存。正常用GRANT语句永远不需要。再来验证 REVOKE 的即时性把权限收掉$ mysql -uroot mysql -e revoke select on b2.* from ua%; 21然后 ua 再查一次$ mysql -uua -pExpPass_2026 b2 -e select count(*) from t 21 | grep -v Using a password ERROR 1044 (42000): Access denied for user ua% to database b2REVOKE 同样即时生效新连接直接被拒抛出真实的ERROR 1044 (42000): Access denied。这条回显很关键——它证明权限回收也不是等下次登录而是立刻对新建连接生效。一句话记住用GRANT语句管权限别手贱加FLUSH PRIVILEGES只有手改系统表才需要 flush。第三部分到底要不要上分区表分区表是听起来很美、用起来要命的典型。45 讲第 43 讲的态度很明确除非你的场景真的命中分区的核心价值快速 drop 历史数据、分区裁剪缩小扫描面否则普通表 好索引更省心。我们用实机数据说话。建一张按年份分区的表4 个分区p2024 / p2025 / p2026 / pmax$ mysql -uroot b2 -e drop table if exists tpart; create table tpart(ftime datetime not null, c int, primary key(ftime,c)) partition by range(year(ftime)) (partition p2024 values less than (2025), partition p2025 values less than (2026), partition p2026 values less than (2027), partition pmax values less than maxvalue); insert into tpart values(2024-06-01,1),(2025-03-15,2),(2026-01-10,3),(2026-07-01,4); 21插入 4 行分别落进不同年份分区。先看物理文件——每个分区是独立的 .ibd 文件$ ls /var/lib/mysql/b2/ | grep tpart tpart#p#p2024.ibd tpart#p#p2025.ibd tpart#p#p2026.ibd tpart#p#pmax.ibd4 个分区4 个tpart#p#pXXXX.ibd各自独立。这就是分区表可按分区管理物理文件的底层基础。分区裁剪查询只扫命中分区按分区键ftime精确查询$ mysql -uroot b2 -e explain select * from tpart where ftime2026-01-10; 21 id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE tpart p2026 ref PRIMARY PRIMARY 5 const 1 100.00 Using index注意partitions列只列出了p2026。优化器做了分区裁剪partition pruning直接跳过另外 3 个分区扫描面缩小到 1/4rows 估算为 1。没有分区键照样全扫如果 WHERE 条件里没有分区键ftime只用了c$ mysql -uroot b2 -e explain select * from tpart where c3; 21 id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE tpart p2024,p2025,p2026,pmax index PRIMARY PRIMARY 9 NULL 4 25.00 Using where; Using indexpartitions列变成了p2024,p2025,p2026,pmax——四个分区全扫。分区在这里完全没帮上忙反而因为每个分区都要走一遍而增加了管理成本。drop partition历史数据秒删分区表最硬核的卖点就是删某个时间块的数据时不必逐行DELETE产生大量 undo/redo、锁表久而是直接把整个分区物理丢弃$ mysql -uroot b2 -e alter table tpart drop partition p2024; select count(*) from tpart; 21 count(*) 3DROP PARTITION p2024后count(*)从 4 直接变3——2024-06-01那一行随分区整体消失命令几乎是秒级返回无需逐行删除。这对按时间保留日志、订单等冷数据定期清理的场景价值巨大。上不上分区给出判断标准该上超大数据量 明确按时间/范围清理 查询常带分区键命中裁剪。例如日志表、历史订单表。别上小表、查询基本不带分区键、或者你只是想显得专业。分区表有坑——分区数过多影响优化器、某些 DDL 在分区表上行为不同、MDL 锁在分区级操作上也会放大。普通表 合理索引往往更香。本篇提示分区裁剪是否成立完全取决于你的 WHERE 是否带上分区键。没有分区键的查询分区表反而更慢。第四部分8.0 让 DBA失业的新特性实战如果说前三部分是 45 讲的老话题那 8.0 的新特性就是 DBA 效率的真实跃迁。下面每一个我都跑了真实回显。4.1 invisible index灰度下线索引的正确姿势线上要下线一个疑似没人用的索引最怕什么直接DROP INDEX结果某个隐藏的慢查询瞬间全表扫、CPU 打满、告警炸锅。8.0 的invisible index就是为这个场景生的把索引设为对优化器不可见但物理上还在随时能恢复。先看看正常状态下where a5000走哪个索引$ mysql -uroot b2 -e explain select * from t where a5000; 21 id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t NULL ref a,ab a 5 const 1 100.00 NULL正常possible_keys是a,ab最终keya走单列索引a干净利落。现在把索引a设为 invisible$ mysql -uroot b2 -e alter table t alter index a invisible; show index from t where Key_namea; 21 Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Visible Expression t 1 a 1 a A 64897 NULL NULL YES BTREE NO NULLshow index的Visible列变成了NO索引确实被标记为不可见但数据文件里它还在。再看执行计划$ mysql -uroot b2 -e explain select * from t where a5000; 21 id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t NULL ref ab ab 5 const 1 100.00 NULL注意变化possible_keys里不再出现a优化器已经看不见它了最终key落到了联合索引ab上——因为ab(a,b)的最左列也是a仍能服务a5000所以查询没退化为全表扫只是换了个索引。这正是 invisible index 的安全之处即便你下线错了业务 SQL 往往还能靠其他索引兜底而不是瞬间崩。确认没问题后恢复 visible$ mysql -uroot b2 -e alter table t alter index a visible; explain select * from t where a5000; 21 id select_type table partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE t NULL ref a,ab a 5 const 1 100.00 NULLkey立刻回到a。灰度下线索引的标准流程先ALTER ... INVISIBLE观察一阵慢查询、QPS 有无退化稳妥后再DROP INDEX一旦有闪失ALTER ... VISIBLE秒级回滚。4.2 instant DDL10 万行加列只要 0.04 秒传统ALTER TABLE ... ADD COLUMN在大表上是噩梦——要 rebuild 整张表锁、拷贝、重建索引动辄小时级。8.0 的instant DDL改写了这个叙事加列只改数据字典的行版本元数据不碰既有数据页。实测10 万行的 t 表加一列$ mysql -uroot b2 -e set profiling1; alter table t add column tag varchar(32) default n/a, algorithminstant; show profiles; 21 Query_ID Duration Query 1 0.04012975 alter table t add column tag varchar(32) default n/a, algorithminstantDuration 0.04012975 秒。注意显式写了ALGORITHMINSTANT8.0 对加列默认就会优先 instant但显式声明更稳也方便在被迫降级时报错而非偷偷变成 inplace。新增列有默认值n/a旧行的默认值通过行版本在读取时即时补全不回填数据页——这就是它能秒完的底层原理。4.3 一个真实的踩坑想看 row version结果列名报错讲到 instant DDL 的行版本很多文章会让你去information_schema.innodb_tables查total_row_versions。我也试了结果翻车——在本机 8.0.46 上$ mysql -uroot b2 -e select table_name, total_row_versions from information_schema.innodb_tables where name like %b2/t% limit 5; 21 ERROR 1054 (42S22) at line 1: Unknown column table_name in field list [exit1]exit1直接报ERROR 1054 Unknown column table_name。这个坑要如实讲8.0.46 的information_schema.innodb_tables里并没有table_name这个列该视图的表名列实际叫name行版本相关字段在不同小版本间的命名/可用性也有差异。踩到这种照着网上文章抄命令却跑不通的情况第一反应应当是DESC information_schema.innodb_tables;看真实列名而不是怀疑自己。这也是本篇宁缺毋假的原则——实机报错就是报错如实呈现。正确姿势先DESC确认列存在再查想观察 instant 加列痕迹更靠谱的是看information_schema.innodb_tables里本版本真实存在的列如instant_cols或SHOW CREATE TABLE观察元数据变化。4.4 EXPLAIN ANALYZE让优化器不再估算着骗你传统EXPLAIN给的是估算rows、cost 都是优化器猜的。8.0 的EXPLAIN ANALYZE直接真正执行这条语句把实际耗时、实际行数、循环次数打出来。先来个单表范围扫描$ mysql -uroot b2 -e explain analyze select * from t where a between 10000 and 20000\G 21 *************************** 1. row *************************** EXPLAIN: - Index range scan on t using ab over (10000 a 20000), with index condition: (t.a between 10000 and 20000) (cost7585 rows16856) (actual time1.19..18 rows10038 loops1)解读这一行Index range scan on t using ab实际走的是联合索引ab的范围扫描覆盖a between 10000 and 20000。cost7585 rows16856这是优化器的估算猜 16856 行。(actual time1.19..18 rows10038 loops1)这是真跑出来的——实际只扫了10038 行耗时 1.19ms 启动、到 18ms 完成循环 1 次。估算 16856 vs 实际 10038差距近 40%。这就是 EXPLAIN ANALYZE 的价值它能揪出优化器估算严重偏离实际的慢查询根因而不是让你盲调索引。再看一个两表 JOIN 的完整执行树$ mysql -uroot b2 -e explain analyze select count(*) from t t1 straight_join t t2 on t1.at2.id where t1.id1000\G 21 *************************** 1. row *************************** EXPLAIN: - Aggregate: count(0) (cost650 rows1) (actual time1.43..1.43 rows1 loops1) - Nested loop inner join (cost550 rows999) (actual time0.0676..1.39 rows999 loops1) - Filter: ((t1.id 1000) and (t1.a is not null)) (cost200 rows999) (actual time0.0599..0.268 rows999 loops1) - Index range scan on t1 using PRIMARY over (id 1000) (cost200 rows999) (actual time0.0583..0.192 rows999 loops1) - Single-row covering index lookup on t2 using PRIMARY (idt1.a) (cost0.25 rows1) (actual time972e-6..995e-6 rows1 loops999)逐行拆解这棵执行树从下往上读和真正执行顺序一致最内层Index range scan on t1 using PRIMARY over (id 1000)用主键扫 t1 的id1000实际999 行耗时 0.0583…0.192ms。Filter在 t1 上再过滤(id1000) and (a is not null)实际 999 行0.0599…0.268ms。Nested loop inner join对 t1 的 999 行逐行去 t2 上做Single-row covering index lookup using PRIMARY (idt1.a)——这是个覆盖索引回表因为只需要主键每次约 972e-6…995e-6 秒也就是 ~1 微秒循环了999 次loops999对应外层 999 行实际产出 999 行。整个 join 实际耗时 0.0676…1.39ms。最外层 Aggregate count(0)汇总出 1 行1.43ms。一眼就能看出瓶颈在哪join 里那个loops999的嵌套循环每行 ~1 微秒合计约 1ms——非常高效因为走的是主键覆盖索引。EXPLAIN ANALYZE 把循环 999 次这种传统 EXPLAIN 看不到的细节直接摊开调优时再也不用猜。4.5 直方图让优化器不再瞎猜数据分布优化器估算 rows 准不准取决于它对列上数据分布的了解。普通索引只记录有多少不同值不知道哪些值集中、哪些值稀疏。8.0 的**直方图histogram**补上了这块拼图。给b列建一个 32 桶的等宽/等高直方图$ mysql -uroot b2 -e analyze table t update histogram on b with 32 buckets; 21 Table Op Msg_type Msg_text b2.t histogram status Histogram statistics created for column b.再去information_schema.column_statistics看它真实记了什么$ mysql -uroot b2 -e select schema_name, table_name, column_name, json_extract(histogram,$.histogram-type) as htype, json_extract(histogram,$.number-of-buckets-specified) as buckets from information_schema.column_statistics; 21 SCHEMA_NAME TABLE_NAME COLUMN_NAME htype buckets b2 t b equi-height 32实机记录显示列b的直方图类型是equi-height等高直方图桶数32。等高直方图的每个桶装差不多数量的行对偏态分布少数极值撑满、大部分集中描述得比等宽更准。有了它优化器在面对WHERE b 某个高频值时能更精准地估算出会命中几行从而选对索引、选对 join 顺序。直方图适合非索引列的过滤条件估算如果列上已经有索引优先靠索引统计。两者是互补关系。4.6 窗口函数一行 SQL 干掉一堆自连接过去做排名、分组TopN、四分位这类分析得写嵌套子查询甚至自连接又慢又难读。8.0 的窗口函数一句话搞定。先来row_number()倒序排名 ntile(4)四分位取 id20 的前 12 行看效果$ mysql -uroot b2 -e with ranked as (select id, a, row_number() over (order by a desc) as rn, ntile(4) over (order by a) as quartile from t where id20) select * from ranked limit 12; 21 id a rn quartile 19 5900 20 1 1 12340 19 1 11 16174 18 1 5 20151 17 1 6 27626 16 1 17 27906 15 2 18 29319 14 2 13 32497 13 2 15 36618 12 2 12 40951 11 2 7 43531 10 3 20 48234 9 3解读rn是row_number() over (order by a desc)——按a从大到小排的名次。看id19, a5900排第20倒数第一因为 a 最小id20, a48234排第 9逻辑完全自洽。quartile是ntile(4) over (order by a)——把按a升序的行切成 4 等份。前 5 行a 最小的那批都是 quartile1a 升到中段变成 2、再变 3完美体现四分位分组。再做一个真正的四分位统计——每段多少行、a 的 min/max$ mysql -uroot b2 -e select quartile, count(*), min(a), max(a) from (select ntile(4) over (order by a) quartile, a from t where id10000) x group by quartile; 21 quartile count(*) min(a) max(a) 1 2500 7 25225 2 2500 25235 50230 3 2500 50236 75163 4 2500 75177 999864 个四分位每段正好 2500 行共 10000 行a的范围从[7,25225]平滑递进到[75177,99986]。以前要算出这种按数值均匀切四段、每段极值的分布得写好几层自连接现在一个ntile(4)加一层聚合干净利落。踩坑记录实机真实翻车如实记录secure_file_priv 拦路SELECT ... INTO OUTFILE不是随便写路径就能跑的。本机secure_file_priv/var/lib/mysql-files/写别的目录直接被拒。生产上要么改这个变量需重启要么老老实实用指定目录。invisible 后 possible_keys 消失把a设为 invisible 后a从possible_keys中彻底消失查询退而走ab。这是预期行为但如果你只盯着key列没注意possible_keys变化容易误判索引还在用。information_schema.innodb_tables 列名版本差异照抄的total_row_versions/table_name查询在本机 8.0.46 直接ERROR 1054退出码 1。教训不同小版本的information_schema视图列名会变先DESC再查别盲信博客。分区表无分区键必全扫WHERE c3不带ftime四分区全扫分区表毫无优势。设计分区时务必保证高频查询带分区键。LOOP999 的嵌套循环EXPLAIN ANALYZE 暴露 join 内层循环 999 次虽本次每次仅 ~1μs 不慢但若内层不是主键覆盖 lookup 而是全表扫999 次放大就是灾难——用它提前发现隐患。面试高频问答Qgrant 之后必须 flush privileges 吗A不需要。GRANT/REVOKE/CREATE USER会同时更新内存权限缓存和mysql系统库新连接直接读内存即生效。只有手改mysql系统表时才需要FLUSH PRIVILEGES把磁盘刷回内存。Q最快复制一张表的推荐做法A追求省心用mysqldump自带 DDL数据跨版本友好追求速度用SELECT INTO OUTFILELOAD DATA INFILE本机 10 万行导出 0.036s/1921K导入 0.892s比 dump 的 1.206s 快。注意secure_file_priv目录限制。同版本整机迁移可上物理.ibd拷贝。Q分区表一定能提升性能吗A不一定。只有在查询带分区键、命中分区裁剪如本例ftime只扫 p2026或需要DROP PARTITION秒删历史数据时才划算。无分区键的查询如WHERE c3会全分区扫描反而更慢且有分区数过多、DDL 行为差异、MDL 放大等坑。Q8.0 的 instant DDL 加列为什么这么快A只修改数据字典里的行版本元数据旧行默认值在读取时即时补全不回填既有数据页。本机 10 万行加列Duration0.04012975s。注意需ALGORITHMINSTANT8.0 对加列默认也优先 instant且不是所有 DDL 都支持 instant。Qinvisible index 和直接 drop index 有什么区别Ainvisible 只让优化器看不见该索引物理上仍在可随时ALTER ... VISIBLE秒级恢复。适合灰度下线索引先 invisible 观察确认无退化再 drop万一误伤能立刻回滚。直接 drop 没有后悔药。QEXPLAIN 和 EXPLAIN ANALYZE 有什么本质区别AEXPLAIN 只给估算cost/rows 是优化器猜的EXPLAIN ANALYZE 会真正执行输出actual time / rows / loops真实值。本例a between 10000 and 20000估算 16856 行、实际仅 10038 行差距近 40%靠 ANALYZE 才能暴露这种估算偏差。Q直方图和索引在优化器里是什么关系A互补。索引记录不同值数量直方图记录列上数据分布等高/等宽多少桶帮助优化器对非索引列的过滤条件更准地估算行数。本例给b列建了 32 桶等高直方图类型为equi-height。实战落地清单按场景对号入座前面把原理和实机数据都摊开了最后用一张场景对照表把本篇所有能力收敛成可执行的动作方便你直接抄进日常运维 SOP。复制/迁移类单机跨库搬数、且要连带索引结构一次带走首选mysqldump本机 10 万行导出 0.042s、恢复 1.206s它自带 DDL最省心。大数据量纯搬运、可接受手动建表用OUTFILELOAD DATA导出 0.036s、导入 0.892s文本协议比 SQL 解析快约四分之一。切记路径必须落在secure_file_priv指定的/var/lib/mysql-files/。同版本整机迁移、停写窗口允许再考虑物理.ibd拷贝速度上限最高但约束最严。权限类日常授权一律走GRANT/REVOKE语句不要画蛇添足FLUSH PRIVILEGES只有手改mysql系统表导致内存磁盘不一致时才需要 flush。新连接即时生效、即时回收本机已用ERROR 1044验证。分区类命中按时间清理 查询带分区键才上分区否则普通表加好索引更优。上分区后务必用EXPLAIN的partitions列确认裁剪是否生效本例带ftime只扫 p2026带c扫全部分区并善用DROP PARTITION秒删冷数据。8.0 新特性类怀疑某索引无用先ALTER ... INVISIBLE灰度观察确认无退化再DROP误伤可VISIBLE秒回。大表加列/加字段显式ALGORITHMINSTANT10 万行实测 0.04 秒避免 inplace 全表 rebuild。慢查询调优用EXPLAIN ANALYZE看真实actual time/rows/loops别再只信估算值。非索引列过滤条件估算偏差大建直方图本例 32 桶等高补数据分布。排名 / 分组 TopN / 分位统计优先窗口函数row_number()、ntile()替代又慢又难维护的自连接。把这张清单贴在工位上本篇就算没白写。总结45 讲之后MySQL 还在进化从最快复制一张表的 0.036s 文本搬运到 grant 即时生效的权限模型再到分区表裁剪与全扫一线之隔的清醒认知——这些是 45 讲打下的底子解决的是把事做对。而 MySQL 8.0 这一波新特性解决的是把事做快、做稳invisible index让索引下线从赌博变成灰度实验instant DDL把加列从小时级压到 0.04 秒EXPLAIN ANALYZE把优化器从黑盒估算拉到白盒实测直方图补上数据分布这块拼图窗口函数一行顶过去一堆自连接。技术的价值不在新名词多炫而在真实业务里少一次半夜告警、少一次锁表事故、少写一段又臭又长的 SQL。本篇的全部数字都来自真实云服务器的实机回显——可以放心地拿去压测、去面试、去说服你的组长这功能咱们能上。本文实验均在真实云服务器完成输出为实机回显。
