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
noble4cc
V2EX  ›  MySQL

mysql 怎么防止重复插入呢

  •  
  •   noble4cc · 2019-05-17 10:42:44 +08:00 · 9827 次点击
    这是一个创建于 2018 天前的主题,其中的信息可能已经有所发展或是发生改变。

    使用 unique key 可以保证

    但是不使用唯一 key 的情况并且在并发提交的情况下怎么防止重复插入呢?

    比如浏览器卡了一下,导致用户重复点击提交了

    47 条回复    2019-05-23 17:05:54 +08:00
    JacFrak
        1
    JacFrak  
       2019-05-17 10:46:10 +08:00
    如果是 ajax 提交,点击提交按钮事件里面将按钮设置为不可操作
    noble4cc
        2
    noble4cc  
    OP
       2019-05-17 11:01:51 +08:00
    @JacFrak 嗯嗯如果是单纯的 form 呢
    DovaKeen
        3
    DovaKeen  
       2019-05-17 11:02:24 +08:00
    引入事务,在插入前先搜索一下有没有重复,如果有的话就取消插入,没有再插入?但是这样的话似乎要把隔离级别设置的很高…
    DovaKeen
        4
    DovaKeen  
       2019-05-17 11:03:27 +08:00
    @noble4cc
    @JacFrak 我觉得虽然是 web 工程,但是浏览器并不一定是唯一的客户端,不能排除有潜在的攻击者自己构建 http 请求往服务器发的可能啊
    zhuzhiqiang
        5
    zhuzhiqiang  
       2019-05-17 11:03:46 +08:00
    form 埋个 token form 提交后就把这个 token 删了 token 不存在即是重复提交
    peyppicp
        6
    peyppicp  
       2019-05-17 11:04:06 +08:00
    分布式锁
    hlwjia
        7
    hlwjia  
       2019-05-17 11:04:42 +08:00 via iPhone
    没有 unique 的情况下只能先查重再插了
    Immortal
        8
    Immortal  
       2019-05-17 11:05:50 +08:00
    服务端搞个 token 存到 session 或者什么地方 然后发给前端
    前端提交表单的时候一起提交着个 token
    如果一致就处理
    不一致当成重复提交 不处理
    以上是一个成熟且常规的做法
    noble4cc
        9
    noble4cc  
    OP
       2019-05-17 11:07:57 +08:00   ❤️ 1
    @DovaKeen 好像是可以解决并发问题,查的时候加锁能解决
    eluotao
        10
    eluotao  
       2019-05-17 11:08:06 +08:00
    插入或忽略
    如果我们希望插入一条新记录( INSERT ),但如果记录已经存在,就啥事也不干直接忽略,此时,
    可以使用 INSERT IGNORE INTO ...语句:

    INSERT IGNORE INTO students (id, class_id, name, gender, score) VALUES (1, 1, '小明', 'F', 99);
    jorneyr
        11
    jorneyr  
       2019-05-17 11:10:00 +08:00
    根据业务数据特点建立唯一索引
    aitaii
        12
    aitaii  
       2019-05-17 11:12:22 +08:00 via iPhone   ❤️ 2
    幂等
    fl0w
        13
    fl0w  
       2019-05-17 11:13:42 +08:00
    insert into table(field1,field2)
    select value1,value2 from dual where not exists( select 1 from table where field1 = value1 and field2 = value2)
    JacFrak
        14
    JacFrak  
       2019-05-17 11:25:24 +08:00
    @Immortal 这个不错,受教
    vance
        15
    vance  
       2019-05-17 11:26:18 +08:00
    分布式锁,单个数据库个可以 insert into XX ... select x from dual where not exists( select from xx )
    noble4cc
        16
    noble4cc  
    OP
       2019-05-17 11:26:20 +08:00
    @fl0w 不加锁查恐怕不行吧,会有幻读问题
    noble4cc
        17
    noble4cc  
    OP
       2019-05-17 11:26:54 +08:00
    @eluotao ignore 必须唯一 key
    noble4cc
        18
    noble4cc  
    OP
       2019-05-17 11:28:00 +08:00
    @Immortal 相当于这个 form 是一次性的也可以的
    tabris17
        19
    tabris17  
       2019-05-17 11:28:06 +08:00
    一次性 token
    Hellert
        20
    Hellert  
       2019-05-17 11:30:41 +08:00
    难道不应该是在每次提交前手动检查业务键是否重复吗?比如一些 编号 字段。
    eziooooo
        21
    eziooooo  
       2019-05-17 11:34:55 +08:00
    1. 请求锁(这个请求这个人 N 秒内只能请求一次)
    2. 事务
    xuanbg
        22
    xuanbg  
       2019-05-17 11:40:07 +08:00
    先查后写是需要加锁作为前提的,没有锁,就有幻读的可能。如果是分布式系统,那这个锁就必须是分布式锁才行。
    前端防重发是标准配置,不做的都不是合格的前端。
    dilu
        23
    dilu  
       2019-05-17 11:56:55 +08:00
    前端防连点,后端做个锁,记得开事务,表上加个唯一索引
    est
        24
    est  
       2019-05-17 12:00:12 +08:00
    1. 楼主问的是表单提交如何防止重复,这个其实很简单。用 E-tag 这个 http 头控制即可。根本 不用太涉及 db 层面的东西。在 session 就处理了。
    2. 如果是 db 层面需要防止重复,我个人遇到更麻烦的问题是批量插入的时候。没有主键和 uniq key 可以用来鉴别。很麻烦。
    jowan
        25
    jowan  
       2019-05-17 12:00:31 +08:00
    CSRF_TOKEN 可以解决这个问题,顺便还能解决跨站的表单提交
    Weixiao0725
        26
    Weixiao0725  
       2019-05-17 12:17:52 +08:00
    只有 uniq key 一条路子要想真正有效。
    sun1991
        27
    sun1991  
       2019-05-17 12:28:53 +08:00
    从数据库层面上, 用 unique key 解决此类问题是最合适的, 为什么要给自己找不痛快呢?
    glfpes
        28
    glfpes  
       2019-05-17 12:40:33 +08:00 via Android
    为何不能设置 unique ?
    AngryPanda
        29
    AngryPanda  
       2019-05-17 12:40:37 +08:00   ❤️ 1
    @jowan CSRF Token 并不是(也不能)用来解决这个问题的;

    最大的问题是,CSRF Token 并不会一直变化。
    fmumu
        30
    fmumu  
       2019-05-17 12:41:34 +08:00
    先查重再插入,并发时候有重复插入的可能
    应该先插入再查重(使用隔离级别 read commit)吧
    Comdex
        31
    Comdex  
       2019-05-17 12:44:40 +08:00 via Android
    使用队列使并发请求串行
    jason19659
        32
    jason19659  
       2019-05-17 12:45:23 +08:00
    前端是用户表单的重复提交还是什么?无意的还是故意的?
    浏览器卡一下。。每次刷新生成一个 id 就完了,提交了这个 id 就不再处理
    R18
        33
    R18  
       2019-05-17 12:47:47 +08:00 via Android
    悲观锁
    pudgedoor
        34
    pudgedoor  
       2019-05-17 12:50:59 +08:00 via Android
    数据库锁怎么样,乐观锁
    opengps
        35
    opengps  
       2019-05-17 12:55:38 +08:00
    如果是浏览器卡了一下多点击了提交,那么先查询在写入可以应对
    BestSera
        36
    BestSera  
       2019-05-17 14:06:24 +08:00
    for update
    悲观锁 乐观锁都行
    799635347
        37
    799635347  
       2019-05-17 14:06:35 +08:00
    分布式锁,或者丢消息队列串行消费,或者数据库级别加 unique 约束,token 这种应该不能解决
    mikicomo
        38
    mikicomo  
       2019-05-17 14:13:05 +08:00
    @eluotao #10 这也是要唯一键的,他 ignore 是一直监听着唯一键,如果抛出重复键错误直接忽略
    luwies
        39
    luwies  
       2019-05-17 14:15:10 +08:00
    貌似有一种办法是刷出这个提交信息的页面的时候给前端一个唯一值,提交信息的时候把找个值带上,如果是重复的就忽略了。
    kiddult
        40
    kiddult  
       2019-05-17 14:20:38 +08:00
    @AngryPanda 他想的是在每次提交前的页面访问都重新生成 CSRF Token,解除和 session 的绑定
    momocraft
        41
    momocraft  
       2019-05-17 14:37:03 +08:00
    实施难度和风险和效果看 unique key 还不错?
    loveCoding
        42
    loveCoding  
       2019-05-17 14:44:41 +08:00
    数据库层 唯一约束
    应用层 分布式锁
    arthas2234
        43
    arthas2234  
       2019-05-17 14:56:06 +08:00
    重复提交这个事,不应该用 unique key 来防御,建议使用 token
    rockyou12
        44
    rockyou12  
       2019-05-17 15:07:54 +08:00   ❤️ 1
    纯 rest,直接请求里带一个 requestId,由前端生成的 uuid。如果表(或者放 redis 或直接后端缓存下)已经出现,就直接拒绝
    seanpan
        45
    seanpan  
       2019-05-17 17:33:25 +08:00 via iPhone
    表单令牌
    lusi1990
        46
    lusi1990  
       2019-05-17 18:35:36 +08:00 via Android
    用锁或者队列 ,插入前判断
    JohnYehyo
        47
    JohnYehyo  
       2019-05-23 17:05:54 +08:00
    服务器生成 token 存入表单隐藏字段 表单提交 服务器端获取该值与 token 比较 一致则进行业务处理并在 session 删掉 token
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   877 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 21:04 · PVG 05:04 · LAX 13:04 · JFK 16:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.