ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

YashanDB日常运维实战:5个技巧破解连接、慢SQL与卡顿难题

YashanDB日常运维实战:5个技巧破解连接、慢SQL与卡顿难题

干了几年数据库相关工作,我越来越确信一件事:数据库给人留下的“体验”,往往不在跑分和参数表里,而在日常的一连串小摩擦里。最近在梳理一套YashanDB环境的日常使用流程时,我统计了一下大家抱怨最多的事情——连接一多就不知道谁在占用、慢查询出来了看不懂执行计划、业务卡住时分不清是锁的问题还是负载的问题、改个参数要提心吊胆地等窗口。这些问题算不上核心故障,但每一个都在不停消耗使用者的耐心。

YashanDB本身在SQL兼容性、事务处理和数据可靠性上都有自己的底子,真正决定使用者感受的,是能不能把这五件事理顺:连接与会话管理、执行计划解读、等待事件定位、参数变更边界、运维自动化。这篇文章就把这5个实用技巧逐个拆开讲,整理出我实际用下来比较顺手的做法。适合正在使用或计划评估YashanDB的开发、测试和运维同学参考,也欢迎对国产数据库使用体验感兴趣的朋友一起交流。

1. 连接管理里的隐形体验:先把会话理顺

1.1 连接池参数不是拍脑袋定的

连接池是应用和数据库之间的缓冲地带。很多团队在YashanDB上线初期,直接沿用旧项目的连接池配置,或者干脆用中间件默认值,等到了业务高峰发现连接数被打满,才想起来要调参数。这时候业务已经出现连接超时,用户侧反馈一片,再来调整就非常被动。

我的建议是先按下面这个模板做起点,再结合压测结果逐步修正,不要一上来就追求大数值:

参数建议起点说明
initialSize5启动时建立的初始连接数
minIdle5保持的最小空闲连接数
maxActive50最大活跃连接数,先保守再逐步调大
maxWait3000获取连接的最长等待时间,单位毫秒
testWhileIdletrue空闲连接是否做有效性检测

这里想特别强调一点:连接数不是越大越好。之前我在一次压测验证中把maxActive从50调到200,结果吞吐量没涨多少,数据库端的CPU使用率倒是明显上升,因为大量并发连接带来的上下文切换和内部锁竞争反而吞噬了资源。连接数的合理值需要结合你的SQL平均执行时长和并发量来算,一个粗粒度的估算方法是:用并发目标业务量乘以单请求占用连接的平均时长,再对比单连接每秒能处理的请求数,最后留出20%到30%的余量。

1.2 会话失控时怎么快速止血

连接池只是第一道防线。实际运维里最常见的失控场景是:某个应用出了Bug,连接没有正常归还,连接池里的连接被一点点耗尽,新的请求全部卡在“获取连接”这一步。这时候用户体验已经不是“慢”,而是“完全没响应”。

止血的第一步不是重启应用,而是先看清楚是谁占着连接不放。用类似下面的查询看一眼当前会话状态:

SELECT sid, serial#, username, status, sql_id, event, last_call_et FROM v$session WHERE status = 'ACTIVE' ORDER BY last_call_et DESC;

这个查询会把当前活跃会话按最后一条语句的执行时长倒序排出来,你通常能在前三行找到元凶。比如一个跑了很久的批量更新、一个忘了提交的事务、一个循环调用存储过程的应用会话。

确认之后再决定要不要处理。对于长时间空闲但不归还的连接,使用类似下面的命令强制结束会话:

ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;

这里有个很重要的经验:动手杀会话之前,一定先确认这个会话当前是否在关键事务里。宁可多花两分钟查一下它的SQL和事务状态,也不要误杀正在执行核心批量任务的会话。我在早期处理过一次故障,看到某个会话占用连接时间很长,直接kill掉,结果那是正常跑批任务,只是因为数据量大导致执行时间偏长,误杀之后反倒造成业务数据中断,得不偿失。

2. 慢SQL不是玄学:把执行计划当成体检报告

2.1 学会看懂三种访问路径

