一、迁移后的性能困局

1.1 为什么迁移完性能会下降?
我原来在SQL Server上跑得好好的,怎么迁到金仓就慢了?其实大多数情况下,不是数据库不行,是你的SQL和配置,还是SQL Server那一套,没跟着迁过来的数据库一起调整。为什么会有这种情况,我总结了几个常见原因:
SQL Server有自己的SQL方言,很多写法是它独有的。迁到金仓之后,语法虽然能跑通,但优化器不一定能高效处理。比如SQL Server里的一些表提示、连接提示,到了金仓就没用了;还有一些特殊的函数用法,执行计划完全不一样。然后很多团队迁移的时候,只迁了表结构和数据,索引要么没迁全,要么直接照搬SQL Server的索引策略。可不同数据库的索引机制、优化器行为是不一样的。SQL Server里好用的索引,金仓里不一定好用;SQL Server里能走索引的查询,金仓里可能就全表扫描了。参数配置不对,很多人装完金仓,参数全用默认值,就直接上线了。内存分配太小、并发参数太低、IO相关参数没调啥的, 这些问题都要确认好,迁移才没问题。
或者是锁和阻塞没处理好,SQL Server和金仓的锁机制、事务隔离级别都有差异。原来的事务写法,在SQL Server里没问题,迁到金仓可能就容易出现锁等待、阻塞。并发一高,整个系统就跟堵着了,都在等,然后谁也跑不动。所以你看,不是数据库不行,是迁移工作只做了一半------数据迁过来了,配置、SQL、索引这些都没搞好
1.2 先定位,再优化
很多人一遇到性能问题,上来就瞎调------改改这个参数,加加那个索引,改改写SQL。调对了是运气,调不对的话就会越调越差。我总结了一下调优的步骤:先定位,再优化;先SQL,再系统
说白了就是先定位,再优化,先搞清楚问题是出在哪儿,是慢SQL多?还是内存不够?还是IO瓶颈?还是锁阻塞?博主发现其实80%的性能问题,都是SQL和索引的问题。先把SQL优化好,把索引建对,大部分问题就解决了。剩下的20%,再去调系统参数。先做成本低、见效快的优化,比如加索引、改SQL写法,这些改动小、风险低、见效快。复杂的参数调整、架构调整,放在后面。这个思路来的话,调优效率高,风险也小。
1.3 金仓的性能工具链:比你想象的丰富
很多人用金仓,只知道一个EXPLAIN看执行计划,别的啥也不知道。其实金仓自带的性能诊断工具,非常丰富,而且都是针对金仓内核专门优化过的,特别好用。
- KWR:金仓负载信息库,相当于一个全面的性能健康体检报告,告诉你数据库整体哪儿有问题;
- KSH:会话历史采样,可以回溯某个时间点数据库在干什么,哪个会话在等什么;
- KDDM:数据库诊断监控,自动帮你分析性能问题,给出优化建议;
- 还有
sys_stat_statements统计视图、慢日志、锁等待视图等等。
这些工具用好了,我们开发运维找问题调优就简单很多,问题在哪儿都能看到。可惜很多伙伴不知道,或者知道但不会用。今天这篇文章,博主就一边讲调优,一边把这些工具的用法也给大家讲清楚。
二、SQL优化篇:从定位到改写,一套完整的打法
SQL优化是性能调优的很重要的。我做过的调优项目里,80%以上的性能问题,都是通过SQL优化和索引优化解决的。这一部分,我就从怎么找到慢SQL开始,一步步讲到怎么把慢SQL改快
2.1 定位低效SQL
调优的第一步,永远是找到那些跑得慢的SQL。金仓里找慢SQL,其实有好几个方法来找
- 慢查询日志
这是最基础是在sys_kingbase.conf这个里面开启慢日志,把执行时间超过某个阈值的SQL都记录下来。
properties
log_min_duration_statement = 1000 # 单位毫秒,超过1秒的SQL都记录
开启之后,所有超过1秒的SQL都会写到日志文件里。我们就可以定期去分析慢日志,就能知道哪些SQL跑得
- sys_stat_statements统计视图
这是博主比较常用的方法,比慢日志好用多了。开启这个插件之后,金仓会自动统计所有SQL的执行情况:执行了多少次、总耗时多少、平均耗时多少、最慢的一次是多少这些信息,然后我们就可以直接查视图就能拿到数据,不用去翻日志文件
就比如查看总耗时最长的TOP 20 SQL
sql
SELECT query, calls, total_time, mean_time, max_time
FROM sys_stat_statements
ORDER BY total_time DESC
LIMIT 20;
这个视图的信息非常全,我们可以按总耗时排、按平均耗时排、按调用次数排,博主一般调优的第一件事就是把这个视图拉出来,找出那些消耗总时间最多的SQL,优先优化一下这些。
该有人问博主了,为什么优先优化总时间最多的,因为一条SQL哪怕再慢,如果一天只跑一次,影响也有限;但一条SQL每次只慢100毫秒,一天跑10万次,总时间就是10000秒,这样的话我们就可以看出来影响的大小了。
- KWR报告
还有一个方法,KWR报告,这个相比上面两个的话就比较高级了,后面博主会专门讲。KWR报告会自动帮你统计TOP SQL,按各种维度排序,还会给你分析SQL的问题,特别方便。
分析执行计划
找到慢SQL了,接下来我们就要分析一下为什么会慢,分析的工具,就是执行计划EXPLAIN。很多人看执行计划,就看一个有没有走索引,走了索引就觉得没问题,没走索引就觉得有问题。
博主一般看执行计划,重点看这几个东西:
第一,看操作类型。是顺序扫描(全表扫描)还是索引扫描、是哈希连接还是嵌套循环,或者说是排序还是用了索引排序,主要是不同的操作,性能差距很大嘞。全表扫描几百万行,跟索引扫描几行,就品吧。
第二,看预估行数和实际行数的差距。这个的话就比较重要了,优化器是根据统计信息来估算行数的,估算得准,执行计划就好;估算不准,执行计划就会选错。如果预估行数跟实际行数差了好几倍、几十倍,说明统计信息不准,优化器选错了执行计划,SQL自然就慢了。
这时候我们要做的不是改SQL,而是更新统计信息
sql
ANALYZE sys_order;
第三,看过滤条件的位置。过滤条件是在扫描阶段就用上了,还是在后面才过滤,谓词下推做得好不好,直接影响中间结果的数据量。如果一个很严的过滤条件,到很后面才用上,中间产生了大量无用数据,那性能肯定好不了。
第四,看连接顺序和连接方式。多表关联的时候,表的关联顺序对不对,用的连接方式合理不,就比如小表驱动大表的话,用嵌套循环;如果是大表关联大表的话,用哈希连接。这些基本的东西我们还是要有的。
给大家举个真实的例子,这次项目里有一条慢SQL,查某个时间段的订单明细,关联了订单表、订单明细表、商品表、客户表四张表。
我看执行计划,发现优化器把订单明细表当成了驱动表,先全表扫了一遍,再去关联其他表。
为什么?因为统计信息不准,优化器以为订单明细表符合条件的只有几百行,实际上有几十万行。
执行计划选错了,SQL能不慢吗?
更新了统计信息之后,优化器重新选了执行计划,先用订单表过滤出符合条件的订单,再去关联明细表,SQL直接从30秒跑到了1秒。
你看,就这么简单一个操作,性能提升了30倍。很多时候不是SQL写得有多烂,就是统计信息不准,优化器选错了计划。
2.3 第三步:索引优化
关于索引优化,博主总结了几个比较常见的
-
WHERE条件里的字段,优先建索引
这个是最基本的。经常用来过滤的字段,比如订单号、用户ID、状态、时间,都应该建索引。
但要注意,选择性高的字段建索引效果才好。比如性别这种只有两个值的字段,建了索引也没用,优化器不会走的。
-
复合索引,遵循最左匹配原则
多个字段经常一起用在WHERE条件里,就建复合索引。
复合索引要注意字段顺序,把选择性最高的、最常用的字段放左边。
比如经常按"部门+状态+时间"查询,那就建
(dept_id, status, create_time)的复合索引。 -
覆盖索引,让查询不用回表
什么是覆盖索引?就是你要查的所有字段,都在索引里,不用再回表去取数据。
比如你要查
SELECT emp_name, emp_salary FROM sys_employee WHERE dept_id = 10,如果建了
(dept_id, emp_name, emp_salary)的复合索引,那数据库直接从索引里就能拿到所有数据,不用再回表,性能特别好。覆盖索引是性能提升的利器,尤其是那些高频的查询,能用就用。
-
不要建太多索引
索引不是越多越好。每个索引都要占存储空间,而且每次插入、更新、删除数据,都要维护所有索引,会拖慢写入性能。
一般来说,一张表的索引数量控制在5个以内比较合适。不要每个字段都建索引,那是懒人的做法。
-
定期清理无用索引。
系统跑久了,会有很多没人用的索引,白白浪费空间、拖慢写入。金仓里可以通过
sys_stat_user_indexes视图看索引的使用情况:
sql
SELECT relname, indexrelname, idx_scan
FROM sys_stat_user_indexes
WHERE idx_scan = 0
ORDER BY relname;
那些从来没被用过的索引,该删就删,没啥影响,因为这个索引从来没被使用过的索引
这次调优项目里,光是索引优化这一项,俺们团队就加了二十多个复合索引和覆盖索引,删了十几个没用的索引,这个项目的整体查询性能提升了很多
2.4 第四步:SQL改写优化
索引加了,统计信息也更新了,SQL还是慢咋弄,那可能就是SQL写法的问题了。有些SQL写法,优化器很难优化,就得靠人来改常见的SQL改写优化,有这么几种,博主给大家举例说一下
改写一:避免在索引列上做运算和函数。
错误写法:索引列上套函数,索引失效
sql
SELECT * FROM sys_order WHERE DATE(create_time) = '2026-08-10';
正确的是这样的
sql
SELECT * FROM sys_order
WHERE create_time >= '2026-08-10'
AND create_time < '2026-08-11';
第一种写法,索引列上有函数,优化器没法用索引,只能全表扫;
第二种的话条件值是范围,索引列是干净的,就能走索引范围扫描。
性能差几十上百倍都正常。
改写二:子查询改JOIN。
很多人喜欢写子查询,觉得逻辑清晰。但有些子查询,优化器处理不好,性能特别差。比如IN子查询、EXISTS子查询,有时候改成JOIN性能会好很多。
子查询写法
sql
SELECT * FROM sys_order
WHERE cust_id IN (SELECT cust_id FROM sys_customer WHERE city = '上海');
JOIN写法
sql
SELECT o.*
FROM sys_order o
JOIN sys_customer c ON o.cust_id = c.cust_id
WHERE c.city = '上海';
当然了,这个不是绝对的,得看具体场景。改完之后看执行计划,哪个好用哪个。
改写三:大偏移量分页改游标分页。
这个我之前也讲过。LIMIT 100000, 20这种深分页,越翻越慢。改成游标分页,带上一页的最后一个ID,直接定位,性能好得多。
慢的写法:大偏移量
sql
SELECT * FROM sys_log ORDER BY log_id LIMIT 100000, 20;
游标分页
sql
SELECT * FROM sys_log
WHERE log_id < 上一页最小log_id
ORDER BY log_id DESC
LIMIT 20;
改写四:避免SELECT *。
这个也说过很多遍了。只查需要的字段,不要查全部。一是减少数据传输,二是更容易用上覆盖索引。我见过太多系统,一个列表查询查了三十多个字段,页面上只显示五个,纯浪费
SQL改写的技巧还有很多,这里就不一一列举了。
核心思路就是:站在优化器的角度思考,怎么写能让优化器更容易选出好的执行计划。
2.5 第五步:Hint------给优化器"指路"
SQL改了,索引加了,优化器还是选错执行计划怎么办?
这时候就可以用Hint了。
Hint是什么?就是你给优化器的提示,告诉它"你应该这么做"。
金仓支持很多种Hint,比如:
- 索引Hint:强制走某个索引,或者不走某个索引;
- 连接方式Hint:强制用哈希连接、嵌套循环连接或者合并连接;
- 连接顺序Hint:强制表的关联顺序;
- 并行度Hint:指定查询用多少并行度。
举个例子:
sql
-- 强制走idx_sys_order_custid索引
SELECT /*+ IndexScan(o idx_sys_order_custid) */ *
FROM sys_order o
WHERE cust_id = 1001;
再比如:
sql
-- 强制用哈希连接
SELECT /*+ HashJoin(a b) */ *
FROM sys_order a
JOIN sys_customer b ON a.cust_id = b.cust_id;
Hint是个好东西,特别适合那种"优化器就是选不对计划"的场景。
但我要提醒一句:Hint要慎用。
为什么?因为数据是会变的。今天这个执行计划是最优的,过半年数据变了,可能就不是最优的了。但Hint是写死的,优化器没法自动调整了。
所以Hint是最后手段,能不用就不用。实在没办法了,再用。
2.6 第六步:QueryMap------不改代码也能优化SQL
这是金仓一个特别好用的功能,很多人不知道。
什么是QueryMap?简单说,就是SQL映射。
你应用里发过来一条SQL,金仓收到之后,自动把它替换成另一条等价的、性能更好的SQL去执行。
应用代码完全不用改,数据库层面自动帮你改写SQL。
这个功能太实用了。
什么时候用?
- 应用是第三方的,你改不了代码;
- SQL写得太烂,但改代码风险大、周期长;
- 紧急故障,需要快速优化,来不及改代码发版。
举个例子,应用里发过来的SQL是:
sql
SELECT * FROM sys_order WHERE DATE(create_time) = '2026-08-10';
这条SQL因为索引列上有函数,走不了索引,很慢。
但你又改不了应用代码,怎么办?
用QueryMap,配置一条映射规则,把这条SQL自动改写成:
sql
SELECT * FROM sys_order
WHERE create_time >= '2026-08-10'
AND create_time < '2026-08-11';
应用那边完全无感知,数据库自动帮你改写,性能直接上去了。
QueryMap支持精确匹配、模糊匹配、正则匹配等多种匹配方式,非常灵活。
配置也不复杂,在sys_kingbase.conf里配置一下就行,或者用SQL函数来管理。
这次调优项目里,我们就用QueryMap优化了七八条特别难搞的SQL。
都是第三方应用里的烂SQL,改不了代码,用QueryMap一映射,立马就快了。
客户的运维主管说,这功能简直是第三方应用的救星。
三、系统调优篇:内存、IO、锁,一个都不能少
SQL优化完了,大部分性能问题就解决了。
但如果系统整体还是慢,那就要往深了挖------看看系统层面有没有瓶颈。
内存够不够?IO是不是瓶颈?有没有锁阻塞?
这一部分,我们就来讲讲系统层面的调优。
3.1 内存调优:让数据库"吃饱了"干活
数据库性能好不好,内存配置是关键。
内存给够了,很多数据都在内存里,不用去读磁盘,自然就快。
内存不够,动不动就要读磁盘,IO一上来,性能就掉下来了。
金仓的内存相关参数有很多,最核心的有这么几个:
第一个:shared_buffers------共享缓冲区
这是金仓最重要的内存参数,没有之一。
shared_buffers是金仓自己用的缓存,用来缓存表数据、索引数据。
这个值设多大合适?
一般来说,专用数据库服务器的话,设成系统总内存的25%到40%比较合适。
比如服务器128G内存,shared_buffers可以设成32G到48G左右。
太小了,缓存不够,频繁读磁盘;太大了,系统内存不够用,反而会用swap,更慢。
第二个:work_mem------工作内存
这个是排序、哈希连接、聚合这些操作用到的内存。
每个查询、每个操作都可以用这么多内存。
work_mem太小的话,排序、哈希这些操作放不下,就会写到磁盘上(也就是磁盘排序、磁盘哈希),性能特别差。
work_mem设大一点,这些操作就能在内存里完成,快很多。
但也不能设太大,因为每个查询都可以用这么多,并发高了,总内存使用会爆炸。
一般来说,OLTP系统可以设小一点,比如4MB到16MB;OLAP/报表系统可以设大一点,比如32MB到128MB。
根据你的并发数和查询类型来调整。
第三个:maintenance_work_mem------维护操作内存
这个是给VACUUM、CREATE INDEX、ALTER TABLE这些维护操作用的。
这个值设大一点没关系,因为维护操作不是经常跑,而且同时跑的也不多。
一般设成1GB到2GB就够用了,建索引、vacuum的时候会快很多。
第四个:effective_cache_size------优化器估算用的缓存大小
这个参数不实际分配内存,是告诉优化器:系统里大概有多少缓存可用(包括数据库自己的缓存和操作系统的缓存)。
优化器估算成本的时候会用到这个值,设得准不准,会影响优化器选不选索引扫描。
一般设成系统总内存的50%到75%左右。
这些参数都在sys_kingbase.conf里配置,改完之后重启数据库生效(有些可以reload,不用重启)。
这次项目里,客户原来的shared_buffers只设了128MB(默认值!),work_mem才4MB。
128G内存的服务器,数据库只用了128MB缓存,这不扯吗?
我们给调成了shared_buffers=40GB,work_mem=32MB,effective_cache_size=80GB。
调完之后,整体性能直接上了一个台阶,磁盘IO降了一大半。
为什么?因为大部分数据都在内存缓存里了,不用读磁盘了。
3.2 IO瓶颈排查与优化
内存调完了,如果还是慢,就得看看IO是不是瓶颈了。
IO瓶颈怎么判断?
几个方法:
方法一:看操作系统的IO指标
用iostat、iotop这些工具,看看磁盘利用率是不是经常100%,IO等待是不是很高,吞吐量是不是到上限了。
如果磁盘天天跑满,那IO就是瓶颈。
方法二:看数据库里的等待事件
金仓里有等待事件的统计,可以看数据库是不是经常在等IO。
比如sys_stat_bgwriter视图,看看检查点、后台写的情况;
还有sys_stat_database视图,看看块读的情况。
如果大量的时间都花在IO等待上,那就是IO瓶颈。
方法三:KWR报告
KWR报告里会有IO相关的统计,读了多少块、写了多少块、物理读多少、缓存命中多少,一目了然。
IO瓶颈怎么优化?
几个思路:
第一,加内存,提升缓存命中率。
这是最简单有效的方法。很多IO问题,本质是内存不够,缓存命中率低,动不动就要读磁盘。
内存加够了,缓存命中率上去了,IO自然就降下来了。
第二,优化SQL和索引,减少IO量。
烂SQL读大量无用数据,自然IO高。SQL优化好了,读的数据少了,IO自然就降了。
索引建好了,不用全表扫了,IO也会降。
第三,调整检查点相关参数。
金仓的检查点(checkpoint)会把脏数据批量刷到磁盘上,如果刷得太猛,会造成IO尖峰。
调整checkpoint相关的参数,让刷盘更平滑,避免IO尖峰。
比如调整checkpoint_completion_target,让检查点在更长的时间内完成,IO更平稳。
第四,换更快的存储。
如果上面的都做了,IO还是瓶颈,那就是存储本身性能不够了。
机械盘换SSD,SSD换NVMe,存储性能上去了,IO瓶颈自然就解了。
这个是硬件层面的优化,花钱就能解决,简单粗暴但有效。
3.3 锁与阻塞:并发场景的"堵车"问题
系统慢,还有一个常见原因------锁和阻塞。
就像马路上堵车了,大家都走不了,整个系统就慢下来了。
这个问题在高并发的OLTP系统里特别常见。
锁阻塞是怎么产生的?
简单说,就是一个事务锁住了某行数据或者某张表,另一个事务也要改这行数据,就得等。
等的时间长了,就堆积起来了,后面的事务都在等,系统就慢了。
常见的锁阻塞原因:
- 事务太大,执行时间太长,锁的时间太久;
- 更新操作没有合适的索引,导致锁了很多不该锁的行;
- 不同的事务更新数据的顺序不一样,导致死锁;
- 全表更新、批量删除这种大操作,锁了整张表,其他操作都等着。
怎么排查锁阻塞?
金仓里排查锁问题,有几个好用的视图:
sys_locks视图:查看当前所有的锁,谁持有的、什么类型的锁、锁的是哪个对象。
sql
SELECT * FROM sys_locks WHERE NOT granted; -- 正在等待的锁
sys_stat_activity视图:查看当前所有的会话,在干什么、等什么、等了多久。
sql
SELECT pid, wait_event_type, wait_event, query, state
FROM sys_stat_activity
WHERE wait_event IS NOT NULL; -- 正在等待的会话
还有一个更方便的:KSH工具,后面会讲。KSH可以看到历史上的等待事件,某个时间点谁在等什么、等了多久,都能查出来。
锁阻塞怎么解决?
找到了阻塞源,解决就好办了:
第一,优化事务,让事务尽量短。
事务越短,锁的时间越短,冲突的概率越低。
别在事务里干乱七八糟的事,比如调用外部接口、等用户输入,这些都放到事务外面。
事务里只做数据库操作,做完赶紧提交。
第二,确保更新操作走索引。
更新的时候,如果WHERE条件能走索引,就只会锁符合条件的那几行;
如果没走索引,全表扫,可能会锁很多行,甚至锁整张表,冲突概率大大增加。
所以更新操作的WHERE条件,一定要有合适的索引。
第三,统一更新顺序,避免死锁。
不同的事务,如果更新数据的顺序不一样,很容易死锁。
比如事务A先更新表1再更新表2,事务B先更新表2再更新表1,就很容易死锁。
所有事务都按统一的顺序更新,就能大大减少死锁的概率。
第四,大操作分批做。
大批量更新、删除,别一条SQL干到底,分批做。
比如一次更新1000条,提交一次,歇一会儿再继续。
这样锁的时间短,不会长时间堵着其他操作。
这次项目里,锁阻塞也是个大问题。高峰期经常有几十个会话在等锁,系统卡得不行。
我们排查下来,主要原因是几个批量更新的SQL没走索引,锁了太多行。
加了索引、改了分批更新之后,锁等待直接降了80%,系统顺畅多了。
四、金仓神器篇:KWR、KSH、KDDM三大诊断工具
前面零零散散提到了几个金仓自带的性能工具,这一部分我专门拿出来,给大家详细讲讲。
这三个工具------KWR、KSH、KDDM,是金仓性能调优的三大利器,用好了事半功倍。
4.1 KWR:数据库的"体检报告"
先讲KWR,全称是KingbaseES Workload Repository,金仓负载信息库。
你可以把它理解成数据库的全面体检报告。
KWR会定期自动采集数据库的性能数据,存到自己的库里。
比如:等待事件、SQL统计、IO统计、内存使用、表和索引的使用情况......
你想看某个时间段的性能情况,就生成一份KWR报告,它会把那个时间段的所有性能数据汇总起来,给你一份完整的分析报告。
KWR报告里有什么?
KWR报告的内容非常丰富,主要有这么几部分:
- 数据库概览:数据库基本信息、报告时间段、负载情况;
- TOP等待事件:数据库主要在等什么,是CPU、IO还是锁;
- TOP SQL:按总耗时、平均耗时、调用次数等维度排名的SQL;
- IO统计:物理读、逻辑读、写次数、缓存命中率;
- 内存统计:缓冲区使用情况、命中率;
- 表和索引统计:哪些表访问最多、哪些索引用得最多/最少;
- 锁和事务统计:锁等待情况、事务提交回滚情况;
- 建议:自动给出一些优化建议。
一份KWR报告在手,数据库的整体健康状况一目了然。
哪里有问题、问题有多严重、该从哪儿入手,全都清清楚楚。
怎么生成KWR报告?
生成KWR报告很简单,几个步骤:
- 确保KWR插件已经开启(默认是开的);
- 调用KWR的快照函数,生成两个时间点的快照;
- 调用生成报告的函数,指定两个快照ID,生成报告。
sql
-- 手动生成快照
SELECT * FROM kwr.snapshot();
-- 查看快照列表
SELECT snap_id, snap_time FROM kwr.snapshot ORDER BY snap_id;
-- 生成KWR文本报告
SELECT * FROM kwr.report(开始快照ID, 结束快照ID);
生成的报告是文本格式的,你可以直接看,也可以导出来保存。
我一般怎么用KWR?
我做调优的时候,KWR是必看的。
一般先看TOP等待事件,知道数据库主要瓶颈在哪儿;
再看TOP SQL,找到最耗时的那些SQL,优先优化;
然后看看IO、内存、缓存命中率这些指标,判断系统层面有没有问题。
看完一份KWR报告,整个数据库的性能状况心里就有数了,接下来该调什么、从哪儿开始,就很清晰了。
说真的,有KWR这么好用的工具,调优效率至少提升一倍。
不用你一个个视图去查、去算,它都帮你整理好了。
4.2 KSH:穿越时空的"性能监控录像"
第二个神器是KSH,全称KingbaseES Session History,会话历史。
如果说KWR是体检报告,那KSH就是监控录像。
KSH会定期采样(默认每秒一次)所有活跃会话的状态,存下来。
比如:某个会话在干什么、在执行什么SQL、在等什么事件、用了多少CPU、读了多少数据......
这些采样数据会保留一段时间,你可以回溯任何一个历史时间点,看看当时数据库在干什么。
KSH有什么用?
KSH最大的用处,就是回溯问题。
很多时候,性能问题是偶发的,等你发现的时候,问题已经过去了,现场没了。
你不知道当时发生了什么,也不知道是谁导致的,只能瞎猜。
有了KSH就不一样了,你可以回到问题发生的那个时间点,看看当时各个会话在干什么、谁在占CPU、谁在等锁、哪条SQL跑得慢。
就像调监控录像一样,一清二楚。
举个例子,凌晨三点数据库卡了五分钟,早上起来发现的,但不知道为什么。
有KSH的话,你直接查凌晨三点的会话历史,看看那五分钟里,数据库的主要等待事件是什么,哪条SQL跑得最久,谁阻塞了谁。
分分钟定位根因。
怎么用KSH?
KSH的用法也很简单,主要是查视图:
sql
-- 查看某个时间段的会话历史
SELECT * FROM ksh.history
WHERE sample_time BETWEEN '开始时间' AND '结束时间'
ORDER BY sample_time;
你可以按时间查、按会话查、按等待事件查、按SQL查,想怎么查就怎么查。
还有一些更高级的用法,比如生成ASH报告,分析某个时间段的等待事件分布。
这次调优项目里,KSH帮了大忙。
有一个间歇性的性能问题,每天下午两点左右会卡几分钟,但每次我们去看的时候都恢复了。
后来用KSH一查,发现每天下午两点,有个定时任务跑一条特别烂的SQL,把IO全占了,导致其他查询都慢。
找到原因就好办了,优化了那条SQL,问题就解决了。
4.3 KDDM:自动给你"开药方"的诊断专家
第三个神器是KDDM,全称KingbaseES Database Diagnostic Monitor,数据库诊断监控。
如果说KWR是体检报告,KSH是监控录像,那KDDM就是自动给你开药方的诊断医生。
KDDM会自动分析数据库的性能数据,识别出性能问题,然后给你出诊断报告和优化建议。
不用你自己去分析KWR、去查视图,它自动帮你分析完了,直接告诉你:哪儿有问题、问题是什么、建议怎么优化。
KDDM能诊断什么?
KDDM能诊断的问题很多,比如:
- TOP SQL问题:哪些SQL跑得慢,建议怎么优化(加索引、改写SQL等);
- 索引问题:哪些索引缺失、哪些索引没用、哪些索引效率低;
- IO问题:IO瓶颈在哪儿,怎么优化;
- 内存问题:内存配置合不合理,要不要调整;
- 锁问题:有没有严重的锁等待,怎么解决;
- 参数问题:哪些参数配置不合理,建议改成多少。
基本上你能想到的性能问题,它都能给你诊断一下,还会给出具体的优化建议。
怎么用KDDM?
KDDM的使用也很简单,调用它的诊断函数就行:
sql
-- 生成诊断报告
SELECT * FROM kddm.report(开始快照ID, 结束快照ID);
它会输出一份诊断报告,里面有问题描述、严重程度、影响范围、优化建议、预期收益等等。
特别详细,照着做就行。
我的使用感受
说实话,第一次用KDDM的时候,我挺惊讶的。
没想到国产数据库的自动诊断工具,已经做得这么好了。
很多问题,它分析得比人还快、还准,建议也很实在,不是那种空话套话。
当然了,它也不是万能的,特别复杂的问题还是得人来分析。
但对于大部分常见的性能问题,KDDM完全够用了,能帮你省很多时间。
尤其是新手,不知道怎么分析性能问题的,先用KDDM跑一遍,大概就知道问题在哪儿了。
4.4 三大工具配合使用,效果翻倍
这三个工具,不是孤立的,配合起来用效果最好。
我的一般流程是:
- 先跑一份KDDM诊断报告,看看它说有什么问题,大概有个方向;
- 再看KWR报告,验证一下KDDM说的对不对,深入了解整体性能状况;
- 如果有偶发问题或者需要回溯的,用KSH去查历史,定位具体时间点的问题。
三个工具一套组合拳打下来,什么性能问题都藏不住。
以前调优靠经验、靠瞎试,现在有了这些工具,就跟开了挂似的,问题在哪儿一目了然。
五、完整调优案例:三周时间,性能翻了三倍
讲了这么多方法和工具,最后给大家讲一个完整的调优案例,就是我开头说的那个项目。
看看从慢到快,到底是怎么一步步调出来的。
5.1 项目背景
客户是一家中型企业,刚做完SQL Server数据迁移,迁到金仓KES V9R4C019。
迁移完之后,系统性能很差,用户投诉不断。
问题的话就是下面这些:
- BI报表慢,原来几分钟的报表现在要跑半小时;
- 高峰期系统卡,页面响应慢;
- 经常出现锁等待,业务操作超时;
- CPU和IO经常跑满。
客户自己的团队调了一个月,越调越乱,最后找到了我们。
5.2 第一周:诊断与SQL优化
我们去的第一周,主要做诊断和SQL优化。
第一天,我们是先跑了一份KWR报告和KDDM诊断报告,看看整体情况。KDDM一跑,直接列出来十几个问题,按严重程度排好序了,其实后面我们就优化就好了
严重:TOP 5 SQL占用了70%的数据库时间,急需优化;
严重:缺失多个重要索引,大量全表扫描;
警告:shared_buffers设置过小,缓存命中率低;
警告:work_mem过小,大量磁盘排序;
警告:存在较多锁等待。
看完报告,我们心里就有数了,问题主要还是在SQL和索引上,系统参数也得调。
第二天到第五天:SQL和索引优化
我们从TOP SQL里挑了最耗时的二十条,一条一条优化。
- 加复合索引、覆盖索引,一共加了二十多个;
- 改写了十几条SQL,把索引列上的函数都去掉了,子查询改JOIN了;
- 几条第三方应用的SQL改不了代码,用QueryMap配置了映射;
- 更新了所有大表的统计信息,让优化器选对执行计划。
就这样我们优化了好几天,但是效果还是很明显的
TOP 20 SQL的平均响应时间,从原来的十几秒,降到了不到一秒,整体数据库时间降了一半多,CPU使用率从高峰期90%降到了50%左右。
第一周搞完,客户就说感觉系统快多了
5.3 第二周:系统参数调优
SQL优化完了,第二周我们开始调系统参数。
内存参数调整:
- shared_buffers从128MB调到40GB;
- work_mem从4MB调到32MB;
- maintenance_work_mem从64MB调到2GB;
- effective_cache_size从4GB调到80GB。
IO相关参数调整:
调整了检查点参数,让刷盘更平滑,调整了后台写进程的参数,优化了预读参数
并发和锁相关参数调整:
调整了连接数参数;调整了锁超时参数;优化了事务相关参数。
调完之后重启了一次数据库
缓存命中率从85%左右提升到了98%以上,物理IO降了一大半,磁盘利用率从高峰期80%降到了30%,报表查询又快了一截,原来跑半小时的报表,现在五分钟就跑完了,这周搞完后,俺们这个系统性能又上了一个台阶。
5.4 第三周:锁优化与收尾
第三周,我们主要处理锁和阻塞的问题,还有一些收尾工作。
锁优化:
用KSH回溯了几次锁阻塞事件,找到了出现这种问题的地方。之前博主记得有几个批量更新的SQL没走索引,加了索引之后,锁的行数减少很多。还有那种大事务拆成小事务,缩短锁持有时间,了解并且统一了更新顺序,死锁基本消失了。
像其他的优化,清理了几十个无用索引,写入性能也提升了一些,还调整了几个表的存储参数,教了一下运维他们怎么用KWR、KSH、KDDM这些工具。
三周下来,整体效果:
- 核心查询平均响应时间,从原来的2-3秒,降到了200-300毫秒,快了将近10倍;
- BI报表整体提速5-10倍,大部分报表秒级返回;
- 高峰期CPU使用率从90%降到40%左右;
- 锁等待减少了90%,业务操作几乎不再超时;
- 系统整体吞吐量提升了3倍以上。
验收的时候,客户CTO看完监控数据,说了那句让我印象很深的话:"跟换了个数据库似的。"
5.5 调优的心得
大部分性能问题,都不是数据库本身的问题,是使用方式的问题。SQL写得烂、索引建得不对、参数没调好,这些才是性能差的主要原因。数据库本身的性能,其实都不差,就看你会不会用。
调优是有方法论的,不是靠瞎试。先定位、再优化,先SQL、后系统,先易后难。按照这个思路来,效率高,风险小。
用好工具是很重要的。KWR、KSH、KDDM这些工具,真的能帮你省很多时间。别再靠经验、靠瞎猜了,工具都给你做好了,用就是了。
其实金仓的性能,真的被很多人低估了。很多人觉得国产数据库性能不行,那是你没调好。调好了之后,性能真的不差,甚至比很多国外数据库还好。关键是你得会调、得用好它的特性。这个也是要花时间了解的,哪有那种不学就会的。
六、总结
做了这几年的从SQL Server到Oracle,从MySQL到金仓的迁移和性能调优,我最大的感受就是:没有不好的数据库,只有不会用的人。其实就是再好的数据库,你乱建索引、乱写SQL、乱配参数,它也快不了;再普通的数据库,你SQL写得好、索引建得对、参数调得合理,其实也能跑得很快的。
电科金仓作为国内自主研发的数据库,这些年在性能优化上的进步,我是有体会的,因为用的比较早,了解的也相对比较多一些。从优化器到执行引擎,从内存管理到IO调度,从诊断工具到自动化运维,方方面面都在进步。尤其是KWR、KSH、KDDM这些工具,做得真的很用心,很懂DBA的痛点。有了这些工具,调优不再是靠经验靠感觉了,而是有数据支撑、有方法可循的了。