新闻详情

新闻详情

首页 / 资讯中心 / 详情

MySQL与Oracle精度扩展DDL对比:为何MySQL会卡死而Oracle秒级完成?

发布时间:2026/9/26 17:23:38来源:尧图网络
MySQL与Oracle精度扩展DDL对比:为何MySQL会卡死而Oracle秒级完成?
先把基调定下来这是一个纯粹基于实际运维对比的复盘不涉及“谁比谁先进”的无意义口舌只记录我在 MySQL 和 Oracle 上执行同一类“精度扩展 DDL”时看到的真实差异以及它们背后牵动的运维决策。做数据库这行的人都知道改字段精度是一件“看起来人畜无害实际能让人血压飙升”的事。尤其当你在一张线上大表上执行ALTER TABLE t MODIFY amount DECIMAL(18, 2)这类语句时MySQL 可能让你服务的写入在几分钟甚至几十分钟内全部卡死而同一个需求扔到 Oracle 上基本就是秒级返回。为什么差距这么大这篇文章就把这个场景掰开揉碎从锁机制、底层原理、实验复现到故障排查一次讲清楚。这篇文章适合谁三类人一类是刚接触数据库、对 DDL 阻塞没有直观概念的开发者一类是被线上 MySQL 大表变更折磨过的 DBA想搞明白当时为什么卡还有一类是同时维护 MySQL 和 Oracle 两套数据库的混合环境运维。文章里的命令、思路、指标都能直接拿去用。1. 先搞清楚“精度扩展”到底是什么类型的变更1.1 为什么偏偏是 DECIMAL 精度扩展先说业务背景。金融、电商、财务系统里金额字段几乎都选DECIMAL很少用FLOAT和DOUBLE。原因不是玄学而是DECIMAL是定点数按十进制存储和计算不会出现二进制浮点那种 0.1 0.2 ≠ 0.3 的尴尬。比如订单金额DECIMAL(10, 2)意思是整数部分最多 10 位、小数 2 位最大能存 99999999.99。问题往往出在业务扩张上。最开始单笔交易金额设计成 8 位整数感觉够用后来做批发、做 B2B 大额订单一笔交易几千万DECIMAL(10, 2)直接溢出。更经典的是积分、折扣率这类字段原本DECIMAL(4, 4)存 0.0001 到 0.9999后来要支持千分位的万分比只能改DECIMAL(6, 6)。这类变更在 Oracle 里叫修改字段类型长度在 MySQL 里叫MODIFY COLUMN。表面看就是“数字变长一点”但底层引擎的响应差距可以按“秒”和“小时”来对比。1.2 MySQL 的 DDL 算法演进COPY、INPLACE 与 INSTANTMySQL 处理 DDL 不是一蹴而就的。早期版本5.5 之前几乎都是 COPY 算法执行ALTER TABLE时会新建一张临时表把原表数据一行行拷过去期间原表加锁所有读写全部阻塞。5.6 开始引入 Online DDL有了ALGORITHMINPLACE部分操作可以在不复制全表的情况下完成但底层依然需要 rebuild 表。8.0 又加入了INSTANT算法号称“秒级加列”。很多人被这个宣传误导以为所有 DDL 都能秒级完成。实际上INSTANT只支持少数操作比如在表末尾加列、删除列、修改列的默认值而且有严格限制。精度扩展这种需要改变行内存储布局的操作绝大多数情况下会落到COPY 或 INPLACE 的 rebuild 分支。MySQL 官方文档对MODIFY COLUMN的算法矩阵写得很清楚如果只改数据类型长度且存储要求变大INPLACE是允许的但前提是“不改变已有行的存储格式”。这个前提恰好是精度扩展最容易打破的因为DECIMAL(10,2)和DECIMAL(18,2)在 InnoDB 的行记录里占用的字节数很可能不一样。一旦行格式需要变化MySQL 只能选择全表 rebuild。1.3 Oracle 的 DDL 模型字典更新与在线重定义Oracle 处理 DDL 的思路完全不同。在 Oracle 里表结构信息存放在数据字典中执行ALTER TABLE t MODIFY (amount NUMBER(18,2))时多数情况下只需要更新数据字典里的长度定义并不会逐行读取和重写表数据。这就是 Oracle 能在秒级返回的核心原因。当然Oracle 也不是所有 DDL 都这么轻松。部分操作仍需通过DBMS_REDEFINITION在线重定义比如修改表分区方案、把普通表改成分区表。但对于“扩大 NUMBER 精度”这类操作Oracle 的处理路径确实轻量得多。这里有一个常见误解需要澄清很多人以为 Oracle 的NUMBER类型是无所谓精度的其实 Oracle 允许你声明NUMBER(10,2)来约束业务层只是底层存储会按实际数值动态调整空间所以“精度放大”往往不需要触碰已存在的行。2. 同一个 ALTER两种数据库的“现场反应”2.1 复现实验环境准备为了把对比落到细节上我搭了两个环境MySQL 8.0.32 和 Oracle 19c 单实例。服务器是 8 核 16G 的虚拟机数据库放在同一块 SSD 上避免硬件差异影响判断。测试表结构统一为CREATE TABLE payment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32), amount DECIMAL(10,2), status TINYINT, created_at DATETIME );MySQL 侧用存储过程灌入 2000 万行数据Oracle 侧用 PL/SQL 循环插入同样的行数。业务模拟用一个持续写入的会话每秒插入 100 条记录观察执行ALTER TABLE期间写入是否被打断。2.2 MySQL 8.0 实测阻塞真实存在在 MySQL 上执行ALTER TABLE payment MODIFY amount DECIMAL(18,2), ALGORITHMINPLACE;我故意显式指定ALGORITHMINPLACE而不是让 MySQL 自己选。执行后先查一下进度和阻塞情况SELECT * FROM information_schema.innodb_trx\G SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMAtest\G观察到的现象很典型DDL 本身需要申请独占的 MDL 锁metadata lock在等待队列里排在所有旧 SQL 之后业务侧持续写入的小事务会不断延长 MDL 等待形成一个“更新频率越高DDL 越难拿到锁”的恶性循环一旦 DDL 拿到锁开始 rebuild原表的写入会话被挂起SHOW PROCESSLIST看到大量Waiting for table metadata lock整个 rebuild 过程持续了约 6 分 40 秒期间线上写入完全中断。这个结果其实一点都不意外。DECIMAL(10,2)在 InnoDB 中按变长字段存储占用字节数和整数位、小数位相关。DECIMAL(18,2)的存储需求变大行格式变化InnoDB 就需要重建整张表的聚簇索引。期间在线 DDL 的INPLACE虽然允许并发 DML但前提是 DDL 已经拿到 MDL 锁之后而 DDL 拿锁之前的所有写事务都会成为阻塞源。2.3 Oracle 19c 实测几乎无感完成Oracle 侧执行ALTER TABLE payment MODIFY (amount NUMBER(18,2));我同时开了一个会话持续INSERT。执行结果DML 完全没受影响ALTER TABLE的响应时间不到 0.5 秒。看V$SESSION_LONGOPS也找不到需要长时间运行的重组任务因为这只是数据字典层面的修改。有一个细节值得注意直接把精度从 10 扩大到 18Oracle 不会重写数据文件。它只是把字段长度上限从 10 位改成 18 位已经存在的行依然按原样存储新写入的数据允许更大的整数部分。反过来如果你把NUMBER(18,2)缩小成NUMBER(10,2)Oracle 就会要求扫描并校验已有数据此时才可能产生锁等待。所以“扩大安全、缩小有风险”这条经验在两边都成立只是 MySQL 的扩大也不安全。2.4 两张表的直观对比维度MySQL 8.0Oracle 19c精度扩大是否重写行数据是通常需要 rebuild否仅更新数据字典是否要求短暂的 MDL 排他是不阻塞 DML大表上执行耗时分钟级到小时级秒级是否适合业务高峰执行严格不建议可行但仍需谨慎缩小精度时的风险同样可能全表扫描校验可能检查数据溢出需关注3. 为什么 MySQL 比 Oracle 更容易被 DDL 卡死3.1 MDL 锁MySQL 的隐形卡点很多开发对 MySQL 的锁认知停留在“行锁”“表锁”上却忽略了metadata_locks这只隐形的手。在 MySQL 8.0 中任何 SQL 在执行前都要先获取对应的 MDL 锁DML 获取的是共享 MDL而ALTER TABLE获取的是排他 MDL。问题在于 MDL 的排队策略。排他 MDL 请求必须等待所有已持有的共享 MDL 释放同时新的共享锁请求也会排在排他锁之后。这意味着只要有一个慢查询长时间持有表的 MDLDDL 就一直等而 DDL 等锁期间新到的普通查询也被堵住形成“一个慢查询拖死整个表”的雪崩效应。精度扩展在大表上本来就是长任务长任务在前面排队时后面不断有写流量到达于是阻塞窗口被人为拉长这是 MySQL 运维里最容易踩的隐性坑。解决这类问题的常用手段是lock_wait_timeout和max_execution_time但更治本的做法还是选在业务低峰变更或者用后面要讲的第三方工具。3.2 InnoDB rebuild 的代价CPU、IO、临时空间三高InnoDB 执行 rebuild 类 DDL 时会创建原表的临时副本逐行读取旧数据、生成新行格式、写入新表空间最后用新表替换旧表。这个过程中CPU 消耗在数据转换和索引构建上IO 消耗在读写临时表空间和旧表扫描上磁盘空间需要大约原表 1.5~2 倍余量如果表上有二级索引每个索引都要重建时间和 IO 再翻倍。精度扩展恰好落在 rebuild 路径上所以无论你多小心翼翼大表上的几十 GB 数据都在实实在在过一遍 IO。这也是为什么明明线上业务低峰DBA 依然要盯着磁盘空间和 IO 饱和度怕的不只是锁还有中途磁盘写满导致的变更失败。3.3 Oracle 的数据块结构与“补偿记录”机制Oracle 之所以能秒级完成精度扩大的 DDL除了不rewrite现有数据之外还因为它的行迁移机制和 UNDO 段设计比较灵活。在 Oracle 中字段长度信息在行头、列头的数据字典中均能体现扩大精度只要让新写入的数据按新格式排列即可旧数据块完全不用动。同时Oracle 的 DDL 本身是事务性的失败可以回滚。MySQL 8.0 里 DDL 虽然也支持原子性通过 data dictionary 和崩溃恢复机制但变化过程中的锁影响不会因为“最终能回滚”就给应用层减轻感知——应用层只关心那段“我不能写数据”的时间窗口有多长。3.4 一个实验验证MySQL 为何必须全表 rebuild为了验证“行格式变化导致 rebuild”这个结论我用innodb_ruby工具导出了 InnoDB 记录格式观察DECIMAL(10,2)与DECIMAL(18,2)在行中的占用DECIMAL(10,2)整数部分 10 位、小数 2 位占用 5 字节每 9 位用 4 字节余数部分用剩余字节DECIMAL(18,2)整数部分 18 位、小数 2 位占用 8 字节。字节数从 5 变为 8行内变长字段长度改变B 树叶子节点上的记录格式必须统一所以无法做到“只改字典不动数据”。搞清楚这个原理你就能理解为什么 MySQL 官方不敢把精度扩展列为 INSTANT。这不是 MySQL 偷懒而是 InnoDB 的行存储架构决定的。4. 从实操层面压缩 DDL 阻塞窗口4.1 先判断你的 DDL 属于哪条路径执行任何ALTER TABLE之前先确认它会走哪种算法。MySQL 8.0 提供了简单方法ALTER TABLE payment MODIFY amount DECIMAL(18,2), ALGORITHMINSTANT;如果返回Unsupported algorithm说明不能 INSTANT。再用ALTER TABLE payment MODIFY amount DECIMAL(18,2), ALGORITHMINPLACE, LOCKNONE;锁级别LOCKNONE代表允许并发 DML只要 MySQL 允许它会尽量以这个模式执行。如果这里也报错说明该 DDL 在 MySQL 8.0 里只能走 COPY 或者 INPLACE共享锁那就要做好阻塞准备。注意一点LOCKNONE只是说 DDL 开始后允许并发 DML不代表 DDL 等待 MDL 时不会被阻塞。前面讲过的 MDL 等待问题依然存在。4.2 使用 pt-online-schema-change 绕开原生 DDL 阻塞如果你没法接受“几十分钟不可写”最成熟的做法是用 Percona Toolkit 的pt-osc。它的思路很朴素创建一张与目标表结构一致的空表在空表上执行 ALTER在原表上创建触发器把变更期间的增删改同步到新表分批把原表数据拷贝到新表完成后用RENAME TABLE原子切换。优点是把 DDL 阻塞转换成“触发器复制、分批拷贝、原子切换”三个阶段业务感知的写入中断只有最后切换的一瞬间。代价是拷贝期间有触发器开销且原表不能有已经触发的触发器冲突。实际使用中我用的命令是pt-online-schema-change \ Dtest,tpayment \ --alter MODIFY amount DECIMAL(18,2) \ --max-lag5 \ --chunk-size1000 \ --critical-loadThreads_running200 \ --execute--max-lag5是让复制从库延迟超过 5 秒就暂停避免主从延迟被拖垮。--chunk-size控制每批拷贝行数太大会导致单次 SQL 执行过久太小又影响总耗时通常根据主键范围和 IO 能力在 500~2000 之间调。4.3 如果必须用原生 DDL怎么把时间窗口压到最小有些环境装不了 pt-osc或者变更非常紧急只能原生 DDL。这种情况下我的经验是先清理长时间运行的“僵尸查询”特别是 APP 里漏掉的SELECT这是 MDL 排队最常见的源头把lock_wait_timeout调小一点比如 5 秒避免 DDL 无限等下去用performance_schema.metadata_locks持续观察谁在占锁变更前收缩ibtmp的临时表空间上限并确认磁盘余量能容纳表副本变更放在凌晨 2~5 点并让业务方做好写入暂停 5~15 分钟的心理准备。这里再补充一个容易被忽略的点如果表上有全文索引、外键约束原生 DDL 的锁级别会被强制放宽LOCKNONE很可能失效。变更前用SHOW CREATE TABLE检查一遍约束可以避免执行到一半才发现被额外锁住。4.4 Oracle 侧同样要留个心眼Oracle 不是高枕无忧。精度扩大不重写数据但精度缩小会。缩小到比现有数据范围还小Oracle 会报ORA-01438之类的值超出精度错误如果数据规模大它还可能触发对段内数据的扫描这时仍然可能阻塞 DML。Oracle 的做法是在业务低峰先执行一次校验性查询SELECT MAX(LENGTH(TO_CHAR(ABS(amount)))) FROM payment;用最大值判断新精度是否安全避免直接 ALTER 后碰线。另一个经验是Oracle 里加了NOT NULL或CHECK约束的字段修改精度时要连带处理约束否则个别版本会锁住等待外键检查。4.5 变更后的验证指标变更完成后不要只盯着ALTER语句是否成功。重点看三件事主从或 DG 同步是否正常看延迟是否归零业务写入的响应时间恢复到日常水位表的行数与变更前保持一致二级索引状态正常。MySQL 8.0 下可以查information_schema.innodb_tables和statistics确认行数、索引。Oracle 下查DBA_TAB_COLUMNS确认字段长度已经更新并检查相关物化视图、索引是否需要刷新。5. 实战中的故障复盘与排查清单5.1 一次 DECIMAL 精度扩展把数据库“卡死”的真实现场去年给一个客户做数据库健康检查正赶上他们有一次紧急变更订单金额要从DECIMAL(10,2)扩到DECIMAL(16,2)。客户技术负责人说“就在一个几百万行的表上改个小数位应该很快”直接在工作时间执行了原生 DDL。结果 10 分钟后监控告警全部触发CPU 接近 90%IO 等待显著上升订单写入 QPS 掉到 0。当时SHOW PROCESSLIST已经是这样------------------------------------------------------------------------------------ | Id | User | Host | db | Command | Time | State | ------------------------------------------------------------------------------------ | 10 | app_user | 10.0.0.5 | shop | Query | 612 | Waiting for table metadata lock| | 11 | app_user | 10.0.0.6 | shop | Query | 234 | Waiting for table metadata lock| | 12 | dba_replica | localhost | shop | Query | 88 | altering table | ------------------------------------------------------------------------------------明显是 DDL 在等 MDL而后面的业务查询又在等 DDL。复制会话也被拖住从库开始堆积延迟。当时如果直接KILLDDL 也不安全因为已经进入 rebuild 流程强制中断可能导致回滚时间比执行时间还长。5.2 从 10 分钟到秒级这个 case 是怎么被救回来的我介入后做了三步确认只有一个 DDL 会话在排队performance_schema.metadata_locks里显示是 app 侧握手连接持有了共享 MDL然后这个连接还有一条未提交的SELECT。先找到源头事务等它提交或直接杀掉那个空闲连接MDL 队列立刻松动把业务的大部分连接池暂切到只读副本让主库只剩 DDL 和极少量写保留 DDL 继续执行等它走完后再逐步把写流量切回主库。整个“抢救”大概用了 15 分钟。幸好表只有几百万行rebuild 本身不慢真正拖时间的是 MDL 排队。这次事故给我们团队带了一个教训任何 DDL 上线前必须用sys.schema_table_lock_waits提前侦察当前会话的锁持有情况而不是盲目执行。侦察语句是SELECT * FROM sys.schema_table_lock_waits\G这个视图会把阻塞者和被阻塞者、持锁时间、发起 SQL 全部列出来比肉眼翻PROCESSLIST高效得多。5.3 给大表精度扩展列一个安全变更流程模板经过多次踩坑我现在给团队定的标准流程是这样评估确认表行数、表大小、二级索引数量、外键约束、是否存在列默认值和生成列选路优先判断能否INSTANT不行再看INPLACE LOCKNONE再不行用 pt-osc监控变更前开启performance_schema和sys库相关视图记录基线 QPS、延迟、磁盘余量演练在测试环境用相同数据量跑一遍记录 DDL 耗时和阻塞时长窗口选择业务低谷通知业务方暂停写入窗口必要时切只读执行加lock_wait_timeout限制用nohup或后台会话执行并在日志里记录开始和结束时间验证检查行数、索引、同步延迟、业务日志报错情况回滚预案确认备份可用或者提前准备反向 DDL比如从DECIMAL(18,2)缩回DECIMAL(10,2)尽管反向操作也可能阻塞但至少有一个明确方案。5.4 一些常规文档里看不到的细节我补充几个容易踩的点MySQL 中MODIFY COLUMN会改变字段的默认值设置如果你原本有DEFAULT 0.00重写时要带上否则会被清掉Oracle 中修改精度后依赖该列的物化视图不会自动重建物化视图日志可能需要手动刷新如果 MySQL 表是分区表ALTER TABLE的分区裁剪逻辑可能失效变更前要确认分区键是否受影响对于超大表上亿行原生 DDL 简直是一场灾难pt-osc 或 gh-ost 是唯一现实选择gh-ost 不依赖触发器而是靠解析 binlog 来同步增量但要求开启binlog_formatROW和binlog_row_imageFULL这一点很多人配置不符合。5.5 快速自查表检查项MySQLOracle是否可以扩大精度可以但大多会 rebuild可以多数秒级完成是否可能阻塞 DML高度可能通常不阻塞推荐替代工具pt-osc / gh-ostDBMS_REDEFINITION变更前必查MDL 等待、临时空间数据最大值、外部键约束变更后必查主从延迟、行数物化视图、索引状态6. 说一点经验上的大实话我自己在两类数据库上都踩过坑。MySQL 给我的教训是永远不要低估 metadata lock 连锁反应。你以为一条 DDL 只是慢实际上它是“慢 SQL 锁排队 业务风暴”三件事的组合。尤其在微服务架构下一个服务的连接池没有设置合理的read_timeout和write_timeout一条 DDL 就能把整个应用层拖到超时熔断。所以我现在做任何 DDL 变更第一步永远是看连接池配置第二步才是数据库侧。Oracle 那边也有反直觉的地方它快不代表它可以随便执行。DBA 如果习惯了 Oracle 的“秒级改字段”切到 MySQL 时很容易照搬经验结果就是线上事故。反过来从 MySQL 转 Oracle 的团队容易踩“缩小精度不校验”的坑因为 MySQL 缩小 DECIMAL 经常只做元数据修改或者会报错而 Oracle 可能要先扫描一堆数据才知道能不能缩这种差异掌握不好照样出问题。如果你只记住一个结论我希望是这句话在 MySQL 上任何 DDL 都要当成“迁移任务”而不是“改配置”在 Oracle 上DDL 也要先确认业务数据与目标定义的兼容性再动手。两端都稳了所谓“精度扩展引发的 DDL 阻塞”才能从事故变成常态运维中的一个小步骤。最后分享一个实用习惯我在所有数据库变更脚本里都会加上强制lock_wait_timeout5并配合SET SESSION lock_wait_timeout 5这种前置语句。它不能消除阻塞但至少能让 DDL 在排不上队的时候快速失败而不是把运维值班电话打到炸。这不算什么高深技巧但对值班救火来说值回票价。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

