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

Issue with PostgreSQL: 'now' keeps returning same old value

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

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

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

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

最近,也许在一些库更新和重组之后(似乎没什么重要的),我遇到了(间歇性的,难以重现)一些讨厌的问题:now 似乎冻结了,它开始返回相同的 "old" 值。然后,许多记录出现在几个小时或几天前,具有相同的创建时间戳:

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前几分钟创建的)

怎么会这样?这可能是与事务 and/or 连接池相关的一些问题,但我不知道是什么。 我知道'now()'是transaction_timestamp()的别名,它returns交易开始的时间。这表明事务未正确关闭,并且上面的记录插入(无​​意中)写入了单个长事务。但这对我来说看起来相当不可思议。

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

此外,pg_stat_activity 视图仅显示 idle 个连接。

有什么线索吗?

我认为您要查找的函数是 now(),而不是 'now'...

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

或者至少可以从 psql shell.

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

有常量(特殊时间戳值)'now'.
还有函数 now().

你随意混合它们的事实表明你没有意识到最重要的区别。 The manual:

Special Values

PostgreSQL supports several special date/time input values for convenience, as shown in Table 8-13. The values infinity and -infinity are specially represented inside the system and will be displayed unchanged; but the others are simply notational shorthands that will be converted to ordinary date/time values when read. (In particular, now and related strings are converted to a specific time value as soon as they are read.) All of these values need to be enclosed in single quotes when used as constants in SQL commands.

大胆强调我的。

并且(就像你自己提到的那样),但引用 the manual:

now() is a traditional PostgreSQL equivalent to transaction_timestamp().

并且:

transaction_timestamp() is equivalent to 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
-------------------------------+-------------------------------
 <b>2016-06-11 03:09:05.622783+02 | 2016-06-11 03:09:05.622783+02</b> -- identical
(1 row)

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

当您 运行 在同一个 事务中 'now'now() 产生相同的值。但是准备好的语句旨在持续 session 的持续时间(可能跨越许多事务)。下次执行准备好的语句时,您会看到不同之处。

换句话说:'now'实现了"early binding",而now()实现了"late binding".

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

您看到的 "idle connections" 表明:连接保持打开状态,保留准备好的语句。

简而言之:使用now().

或者,将begin_date的列默认设置为now()(不是'now' !) 并且不要在 INSERT 中提及该列。您的 "creation timestamp" 会自动保存。