JDBC 语句中的 SYSDATE 在针对同一 Oracle 数据库查询时似乎 return 不同时间
SYSDATE in JDBC statements seems to return different times when queried against the same Oracle database
我们有 3 到 4 个具有相同设置的环境,每个环境都有多个集群应用程序服务器 (WebSphere) 和 Oracle Supercluster 数据库。
我看到的问题似乎是 仅在其中一种环境中发生,大约 20% 的时间。
我们有
两个应用部署到同一个应用服务器集群
两个应用程序使用相同的数据源配置为使用 Oracle 数据库,即 Oracle Supercluster。
以下是有问题的table的结构
TABLE SESSION_TBL
(
ID NUMBER(12, 0) NOT NULL,
USER_ID VARCHAR2(256 BYTE) NOT NULL,
SESSION_ID VARCHAR2(60 BYTE) NOT NULL,
LOGIN_TIME TIMESTAMP(6) NOT NULL
)
应用程序 1 使用 JDBC
存储记录
String sql = "Insert into SESSION_TBL " +
" (USER_ID, SESSION_ID, LOGIN_TIME ) " +
" values (?,?,SYSDATE)";
try
{
sessionId = getNewSessionId(userId);
st = conn.prepareStatement(sql);
st.setString(1, userId);
st.setString(2, sessionId);
int rows = st.executeUpdate();
}
几秒后,应用2执行以下代码,查找第一个应用插入的记录,并与当前时间进行比较
Statement st = null;
ResultSet result = null;
String sql = " Select * " +
" from SESSION_TBL " +
" where USER_ID = '" + userId + "' " +
" and SESSION_ID = '" + sessionId + "' ";
try {
st = conn.createStatement();
result = st.executeQuery(sql);
if(!result.next()) { // We have no data, need to expire the session
logger.debug("End hasSessionExpired()");
return true;
}
else {
logger.debug("SessionInfo:ResultSet not null");
}
// Get the time user logged in
// java.sql.Timestamp
Timestamp database_date = result.getTimestamp("LOGIN_TIME"); // get date from db
long databaseDate = database_date.getTime(); // Convert to UNIX time
// Get the current time from the database, so we are getting the time
// from the same sources to compare
// See the code below for this function
long currentTime = getCurrentTimeFromDB (conn);
// Both time values would be in milli seconds
long diffSecs = (currentTime - databaseDate)/1000;
logger.info ("db:" + databaseDate + " now:" + currentTime + " diffSecs:" + diffSecs);
从数据库获取当前时间的代码
public static long getCurrentTimeFromDB () {
.
.
// Using SYSDATE. We only need precision upto seconds
String s = "SELECT SYSDATE NOW FROM DUAL";
rs = statement.executeQuery(s);
if (rs.next()) {
Timestamp dbTime = rs.getTimestamp("NOW");
currentTime = dbTime.getTime();
}
return currentTime;
}
在大约 5 次执行中的大约 1 次中,我看到当前时间比记录创建时间(登录时间)早。发生这种情况时,我会看到如下调试语句的输出:
db:1538793249000 now:1538793023000 diffSecs:-226
db:1538793249000 now:1538793023000 diffSecs:-202
db:1538793249000 now:1538793023000 diffSecs:-225
好像早了 200 多秒
如果您注意到一件事,列 (LOGIN_TIME) 的数据类型是时间戳,我正在使用 SYSDATE 来填充它。但是,我还使用 SYSDATE 从 DUAL 获取时间。在我们拥有的 4 种环境中,这种情况只发生在一种环境中,但并非总是如此。是代码有问题还是数据库 Oracle super cluster) 实际上返回了一个不正确的日期。
我想我会给出答案以防其他人遇到类似问题。正如人们所建议的(在评论中),集群节点时间可能不同步。我们在集群中有 2 个节点,一个节点(当前时间)提前了几分钟。可以通过 运行 针对每个节点的 SYSDATE 查询来检测时间是否不同步。
我们有 3 到 4 个具有相同设置的环境,每个环境都有多个集群应用程序服务器 (WebSphere) 和 Oracle Supercluster 数据库。
我看到的问题似乎是 仅在其中一种环境中发生,大约 20% 的时间。
我们有
两个应用部署到同一个应用服务器集群
两个应用程序使用相同的数据源配置为使用 Oracle 数据库,即 Oracle Supercluster。
以下是有问题的table的结构
TABLE SESSION_TBL
(
ID NUMBER(12, 0) NOT NULL,
USER_ID VARCHAR2(256 BYTE) NOT NULL,
SESSION_ID VARCHAR2(60 BYTE) NOT NULL,
LOGIN_TIME TIMESTAMP(6) NOT NULL
)
应用程序 1 使用 JDBC
存储记录 String sql = "Insert into SESSION_TBL " +
" (USER_ID, SESSION_ID, LOGIN_TIME ) " +
" values (?,?,SYSDATE)";
try
{
sessionId = getNewSessionId(userId);
st = conn.prepareStatement(sql);
st.setString(1, userId);
st.setString(2, sessionId);
int rows = st.executeUpdate();
}
几秒后,应用2执行以下代码,查找第一个应用插入的记录,并与当前时间进行比较
Statement st = null;
ResultSet result = null;
String sql = " Select * " +
" from SESSION_TBL " +
" where USER_ID = '" + userId + "' " +
" and SESSION_ID = '" + sessionId + "' ";
try {
st = conn.createStatement();
result = st.executeQuery(sql);
if(!result.next()) { // We have no data, need to expire the session
logger.debug("End hasSessionExpired()");
return true;
}
else {
logger.debug("SessionInfo:ResultSet not null");
}
// Get the time user logged in
// java.sql.Timestamp
Timestamp database_date = result.getTimestamp("LOGIN_TIME"); // get date from db
long databaseDate = database_date.getTime(); // Convert to UNIX time
// Get the current time from the database, so we are getting the time
// from the same sources to compare
// See the code below for this function
long currentTime = getCurrentTimeFromDB (conn);
// Both time values would be in milli seconds
long diffSecs = (currentTime - databaseDate)/1000;
logger.info ("db:" + databaseDate + " now:" + currentTime + " diffSecs:" + diffSecs);
从数据库获取当前时间的代码
public static long getCurrentTimeFromDB () {
.
.
// Using SYSDATE. We only need precision upto seconds
String s = "SELECT SYSDATE NOW FROM DUAL";
rs = statement.executeQuery(s);
if (rs.next()) {
Timestamp dbTime = rs.getTimestamp("NOW");
currentTime = dbTime.getTime();
}
return currentTime;
}
在大约 5 次执行中的大约 1 次中,我看到当前时间比记录创建时间(登录时间)早。发生这种情况时,我会看到如下调试语句的输出:
db:1538793249000 now:1538793023000 diffSecs:-226
db:1538793249000 now:1538793023000 diffSecs:-202
db:1538793249000 now:1538793023000 diffSecs:-225
好像早了 200 多秒
如果您注意到一件事,列 (LOGIN_TIME) 的数据类型是时间戳,我正在使用 SYSDATE 来填充它。但是,我还使用 SYSDATE 从 DUAL 获取时间。在我们拥有的 4 种环境中,这种情况只发生在一种环境中,但并非总是如此。是代码有问题还是数据库 Oracle super cluster) 实际上返回了一个不正确的日期。
我想我会给出答案以防其他人遇到类似问题。正如人们所建议的(在评论中),集群节点时间可能不同步。我们在集群中有 2 个节点,一个节点(当前时间)提前了几分钟。可以通过 运行 针对每个节点的 SYSDATE 查询来检测时间是否不同步。