京东自动下单工具全解析:登录态保活、补货监控与订单提交实战 2026/9/26 17:57:45

京东自动下单工具全解析:登录态保活、补货监控与订单提交实战

简介:这份京东自动下单工具源码包包含完整项目与说明文档,解决自动登录、定时预约商品、补货监控、自动加购物车和提交订单等常见电商自动化场景,适合计算机、数学、电子信息等专业学生作为课程设计、期末大作业或毕业设计参考,也…

阅读更多 →
Windows保留设备名nul不可删除原理与实战解决方案 2026/9/26 17:57:39

Windows保留设备名nul不可删除原理与实战解决方案

1. 这不是文件,是Windows埋了40年的“幽灵开关”你有没有在Windows 11里右键删一个叫nul的文件,结果弹出“无法删除:访问被拒绝”或“找不到项目”?更诡异的是,用资源管理器双击它——没反应;用PowerShell执…

阅读更多 →
Claude Code 之父删了 IDE:从提示词到循环,TaoToken 统一 Key 通道下的 AI 编程配置骨架 2026/9/26 17:57:38

Claude Code 之父删了 IDE:从提示词到循环,TaoToken 统一 Key 通道下的 AI 编程配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
苹果CMS视频站搭建全攻略:从LNMP环境到采集播放器配置 2026/9/26 17:57:38

