Django的cache.set()导致重复键错误
问题描述:
我的Django的网站最近开始从我的缓存代码抛出错误,我想不通为什么...Django的cache.set()导致重复键错误
我打电话:
from django.core.cache import cache
cache.set('blogentry', some_value)
而且Django在抛出的错误是:
TransactionManagementError: This code isn't under transaction management
但看PostgreSQL数据库日志,似乎从这个错误中干:
STATEMENT: INSERT INTO cache_table (cache_key, value, expires) VALUES (E'blogentry', E'pickled_version_of_some_value', E'2009-07-27 11:10:26')
ERROR: duplicate key value violates unique constraint "cache_table_pkey"
对于我的生活,我无法弄清楚为什么Django试图做INSERT而不是UPDATE。有什么想法吗?
cursor.execute("SELECT cache_key, expires FROM %s WHERE cache_key = %%s" % self._table, [key])
try:
result = cursor.fetchone()
if result and (mode == 'set' or
(mode == 'add' and result[1] < now)):
cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, [encoded, str(exp), key])
else:
cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
所以我说,你正在做的INSERT INTO,而不是更新,因为结果计算为false:
答
这是一场典型的比赛。它会检查您插入的密钥是否存在;如果没有,它会执行插入,但其他人可以在计数和插入之间插入密钥。事务不会阻止这一点。
该代码似乎期望这一点,并尝试处理它,但是当我查看代码来处理这种情况时,我可以立即看到它已被破坏。这里报告:http://code.djangoproject.com/ticket/11569
我强烈建议坚持memcache后端。
答
核心/缓存/后端代码/ db.py的部分内容。出于某种原因,cursor.fetchone()在实际存在一个时返回0行。
如果你不能在这里调试器中断,我会把跟踪语句放入源代码中,以确认这实际上正在发生。
答
我通过创建自定义缓存后端,覆盖_base_set()函数并像这样更改INSERT INTO语句来解决此问题。这种SQL技巧可以防止在cache_key已经存在的情况下发生INSERT。
cursor.execute("INSERT INTO %s (cache_key, value, expires) SELECT %%s, %%s, %%s WHERE NOT EXISTS (SELECT 1 FROM %s WHERE cache_key = %%s)" % (table, table),
[key, encoded, connections[db].ops.value_to_db_datetime(exp), key])
是不是缓存到数据库有一种破坏缓存的目的? – thedz 2009-07-27 21:52:07
取决于你正在缓存什么。 – 2009-07-27 22:14:36
我将它编辑为“blogentry”,但它实际上缓存了博客边栏小部件的大量相关数据。 – 2009-07-27 23:54:07