请问 Django 并发条件下,生成雪花 ID 为什么会重复?

2021-07-30 13:31:32 +08:00
 Phishion

我用的是别人写好的模块,如下

https://github.com/tarzanjw/pysnowflake/blob/master/snowflake/server/generator.py

我首先自己跑单线程测了一下,完全不会有重复,多线程在加线程锁的情况下也完全没有发生重复。

但是我在实际项目中生成的时候,大概 10 条线提交总共 1600 条数据,每次都会产生大概几十条重复

我打印过 ID,这个雪花生成器实例并没有被初始化多个,请问如何排查?

大致代码如下:

from newsnow import Generator
logger = logging.getLogger('django-production')
get_flake_id = Generator(dc=0, worker=0)

def create_product_meta(prepare_product_meta):
    new_product = models.productMeta(
        own_store=prepare_product_meta.get("own_store").upper(),
        product_name=prepare_product_meta.get("product_name"),
    )
    logger.warning(id(get_flake_id))
    new_product.flake_id = get_flake_id.get_next_id()
    return new_product
3605 次点击
所在节点    Python
26 条回复
todd7zhang
2021-07-30 13:42:15 +08:00
如果生产是多进程跑的话,应该是初始化 Generator(dc=0, worker=0)这个时候,所有进程的 10bit 的 node_id 都是一样的,然后就重复了。可以考虑 worker=os.getpid()
find456789
2021-07-30 13:43:04 +08:00
有考虑过 hashid 吗, 这个支持很多语言,Python 、django 也有对应的 包
LeeReamond
2021-07-30 13:44:06 +08:00
一个个人猜测是,因为 sleep 间隔固定的原因,线程数量一多了之后系统挨个唤醒,可能搞不好获取的时间戳也一样。
cszchen
2021-07-30 13:47:40 +08:00
你有没有用到多进程
cszchen
2021-07-30 13:48:12 +08:00
1 楼的方法可以解决
Phishion
2021-07-30 13:55:41 +08:00
@todd7zhang
@cszchen
没有多进程,我就一台机器单跑了一个 Django 服务没有啥特别的设置
Phishion
2021-07-30 13:58:08 +08:00
@cszchen
@todd7zhang

我使用了 uwsgi,请问下面的 processes 算多进程么?

[uwsgi]
chdir=/www/project/
module=project.wsgi
master=True
processes=4
chenqh
2021-07-30 14:05:05 +08:00
这种东西 py 最简单应该是用 redis 写一个把

```

def util_redis_get_next_id_str(redis_client, name="id_sequence", mod_base=1000):
"""
1 秒并发最多 mod_base 这么多,也就是 10W, 所以我并不怕
"""
value = redis_client.incr(name)
value = value % mod_base
utcnow = datetime.datetime.utcnow()
now = util_time_utc_to_local(utcnow)
now = now.strftime("%Y%m%d%H%M%S")
second = int(time.time() * 1000) % 1000
return '{}{:03d}{:03d}'.format(now, second, value)
```
chenqh
2021-07-30 14:05:45 +08:00
@Phishion 算的应该
Phishion
2021-07-30 14:05:54 +08:00
@cszchen
@todd7zhang
好像确实是这个问题,我把 processes 配置减少到 1,就没有发现重复了
LemonK
2021-07-30 14:10:43 +08:00
snowflake 多进程需要不同的 worker 值,我是另外写了个方法分配的。如果是 docker 部署的话 pid 也有可能重复。
Phishion
2021-07-30 14:12:47 +08:00
@LemonK 请问您有什么方法解决这个问题?
Phishion
2021-07-30 14:15:25 +08:00
@find456789 我主要就想要一连串数字,实际业务上也需要显示出来
cszchen
2021-07-30 14:34:08 +08:00
如果多台机器,可以用 redis 的原子特性来分配 workerid
Phishion
2021-07-30 14:41:42 +08:00
@chenqh

请问 pid 在整个程序运行周期会不断变化么?
另外 pid 范围是 几十到上万不等,这个数字是远超出 work id 范围的,是要写一个映射么?
Phishion
2021-07-30 14:43:42 +08:00
@cszchen 目前没有多台,我不想跑 redis 是因为目前没有其他地方用到这个,不想就为了生成 ID 单跑一个数据库
chenqh
2021-07-30 15:12:12 +08:00
@Phishion 你没有用 celery 或者 rq 吗?
est
2021-07-30 15:16:05 +08:00
@Phishion 看源码

self.node_id = ((dc & 0x03)<< 8) | (worker & 0xff)
Phishion
2021-07-30 15:32:09 +08:00
@chenqh 有的,这个能获取到 pid 么
Phishion
2021-07-30 15:42:31 +08:00
@est 这个不是一共支持 10 位,共 1024 个节点么,直接填 pid 号肯定有几率溢出啊,关键这一块儿我觉得也不是填 PID 的地方,只是 PID 确实能解决这个问题,最理想情况下,这个 worker 应该就是从 0 开始,多一个进程就加 1

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

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

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

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

© 2021 V2EX