SQLAlchemy报键冲突,跑进去一看却没有对应键值的行

2013-09-24 15:55:06 +08:00
 banxi1988
RT:
错误信息如下:
(IntegrityError) duplicate key value violates unique constraint "ix_cid"

下面是我生成cid的方法:
_cid_rlock = threading.RLock()
def generate_cid():
with _cid_rlock:
return long((time.time()-1377964800.000000)*1000000000)

但是经过我使用多线程测试,发现不太可能生成相同的id(测试代码见下)

应用用的是Flask,数据库层SQLAlchemy,Flask-SQLAlchemy插件(默认配置)
数据库postgresql 9.1.9
系统ubuntu 12.04.2
应用跑在 gunicorn 加gevent
gunicorn -k gevent app:app -w 2

在做提交的时候,直接用的是:
类似如下的代码:
db.session.execute(Story.__table__.insert(), story_values)
db.session.commit()

Google了一阵子了,还是没有找出原因来. 不知道从哪里debug起了....


测试生成cid的代码如下:
https://gist.github.com/banxi1988/6681583
3132 次点击
所在节点    Python
6 条回复
BOYPT
2013-09-24 17:48:33 +08:00
什么叫不太可能生成相同的id,你的核心id生成要素仅仅是time.time()一个元素而已。
banxi1988
2013-09-24 21:23:27 +08:00
@BOYPT
你可以跑下我的测试代码,我跑过很多,没有可能生成相同的id,相差都比较大.
除非系统的时钟不可信,
而且我们是单系统.

而且就算是重复的id,冲突之后数据库中没有对应id值的数据行怎么解释呢?
keakon
2013-09-24 22:54:18 +08:00
time.time() 的精度甚至不保证达到一秒,你放大那么多倍就别期待它的精度了……

你用了 2 个进程,加锁也没意义……

story_values 里如果有重复的键,插入第一次是成功的,第二次会失败,然后抛出异常,程序退出,断开连接,事务回滚,数据库里当然就没这条记录了。
banxi1988
2013-09-25 00:30:53 +08:00
@keakon
你说的给了我点感觉.感谢已发.
你说time.time()精度1秒都达不到,我看到文档有句原话:
not all systems provide time with a better precision than 1 second
但是我上面测试的时候基本可以达到很高很高的精度.001纳秒.

嗯,我在服务器上测试下先.

PS:请教下,要生成比较可靠的id(要求整型,不太连续)应该怎么做?
banxi1988
2013-09-25 00:46:19 +08:00
@keakon
我试了下:
[20760718020319L, 20760718020689L, 20760718020811L, 20760718020908L, 20760718020999L, 20760718021090L, 20760718021180L, 20760718021259L, 20760718021349L, 20760718021440L, 20760718021531L, 20760718021609L, 20760718021700L, 20760718021779L, 20760718021860L, 20760718021950L, 20760718022029L, 20760718022120L, 20760718022210L, 20760718022298L, 20760718022379L, 20760718022460L, 20760718022539L, 20760718022630L, 20760718022708L, 20760718022789L, 20760718022880L, 20760718022971L, 20760718023049L, 20760718023130L, 20760718023219L, 20760718023300L, 20760718023390L, 20760718023469L, 20760718023550L, 20760718023629L, ]

上面提我在服务端跑出来的部分id.可以看出几乎不会重复的.而且还是程序单纯生成id不做其他事件的情况下.在正常的应用声场景下原因是id重量的可能性不太.
time.time()在对应服务器的精度也是很高的.
keakon
2013-09-25 01:00:30 +08:00
@banxi1988 那个精度是骗你的,它只负责给你一个 float,这玩意是 64 位的,但并不表示实际精度是那么多位。

生成 ID 可以参考这个: http://www.zhihu.com/question/20180484
比较简单的办法是用 Redis 的 INCRBY / HINCRBY,每次增加一个随机数即可。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/83503

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX