我有一个旧的Web应用程序,相关的当前堆栈是:Java 8,Tomcat 7,Apache Commons DBCP 2.1,Spring 2.5(用于事务),iBatis,PostgreSQL 9.2和postgresql-9.4.1208.jar
部分代码在incidents
表中插入新记录,其中字段begin_date
(timestamp(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
个关联。
任何提示?
答案 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)