› MySQL 5.5 Community Server
› MySQL 5.6 Community Server
› Percona Configuration Wizard
› XtraBackup 搭建主从复制
Great Sites on MySQL
› Percona
› MySQL Performance Blog
› Severalnines
推荐管理工具
› Sequel Pro
› phpMyAdmin
推荐书目
› MySQL Cookbook
MySQL 相关项目
› MariaDB
› Drizzle
参考文档
› http://mysql-python.sourceforge.net/MySQLdb.html
dyllen
V2EX  ›  MySQL

msyql 除了加唯一索引,又其他办法防止同一时刻写入重复数据么?

  •  
  •   dyllen · Jun 29, 2019 · 10288 views
    This topic created in 2685 days ago, the information mentioned may be changed or developed.

    情况是这样的,有些 api,第三方系统会推数据过来,有两个不同的 api,这两个 api 都会往一个表里面写同样的数据,两边业务逻辑上都有查重的,现在查历史记录,看见在有同时推数据到这两个接口的请求,导致写入重复的数据,创建时间都一样。除了加唯一索引有没有其他什么办法避免?

    Supplement 1  ·  Jun 29, 2019
    唯一索引加不了了,里面有重复的不知道有没有用的数据了
    43 replies  •  2019-07-01 14:11:34 +08:00
    jugelizi
    jugelizi
    Jun 29, 2019❤️1
    1
    队列 加锁
    haiyang416
    haiyang416
    Jun 29, 2019
    2
    写操作串行化,扔队列呗。
    srx1982
    srx1982
    Jun 29, 2019
    3
    "两边业务逻辑上都有查重的",都查重了,还能重,那就是查重写得不对
    LeeSeoung
    LeeSeoung
    Jun 29, 2019
    4
    先查后写。。mysql 好像没 merge into
    laravel
    laravel
    Jun 29, 2019
    5
    为啥把 unique 除了?
    R18
    R18
    Jun 29, 2019 via Android
    6
    查重没有加锁…
    qiyuey
    qiyuey
    Jun 29, 2019
    7
    你没有开事务吧
    dyllen
    dyllen
    OP
    Jun 29, 2019
    8
    @laravel 不知道,原来就没有加,现在加不了了,里面有不少重复的也不知道有没有用的数据
    dyllen
    dyllen
    OP
    Jun 29, 2019
    9
    @qiyuey 开了,两边是独立的。
    dyllen
    dyllen
    OP
    Jun 29, 2019
    10
    @R18 查到没有,我锁哪里,我锁表?
    xuanbg
    xuanbg
    Jun 29, 2019
    11
    数据库唯一索引外,唯一解决办法,API 上面加分布式锁
    Vegetable
    Vegetable
    Jun 29, 2019
    12
    队列
    API 将数据塞到队列里,顺序消费掉避免并发写入.
    这牺牲了实时性.如果要求实时返回插入结果的话不适用,只能使用事务
    chrisliu1314
    chrisliu1314
    Jun 29, 2019 via iPhone
    13
    1 )加个字段,然后加唯一索引
    2 )或者这两个 api 都调用底层的一个服务接口。服务接口里面采用分布式锁+先查后插
    不知道是否可行
    zisway
    zisway
    Jun 29, 2019
    14
    数据库解决,pg 是有语法可以插入时遇到重复直接忽略,但不知道 mysql 有没有,可以查文档看看。业务上解决可以用分布式锁,或者 api 不入库,而是扔到 redis 队列中,由 job 去取出入库。
    799635347
    799635347
    Jun 29, 2019 via iPhone
    15
    1.分布式锁
    2.mq
    zy445566
    zy445566
    Jun 29, 2019 via Android
    16
    如果没插入,开了事务还重复,九成是因为你没有判断每条 sql 是否有影响行数,就一个 try catch 就回滚了。事实上每条 sql 都要判断影响行数,如果影响行数为 0,理论上也是要进行回滚的。
    zy445566
    zy445566
    Jun 29, 2019 via Android
    17
    @zy445566 如果有插入,就开串行化级别的事务,但这个特别影响性能。
    limuyan44
    limuyan44
    Jun 29, 2019 via Android
    18
    唯一索引建不了你怎么判断重复的,按你说如果重复的数据你都分不清哪一条有没有用,那你去重的意义在哪里?
    harvies
    harvies
    Jun 29, 2019
    19
    15 楼正解
    luman
    luman
    Jun 29, 2019
    20
    看着是有历史遗留问题,导致存量数据有重复数据。而且不好清洗。如果对数据及时性要求不高,我觉得可以 copy 一张一模一样的表,并添加唯一索引。使用 duplicate key update 插入数据。然后定时 insert select 同步表数据。相对来说插入效率和开发难度比自己加锁要快吧。
    Navee
    Navee
    Jun 29, 2019
    21
    何必为难自己
    yiplee
    yiplee
    Jun 29, 2019
    22
    建一新张表它的主键当唯一索引用。插入的时候开启事务,先插入新表,如果插入成功了,再插入现有的业务表。
    qf19910623
    qf19910623
    Jun 29, 2019
    23
    做一个短时间的缓存锁队列
    agui2200
    agui2200
    Jun 29, 2019
    24
    用 for update + 事务,查询共有的全局锁表,做悲观锁
    agui2200
    agui2200
    Jun 29, 2019
    25
    @agui2200 这个方案实施起来比较简单,但是性能差一些
    Asice
    Asice
    Jun 29, 2019
    26
    @agui2200 #24 已经是高并发才会出现重复了,还悲观锁,要搞死数据库吗
    lihongjie0209
    lihongjie0209
    Jun 29, 2019
    27
    只能用队列了
    Kylinsun
    Kylinsun
    Jun 29, 2019 via iPhone
    28
    建议增加一个字段,然后需要加唯一键的列加强这个辅助唯一键作为唯一键。
    hosaos
    hosaos
    Jun 29, 2019
    29
    针对你该条数据的业务唯一建做分布式锁,抢锁成功后先查后插入
    karllynn
    karllynn
    Jun 29, 2019
    30
    开分布式锁或者串行化

    或者你新建一张表 /加个字段,然后把原来的数据处理一下迁移过来
    Cbdy
    Cbdy
    Jun 29, 2019
    31
    把其中一个 API 的请求代理另一个 API
    linbiaye
    linbiaye
    Jun 29, 2019
    32
    笨方法:分布式锁,可以基于数据库做,不需要引入其它组件。
    begin(read committed 级别即可)
    1. 插入锁表
    2. 根据待插入数据 count 是否已存在,存在则 rollback
    4. 插入数据
    5. 删除锁
    commit
    个人倾向的方法:表新加个唯一 column
    passerbytiny
    passerbytiny
    Jun 29, 2019
    33
    悲观锁方式:查重的时候直接锁表(因为后面是要新增数据的,所以只能锁表),新增数据或超时后解锁。此方式基本没人用。
    变相乐观锁方式:第三方直接推送,若收到“有重复数据”错误再做后续处理;你这边单事务内查重加插入。此方式没啥特殊性,就是注册用户判断重复的逻辑,但是若你这个业务是高并发并且冲突情况占比大,此方式也不是太合适。

    如果是高并发场景,并且第三方确实会发送重复数据,建议还是允许重复数据的好。或者,给第三方分配 ID,表中加一列“来源”,这样就不会出现重复数据了。

    你的描述少了一个关键场景:第三方查重后如果发现重复了,是怎么处理的。
    Takamine
    Takamine
    Jun 29, 2019 via Android
    34
    得看具体业务,乐观一点或者悲观一点。
    jaskle
    jaskle
    Jun 30, 2019 via Android
    35
    你无法保证外部数据是否有重复,就算你有队列和分布式锁,但是他就是发了两个一样的,所以加唯一索引是最佳选择,如果旧数据过多可以考虑双联合索引,手动差异化一下。
    msg7086
    msg7086
    Jul 1, 2019
    36
    要避免同时写入那就只能串行化。串行化要么加锁,要么队列。
    qsbaq
    qsbaq
    Jul 1, 2019
    37
    唯一索引是最佳选择。
    justRua
    justRua
    Jul 1, 2019
    38
    这是发生幻读了吧,把数据库隔离级别设置成串行化,也可以用队列在业务层串行化,唯一索引貌似是最方便的。
    werty
    werty
    Jul 1, 2019
    39
    写之前全表加锁, 然后再 select 一次, 看看有没有主键重复, 最后 insert;
    只需要改改 DAO 层就够了
    IamUNICODE
    IamUNICODE
    Jul 1, 2019
    40
    用 redis 的话,setnx 一下?
    dyllen
    dyllen
    OP
    Jul 1, 2019
    41
    @passerbytiny 其实这个也算不上什么高并发,就是第三方会完全不分青红皂白,同时请求了两个会写相同数据的不同接口。
    dyllen
    dyllen
    OP
    Jul 1, 2019
    42
    @passerbytiny 重复了要把重复的删掉,反馈之后我查了一下,数据不多,有个 20 条左右相同的数据的吧,因为都还在部分测试吧。
    javaWeber
    javaWeber
    Jul 1, 2019
    43
    select for update,排他锁。先查再写入。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2898 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 105ms · UTC 12:39 · PVG 20:39 · LAX 04:39 · JFK 07:39
    ♥ Do have faith in what you're doing.