联系我们
18591797788
hubin@rlctech.com
北京市海淀区中关村南大街乙12号院天作国际B座1708室
18681942657
lvyuan@rlctech.com
上海市浦东新区商城路660号乐凯大厦26c-1
18049488781
xieyi@rlctech.com
广州市越秀区东风东路华宫大厦808号1608房
029-81109312
service@rlctech.com
西安市高新区天谷七路996号西安国家数字出版基地C座501
日常运维中,大家普遍认为加字段、删索引、修改字段类型这类实质性表结构变更才会触发 Plan Cache 刷新;仅修改列注释属于纯备注文本调整,不会影响 SQL 执行逻辑,大概率不会刷新执行计划。
今天我们通过一套完整复现实验,推翻固有认知:OceanBase MySQL 租户下,ALTER TABLE MODIFY COLUMN COMMENT 修改列注释,同样会刷新该表关联 SQL 的执行计划缓存,生产运维务必注意该隐性性能损耗。
本次实验在 OceanBase MySQL 租户 test 库中进行,目的是验证“修改列注释”操作是否会刷新该表相关 SQL 的执行计划。
实验对象表为 t_plan_test,建表语句如下:
CREATE TABLE t_plan_test (
id BIGINT NOT NULL PRIMARY KEY,
c1 BIGINT NOT NULL,
c2 VARCHAR(100) DEFAULT NULL COMMENT 'comment_v1',
c3 DATETIME DEFAULT NULL,
KEY idx_t_plan_test_c1 (c1)
);
插入 10 条测试数据后,先执行如下语句清空计划缓存:
ALTER SYSTEM FLUSH PLAN CACHE GLOBAL;
随后连续 3 次执行目标 SQL:
SELECT * FROM t_plan_test WHERE c1 = 1001;
执行结果如下,3 次返回结果一致:
MySQL [test]> SELECT * FROM t_plan_test WHERE c1 = 1001;
+----+------+------+---------------------+
| id | c1 | c2 | c3 |
+----+------+------+---------------------+
| 1 | 1001 | a | 2026-05-06 16:19:30 |
| 4 | 1001 | d | 2026-05-06 16:19:30 |
| 6 | 1001 | f | 2026-05-06 16:19:30 |
| 8 | 1001 | h | 2026-05-06 16:19:30 |
| 10 | 1001 | j | 2026-05-06 16:19:30 |
+----+------+------+---------------------+
5 rows in set
同时通过EXPLAIN查看执行计划,可以看到该 SQL 走的是索引idx_t_plan_test_c1:
MySQL [test]> explain SELECT * FROM t_plan_test WHERE c1 = 1001;
+--------------------------------------------------------------------------------------------------------------+
| Query Plan |
+--------------------------------------------------------------------------------------------------------------+
| =========================================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| --------------------------------------------------------------------------- |
| |0 |TABLE RANGE SCAN|t_plan_test(idx_t_plan_test_c1)|5 |18 | |
| =========================================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([t_plan_test.id], [t_plan_test.c1], [t_plan_test.c2], [t_plan_test.c3]), filter(nil), rowset=16 |
| access([t_plan_test.id], [t_plan_test.c1], [t_plan_test.c2], [t_plan_test.c3]), partitions(p0) |
| is_index_back=true, is_global_index=false, |
| range_key([t_plan_test.c1], [t_plan_test.id]), range(1001,MIN ; 1001,MAX), |
| range_cond([t_plan_test.c1 = 1001]) |
+--------------------------------------------------------------------------------------------------------------+
之后查询计划缓存视图。由于 OceanBase 会对 SQL 做参数化,因此视图中记录的语句并不是 c1 = 1001,而是 c1 = ?。实际查询结果如下:
MySQL [test]> SELECT tenant_id, svr_ip, svr_port, sql_id, plan_id, plan_hash,
-> first_load_time, schema_version, hit_count, executions, statement
-> FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
-> WHERE statement LIKE 'SELECT%t_plan_test%';
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
| tenant_id | svr_ip | svr_port | sql_id | plan_id | plan_hash | first_load_time | schema_version | hit_count | executions | statement |
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
| 1014 | 20.8.XX.XX | 2882 | DD2A9B8AC9A2E9998ACEB39032474DC8 | 11304 | 9462375352054846661 | 2026-05-06 16:19:44.761531 | 1778055563385160 | 2 | 3 | SELECT * FROM t_plan_test WHERE c1 = ? |
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
1 row in set
在不做任何 DDL 变更的情况下,再执行 2 次相同 SQL,然后再次查询计划缓存,结果如下:
MySQL [test]> SELECT tenant_id, svr_ip, svr_port, sql_id, plan_id, plan_hash,
-> first_load_time, schema_version, hit_count, executions, statement
-> FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
-> WHERE statement LIKE 'SELECT%t_plan_test%';
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
| tenant_id | svr_ip | svr_port | sql_id | plan_id | plan_hash | first_load_time | schema_version | hit_count | executions | statement |
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
| 1014 | 20.8.XX.XX | 2882 | DD2A9B8AC9A2E9998ACEB39032474DC8 | 11304 | 9462375352054846661 | 2026-05-06 16:19:44.761531 | 1778055563385160 | 4 | 5 | SELECT * FROM t_plan_test WHERE c1 = ? |
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
1 row in set
可以看到,sql_id、plan_id、plan_hash、first_load_time、schema_version都没有变化,只是hit_count从 2 增加到 4,executions从 3 增加到 5,说明在没有表结构变化的情况下,该 SQL 持续复用原执行计划,没有发生刷新。
接着执行修改列注释语句:
ALTER TABLE t_plan_test
MODIFY COLUMN c2 VARCHAR(100) DEFAULT NULL COMMENT 'comment_v2';
执行成功后,通过SHOW CREATE TABLE 确认列注释已经发生变化,结果如下:
MySQL [test]> SHOW CREATE TABLE t_plan_test;
+-------------+------------------------------------------------------------------+
| Table | Create Table |
+-------------+------------------------------------------------------------------+
| t_plan_test | CREATE TABLE `t_plan_test` (
`id` bigint(20) NOT NULL,
`c1` bigint(20) NOT NULL,
`c2` varchar(100) DEFAULT NULL COMMENT 'comment_v2',
`c3` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_t_plan_test_c1` (`c1`) BLOCK_SIZE 16384 LOCAL
) ...
+-------------+------------------------------------------------------------------+
1 row in set
随后再次执行 2 次目标 SQL,并查询计划缓存,结果如下:
MySQL [test]> SELECT tenant_id, svr_ip, svr_port, sql_id, plan_id, plan_hash,
-> first_load_time, schema_version, hit_count, executions, statement
-> FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
-> WHERE statement LIKE 'SELECT%t_plan_test%';
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
| tenant_id | svr_ip | svr_port | sql_id | plan_id | plan_hash | first_load_time | schema_version | hit_count | executions | statement |
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
| 1014 | 20.8.XX.XX | 2882 | DD2A9B8AC9A2E9998ACEB39032474DC8 | 11403 | 9462375352054846661 | 2026-05-06 16:21:02.846497 | 1778055649996008 | 1 | 2 | SELECT * FROM t_plan_test WHERE c1 = ? |
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
1 row in set
与修改前相比,这里的 sql_id 保持不变,仍为 DD2A9B8AC9A2E9998ACEB39032474DC8,但 plan_id 已经由 11304 变为 11403,first_load_time 由 2026-05-06 16:19:44.761531 变为 2026-05-06 16:21:02.846497,schema_version 也从 1778055563385160 变为 1778055649996008,而 plan_hash 仍保持为 9462375352054846661。
这说明修改列注释后,目标 SQL 的执行计划确实发生了刷新,但刷新前后执行计划形状没有变化。
为进一步验证该观察方式本身有效,实验中又执行了一个明确的表结构变更操作:
ALTER TABLE t_plan_test ADD COLUMN c4 BIGINT DEFAULT NULL;
之后再次执行相同 SQL,并查询计划缓存,结果如下:
MySQL [test]> SELECT tenant_id, svr_ip, svr_port, sql_id, plan_id, plan_hash,
-> first_load_time, schema_version, hit_count, executions, statement
-> FROM oceanbase.GV$OB_PLAN_CACHE_PLAN_STAT
-> WHERE statement LIKE 'SELECT%t_plan_test%';
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
| tenant_id | svr_ip | svr_port | sql_id | plan_id | plan_hash | first_load_time | schema_version | hit_count | executions | statement |
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
| 1014 | 20.8.XX.XX | 2882 | DD2A9B8AC9A2E9998ACEB39032474DC8 | 11405 | 9462375352054846661 | 2026-05-06 16:21:22.235974 | 1778055676741792 | 1 | 2 | SELECT * FROM t_plan_test WHERE c1 = ? |
+-----------+------------+----------+----------------------------------+---------+---------------------+----------------------------+------------------+-----------+------------+----------------------------------------+
1 row in set
可以看到,在执行 ADD COLUMN 之后,plan_id又从 11403 变为 11405,first_load_time和schema_version也再次更新,说明本实验使用的判定方法能够有效识别 DDL 引起的执行计划刷新,因此实验结论可信。
综合本次实验结果可以得出结论:在 OceanBase MySQL 租户中,执行
ALTER TABLE ... MODIFY COLUMN ... COMMENT ...
之后,会触发该表相关 SQL 的执行计划刷新。
该结论的直接证据是:修改列注释前后,目标 SQL 的sql_id保持不变,但plan_id、first_load_time 和 schema_version 发生变化。同时,plan_hash 保持不变,说明刷新前后执行计划形状一致。