mysql 反初始化_MySQL 初始化数据的一些方案

本文探讨了在分布式系统中处理幂等数据的策略,以避免事务回滚导致的数据不一致问题。提出了先生成数据再进入事务、使用`INSERT IGNORE`、分布式锁、分布式重试锁等解决方案,并分析了各自的优缺点。对于非幂等类型的数据,通过限频、预生成单号等方式确保数据唯一性。此外,还讨论了在博客系统中防止重复保存文章的实现方法。
摘要由CSDN通过智能技术生成

先强调一点,不要在事务内生成数据

之所以不要这样做,是因为事务如果回滚,我们就找不到这条数据了。特别是在分布式系统中,事务内插入数据后,调用其他服务,如果出现 RPC 异常,事务回滚导致当前服务的数据找不到,但是下游的数据可能生成成功了。也就是,当前服务无数据,下游服务有数据,这种数据的不一致,一般都不能接受,并且要花精力去修复。

正确的做法是,先生成数据,然后进入事务。

幂等类型的数据生成

这类数据本身有幂等标识,数据库本身会加必要的唯一索引。例如:

用户维度的账户余额,一个用户在 DB 中只会有一条余额记录。DB 中会在 用户标识 字段上加唯一索引。

上游传过来的一笔扣款请求,带有幂等标识。一笔请求在DB中只会存一条记录。DB 中会在 用户标识 + 幂等标识 上加唯一索引。

以初始化用户余额为例,用户余额表设计如下:

方案1: 无则插入

Java 伪代码:

问题:

并发时,会出现唯一索引冲突异常;

出现唯一索引冲突时,会导致id出现跳跃。这个是可接受的。

方案2: 无则插入,吞掉异常后,再反查一次

Java 伪代码:

方案3: 无则插入,但使用 insert ignore 插入

使用insert ignore 插入数据时,若主键冲突或者唯一索引冲突,则不再插入,同时不会报错。从这点上,和 redis 的 setnx 指令很像。

Java 伪代码:

个人认为这是最好的一个方案。

需要注意的是,反查到数据后,可能需要增加校验关键字段的逻辑。比如,在扣款场景中,根据用户标识 + 幂等标识查询到数据后,需要将金额字段和请求中的金额字段做下比较,不一致时要报错,然后人工处理。

方案4: 使用分布式锁保证插入不会出现唯一索引冲突异常

比如,使用 redis 作为分布式锁。

这个和设计模式单例模式中的双检锁思路相同。

优点:

* 基本解决了会出现唯一索引冲突异常的可能。

* 减少了对 MySQL 的读写。

问题:

* 没有完全解决出现唯一索引冲突异常的可能。因为加锁成功后的操作,不能完全保证是在 60s 之内完成。极端情况下,可能因为 GC、网络抖动、组件Bug等原因,导致超过 60s ,此时相当于redis锁失效,还是可能出现唯一索引冲突异常。

* 引入 redis 后,系统可用性降低。redis 加锁、结果可能因为网络原因出现异常。比如解锁失败,会导致该账号加锁时间过长。

* 虽然没有了唯一索引冲突异常,但是多了redis加锁异常。使用该方案后,只是从一个异常变成另外一个异常而已。

* 业务逻辑对应的接口正常响应 QPS 变低。

方案5: 使用分布式重试锁保证插入不会出现唯一索引冲突异常

该方案比方案4多了重试两个字。

重试锁,加锁会重试,直至加锁成功,或者到达一定重试次数为止。

伪代码:

问题和方案4类似。

同时多了一些优点和缺点。

优点:

* 加锁失败的概率变低了。

缺点:

* 重试锁,真的发生重试时,会导致耗时增加。

方案6: 分布式重试锁 + 锁失败后再查一次

在方案5中,在非网络异常的情况下,锁失败时,已经比开始尝试加锁时过去了 30ms 左右,此时另外一个加锁成功的线程极有可能已经成功初始化数据,所以可以再查一次。

实际业务中,我倾向选择方案2和方案3。

非幂等类型的数据生成

非幂等型数据一般来自与用户交互的地方。比如博客系统中,用户写一篇博客,连续点了两次【保存】按钮,博客系统可能会保存两篇一模一样的文章。

如何保证只出现一篇文章呢?

方案1: 限频

比如博客系统中,限制用户5秒内只能提交一次。

技术上,用 redis 锁可以实现。

方案2: 预生成单号

在业务系统之前加一层预生成单据层。

比如博客系统中,预生成单据表和文章表可以这样设计:

用户点击【新建博客】按钮时,后台在 article_prepare_record 表中生成一条记录,其中user_id是用户ID,基于用户登录态获取,pre_id 是随机生成或者基于ID生成器生成的全局唯一ID,biz_id 为空。

pre_id 会传给前端。

用户写完博客后,点击【保存】按钮,前端将pre_id、title、content 一起传到后台。

后台先校验 article_prepare_record 中是否有 user_id、 pre_id 对应的记录:

无则报错。

有,但是 biz_id 不为空,报错。

有,且 biz_id 为空,则在 article_record 插入文章记录,其中biz_id用ID生成器生成。然后将 biz_id 写入 article_prepare_record 表中的 biz_id 字段。

注意,这里要用到数据库事务,并在事务内锁住 article_prepare_record 中的记录,保证数据正确。

article_prepare_record 表中的旧数据可以定期清理掉。比如用户的一次操作流程不应该超过 12 个小时,那么可以将 12小时之前生成的数据清理掉,不影响业务,也节省磁盘。

熟悉网络安全的同学会发现,这个方案和 CSRF 攻击的一个防御手段是类似的。

这个方案可以用于很多类似的场景中,比如支付系统中,用户下单时,生成一个预下单号,用户通过预下单号进入支付页面进行支付,预下单号传到后台后会过渡到真正的订单号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值