PostgreSQL问题:'now'不断返回相同的旧值

时间:2016-06-10 22:32:40

标签: postgresql transactions apache-commons-dbcp

我有一个旧的Web应用程序,相关的当前堆栈是:Java 8,Tomcat 7,Apache Commons DBCP 2.1,Spring 2.5(用于事务),iBatis,PostgreSQL 9.2和postgresql-9.4.1208.jar

部分代码在incidents表中插入新记录,其中字段begin_datetimestamp(3) with time zone)是创建时间戳,填充now

    insert into incidents
    (...., begin_date,    )
    values
    (..., 'now' ....)

所有这些都是通过iBatis执行的,通过Spring以编程方式管理的事务,通过DBCP池获取的连接。 webapp(实际上是一对,客户端和后台,共享大部分代码和jar)多年来一直在工作。

最近,也许在一些库更新和重组之后(似乎没什么重要的),我一直在经历(间歇性,难以复制)一些令人讨厌的问题:now似乎冻结了,它开始返回相同的“旧”价值。然后,许多记录显示具有相同的创建时间戳,小时或几天前:

db=# select 'now'::timestamptz;
        timestamp
-------------------------
 2016-06-10 21:59:03.637+00

db=#  select rid,begin_date from incidents order by rid desc limit 6;
  rid  |         begin_date
-------+----------------------------
 85059 | 2016-06-08 00:11:06.503+00
 85058 | 2016-06-08 00:11:06.503+00
 85057 | 2016-06-08 00:11:06.503+00
 85056 | 2016-06-08 00:11:06.503+00
 85055 | 2016-06-08 00:11:06.503+00
 85054 | 2016-06-08 00:11:06.503+00

(所有上述记录实际上是在2016-06-10 21:50之前几分钟创建的)

这怎么可能发生?它可能是与事务和/或连接池有关的一些问题,但我无法弄清楚是什么。 我知道'now()'是transaction_timestamp()的别名,它返回事务开始时的时间。这表明交易没有正确关闭,上面的记录插入(无​​意中)写在一个长期交易中。但这对我来说相当不可思议。

首先,我可以插入一条新记录(通过webapp),然后使用psql控制台,我看到它已经写入了相同的begin_date(如果交易是未公开的,我不应该看到新记录,我有默认的序列化级别)。

此外,pg_stat_activity视图仅显示idle个关联。

任何提示?

2 个答案:

答案 0 :(得分:3)

常量(特殊时间戳值) 'now'
功能 now()

你自由地混合它们这一事实表明你并不知道最重要的区别。 The manual:

  

特殊值

     

PostgreSQL支持几种特殊的日期/时间输入值   方便,如表8-13所示。值infinity-infinity   在系统内部特别显示并将显示   不变;但其他人只是那些符号的标志性短缺者   读取时转换为普通日期/时间值。 (特别是,   now及相关字符串会尽快转换为特定时间值   因为它们被阅读。)所有这些值都需要单独包含在内   在SQL命令中用作常量时引用。

大胆强调我的。

并且(就像你已经提到的那样),但引用了the manual

  

now()是传统的PostgreSQL,相当于transaction_timestamp()

  

transaction_timestamp()相当于CURRENT_TIMESTAMP

还有更多,请阅读整章。

现在(没有双关语意),因为您使用的是特殊值而不是函数,所以使用预备语句会得到不同的(意外的)行为。

考虑一下这个演示:

test=# BEGIN;
BEGIN
test=# PREPARE foo AS
test-# SELECT timestamptz 'now' AS now_constant, now() AS now_function;
PREPARE
test=# EXECUTE foo;
         now_constant          |         now_function
-------------------------------+-------------------------------
 2016-06-11 03:09:05.622783+02 | 2016-06-11 03:09:05.622783+02 -- identical
(1 row)

test=# commit;
COMMIT
test=# EXECUTE foo;
         now_constant          |         now_function
-------------------------------+------------------------------
 2016-06-11 03:09:05.622783+02 | 2016-06-11 03:10:00.92488+02  -- different!
(1 row)

当您在同一 交易 中同时运行时,'now'now()会生成相同的值。但是,准备好的声明旨在持续 会话 的持续时间(可能跨越许多事务)。下次执行准备好的语句时,您将看到差异。

换句话说:'now'实现 "早期绑定" ,而now()实现 "后期绑定"

您可能已经引入了预准备语句和/或连接池(可以在更长的时间内保留准备好的语句) - 这两者通常都是好主意。但是INSERT中隐藏的问题现在开始了。

"空闲连接"你看到了多少:连接保持开放,保留准备好的陈述。

简而言之:使用 now()

或者,将列begin_date的默认设置为now()(不是 'now' !)并且不要提到INSERT中的列。您的创建时间戳"自动保存。

答案 1 :(得分:0)

我认为您正在寻找的功能现在是(),而不是'现在' ...

  insert into incidents
  (..., begin_date, ...)
  values
  (..., now(), ...)

或者至少是从psql shell开始的。

robert.kuhar=# select now();
              now
-------------------------------
 2016-06-10 18:10:05.953661-07
(1 row)