MariaDB是否存在类似PostgreSQL的transaction_timestamp()事务起始时间函数
问题内容
我在调研时态查询时发现,MariaDB的时态查询实现属于表现较好的方案,但我按照官方文档步骤操作时遇到了问题。
研究过程中我发现MariaDB缺少PostgreSQL提供的transaction_timestamp()函数,PostgreSQL相关时间函数说明如下:
PostgreSQL还提供了返回当前语句起始时间、函数调用瞬间实际时间的函数,完整非SQL标准时间函数列表为:
transaction_timestamp() statement_timestamp() clock_timestamp() timeofday() now()
我执行了如下操作(步骤参考官方文档):
create table t ( x int, test timestamp(6), start_tid bigint unsigned generated always as row start invisible, end_tid bigint unsigned generated always as row end invisible, period for system_time(start_tid, end_tid) ) with system versioning;
随后运行以下语句:
start transaction; insert into t (x, test) values (1, now()), (2, now()), (3, now()); select sleep (5); -- 模拟一个运行时间很长的报表查询 insert into t (x, test) values (11, now()), (12, now()), (13, now()); commit work;
之后执行查询:
select x, test, start_tid, end_tid from t;
得到如下结果:
x test start_tid end_tid 1 2021-11-07 11:43:25 60612 18446744073709551615 2 2021-11-07 11:43:25 60612 18446744073709551615 3 2021-11-07 11:43:25 60612 18446744073709551615 -- 注意此处存在5秒间隔! 11 2021-11-07 11:43:30 60612 18446744073709551615 12 2021-11-07 11:43:30 60612 18446744073709551615 13 2021-11-07 11:43:30 60612 18446744073709551615
我可以获取到两次insert操作各自的执行时间,但无法在MariaDB中获取整个事务的统一起始时间。
在PostgreSQL中执行几乎相同的操作得到结果如下:
x tx_time clock_time 1 2021-11-07 12:01:05.574651 2021-11-07 12:01:05.575062 2 2021-11-07 12:01:05.574651 2021-11-07 12:01:05.575145 3 2021-11-07 12:01:05.574651 2021-11-07 12:01:05.57515 -- 此处tx_time保持不变,clock_time符合预期产生了5秒偏移 11 2021-11-07 12:01:05.574651 2021-11-07 12:01:10.577289 12 2021-11-07 12:01:05.574651 2021-11-07 12:01:10.577307 13 2021-11-07 12:01:05.574651 2021-11-07 12:01:10.577324
我了解到可以通过关联mysql.transaction_registry表实现该需求,但希望能直接通过SQL内置功能实现,请问是否有对应解决方案?
解决方案
方案1:手动捕获事务起始时间(推荐,零额外依赖)
MariaDB目前没有内置和PostgreSQL transaction_timestamp() 完全等价的原生函数,最低成本的替代方案是在事务启动时一次性捕获时间存入用户变量,后续所有操作复用该变量即可:
start transaction; -- 事务开始时仅捕获一次时间,全事务生命周期内复用 set @transaction_start = now(6); insert into t (x, test) values (1, @transaction_start), (2, @transaction_start), (3, @transaction_start); select sleep(5); insert into t (x, test) values (11, @transaction_start), (12, @transaction_start), (13, @transaction_start); commit;
该方案返回的test字段值完全和PostgreSQL的transaction_timestamp()效果一致,无需修改数据库配置、也无需关联系统表。
方案2:调整时间函数行为适配需求
MariaDB的now()默认返回语句执行起始时间,你可以通过调整会话参数修改时间返回逻辑:
- 若需要和PostgreSQL的
statement_timestamp()等价的效果,直接使用原生now()即可,无需额外修改 - 若需要和
clock_timestamp()等价的效果(返回函数执行瞬间的实时时间),可以使用sysdate(6)函数
补充说明
你观察到的同一事务内所有行start_tid相同是预期设计:start_tid是事务提交时分配的全局唯一事务ID,同一个事务内写入的所有行都会共用同一个ID值。如果不想手动维护用户变量,只能关联mysql.transaction_registry系统表,通过start_tid匹配对应的事务起始时间,该表查询性能足够支撑常规业务场景。
内容的提问来源于stack exchange,提问作者SQL_Padawan

