简介:银行核心应用MySQL治理实践是一份面向金融行业数据库运维、架构设计与开发人员的实战型PDF资料,系统梳理了工商银行在核心应用场景下应对MySQL规模增长与稳定性挑战的完整治理路径。资料围绕现状与挑战、治理思路与方案、后续提升思路三大模块展开,不仅覆盖大规模云化部署、分布式服务体系等关键背景,还重点给出了规范编制、表结构审核、代码审核、健康检查等事前预防措施,同时涉及强制主键、禁止单独字符集、禁用易错数据类型等细化规则,以及慢SQL与大事务的监控查杀、应急恢复、信息采集与日志分析等事中事后处置方法。压缩包内共包含1个PDF文件,大小572KB,以图文演示形式呈现,内容结构清晰,便于直接查阅或用于内部技术分享。目前已有85人学习下载,说明其内容具备一定的行业参考价值。对于希望借鉴银行级治理框架、优化自身数据库监控与应急响应能力的从业者而言,这是一份具备较强落地性的参考资料,值得一线数据库团队深入研读。
1. 银行核心应用MySQL治理:先认清账务重、链路长、窗口短三个现实
银行核心系统里的MySQL,跟互联网后端的MySQL是两个物种。互联网系统追求吞吐,核心系统追求“别在半夜出岔子”——日终批处理、账户余额、流水对账,全部压在有限的实例上,一条慢SQL都可能传导成账务事故。这份《银行核心应用MySQL治理实践》PDF,内容就是把核心场景下的治理思路拆开:从实例分类、慢SQL整改、事务与锁,到主从切换和备份验证,覆盖的是日常维护核心库最常碰到的环节。适合正在管核心应用数据库的DBA、被账务类SQL反复折磨的应用开发,以及刚接手核心库运维还没有完整方法论的团队。它可以直接当成工作清单来用,不是泛泛的概念科普。
2. 资产盘点与现状体检:先给实例打坐标,再谈治理动作
2.1 把实例分成三类:核心账务、外围服务、报表查询
很多团队接到“MySQL治理”的第一反应,是直接调参数、加索引、开并行复制,这通常都踩反了。同一个MySQL实例,放在不同的数据链路上,治理标准完全不同。核心系统里最忌把实例当成一个模子出来的东西统一处理,因为核心账务库要求强一致和高可用,外围服务的容错窗口宽得多,报表库则根本不该被联机业务拖下水。
我一般会先把核心应用涉及的MySQL实例分成三类。第一类是核心账务库,账户表、流水表、总账表都在这上面,特点是数据量不大但强一致要求高,一条SQL写坏就是账务事故。第二类是外围服务库,比如参数配置、流程状态、临时结果集,这类库可用性要求没那么高,丢失几分钟数据往往可以重放或手工修正。第三类是报表查询库,通常是从库或独立分析实例,查询思路跟联机交易完全不同,很容易写出大JOIN和全表扫描。
分完类之后,每类库的治理重点就很清楚了。核心账务库要把慢SQL和长事务当成生产事故来管,外围服务库要控制容量和权限,报表查询库则要把查询隔离做好,尽量别让它反哺给主库压力。这份PDF看完,最值得先抄的作业就是这个分类逻辑,别急着去动数据库参数。
三类库的治理参数,差异主要在连接数上限、事务隔离级别、备份RTO和数据保留周期上。我一般会列一张表来固定分类标准:
| 类型 | 可用性要求 | 典型故障容忍 | 治理侧重点 |
|---|---|---|---|
| 核心账务库 | 99.95%以上,会话级强一致 | 分钟级不可用就算事故 | 慢SQL、长事务、锁等待、备份恢复时效 |
| 外围服务库 | 可容忍短时中断 | 小时级 | 容量、权限回收、参数标准化 |
| 报表查询库 | 无联机写入,延迟可接受 | 小时级以上 | 查询隔离、大查询限流、索引规划 |
这张分类表在日常巡检时很好用:某个实例出了问题,先看它是哪一类,判断优先级,再决定是拉起报警群还是放进排期慢慢处理。很多深夜报警一小时,其实就是外围服务库的一次慢查询,被当成核心故障处理了,折腾到天亮才发现分类定错了。
2.2 体检五件套:慢日志、连接数、主从延时、磁盘增长、备份恢复时长
给实例定完分类,接下来做现状体检。常见做法是做“体检五件套”:慢查询日志、连接数峰值、主从延时、磁盘增长速率、备份恢复时长。前四项是环境观测,第五项是恢复验证。很多团队的备份策略写了三年,却从没实际恢复过,到真的需要时才发现binlog没对齐、备份损坏、恢复耗时超出RTO,这是核心系统最典型的“黑匣子”风险。
第一轮体检可以在从库执行,避免影响线上联机查询。下面这个脚本是快速看一轮核心指标:
-- 从库快速体检(核心库请在低峰期执行) SELECT NOW() AS check_time; SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Threads_running'; SHOW GLOBAL STATUS LIKE 'Max_used_connections'; SHOW GLOBAL VARIABLES LIKE 'max_connections'; -- 主从复制状态,看 Slave_IO_Running / Slave_SQL_Running / Seconds_Behind_Master SHOW SLAVE STATUS\G -- 慢日志基本配置,确认是否一直都在记录 SHOW VARIABLES LIKE 'slow_query_log%'; SHOW VARIABLES LIKE 'long_query_time';这里有几个参数需要解释。Threads_connected是当前连接数,Threads_running才是真正在干活的线程数,如果两者差得很大,说明大量连接停留在Sleep状态,问题多半在应用连接池。Max_used_connections是实例启动以来的连接峰值,跟max_connections一对比就能知道余量还有多少。慢日志配置里,long_query_time如果还是默认的10秒,在核心系统基本等于白开,我一般会把它压到0.5秒,让慢SQL以更细的粒度暴露出来。
体检的频率要看实例分类:核心账务库每天看一次关键指标,外围服务库每周看一次,报表库可以在大查询上线前专项检查。重点是连续观测,不是只看一天数据就能定义清楚。比如磁盘增长,要至少看两周的增量曲线,才能判断是否需要提前扩容。慢日志和连接数这类指标,最好从第一天开始就留存历史,否则治理两个月后回头看,根本说不清是变好了还是变坏了。
2.3 实例画像表:一张表把治理对象固定下来
体检数据收集完,我会把它汇总成一张实例画像表,这也是后续所有治理动作的输入。字段一般包括:实例ID、分类、角色、所在机房、负责人、连接数峰值、慢SQL日均量、日增数据量、保留周期、备份RTO/RPO、本月变更窗口。表里不写技术感悟,只记客观数据和责任人,字少事大。
画像表的作用,是让治理从“遇到问题再翻日志”变成“按表巡检”。比如某核心账务库昨天慢SQL比均值多出一倍,画像表里记录的常规值就能立刻告诉我们异常幅度;某实例已经三个月没更新负责人,也能提醒你梳理交接。实践中最容易被忽略的字段是“本月变更窗口”,银行核心系统的变更窗口经常只有每周两小时,没有这张表,排期永远拍脑袋。
提示:画像表至少每周更新一次,有实例变更或扩容时当天就更新,别等月底汇总时靠回忆补数据。
每次治理动作做完,画像表对应字段要同步更新。我在实际执行中,还会给每个核心账务库建立一条“当日事件记录”日志,把当天看到的异常现象、处理过程、最终结论直接追加在画像维护表后面。这个习惯做过半年以后,排查问题的速度明显变快,很多故障处理直接照历史案例走,不用重新诊断。
3. 慢SQL治理与索引设计:核心系统不许出现隐式全表扫描
3.1 从慢日志找问题,再用EXPLAIN确认扫描范围
慢SQL治理是核心应用治理里投入产出比最高的一项。我见过最典型的场景是:某账户流水查询在日终对账时把整张流水表扫了一遍,数据库CPU飙到90%,批处理延时从15分钟拉长到50分钟。这类问题的起点,永远是慢查询日志。
先把慢日志配置成可用的状态:
-- 开启慢日志,阈值按业务忙闲调整 SET GLOBAL slow_query_log = ON; SET GLOBAL slow_query_log_file = '/var/log/mysql/slow-query.log'; SET GLOBAL long_query_time = 0.5; SET GLOBAL log_queries_not_using_indexes = ON; -- 之后打开慢日志文件,按执行时间排序,重点看 Rows_examined 很大的记录long_query_time=0.5在核心系统里比较合理:日终批处理期间,超过半秒的SQL就值得看。查出来之后不要急着改SQL,先对每条慢SQL单独跑EXPLAIN。EXPLAIN要重点看type、key、rows三列。type从system、const、eq_ref、ref、range到index、ALL,越靠右性能越差;rows是估算扫描行数;key是实际使用的索引,如果key是NULL,说明这条SQL没用上任何索引。
举个例子,一个在流水表上的日期范围查询:
EXPLAIN SELECT * FROM acct_flow WHERE acct_date BETWEEN '2024-11-01' AND '2024-11-05' AND cust_no = 'C0102030405';如果结果显示type=ALL、key=NULL、rows=9800000,说明这条SQL正在全表扫描近千万行。常见做法是给这两个条件的组合加一个复合索引:
ALTER TABLE acct_flow ADD INDEX idx_acct_date_cust (acct_date, cust_no);加完索引后再跑EXPLAIN,type会变成range,rows降到几万甚至几百。这里的核心判断标准是:扫描行数和返回行数之间的差距越小越好。如果索引加上了,rows还是跟全表扫描差不多,那说明这一条SQL本身的设计不匹配索引,需要改SQL或者改业务逻辑,而不是继续堆索引。
3.2 索引设计:交易表只留必要索引,流水表按查询模式设计
索引是核心系统治理里的双刃剑。添加索引能让查询变快,但每次INSERT和UPDATE都要同步维护索引树。核心账务库里的账户表、汇兑表通常都是写多读少,动辄往上面加索引,最后买单的是日终批处理。我在这里的习惯是:账户表、交易登记表这类在线交易表,只保留三类索引——主键、业务键上的唯一索引、外键索引。其他查询需求,一律用从库承担。
流水表的索引设计则反过来,要考虑查询模式。银行核心的流水表典型查询是“某客户在某时间段内的存取款记录”,组合条件通常是客户号、账号、业务日期、交易类型。这个时候复合索引的顺序很关键,一般把等值条件放前面,范围条件放后面:
-- 推荐:查询等值列在前,范围日期列在后 CREATE INDEX idx_flow_cust_date ON acct_flow(cust_no, acct_date); -- 不推荐:把日期放前面 CREATE INDEX idx_flow_date_cust ON acct_flow(acct_date, cust_no);等值列在前、范围列在后的原因是,索引树可以先通过等值条件快速收紧范围,再用范围条件顺序扫描,减少回表次数。如果把日期放前面,索引只能帮到日期这一个条件,客户号过滤效果就发挥不出来。另外,查询里如果带了ORDER BY,索引列顺序匹配排序需求的话,还能省掉一次filesort,这也是MySQL排序性能优化里的常见做法。
在核心系统里还有一个不成文的约定:每次上线涉及的索引变更,必须提前在下线环境用真实数据量压测。一张千万行的流水表在测试库里建索引只需要几秒,在生产库可能就是几分钟的MDL阻塞,直接影响在线交易。这类问题我后面还会在避坑清单里专门说。
3.3 优化顺序:先清SQL,再调参数,最后才动硬件
慢SQL治理最常见的失败方式是反着来。很多团队一上来就调innodb_buffer_pool_size,从8G调到16G,然后把结果说成治理成果,其实那只是暂时掩盖了问题。硬件资源会被新的数据增长吞掉,慢SQL还在那里,只是从报警降到不报警的阈值之下。治理里有个玄学现象:调大缓冲池以后,过两个月参数又不够用了,重新去看慢日志才发现,当初那条全表扫描根本没处理掉。
正确的顺序是先清SQL。把慢日志里的高耗时SQL逐条用EXPLAIN确认,能改SQL的改SQL,能加索引的加索引,能拆成大任务处理的拆成批处理。这一轮做完,再去看连接数、buffer pool、redo log这些参数,参数对不上真实负载就继续微调。最后才是评估硬件,比如磁盘IOPS不够、内存确实太小,这些是参数解决不了的,才需要升级。
这样的顺序在核心系统里还有一层原因:变更窗口紧张。SQL和索引的变更可以在业务低峰完成,参数调整需要重启实例的可能性更高,硬件扩容更是要规划停机。先做风险低的变更,把大部分问题消掉,剩下必须动硬件的那部分,才能争取到足够长的变更窗口。很多日终任务会用存储过程封装批量逻辑,存储过程里如果每个会话都开启事务并长时间等待,锁等待和连接耗尽会同时爆发,治理时要连同存储过程的事务边界一起审计。这也是为什么一份能落实的治理实践,顺序一定是从逻辑到物理。
4. 连接、锁与主从切换:核心链路避坑清单
4.1 连接数被打满:先从线程状态判断是“饿死”还是“撑死”
核心系统里凌晨批处理期间最容易发生的一类事故,是连接数瞬间打满,应用侧全部超时报错。处理这类问题,第一件事不是加连接池,而是先看线程状态。一条命令就能把诊断窗口拉出来:
SHOW FULL PROCESSLIST; -- 主要关注 Time、State、Info 三列如果看到大量Sleep状态的线程,且Time很大,说明连接是被应用撑死的:应用连接池没有回收空闲连接,或者某个连接泄漏一直挂在那边。如果看到大量Query状态线程在跑同一个SQL,说明是SQL本身慢,把连接池塞满。这两种情况处理完全不同:前者要调应用侧连接池的idleTimeout和maxActive,后者要先杀掉慢SQL,再回来谈优化。
我处理过一次,某外围服务库凌晨打印报表时连接数从50涨到500,现象是大量“Waiting for table metadata lock”,原因是有个会话执行过ALTER TABLE没有提交,后续所有会话都卡在元数据锁上。这种问题加连接池没有用,把锁释放掉,连接数立刻回落。所以连接数治理一定要先看状态再动参数,不要一碰到Threads_connected报警就加max_connections,那是在给系统埋更大的坑。
4.2 长事务与锁等待:用系统库找到阻塞源头
核心系统里锁等待最怕遇到长事务。MySQL的InnoDB默认隔离级别是可重复读,一个事务里如果长时间不提交,它读过的数据行都会带着历史版本信息,后面的更新全被堵住。尤其日终批处理里,一个更新语句误操作导致整个事务开着,后续几万个事务排队等着锁,现象就是业务侧大范围超时。
定位阻塞源,我一般先查information_schema和sys库:
-- 找出锁等待会话以及阻塞者 SELECT * FROM sys.innodb_lock_waits\G -- 或查当前所有不再Sleep的会话 SELECT THREAD_ID, PROCESSLIST_ID, PROCESSLIST_TIME, PROCESSLIST_STATE, PROCESSLIST_INFO FROM performance_schema.threads WHERE PROCESSLIST_STATE <> 'Sleep' ORDER BY PROCESSLIST_TIME DESC;sys.innodb_lock_waits会直接给出等锁的事务和持锁的事务,我拿到结果后第一件事是确认持锁事务跑了多久。如果只是几秒钟,可能是正常业务高峰;如果已经几分钟,就要准备跟应用侧确认是否可以回滚。处理手段是KILL持锁会话,但KILL之前一定先留证据:把当前processlist全量记录下来,方便事后复盘是哪一条SQL引起的锁等待。
在核心系统里,锁问题的根治方法通常不在数据库层,而在事务边界。我见过最多的写法是应用里一个事务内循环调接口,每个接口耗时几百毫秒,整个事务就拽了十几秒。后来推进应用层把事务内耗时降到100毫秒以内,锁等待的报警量直接掉了一个量级。
4.3 主从切换:五步演练法,预设一条回滚路
核心系统切换主库,是DBA最紧张的操作之一。切换本身不难,难的是切换之后才发现数据差了一点、应用配置漏改、回滚没有预案,切换变成“回不去”。这里分享我固定使用的五步切换流程:
第一步,确认从库延迟。Seconds_Behind_Master只是参考,最好用pt-heartbeat打点,确认真实延迟小于1秒。第二步,通知应用侧进入维护窗口,联机交易切换到只读或暂停写操作。第三步,在从库上执行STOP SLAVE,确认中继日志已经全部应用结束,对比SHOW MASTER STATUS和SHOW SLAVE STATUS的位点,确保没有残余事务。第四步,提升从库为新的主库,应用连接串切换。第五步,保留旧主库数据,至少观察一天,确认新主库上数据增长正常后再收回只读。
这套流程里最有争议的是维护窗口到底开多长。核心账务库切换一次,很多团队希望控制在5分钟内,实际操作远不止,所以我一般会把演练错峰安排在非交易日凌晨。演练本身也在给真实切换累积参数:需要停写多久、从库延迟多少、应用改配置成功后首笔交易耗时多少。MySQL主从复制只有在演练次数足够多之后,才敢说切换时间是可控的。
4.4 排错记录:五个常见坑的现象、原因与解决
第一坑:批处理偶发报错“Deadlock found when trying to get lock”。现象是日终批处理里同一批次任务随机失败,重跑又恢复正常。原因通常是两个事务在更新账户时使用了相反的顺序形成互等。解决方式是在应用层统一账户表的更新顺序,比如按账号排序后逐行处理,并在事务重试里增加指数退避。
第二坑:主从延时明明显示0,从库读到的数据却比主库旧。原因是Seconds_Behind_Master在某些场景下统计不准,比如从库有大查询占用IO,或者复制线程卡在DDL上。解决方式是引入pt-heartbeat这类独立打点工具,以心跳表的时间差作为真实延迟。
第三坑:连接数没超max_connections,但应用一直报拿不到连接。原因是应用连接池配的maxActive低于数据库的max_connections,从数据库侧看还有余量,应用池实际已经满掉。解决方式是对齐两层参数,并把连接池的探活间隔调低,及时回收坏连接。
第四坑:生产执行ALTER TABLE,业务大面积等待。原因是ALTER TABLE在交易高峰期执行,DDL拿到MDL锁后被长事务阻塞,后续普通查询全部排队。解决方式是改用gh-ost或pt-online-schema-change做在线DDL,并且只安排在低峰期执行对应的上线命令。
第五坑:备份每天在跑,真到恢复时发现耗时超过RTO。原因是从来没做过恢复演练,备份文件损坏或binlog不连续都发现不了。解决方式是按季度做一次恢复演练,从备份机真正把库拉起,再确认应用可用时间在RTO之内。以上五条如果只记一条,我会记第五坑:备份恢复验证永远是治理里的隐形底线。
5. 细水长流的治理习惯:从“救火”到常态化巡检
读完整份PDF,我最大的收获不是某一个排障技巧,而是治理本身该有的节奏。核心系统的MySQL很少出现让人看不懂的故障,绝大多数问题都是量变积累:慢SQL多了一条没人管,连接池配置改了一次没验证,主从切换演练半年没做。等到出问题时,已经不是某一个环节错了,而是整条治理链路都松了。
所以我会把自己执行过的核心库维护流程也固定成三件事。第一件事是每天早上的巡检,只看三个指标:连接数使用率、主从延迟、昨日新增报警数量。这三项里任意一项超出画像表记录的常规值,就说明今天有事件需要处理。MySQL主从复制状态用脚本定时检查并推送告警,避免依赖人工登录。第二件事是每次SQL变更上线前,必须在下线环境跑一次EXPLAIN和一次压测,只有压测结果明显优于变更前才允许提交。这个习惯帮我避开了很多“上线后才显现”的索引问题。第三件事是每周复盘一次慢日志,把新增慢SQL按耗时排序,挑出前三做索引或SQL重写,不是等日终批处理超时了才去翻日志。
最近一次记账服务大报表查询变更,我提前一天用真实流水量在从库做了EXPLAIN,发现优化器选择的索引不是理想的组合,通过强制指定索引后查询耗时从3秒降到0.2秒。从那以后,每次上线核心SQL,我都会强制走一遍慢日志确认、EXPLAIN扫描行数评估、压测对比、复盘归档四步流程。这套流程虽然多花半小时,但省掉了事后无数个痛苦的通宵。治理就是一点点攒出来的确定感,希望这套办法能帮你在核心库里少踩几个坑。
本文还有配套的精品资源,点击获取