【问题标题】:psycopg2 occasionally returns nullpsycopg2 偶尔返回 null
【发布时间】:2014-01-04 09:19:33
【问题描述】:

所以我使用的是 psycopg2,我有一个简单的表:

CREATE TABLE IF NOT EXISTS feed_cache (
    feed_id int REFERENCES feeds(id) UNIQUE,
    feed_cache text NOT NULL,
    expire_date timestamp --without time zone
);

我正在调用以下方法和查询:

@staticmethod
def get_feed_cache(conn, feed_id):
    c = conn.cursor()
    try:
        sql = 'SELECT feed_cache FROM feed_cache WHERE feed_id=%s AND localtimestamp <= expire_date;'
        c.execute(sql, (feed_id,))
        result = c.fetchone()
        if result:
            conn.commit()
            return result[0]
        else:
            print 'DBSELECT.get_feed_cache: %s' % result
            print 'sql: %s' % (c.mogrify(sql, (feed_id,)))
    except:
        conn.rollback()
        raise
    finally:
        c.close()
    return None

我添加了 else 语句来输出正在执行和返回的确切 sql 和结果。

从数据库连接线程池调用 get_feed_cache() 方法。当 get_feed_cache() 方法被“缓慢”调用(~1/sec 或更少)时,结果会按预期返回,但是当同时调用时,它偶尔会返回 None。我尝试了多种编写此查询和方法的方法。

一些观察:

  1. 如果我从查询中删除 'AND localtimestamp
  2. 在 psql 中以串行方式快速执行查询总是返回结果。
  3. 在阅读了 psycopg 游标类的 fetch*() 方法后,他们注意到结果是为游标缓存的,我假设缓存不在不同游标之间共享。 http://initd.org/psycopg/docs/faq.html#best-practices
  4. 我尝试过使用 postgresql 的 now() 和 current_timestamp 函数,结果相同。 (我知道 now() 和 current_timestamp 的时区方面)

需要注意的条件:

  1. 绝不会出现所提供的 feed_id 没有 feed_cache 值的情况。
  2. 永远不会出现 feed_cache 表中的任何值为 NULL 的情况
  3. 在测试时,我完全禁用了对该表的任何和所有写入
  4. 对于所有值,我已将 expire_date 设置为足够远的未来,这样表达式“AND localtimestamp

这是它的复制和粘贴输出,返回无:

DBSELECT.get_feed_cache: None
sql: SELECT feed_cache FROM feed_cache WHERE feed_id=5 AND localtimestamp < expire_date;

差不多就是这样,我不确定发生了什么。也许我犯了一些非常愚蠢的错误,而我只是没有注意到! 我目前的猜测是它与 psycopg2 以及它在游标之间缓存结果的方式有关。如果游标确实共享缓存并且查询几乎同时发生,那么第一个游标可能会获取结果,第二个游标看到有相同查询的缓存,所以它不执行,然后第一个游标关闭并删除缓存,第二个游标尝试获取现在为 null/None 的缓存。*

也就是说,psycopg2 声明它对于只读查询是线程安全的,所以除非我错误地解释了他们的线程安全实现,否则情况不应该如此。

感谢您的宝贵时间!

*给get_feed_cache加线程锁后,先获取再创建游标,再释放再返回,偶尔还是会得到None结果

【问题讨论】:

  • 您确定表中没有 NULL 值吗? SELECT * FROM feed_cache WHERE feed_cache IS NULL 说什么?我问这个是因为你的代码看起来不错,我觉得你的数据有问题。
  • SELECT * FROM feed_cache WHERE feed_cache 为空; feed_id | feed_cache | expire_date ---------+------------+-------------(0行)
  • 抱歉格式化,返回0行。
  • 这里是数据库中所有内容的链接。 pastebin.com/Dwt7B84g
  • 好吧,看了之后,我想我会尝试将 expire_date 放在更远的将来(一个月),一切都很完美。所以我猜这与时区有关。通常将 expire_date 设置为未来 30 分钟。

标签: python sql multithreading postgresql psycopg2


【解决方案1】:

我认为这可能与localtimestampcurrent_timestamp 返回的时间戳在事务开始时固定,而不是在您运行语句时固定有关。而 psycopg 在某种程度上管理着你背后的交易。所以你可能会得到一个稍旧的时间戳。

您可以通过在您的服务器中设置log_statement = all 然后观察BEGIN 语句相对于您的查询何时执行来进行调试。

您可能希望考虑使用诸如clock_timestamp() 之类的函数,该函数会在每个事务中更频繁地更新。见http://www.postgresql.org/docs/current/static/functions-datetime.html

【讨论】:

    猜你喜欢
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 2018-06-12
    • 1970-01-01
    • 2013-03-25
    • 2015-08-27
    • 2015-02-26
    • 1970-01-01
    相关资源
    最近更新 更多