慢SQL排查最常见的拦路虎是执行计划。很多人看一眼执行计划里全是英文术语就直接放弃了,其实你只需要先掌握三种最基础的访问路径:

  • TABLE ACCESS FULL:全表扫描。优化器认为逐行读整张表比走索引更快,或者根本没有可用索引。数据量小的时候这反而是最优解;数据量到了百万级还频繁出现,多半有问题。
  • INDEX RANGE SCAN:索引范围扫描。通过索引定位到满足条件的范围,再回表取数据,这是大多数范围查询的理想形态。
  • INDEX UNIQUE SCAN:索引唯一扫描。主键或唯一索引等值查询,速度最快。

看到执行计划,先问三个问题:表扫了吗?索引走了吗?预估行数和实际返回行数差得远不远?如果表很大且频繁全表扫描,第一反应不是盲目加索引,而是先确认查询条件里的列是否真的有索引,以及索引列的顺序是否和查询条件匹配。组合索引的顺序很有讲究,经常是“等值条件放前面,范围条件放后面”,顺序反了索引效果会大打折扣。

2.2 统计信息:影响优化器判断的隐形因素

比执行计划本身更隐蔽的问题是统计信息不准。很多慢SQL不是SQL写得差,而是表里的数据分布已经变了,统计信息还是旧版本,优化器拿一份过期的“地图”去选路,自然容易走冤枉路。

比如一张表昨天有10万行,今天凌晨批量导入后变成2000万行,如果统计信息没有更新,优化器可能仍然按10万行的规模估算成本,选一个全表扫描的执行计划。等实际跑起来才发现数据量早就上来了,查询时间从几百毫秒恶化到十几秒。

我的习惯是:大表在做完批量导入、大量删除或数据分布发生明显变化之后,主动收集一次统计信息。YashanDB的统计信息收集方式以你当前版本支持的为准,建议在业务低峰期执行,收集完成后观察执行计划是否发生变化。很多时候慢SQL问题在收集统计信息之后就自动消失了,根本不需要改写SQL。

2.3 从“秒级到毫秒级”的一次调优模拟过程

光讲原理不够,我拿一个模拟场景来演示完整的调优链路。假设有张订单表,结构大概包含订单号、客户编号、状态、下单时间等字段,数据量已经到了千万级。业务方反馈:按状态和时间范围筛选订单的查询,之前1秒多,这几天变成4秒以上。

排查过程如下:

  1. 拿到SQL后先做执行计划分析,确认到底慢在哪个环节:
EXPLAIN PLAN FOR SELECT * FROM orders WHERE status = 'PENDING' AND order_time >= TO_DATE('2024-01-01', 'YYYY-MM-DD') AND order_time < TO_DATE('2024-02-01', 'YYYY-MM-DD');
  1. 执行计划显示该查询走了全表扫描,预估返回行数300万。
  2. 检查索引发现,状态列和时间列上的组合索引已经存在,为什么没有走?大概率是统计信息过期或者索引失效。
  3. 收集统计信息后重新看执行计划,发现仍然走全表扫描,这时进一步检查条件写法,发现查询里对时间列用了函数包裹,导致索引无法正常使用。
  4. 调整SQL写法,让索引列保持原始形态,再重新分析执行计划,确认索引路径生效。
  5. 复测,查询耗时从4秒降到80毫秒左右。

这个过程的价值在于:慢SQL的根因往往不是一个,而是多个因素叠加。如果一上来就加索引,可能加了也没用,因为真正的问题出在统计信息和SQL写法上。先看执行计划,再逐项排除,比拍脑袋加索引靠谱得多。

3. 业务卡顿别慌:等待事件分析的逆向排查法

3.1 等待事件到底是什么

等待事件这个概念,可以理解成数据库内部的交通摄像头。每个会话在等什么,都会留下记录。业务卡顿时,与其靠猜,不如先看一眼这些会话到底在等什么。