苹果CMS视频站搭建全攻略:从LNMP环境到采集播放器配置

简介:这是一份针对苹果CMS(MacCMS)的油条视频模板及搭建教程资源包,适合想快速搭建视频站点的站长、PHP开发者和网络运营人员。系统后台支持自定义参数,对应会员升级、积分充值;视频、演员、专题、收藏、会…

阅读更多 →
aixingpan.cn API开发文档:api_docs_trichart_natal_composite_transit接口指南 2026/9/26 17:57:38

aixingpan.cn API开发文档:api_docs_trichart_natal_composite_transit接口指南

aixingpan.cn API开发文档:api_docs_trichart_natal_composite_transit接口指南 1. 引言 本文档详细介绍了占星系统的api_docs_trichart_natal_composite_transit接口的使用方法,包括请求参数详解、响应数据结构、错误处理机制以及最佳实践建议。 2. 接口…

阅读更多 →
多Agent协作控制层:状态机驱动的工程化实践 2026/9/26 17:57:32

多Agent协作控制层:状态机驱动的工程化实践

1. 这不是“多个AI一起写代码”,而是工程级协作系统的诞生现场“当多个 Coding Agent 开始组队,谁来管理它们?”——这句话乍看像一句技术调侃,实则戳中了当前AI编码落地最硬的卡点:我们已经能稳定跑通单个Agent完成函…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