自治事务实现openGauss/MogDB/PanweiDB 计算TPS

2024年 3月 13日 61.7k 0

背景

之前在维护PG数据库的时候,有客户提过需求,是通过SQL实现数据库QPS查询,这种需求当时是借助pg_stat_statements插件,在一段时间内,执行相同的sql,通过这两个sql执行结果的差值来估算qps,具体SQL如下:

select round( (c.sum - a.sum) / 10 ) as qps
from (select sum(calls) from pg_stat_statements ) a
left join ( select pg_sleep(10) ) b on 1=1
left join (select sum(calls) from pg_stat_statements ) c on 1=1;

这个sql可以有效执行的一个前提是,数据库事务隔离级别是"读已提交",即在一个事务内,每次select 都是获取当前的快照结果,如果这段时间内数据有变化,那select得到的结果也不一样。

但在openGauss/MogDB/PanweiDB这个系列的数据库中没有pg_stat_statements,那如果想计算qps基本不可能了,因为在生产环境设置数据库参数log_statment=all 或者 log_min_duration_statement=0 基本是不可能的,对磁盘影响太大, 但是可以通过pg_stat_database这个系统视图的xact_commit 和 xact_rollback 字段来计算tps,初步改写SQL如下:

select round( (c.sum - a.sum) / 10 ) as tps
from (select sum(xact_commit+xact_rollback) from pg_stat_database ) a
left join ( select pg_sleep(10) ) b on 1=1
left join (select sum(xact_commit+xact_rollback) from pg_stat_database ) c on 1=1;

但是当在数据库执行执行这个SQL的时候,奇怪的事情出现了,结果是0,换言之就是这段时间没有任何事务执行提交或者回滚,这不符合预期,因为数据库一直都在监控在做简单的查询操作,而默认的一个查询就是隐含一个事务(即使不消耗事务号)。

问题分析

将此SQL拿到PG16数据库去执行,得到的结果是一样的,也是0

sleep没生效?

第一时间我想到的可能是pg_sleep(10) 没生效,也就是没睡着,但是也不对,这个sql明明执行了10s

还是先验证一下pg_sleep(10)吧,排除一切可能性,这里可以看到是可以生效的。

但是在这之后还做了一个测试,就是把clock_timestamp 替换成now(),虽然是在统一个事务中,但是多次查询now()函数,得到的结果是一样的,这是因为now()是获取的事务开始时的快照,不在读已提交事务隔离级别的控制之下。

函数问题

收到now()现场的启发,初步判定是函数问题引起的,pg_stat_database是一个系统视图,它由很多函数组成,现在我讲查询语句单独拿出来放到一个事务中执行

上面窗口是开启事务的,下面窗口未开启事务,可以看到下面窗口在17:11:18的时候查询事务已经达到了9440317,但是在上面窗口17:11:30执行的时候,事务数量依然保持未9440242,说明一个事务中的查询结果没有变。

现在知道了问题所在,接下来就是看如何规避这个问题了,由于学艺不精,搞了几个小时也没弄明白这个函数实现方式,也没有完成改写/替换这个函数,只能另想办法。

自治事务实现改写

既然知道问题是怎么引起的,现在就是在想该怎么去解决了,既然在一个事务中获取的结果一样,那就分成两个事务来执行,但是一个sql怎么分成两个事务呢,这里用到了数据库的一个特性就是:自治事务

先写一个包含自治事务的函数,使其不受主事务的控制,然后再借助pg_sleep(10)来达到我们想要的效果

create or replace function pg_catalog.get_tpss() returns int as
$$
declare
tpss int;
PRAGMA AUTONOMOUS_TRANSACTION;
begin
select sum(xact_commit + xact_rollback)::int into tpss FROM pg_stat_database;
return tpss;
end
$$ language plpgsql;

--查询tps
select abs(round( (c.sum - a.sum) / 10 )) as tps
from (select sum(xact_commit+xact_rollback) from pg_stat_database) a
left join ( select pg_sleep(10) ) b on 1=1
left join (select get_tpss() as sum) c on 1=1;

验证

开启一个事务,多次执行此sql,如果主事务控制的sql执行结果保持不变,而自治事务执行的结果一直变化的话,那说明此方法有效:

验证结果符合预期

遗留问题:如果知道哪些函数只获取事务开启时候的快照,有没有办法改写?

相关文章

在一台虚拟机上搭建MGR 9.0集群
众所周知的原因安装PMM2
唯一上榜!OceanBase入选 2023“科创中国”先导技术榜!
MySQL 删除数据表
利用 MySQL 克隆插件搭建主从
MySQL索引前缀长度超限怎么办?这种方法帮你搞定

发布评论