SQL Server数据迁移金仓性能优化实战:从慢SQL到系统瓶颈,一套组合拳打下来

一、迁移后的性能困局

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,其实有好几个方法来找

  1. 慢查询日志

这是最基础是在sys_kingbase.conf这个里面开启慢日志,把执行时间超过某个阈值的SQL都记录下来。

properties 复制代码
log_min_duration_statement = 1000  # 单位毫秒,超过1秒的SQL都记录

开启之后,所有超过1秒的SQL都会写到日志文件里。我们就可以定期去分析慢日志,就能知道哪些SQL跑得

  1. 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秒,这样的话我们就可以看出来影响的大小了。

  1. KWR报告
    还有一个方法,KWR报告,这个相比上面两个的话就比较高级了,后面博主会专门讲。KWR报告会自动帮你统计TOP SQL,按各种维度排序,还会给你分析SQL的问题,特别方便。

分析执行计划

找到慢SQL了,接下来我们就要分析一下为什么会慢,分析的工具,就是执行计划EXPLAIN。很多人看执行计划,就看一个有没有走索引,走了索引就觉得没问题,没走索引就觉得有问题。

博主一般看执行计划,重点看这几个东西:

第一,看操作类型。是顺序扫描(全表扫描)还是索引扫描、是哈希连接还是嵌套循环,或者说是排序还是用了索引排序,主要是不同的操作,性能差距很大嘞。全表扫描几百万行,跟索引扫描几行,就品吧。

第二,看预估行数和实际行数的差距。这个的话就比较重要了,优化器是根据统计信息来估算行数的,估算得准,执行计划就好;估算不准,执行计划就会选错。如果预估行数跟实际行数差了好几倍、几十倍,说明统计信息不准,优化器选错了执行计划,SQL自然就慢了。

这时候我们要做的不是改SQL,而是更新统计信息

sql 复制代码
ANALYZE sys_order;

第三,看过滤条件的位置。过滤条件是在扫描阶段就用上了,还是在后面才过滤,谓词下推做得好不好,直接影响中间结果的数据量。如果一个很严的过滤条件,到很后面才用上,中间产生了大量无用数据,那性能肯定好不了。

第四,看连接顺序和连接方式。多表关联的时候,表的关联顺序对不对,用的连接方式合理不,就比如小表驱动大表的话,用嵌套循环;如果是大表关联大表的话,用哈希连接。这些基本的东西我们还是要有的。

给大家举个真实的例子,这次项目里有一条慢SQL,查某个时间段的订单明细,关联了订单表、订单明细表、商品表、客户表四张表。

我看执行计划,发现优化器把订单明细表当成了驱动表,先全表扫了一遍,再去关联其他表。

为什么?因为统计信息不准,优化器以为订单明细表符合条件的只有几百行,实际上有几十万行。

执行计划选错了,SQL能不慢吗?

更新了统计信息之后,优化器重新选了执行计划,先用订单表过滤出符合条件的订单,再去关联明细表,SQL直接从30秒跑到了1秒。

你看,就这么简单一个操作,性能提升了30倍。很多时候不是SQL写得有多烂,就是统计信息不准,优化器选错了计划。

2.3 第三步:索引优化

关于索引优化,博主总结了几个比较常见的

  1. WHERE条件里的字段,优先建索引

    这个是最基本的。经常用来过滤的字段,比如订单号、用户ID、状态、时间,都应该建索引。

    但要注意,选择性高的字段建索引效果才好。比如性别这种只有两个值的字段,建了索引也没用,优化器不会走的。

  2. 复合索引,遵循最左匹配原则

    多个字段经常一起用在WHERE条件里,就建复合索引。

    复合索引要注意字段顺序,把选择性最高的、最常用的字段放左边。

    比如经常按"部门+状态+时间"查询,那就建(dept_id, status, create_time)的复合索引。

  3. 覆盖索引,让查询不用回表

    什么是覆盖索引?就是你要查的所有字段,都在索引里,不用再回表去取数据。

    比如你要查SELECT emp_name, emp_salary FROM sys_employee WHERE dept_id = 10

    如果建了(dept_id, emp_name, emp_salary)的复合索引,那数据库直接从索引里就能拿到所有数据,不用再回表,性能特别好。

    覆盖索引是性能提升的利器,尤其是那些高频的查询,能用就用。

  4. 不要建太多索引

    索引不是越多越好。每个索引都要占存储空间,而且每次插入、更新、删除数据,都要维护所有索引,会拖慢写入性能。

    一般来说,一张表的索引数量控制在5个以内比较合适。不要每个字段都建索引,那是懒人的做法。

  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报告很简单,几个步骤:

  1. 确保KWR插件已经开启(默认是开的);
  2. 调用KWR的快照函数,生成两个时间点的快照;
  3. 调用生成报告的函数,指定两个快照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 三大工具配合使用,效果翻倍

这三个工具,不是孤立的,配合起来用效果最好。

我的一般流程是:

  1. 先跑一份KDDM诊断报告,看看它说有什么问题,大概有个方向;
  2. 再看KWR报告,验证一下KDDM说的对不对,深入了解整体性能状况;
  3. 如果有偶发问题或者需要回溯的,用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的痛点。有了这些工具,调优不再是靠经验靠感觉了,而是有数据支撑、有方法可循的了。

相关推荐
.柒宇.1 小时前
运维常见面试题_02_数据库
运维·数据库·sql·面试
晚安code21 小时前
MySQL 开发规范清单:建表、索引、SQL、ORM 四块一次讲透
数据库·sql·mysql
weixin_727535621 天前
mysql高频八股问答200
sql·mysql
Yan_chen6661 天前
CTFHub SQL 布尔盲注实战攻略
数据库·sql·实战·布尔盲注·ctfhub闯关
橘色的喵1 天前
ARM-Linux 嵌入式消息总线:高优先级驱逐机制设计
linux·arm开发·性能优化·嵌入式·newosp
其实防守也摸鱼1 天前
VS Code Git 工作树:解锁多分支并行开发新体验
数据库·git·安全·oracle·架构·自动化
Csvn1 天前
📊 SQL 入门 Day 16:数据插入技巧
后端·sql
ERD Online1 天前
MySQL/Oracle/PG/SQLServer 存量库一键逆向成关系图
mysql·oracle·sqlserver