比如一个会话一直在等“某个锁释放”,另一个会话在等“I/O读取完成”,它们的处理方向完全不同。前者要去找持锁方,后者要去看存储链路。如果一上来就重启数据库,这些线索就全丢了,下次还会踩同样的坑。

3.2 从等待事件反推瓶颈的完整链路

我习惯按四层顺序排查:

  1. 系统层:CPU、内存、I/O使用率先看一轮,排除资源耗尽的情况。
  2. 数据库层:查当前等待事件的分布,看主要等待集中在哪个方向。
  3. SQL层:把耗时最高的SQL捞出来,分析是否集中在某几条语句上。
  4. 对象层:确认是不是集中在某个表或某个索引上,比如某张表的全表扫描频繁触发大量I/O。

下面是常见等待类别和方向的对照表,排查时可以按这个思路走:

等待方向典型场景优先排查
CPU相关大量计算型SQL并发慢SQL、并发数、优化器是否走错计划
I/O相关数据量突增、磁盘性能下降全表扫描、临时表空间、存储瓶颈
锁等待事务未提交、长事务持锁会话、事务隔离级别
网络相关应用与数据库之间延迟网络链路、批量抓取数据方式

等拉出这个分布后,再决定是用资源扩容还是用SQL优化来解决,而不是盲目重启数据库。重启看起来解决得快,但下一次出现同样问题你还是找不到根因,这是典型的“治标不治本”。

3.3 锁等待:业务卡死的头号嫌疑人

实际故障中,锁等待是我遇到最多的卡顿原因之一。典型现象:应用侧大量会话卡在同一个更新语句上,数据库侧看到很多会话在等待一个锁资源。

找持锁会话的做法,是先锁定等待事件的会话,再顺着它找到正在占用锁资源的那个会话。那个持锁会话可能在做一件本来只需要几百毫秒的事,但因为忘记提交,把整个链路上的其他事务全部堵住。换句话说,一条没提交的UPDATE就能让几十个会话排队等着。

处置思路分两种情况:对持锁会话,先确认它是在跑长任务还是空闲事务。如果是空闲未提交,应当尽早结束该事务;如果是合法长事务,则需要和业务侧确认是否可以分段提交,减小锁持有时间。这里特别提醒,不要一看到锁等待就kill会话,先找持锁源头,判断清楚再动手。误杀合法事务的代价往往比多等几分钟更大。

4. 改参数不是“拆盲盒”:动态生效与重启生效的边界

4.1 参数生效方式直接影响业务连续体验

数据库参数调整是另一个高频体验痛点。想想这个场景:一个参数按文档确认需要重启才能生效,那意味着你要走变更审批、申请维护窗口、通知所有关联方。这个过程的体验自然好不到哪里去。

所以在改参数之前,第一件事是搞清楚这个参数是动态生效还是需要重启。YashanDB的很多参数支持在线动态修改,但仍有部分参数与实例启动时的资源分配有关,需要重启后才会生效,具体以你当前版本的官方文档为准。这个边界如果没搞清楚,很容易出现在线改完参数后发现不生效,白等半天的情况。

4.2 修改前需要完成的安全动作

我的安全动作分成三步:

  1. 查当前值和默认值。先用类似SHOW PARAMETER的方式确认当前值,别凭记忆改。曾经见过有人把缓存参数改错,明明要调大结果调小了,业务高峰直接卡顿,查了半天才发现是参数方向搞反了。
  2. 备份参数配置。把当前参数文件完整导出一份,作为回退基准。这一步很重要,后续发现改动有问题,可以快速回到原状。
  3. 理解参数的业务影响面。有的参数影响内存分配,改了可能引发内存抖动,建议小步修改、观察确认后再继续。不要一次性改好几个参数,否则出问题根本分不清是哪个改出来的。

同时提醒一点:不要盲目照搬网上别人的参数模板。不同业务负载下,最优参数组合差异很大。参数不是越多越激进越好,很多默认值其实已经经过了合理调校,乱改反而可能让性能下降。

4.3 修改后如何确认真的生效并回退

