ARTICLE DETAIL

资讯详情

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

Oracle自动分区只是开始:冷分区压缩与治理方案实践

Oracle自动分区只是开始:冷分区压缩与治理方案实践 先聊个真实场景。我有个朋友维护一套业务流水库表是按月自动分区的INTERVAL分区用得很溜写入从不需要人工干预。结果半年后他发现两个问题一是磁盘快满了历史分区谁都没碰过却占了整个库一大半空间二是每个月的数据越来越多做查询越来越慢尤其是扫跨几个月的报表磁盘要读出一大堆毫无价值的旧数据。他问我分区不是能提升性能吗为什么还卡我说分区确实把“切块”做完了但你没有做“拣货”冷数据一直躺在热区里也没压缩越堆越重。这就是题目那句话的意思Oracle自动分区只是解决了“数据往哪放”的问题没解决“数据放久了怎么办”。真正的后半场是给分区做冷热分层把冷分区自动识别出来、压缩掉甚至挪到便宜的表空间去。这篇文章就把我实际跑过的方案讲透——怎么设计冷分区策略、怎么压缩、怎么和自动分区串起来以及过程中遇到的那些坑。1. 自动分区只是开始冷数据才是真正的大头1.1 自动分区解决了什么没解决什么先说清楚自动分区干了什么。Oracle里最常见的自动分区是INTERVAL分区按时间自动创建分区。比如你定义一个初始分区数据超过边界时Oracle会自动生成下一个分区这个过程不需要任何人工干预。你可以用RANGE加INTERVAL也可以用12c之后的AUTO LIST但实际做时间维度拆分的INTERVAL是最顺手的。它的价值在于两点写入时不愁分区不存在分区裁剪也天然生效。一个查询带上log_time过滤条件优化器只需要扫描对应那几个分区而不是全表。单说这两点自动分区就已经比单表单分区强太多了。但自动分区管不到后面的事。它不会管半年之前的旧数据是否需要压缩不会管历史分区是不是已经膨胀得比热分区还大也不会管冷分区里的索引是不是白白占用空间。换句话说它只是给每一批数据发了一张“入场券”之后这批数据是继续占着珍贵的高速存储还是被归档到角落全看你的运维策略。大多数团队恰恰在这一步断了线。我见过最夸张的一个例子一个按天自动分区的库跑了不到一年分区数量过了300个单表索引的分区跟着涨到几千个备份策略都开始报警。这种状态下自动分区带来的收益被管理成本完全吃掉了。所以我说自动分区做的是前半场冷数据管理才是后半场。1.2 冷分区为什么值得做收益到底在哪冷分区这个词简单说就是“不怎么被访问的分区”。判断标准一般是时间窗口和访问频率日志表超过3个月、流水表超过6个月、历史账单超过一年的基本就算冷。你可以用最后访问时间、最后DML时间或者业务约定去定义没有唯一标准但核心思路一致把不常查的数据和常查的数据分开用不同的资源规格去伺候它们。做冷分区的好处远不止“省点空间”这么简单。我从性能和成本两个维度拆开说。性能层面。冷分区压缩后物理块数量大幅下降。全表扫描一个压缩分区的IO量可能只有原来三分之一甚至更低。对报表查询来说扫描的块越少物理读和buffer cache的占用就越少查询速度自然变快。另一个容易被忽略的点冷分区从buffer cache里“抢位子”的几率也降低了。因为压缩后的数据块更紧凑同样大小的内存能容纳更多有效数据热数据命中的概率反而提升。这个效应在内存紧张的生产库上非常明显。成本层面。压缩以后存储占用下降备份和闪回日志的体量也会跟着下降。很多公司冷数据还躺在昂贵的Flash盘上实际上这种数据放到普通盘上完全没区别。MOVE PARTITION可以顺带把分区挪到冷表空间底层对应不同的存储池成本立刻降下来。把冷分区压缩加上表空间分级才是完整的冷数据治理单纯压缩而不挪位置只能算完成了一半。2. 方案选型手动轮转 vs Oracle ADO自动策略2.1 先说“温度”怎么定义在做任何操作之前先要定义清楚什么叫做“冷”。这是整个方案最容易被拍脑袋决定、后续又最容易返工的地方。我一般建议从三个维度去圈定分区数据的截止时间、业务上的活跃期、以及实际查询里对历史分区扫描的频率。举个例子一个订单表保留全部历史数据业务上只允许查询近三个月的订单那三个月之前的分区可以定义为冷。再比如一套审计日志合规要求至少保留一年但运维只在排查问题时偶尔翻一翻三个月前的日志这种情况三个月之后六个月内可以算温半年以上算冷。温分区可以先只做表空间迁移冷分区再做压缩粒度更细。定义好“冷”之后后续所有脚本和策略都以这个定义为核心。不要用模糊的话“差不多3个月”直接落成SQL判断条件分区边界时间小于SYSDATE减去某个数字。自动化脚本里写着SYSDATE - 180任何人看到都知道逻辑是什么不至于后期靠猜。2.2 手动轮转多数生产库更稳的选择手动轮转方案就是写一个存储过程加一个DBMS_SCHEDULER定时任务定期找出超过“冷点”的分区逐个做MOVE PARTITION加COMPRESS。这套方案听着笨但非常可控也最适合绝大多数生产环境。优点是逻辑透明每一步干了什么都能从日志里看到出了故障知道从哪里排查。压缩什么级别、挪到哪个表空间、先压哪些分区、后压哪些分区全部自己说了算。缺点是需要自己写代码、自己维护调度工作量比配一条ILM策略要大一些。但从我的经验看手动轮转方案的“心智负担”其实更低因为Oracle的自动策略一旦出问题排查路径反而更绕。具体到实现核心就是拼一条ALTER语句。以T_LOG表为例想把某个分区压成OLTP压缩并移动到冷表空间命令就是ALTER TABLE t_log MOVE PARTITION p_2024_01 TABLESPACE ts_cold_data COMPRESS FOR OLTP;如果是Oracle 12cR2以上的版本还可以加ONLINE选项让分区移动期间不阻塞该分区上的读写操作ALTER TABLE t_log MOVE PARTITION p_2024_01 TABLESPACE ts_cold_data COMPRESS FOR OLTP ONLINE;定时任务跑的时候动态拼SQL逐个处理。后面第5章我会给一个完整的联动方案。2.3 ADO/ILM想“自动设置冷分区”可以靠它Oracle从12c开始提供了ILMInformation Lifecycle Management和ADOAutomatic Data Optimization这正是标题里“自动设置冷分区”最贴近官方能力的做法。ADO会在后台基于Heat Map监控的数据访问热度自动对满足条件的分区执行压缩或迁移。要启用ADO第一步打开Heat MapALTER SYSTEM SET HEAT_MAP ON;然后给表加一条ILM策略。比如让T_LOG表6个月未访问的分区自动执行OLTP压缩BEGIN DBMS_ILM.ADD_POLICY( policy_name COLD_PARTITION_COMPRESS, object_type TABLE, object_name T_LOG, action_type DBMS_ILM.ACTION_COMPRESS, scope DBMS_ILM.SCOPE_PARTITION, condition AGE IN MONTHS 6, action COMPRESS FOR OLTP, enabled TRUE ); END; /这条策略一配上理论上你就不用再写任何定时任务了Oracle后台作业会根据数据年龄和访问情况自行决定何时压缩。这个就是“自动设置冷分区”的官方路径。但我要提醒一句ADO不是银弹。它依赖Heat Map的统计需要额外的系统开销而且策略的执行时机不像手动JOB那样精确可控。生产环境里如果对压缩时间点、表空间有比较硬性的要求我仍然建议优先考虑手动轮转。两套方案各有适用场景我在下表里给了对比维度手动轮转方案ADO/ILM自动策略实现复杂度需要编写存储过程和调度器配置策略即可相对简单可控性高可精确控制每个分区中由后台作业决定动作时机版本依赖10g及以后12c及以后依赖条件无特殊权限需要开启Heat Map可能需要额外License排查难度低日志清晰中高策略执行记录分散适合场景生产核心库追求稳定可预期数据量大、运维人力少、对时机不敏感我个人的做法是核心库用手动轮转边缘分析库如果条件允许再上ILM两边都会把状态记录落到一张管理表里方便回头看。3. 压缩冷分区前先搞清楚这些事3.1 压缩类型怎么选OLTP压缩还是HCCOracle的压缩类型不止一种选错了要么压缩比上不去要么直接把数据库搞出性能问题。我把常见的几种列在下面压缩方式说明适用场景COMPRESS基础压缩块级压缩主要在直接路径导入时压缩大批量加载的归档数据COMPRESS FOR OLTP行内压缩支持各类DMLTP场景适用OLTP在线历史分区COMPRESS FOR QUERY LOW/HIGH混合列压缩HCC压缩比更高Exadata或高级压缩选件环境COMPRESS FOR ARCHIVE LOW/HIGHHCC归档压缩压缩比最高几乎不访问的冷分区仅Exadata普通环境里冷分区一般选COMPRESS FOR OLTP就够用因为它对后续偶尔的UPDATE和DELETE还算是友好不会一更新就产生严重的行迁移。HCC虽说压缩比很夸张但有个硬前提底层存储需要是Exadata或者至少启用了高级压缩选件。你在普通服务器上跑COMPRESS FOR ARCHIVE HIGHOracle大概率会直接报ORA-64307意思是当前表空间不支持这种混合列压缩。曾经有同事觉得“archive high一定更省空间”在普通机器上改了后直接作业失败回滚都折腾了半天。所以选型第一条先确认你的环境支不支持HCC。支持且分区确实是纯读冷数据再考虑QUERY或ARCHIVE级别不支持就在OLTP压缩里做取舍。别看着HCC的名字高级就硬上环境不支持时全是坑。3.2 动刀前先估算压缩比不要上来就对所有分区做压缩。压缩比和数据类型强相关。字符串长、重复值多的列压缩效果自然好数值型、随机内容占主导的分区压缩完可能只有10%的空间节省却要付出额外的CPU代价。所以开工之前我建议先抽两个典型的历史分区用Oracle自带的DBMS_COMPRESSION算一下预期压缩比。DECLARE blkcnt_uncomp PLS_INTEGER; blkcnt_comp PLS_INTEGER; cmpratio NUMBER; comptype VARCHAR2(100); BEGIN DBMS_COMPRESSION.GET_COMPRESSION_RATIO( scratchtbsname TEMP, ownname SCOTT, tabname T_LOG, partname P_2024_01, comptype DBMS_COMPRESSION.COMPRESS_FOR_OLTP, blkcnt_cmp blkcnt_comp, blkcnt_uncmp blkcnt_uncomp, cmpratio cmpratio, comptype_str comptype ); DBMS_OUTPUT.PUT_LINE(压缩前块数: || blkcnt_uncomp || , 压缩后块数: || blkcnt_comp || , 压缩比: || cmpratio); END; /这个存储过程会在TEMP表空间里做一次模拟压缩然后返回压缩前后的块数。看到压缩比超过了2倍做MOVE才是划算的如果只有1.2倍我建议再想想是否有必要付出压缩解压的CPU开销。另外一个细节传给它的scratchtbsname必须是实际存在的表空间且要有足够空间。我遇到过因为TEMP空间配小估算作业直接报错的情况。提前把临时表空间放大不然卡在第一步很难看。3.3 压缩会有什么代价很多人以为压缩是对现有数据的再造应该无损。实际上压缩主要换来的是空间和IO收益代价集中在这几块CPU消耗、DML变慢、以及索引维护成本上升。先看CPU。每一次读压缩块Oracle都需要做解压操作每一次写压缩块也要经过压缩逻辑。如果你把热分区也压了等于让所有高频读写路径都额外多了一道工序。这就是为什么冷分区适合压缩而热分区不适合冷分区的读频率低CPU开销几乎可以忽略省下的IO却是实打实的热分区如果每天有上百万次写入压缩带来的CPU开销会非常扎眼。再看DML。COMPRESS FOR OLTP虽然允许UPDATE和DELETE但压缩块内的行更新后很可能放不回原来的块导致行迁移读性能下跌。冷分区出现频繁更新本质上说明你的“冷”定义有问题。真遇到这种业务要么把冷热分界再往后推要么从应用层面禁止对归档数据做UPDATE只允许INSERT和SELECT。我后面第7章会专门讲这个坑。4. 手动压缩冷分区的完整流程照抄就行4.1 第一步确认分区状态任何操作之前先摸清目标分区到底有哪些索引、多大、在哪个表空间。下面这几条SQL是我每次执行前的例行检查-- 查看表的分区概况 SELECT partition_name, tablespace_name, num_rows, blocks, compression FROM user_tab_partitions WHERE table_name T_LOG ORDER BY partition_position; -- 查看分区上的索引状态 SELECT index_name, partition_name, status FROM user_ind_partitions WHERE index_name IN (SELECT index_name FROM user_indexes WHERE table_name T_LOG) AND status USABLE;特别注意compression字段它告诉你哪些分区已经压过哪些还没压。压缩过的重复执行MOVE PARTITION意义不大还白花时间。索引状态则必须确保全部USABLE否则分区移动后索引失效应用端立刻收到一堆ORA-01502错误。4.2 第二步执行MOVE PARTITION压缩确认无误后对选中的分区执行压缩MOVE。以下是完整命令ALTER TABLE t_log MOVE PARTITION p_2024_01 TABLESPACE ts_cold_data COMPRESS FOR OLTP ONLINE;说几个注意事项。第一ONLINE只在12cR2以上可用低版本执行会直接报语法错误。第二即使加了ONLINEMOVE过程中对分区的DML仍然有一定限制必须在维护窗口执行留足时间。第三如果你的数据库版本低或者担心影响业务可以把ONLINE去掉但要接受分区在移动期间不可写。执行完MOVE之后接着做两件事重建该分区的本地索引以及更新统计信息。按顺序来先索引后统计。ALTER INDEX idx_t_log_time REBUILD PARTITION p_2024_01; EXEC DBMS_STATS.GATHER_TABLE_STATS(USER, T_LOG, PARTNAME P_2024_01, GRANULARITY PARTITION, CASCADE TRUE);很多人只做MOVE忘记了索引重建这一步结果业务突然报错回过来查才发现所有本地索引都变成UNUSABLE。MOVE PARTITION和索引重建一定要绑定成一组动作写成同一个存储过程里的连续步骤不能拆开。统计数据这步也别省。MOVE分区之后行数、块数都变了继续用旧统计信息优化器可能做出全表扫而不是分区裁剪的烂计划。GATHER了之后再查一次执行计划确认走的是分区裁剪路径。4.3 第三步验证结果并记录压缩完不是没有后续了。把压缩前和压缩后的块数、行数、大小记录下来日后再复盘会非常有价值。可以查DBA_TAB_PARTITIONS或USER_TAB_PARTITIONS里的blocks字段做对比。如果压缩比不理想识别出数据类型的问题调整后续的“冷点”定义。这些数据积少成多就是你判断这套策略收益最直接的依据。我还会在每次执行后把操作日志写到一张专门的管理表里字段包括表名、分区名、开始时间、结束时间、压缩方式、压缩前后块数。维护久了这张表本身就是一套完整的“数据温度档案”。以后再有人说“你们压缩到底管不管用”把数据摆出来比任何解释都硬。5. 让冷分区自动轮转存储过程加定时任务全解5.1 建表INTERVAL自动分区打底冷分区压缩要配合自动分区才有联动效果。先建一张按月份INTERVAL自动分区的表初始分区边界设定在业务开服时间之前即可CREATE TABLE t_log ( id NUMBER, log_time DATE, msg VARCHAR2(500) ) PARTITION BY RANGE (log_time) INTERVAL (NUMTOYMINTERVAL(1, MONTH)) ( PARTITION p_init VALUES LESS THAN (TO_DATE(2024-01-01, YYYY-MM-DD)) ) ;每个月数据一进来Oracle自动创建下一个分区写入自动路由到新分区这就是自动分区部分。注意这时的表本身不写COMPRESS因为我不希望所有新分区默认压缩。新分区属于“热”状态写入频繁不该压。如果你的业务上确实希望新分区默认就压缩也可以在建表语句里直接加COMPRESS FOR OLTP。这样的话INTERVAL自动创建的新分区也会继承表的压缩属性。具体用哪种取决于你们对热分区的写入量预期。5.2 冷分区自动识别与压缩脚本接下来写一个存储过程找出超过指定月份的分区逐个压缩。我这里的判断逻辑基于分区名先约定分区名格式为P_YYYYMM这样解析起来干净利落。如果你用的是默认SYS_P数字格式建议先给分区改名或者在判断里解析high_value否则脚本写起来很别扭。CREATE OR REPLACE PROCEDURE compress_cold_partitions ( p_keep_months NUMBER : 6 ) AS v_sql VARCHAR2(300); v_part_name VARCHAR2(30); v_month DATE; v_cutoff DATE : ADD_MONTHS(TRUNC(SYSDATE, MM), -p_keep_months); CURSOR c_part IS SELECT partition_name, TO_DATE(SUBSTR(partition_name, 3, 6), YYYYMM) AS part_month FROM user_tab_partitions WHERE table_name T_LOG AND partition_name LIKE P_% AND compression DISABLED; BEGIN FOR r IN c_part LOOP IF r.part_month v_cutoff THEN v_sql : ALTER TABLE t_log MOVE PARTITION || r.partition_name || COMPRESS FOR OLTP ONLINE; DBMS_OUTPUT.PUT_LINE(Executing: || v_sql); EXECUTE IMMEDIATE v_sql; -- 重建该分区上的本地索引 FOR idx IN (SELECT index_name FROM user_indexes WHERE table_name T_LOG) LOOP BEGIN EXECUTE IMMEDIATE ALTER INDEX || idx.index_name || REBUILD PARTITION || r.partition_name; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(Index rebuild skipped for || idx.index_name); END; END LOOP; -- 刷新该分区的统计信息 DBMS_STATS.GATHER_TABLE_STATS( ownname USER, tabname T_LOG, partname r.partition_name, granularity PARTITION, cascade TRUE ); END IF; END LOOP; END; /这里判断compression DISABLED是为了避免反复压缩已压过的分区减少无谓的IO。用DBMS_OUTPUT打印日志配合一张实际记录表会更实用生产环境我建议把日志写入表而不是只打到屏幕。5.3 定时任务调度存过写完用DBMS_SCHEDULER挂一个每月或每周的任务。个人建议每月在业务低峰期跑一次比如每月第一个周日凌晨2点。如果数据量特别大可以拆成每周跑一次每次限制处理分区数避免一次作业把整个库的IO占满。BEGIN DBMS_SCHEDULER.CREATE_JOB( job_name JOB_COMPRESS_COLD_PART, job_type PLSQL_BLOCK, job_action BEGIN compress_cold_partitions(p_keep_months 6); END;, start_date SYSTIMESTAMP, repeat_interval FREQMONTHLY; BYMONTHDAY1; BYHOUR2; BYMINUTE0; BYSECOND0;, enabled TRUE, comments 每月压缩超过6个月的冷分区 ); END; /调度起来之后这个方案就是标题里说的“自动设置冷分区”新分区由INTERVAL自动建档到了冷点由定时任务自动压缩。运维要做的只是隔三差五看看日志和告警不再需要每个月手工盯着分区发呆。5.4 临时表空间和归档压力先评估自动轮转跑起来之后你还要留意两个连带问题。第一MOVE PARTITION的过程中旧分区和新分区会在短时间内同时存在需要表空间里有足够的剩余空间。别等跑了一半才报“空间不足”然后再去抢救。第二每一步MOVE都会产生大量redo和undo如果开启了归档归档日志目录会在短时间内快速增长建议提前把归档空间评估好或者把作业安排在归档允许的窗口里。我遇到过一次压缩作业跑了一个多小时归档目录满了数据库直接卡住。从那以后每次批量MOVE之前我都会先估算该分区的大小并确保表空间空闲空间大于待处理分区总大小的1.5倍。这条经验真的能救命。6. 为什么压缩后查询性能反而变好底层逻辑拆解6.1 分区裁剪只扫该扫的先说分区裁剪这是分区表性能收益的第一来源。一个查询如果带上了分区键log_time的条件优化器会根据条件值和分区边界把扫描范围锁定到少数几个分区。假设表有60个分区只查1月份数据优化器就只扫1月那个分区其他的连碰都不碰。执行计划里会出现PARTITION RANGE SINGLE或PARTITION RANGE ITERATOR字样这就是裁剪生效的标志。自动分区在这个基础上给了额外的红利新分区自动创建边界连续从来不会出现“数据属于某个分区但分区不在”的尴尬。而冷分区压缩则是在分区裁剪之上的第二层收益。裁剪决定了你扫哪些分区压缩决定了这些分区里每一块物理IO读出来的数据有多少有效内容。举个例子同样是扫描1月分区压缩前可能要读1万块数据压缩后只读3000块。逻辑读和物理读同时下降查询自然快。6.2 压缩减IO内存命中率反而升高压缩的收益可以这样理解数据块里每一行都变短了一个块能装下的行就变多扫描同样的行数只需要更少的块。对全表扫描或者大范围扫描来说物理读数量几乎是线性下降的。还有一个容易被忽略的效应是buffer cache命中率。压缩前的数据块个个都是“半满”甚至“八分满”实际上存的还是同一批数据只是行短了块之间多出来的空间可以缓存其他分区。冷分区压缩后对内存的挤占变小等于把更多buffer留给热分区。热分区命中率提高整体查询体验会明显改善。这也是为什么很多系统做完冷分区压缩之后不但历史查询变快连热分区都变快了——内存资源重新分配了。6.3 实测表现成本能降到原来的三分之一我给一个真实的对比数据。一张月份分区流水表单分区大约2000万行未压缩时占用约2.1GB压缩为OLTP后大约700MB压缩比接近3倍。查询单月数据的全扫成本从原来的约8万块逻辑读降到了2.8万块左右耗时下降一半以上。由于分区裁剪只扫目标分区查询语句本身没有任何改动只是底层分区被重新整理了一遍效果就出来了。这里额外提醒一件事压完之后执行计划要重新看一遍。旧统计信息会让优化器误以为分区还是2.1GB可能选择错误的连接顺序或者访问路径。刷新统计信息之后成本估算才跟真实情况匹配。步骤里那行DBMS_STATS.GATHER_TABLE_STATS千万不要删。7. 实战踩坑常见问题与排查技巧实录7.1 非Exadata环境使用HCC报ORA-64307这个前面说过我再详细讲一次排查过程。症状很直接执行ALTER TABLE MOVE PARTITION ... COMPRESS FOR ARCHIVE HIGH的时候Oracle抛出ORA-64307提示当前表空间不支持混合列压缩。很多刚接触HCC的DBA第一反应是“是不是我的命令写错了”反复检查SQL也找不出问题。原因不在SQL而在环境。HCC是Exadata和高级压缩选件的功能普通存储上跑不了。解决办法是把压缩方式换成COMPRESS FOR OLTP或者当你确实处于Exadata环境时确认当前表空间位于Exadata存储上且数据库启用了相应选项。要是不想猜直接查V$PARAMETER里相关的值或者问基础设施团队存储类型。7.2 MOVE后本地索引全部UNUSABLEMOVE PARTITION会改变分区的物理布局Oracle为了保证索引一致性会把该分区对应的本地索引直接标记为UNUSABLE。如果你只做了MOVE没有REBUILD后续查询会报ORA-01502。这个问题的坑不在于不好解决而在于容易被忽略。MOVE本身执行成功很多人就以为任务完成了。我的习惯是写脚本时就固定三步走MOVE、REBUILD INDEX PARTITION、GATHER STATS每一步之间用异常处理把它们绑成一个事务任何一步失败都要完整回看。实际排查的时候先跑一遍USER_IND_PARTITIONS查一下UNUSABLE的索引然后一个个REBUILD就行SELECT index_name, partition_name, status FROM user_ind_partitions WHERE status UNUSABLE; ALTER INDEX idx_t_log_time REBUILD PARTITION p_2024_01;7.3 统计信息过期导致执行计划走偏压缩前一个分区2000万行占2GB压缩后700MB但旧统计信息里行数和块数仍然是旧值。优化器在做成本估算时会按照2GB的扫描成本来判断是否值得走索引。如果它判断走索引更划算而实际数据已经瘦身可能就走了一个本来不需要的索引路径或者反过来选了全扫总之很容易偏。解决方式很简单MOVE之后立马GATHER。但要注意GATHER的粒度直接用PARTNAME参数粒度设成PARTITION比重新GATHER全表快得多。如果你的查询经常跨多个分区建议GATHER完后手工检查一下全局统计信息是否也刷新了必要时再跑一次全表的不带PARTNAME的GATHER。7.4 压缩后分区UPDATE变慢、行迁移增加COMPRESS FOR OLTP允许UPDATE但性能不会太好。压缩块里的行更新后如果新数据超过原来占用的空间Oracle会把整行迁移到另一个块原位置只留一个指针。这个行迁移会让查询多一次额外IO时间长了还会让空间碎片化。处理办法有两个层面。第一个层面是调整冷分区的定义让真正进入“只读状态”的分区才被压缩。第二个层面是如果业务实在无法避免更新退一步别压缩只挪表空间或者把整个冷分区改成只读表空间从物理层面禁止更新一劳永逸。只读表空间对数据完整性也是多一层保护审计和合规背景的系统尤其适用。7.5 分区太多按天分区后的麻烦自动分区用起来太顺手了很多人图省事直接按天INTERVAL。结果一年分出365个分区加上索引分区数目直接破千DDL、统计信息收集、备份配置全都变得笨重。冷分区压缩在这些小分区上跑光动态SQL就要执行几百次维护成本陡然升高。解决思路上我建议除非业务对按天分区的裁剪需求极其强烈否则默认按月更合适。已经在跑按天的库可以把历史小分区用MERGE PARTITIONS合并成月份分区再对合并后的大分区做压缩。合并会改变分区边界业务侧查询条件必须仍然能被裁剪覆盖否则会踩到“跨分区查询变慢”的新问题。这个操作要选在维护窗口里做并且提前用备份验证回滚方案。7.6 DDL锁冲突和在线DDL的边界MOVE PARTITION就算加了ONLINE也不是完全没有锁竞争。Oracle的在线分区移动提升了可用性但和长事务、其他DDL并发时依然可能出现等待或者资源繁忙。我把它排在最后一个坑是因为它通常只在大型库和核心时段才会碰到。实操建议很简单所有压缩和分区调整尽量进维护窗口小的无感调整可以上班时间做但批量压缩坚决夜里跑。另外给作业加上超时和失败重试机制特别是同时压几个大分区时单个分区失败不影响后续分区处理。最后就是监控跑压缩的那几个晚上盯一下会话的等待事件看到enq: TM或者DFS lock handle这类等待说明锁竞争起来了及时介入调整顺序。结束前的一点个人体会这套“自动分区加冷分区压缩”的组合拳我前前后后在多个项目上落地过。最大的感悟是数据库性能优化从来不是某个单一技术点救场而是一套数据生命周期的管理逻辑。自动分区解决写入路由冷分区压缩解决历史堆积统计信息刷新保证优化器不误判调度任务让一切自动运转。每一步单独看都不难难的是把它们像流水线一样串起来并且持续观察、调整。最后再分享一个小技巧不要只盯着压缩比一个指标。压缩比当然重要但更重要的是压缩后查询响应时间和存储增量速度的变化。我的习惯是每次压缩作业执行前后把分区大小、查询平均耗时、总空间占用三个数据拉出来对比过三个月回头看收益和风险都是一目了然。这套监控思路比任何一个SQL都值钱。
返回列表