首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
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
V2EX  ›  MySQL

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

  •  
  •   dyllen · 137 天前 · 4578 次点击
    这是一个创建于 137 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

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