IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    MySQL 防止重复数据插入

    博客 - DannySite发表于 2015-06-07 16:19:13
    love 0
    做后端开发,我们必须考虑多线程的情况,那这时就必须重视线程安全的问题。 有这样一种场景:首先环境是 Python + Django + MySQL,我们的每一张表都有一个字段 is_deleted 用来标识该条数据是否被删除,也就是当用户在删除数据的时候,服务器并不是真正将其删除,而只是简单的在 is_deleted 字段上标记一下。假如有下面的一个 MODEL: class Test(models.Model): name = models.CharField(max_length=32) is_deleted = models.BooleanField(default=False) class Meta: verbose_name = 'test' 一个 name 和之前所说的 is_deleted 两个 field。现在有一个需求是同一 name 在表中有且只能存在一份有效的数据。在 Django 的 ORM 中有一个 get_or_create() 的简单方法貌似能满足需求: test, created = TestModel.objects.get_or_create(name='t1', is_deleted=False) 乍一看这好像是那么回事,但细看 get_or_create() 会发现这里会存在线程安全的问题,而阅读 Django 的官方文档也确实提到了这一点。这里利用 nginx + uwsgi 来运行工程,在使用 webbench 来模拟多用户的并发请求: webbench -c 2000 -t 1 http://127.0.0.1/test/ 最终我们发现这个入口变成了永无止境的 500: 它确实在这种时候不是线程安全的,通常的解决方案是在 name 上增加 unique 限制。但这里因为有 is_deleted 的存在而打破了常规,导致没法这么操作。 我开始一直考虑能从 Python 的角度去解决这个问题,但我发现这反而使问题变得复杂化。而反过来,在 MySQL 的层面却有一个很直观的解决方法:insert ... where not exists,如下所示: ... sql = "INSERT INTO `inserttest_test` (name, is_deleted) SELECT '{name}', 0 FROM DUAL WHERE NOT EXISTS (SELECT name FROM `inserttest_test` WHERE name='{name}' AND is_deleted=0)" from django.db import connection cursor = connection.cursor() cursor.execute(sql.format(name='t1')) ... 再次模拟大并发观察多线程下的情况,数据不在出现重复插入的问题。 当然,这仅是我对此的一种处理方案。


沪ICP备19023445号-2号
友情链接