参数修改完之后,不要只看修改命令返回成功就认为万事大吉。我习惯再查一次当前运行值,确认它确实变成了目标值。如果发现业务指标异常,比如延迟上升、内存占用异常,立即回退到备份配置。

对于动态生效的参数,修改后还可以通过观察系统运行状态和慢查询数量来评估效果。更科学的做法是保留修改前后一组对比数据,比如修改前的TPS、平均响应时间、慢SQL数量,修改后再采集同样时间窗口的数据,两边一对比,改动有没有效果一目了然。这样既能证明改动有效,也为后续同类调整积累了可参考的经验。

5. 把重复劳动交给脚本:从手动巡检到半自动运维

5.1 巡检脚本至少包含五个检查项

很多团队对YashanDB的日常运维还停留在“出了事再查”的阶段,其实把高频的排查动作前置成定期巡检脚本,能明显提升整体使用体验。一个基础的巡检脚本至少应该覆盖五类问题:

  • 会话与连接:当前总连接数、活跃会话数、空闲会话是否异常堆积。
  • 表空间使用:各表空间剩余空间比例、增长趋势。
  • 长事务与锁等待:超过设定时长的事务、当前是否存在锁等待。
  • 慢SQL:近一段时间的慢SQL数量和Top SQL。
  • 错误日志:数据库日志中新增的错误信息。

把上面这些检查项固化下来,每次巡检就是跑一遍脚本,输出一张状态表,有问题提前发现,而不是等业务方报告“系统卡了”才开始排查。

5.2 脚本落地的四个关键细节

脚本能不能从“写完放着”变成“真正天天在跑”,取决于四个细节。

第一,输出必须带时间戳和状态码。没有时间戳的巡检结果,事后复盘根本没法对比趋势。比如昨天连接数300,今天连接数800,只有带时间戳的记录才能看出这种增长曲线。

第二,输出格式要可读。纯文本堆在一起没人愿意看,我用过最简单的办法是输出成固定宽度的表格文本,或者直接落成CSV文件,方便后续用电子表格打开分析。脚本是给人看的,不是只给机器跑的,格式不清的脚本很快就会被遗忘。

第三,连接数据库的凭据不要写在脚本里。尽量使用环境变量或者专门的凭据管理文件,降低泄露风险。脚本如果被提交到代码仓库,明文密码就相当于把数据库钥匙交到了所有人手里,这是大忌。

第四,调度和告警要配合起来。脚本跑完发现问题,要通过现有的告警通道通知到人,否则脚本就变成了事后文件——出了问题你才知道,那巡检的意义就少了一半。

5.3 一人写脚本,全组受益的沉淀方式

脚本写完不要只放在自己电脑里。我会把整理好的巡检脚本和排查SQL按模块拆好,加上详细的注释,提交到团队的运维仓库里。这样新同学接手YashanDB环境时,不用从零开始摸索,拿过来就能用,改起来也有基础。

我见过最理想的状态是:团队里一个人把巡检逻辑搭好,其他人遇到问题时,不是到处问人,而是先跑一遍脚本看看输出,问题基本能定位到具体方向。这种沉淀带来的体验提升,往往比调任何一个数据库参数都明显。脚本里涉及的每条SQL最好都附上“为什么要查这个指标”的注释,这样半年后回来看,依然能快速理解当初的设计意图。

最后再说点个人感受。这5个技巧没有一个是“一键起飞”式的操作,它们更像是一套把日常摩擦逐个磨平的过程。我在实际使用YashanDB时最大的体会是:先把连接和会话管理理顺,再把执行计划和等待事件这两项基本功练扎实,参数变更围绕动态生效边界做管理,最后把重复操作脚本化沉淀下来,整个团队的数据库使用体验会有明显改观。

如果你刚开始接触YashanDB,我的建议是从第二个和第三个技巧入手,因为慢SQL和卡顿定位是每天都会碰到的问题,掌握这两项,你就能在大多数人还在猜的时候,准确说出问题出现在哪个环节。等这两项变成习惯,再逐步引入脚本化巡检,体验会继续往好的方向走。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进