psycopg2偶尔会返回null

时间:2013-12-16 20:21:37

标签: python sql multithreading postgresql psycopg2

所以我使用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 /秒或更短)调用时,结果将按预期返回,但是当并发调用时,偶尔会返回None。我尝试过多种方式来编写这个查询&amp;方法。

一些观察结果:

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

    需要注意的条件:

    1. 绝不会出现提供的feed_id没有feed_cache值的情况。
    2. 不会出现feed_cache表中的任何值为NULL
    3. 的情况
    4. 测试时我完全禁用了任何&amp;所有写入此表
    5. 我已经将expire_date设置为将来所有值都足够远,以便表达式'AND localtimestamp&lt; = expire_date'将始终为真。
    6. 这是一份副本&amp;粘贴的输出返回无:

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

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

      也就是说,psycopg2声明它对于只读查询是线程安全的,所以除非我错过了解释它们的线程安全实现,否则不应该这样。

      感谢您的时间!

      *为get_feed_cache添加一个线程锁,在创建游标之前获取并在返回之前释放,我仍然偶尔得到一个无结果

1 个答案:

答案 0 :(得分:1)

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

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

您可能希望使用clock_timestamp()这样的函数,每个事务更新一次。请参阅http://www.postgresql.org/docs/current/static/functions-datetime.html