【问题标题】:PostgreSQL: find out if a row has been created by the current transactionPostgreSQL:找出当前事务是否已创建行
【发布时间】:2017-12-11 09:15:56
【问题描述】:

是否可以查明表中的一行是否已由当前事务创建(因此对于其他事务尚不可见,因为当前事务仍处于活动状态)?

我的用例:我正在向数据库添加事件日志记录。这是在 plpgsql 触发器中完成的。事件表中的一行如下所示:(event id:serial, event action:text, count:integer:default 1)

现在,我的问题背后的原因是:如果此事务(很可能在另一个触发器中)创建了某个行,我可以增加计数,而不是在事件表中创建一个新行。

【问题讨论】:

  • 您的实际用例是什么?你的问题是“种族条件”和“完整性问题”。
  • 如果您的代码正在创建行,为什么不让它跟踪它创建的行呢?这样你甚至不需要再次查询数据库,你的代码可以立即发送一个 UPDATE。
  • 这是在 几个 plpgsql 触发器中完成的。因此,我可以跟踪已创建事件的行(临时表?时间戳列?),或者更好的是,以某种方式找出。因此我的问题。
  • 我想你可以在日志中添加一个事务 id 字段并保存txid_current()。登录时可能使用ON CONFLICT
  • 谢谢。但是数据库如何跟踪受当前事务影响的行呢?不知何故,数据库需要相同的信息才能提交或回滚。我可以访问这些信息吗?如果是这样,我不需要按照您的建议手动保存事务 ID 并膨胀我的架构(个人感觉)?

标签: sql postgresql transactions commit


【解决方案1】:

你可能会考虑这样的事情:

create table ConnectionCurrentAction (
     connectionID int primary key,
     currentActionID uuid
)

然后在交易开始时:

delete ConnectionCurrentAction where connectionID = pg_backend_pid()
insert ConnectionCurrentAction(connectionID, currentActionID) 
select pg_backend_pid(), uuid_generate_v4()

您可以将其包装在一个名为 say, audit_action_begin 的过程中

注意:您可以改为强制要求通过删除此处的删除来显式创建“操作”。

在事务结束时,执行audit_action_end:

delete ConnectionCurrentAction where connectionID = pg_backend_pid()

当您想知道当前交易时:

(select currentActionID from ConnectionCurrentAction where connectionID - pg_backend_pid()(

你可以把它包装在一个函数audit_action_current()

然后您可以将 currentActionID 放入您的日志中,这将使您能够识别是否在当前操作中创建了行。这也将允许您确定在当前逻辑操作中创建不同审计表中的行的位置。

如果您不想使用 uuid,则序列也可以在这里使用。我喜欢 uuid。

【讨论】:

  • 好主意!这非常有用。不过有一个问题:你为什么使用pg_backend_pid() 而不是txid_current()?只是想知道是否有充分的理由......再次感谢。
  • 很好的问题。因为我不知道txid_current()。现在我做 :-) 这样你就不需要这个答案了,只要把它塞到你的审计表里就行了。
  • 当然,除非你有与 postgresql 事务不同的逻辑“应用程序”操作
  • 太棒了。我们仍然需要注意 xid 环绕,但这在 dba.stackexchange.com/questions/146315/… 和 cmets 中有明确的解释。
  • Unless of course you have logical "application" actions which are distinct from postgresql transactions:目前情况并非如此,但我明白你的意思。感谢您指出。
【解决方案2】:

您可以像这样查找日志条目:

SELECT ...
FROM tablename
WHERE xmin = current_txid() % (2^32)::bigint;

这将找到在当前事务中添加或修改的所有行。

缺点是这将强制对整个表进行顺序扫描,并且您无法避免这种情况,因为您无法在系统列上建立索引。

因此,您可以在表格中添加一个额外的列xid,每当插入或更新一行时,该列就会填充txid_current()::bigint。这样的列可以在搜索中被索引并有效地使用:

SELECT ...
FROM tablename
WHERE xid = current_txid();

【讨论】:

  • 谢谢。我会考虑添加xid 列。
  • 我相信下面的检查也是有效的以避免误报? xmin::text = (txid_current() % (2^32)::bigint)::text;(不需要你的xid 专栏)我在dba.stackexchange.com/questions/146315/…找到它
  • 哦,我这里弄错了。 current_txid() 确实 not 返回与 xmin 包含的相同值。我已经修复(并简化了)答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-26
  • 2021-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多