Django cache.set() 导致重复键错误
Posted
技术标签:
【中文标题】Django cache.set() 导致重复键错误【英文标题】:Django cache.set() causing duplicate key error 【发布时间】:2010-11-14 10:37:47 【问题描述】:我的 Django 网站最近开始从我的缓存代码中抛出错误,我不知道为什么...
我打电话:
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。有什么想法吗?
【问题讨论】:
缓存到数据库是不是有点违背缓存的目的? 我在那里将其编辑为“blogentry”,但它实际上为博客侧边栏小部件缓存了大量相关数据。 【参考方案1】:这是一场典型的比赛。它检查您插入的密钥是否存在;如果没有,它会进行插入,但其他人可以在计数和插入之间插入键。交易不会阻止这一点。
代码似乎预料到了这一点并试图处理它,但是当我查看处理这种情况的代码时,我可以立即看到它被破坏了。在此报告:http://code.djangoproject.com/ticket/11569
我强烈建议坚持使用 memcache 后端。
【讨论】:
我已经阅读了数据库缓存后端的源代码,并且对您打开的票证也有同样的想法。很高兴不仅仅是我觉得我在这里错过了一些东西。我们将改为设置 memcached。阅读 mecached 后端的源代码更加令人放心。谢谢!【参考方案2】:core/cache/backend/db.py 中的代码部分读取:
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 而不是 UPDATE,因为 result 的计算结果为 false。由于某种原因,cursor.fetchone() 在实际上有 1 行时返回 0 行。
如果您无法在此处中断调试器,我会将跟踪语句放入源代码中以确认这确实发生了。
【讨论】:
是的,我会一遍又一遍地阅读源代码......正如 Glenn 指出的那样,这里真正的罪魁祸首是 db 后端存在潜在的并发问题,在 select 之后可能会出现第二个 INSERT 语句返回 0,导致原始 cache.set() 失败,然后触发非功能性 transaction.rollback() 调用。【参考方案3】:我通过创建自定义缓存后端、覆盖 _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])
【讨论】:
以上是关于Django cache.set() 导致重复键错误的主要内容,如果未能解决你的问题,请参考以下文章
“返回该页面可能会导致您重复执行的任何操作” - Django
Postrgersql+Django:NULL值导致IntegrityError:重复键值违反唯一约束