提高系统的吞吐量解决数据库重复写入问题


Posted in MySQL onApril 23, 2022

问题分析

为了提高系统的吞吐量,很多环节下对于数据库的写入是多线程,甚至是多进程的。为了保证写入成功,在很多情况下需要多次重试。这就会带来一个问题,数据重复,同一条数据会被记录多次。有些情况下数据重复无伤大雅,但是很多情况系统是无法容忍数据重复的。因此这个问题需要解决。我个人觉得解决这一问题有两个方向:第一,从数据库上保证数据不重复,第二,从程序上保证数据不重复。

数据库上解决

主要包括:主键,唯一性索引,甚至是临时表。程序上解决无非就是要保证同步,这两种方式能解决很多情况下的数据重复。但是有些情况可能比较棘手,使用者两种方法有时并不能很好的解决,或实现起来比较复杂,如下面的数据

假如下表主要字段如下:

id  studentId  teacherId  states 

状态(states)是有多种的(0,1,2,3),状态可以转换,但是状态为,0的一个只能由一个,其它字段是可以重复的。这个其实就是保证某一种状态下的数据不重复。

首先唯一性索引不能够起作用,因为无法建立唯一性索引。主键也没有效果,没法通过这些字段生成可区分的id。所以这两种方法都失效了。还有一种方法就是临时表,在临时表中插入一条能够区分的数据(比如studentId,teacherId组合),无论是唯一索引还是主键都可以。写入时首先写临时表,临时表写入成功则插入一条数据,然后清空临时表。这在严格保证数据不重复的情况下是能够起作用的,但是比较繁琐,需要处理一个临时表。

另外的一个办法就是根据我们的业务场景,在一个时间段内(比如1分钟)不会出现两条相同数据写入。这样我们可以使用studentIdTeacherid加上精确到分钟的时间来构成一个唯一id,重试时间间隔一般都极短(秒级别),这样通过id来保证数据的唯一性。

从程序上保证数据不重复

如果从程序上来保证数据不重复,则更加复杂。第一种办法是对写入过程加锁,确保只有一次写入成功代码如下(伪代码):

 

Lock lock =new WriteLock();
public void write(Data data){
try{
     if(lock.tryLock() ){
               dataRepository.save(data);
               if(dataRepository.numberOf(data)>1){//在写入的时候检测如果数据库中有该数据抛出异常。再次保证数据不重复。
                   throw new DataException
            }
      
     }  
  }finally{
     lock.unlock();
  }
}

 

这种方式首先会存在效率问题,所有的数据都要顺序写入会导入效率下降。我们只需要保证同一条数据不能并发写入而不是不同数据。另外这种方式还会存在一定概率的重复,因为网络问题和数据库或ORM框架的缓存问题,会导致写入检测时并不能发现数据库的更新。比如使用hibernate,两次线程调用write方法会使用两个session,从而使得第一次写入缓存的数据无法在下一次操作中看到。在write方法中多次调用numberOf方法也是不起用的,由于session的缓存,后面的查询会使用第一次的缓存结果,在第一次查询后的数据库变化,后面的查询仍然无法觉察。

针对写入效率低的问题,这里可以采用数据锁,即通过一种方法比如使用data的hashcode来映射来获取锁,这样不同的数据会获取到不同的锁,解决了所有数据的顺序写入问题。但是跟第一种情况一样仍会存在数据重复问题。

对于多进程的情况,如微服务部署多个的情况,上面的同步会失效。对于这种情况唯一的解决办法就是使用上面所说的数据库同步或者构造一个环节锁,类似于令牌的方法。只有获取到令牌才有写入资格,写入成功后销毁针对该数据的“令牌“。这种实现其实也比较简单,如使用一个redis的hashmap,每次写入首先获取该数据对应的value,通过value来判断该数据是否写入,来保证数据不重复。

总结

上面无论哪种方法,针对本文所提到的数据,解决重复问题都是不容易的。要么实现起来比较复杂,要么还是不能100%保证数据不重复。针对我们的业务场景:state为0的状态下数据能有一条,且0的状态不会持续太久,后面的操作会将其修改。而且多线程重试并不是每次都发生的,多进程情况下,每次数据写入也多是只通过其中一个节点,针对这种情况其实可以采取更简单的处理方式,不做过多的顺序限制只在写入时检查数据库,如果真的因为数据更新或者并发导致了多次写入也没有关系(这种情况很少),后续的操作时再删除多写入的数据。这是一种乐观的处理方式,但是对于很多情况是可以解决数据重复问题的。

以上是我个人对于只有某种状态的数据不能重复写入问题的处理方法的思考。主要是从数据库和程序上来控制及如果场景允许使用乐观(后续补救)的方法。仅供参考!


Tags in this post...

MySQL 相关文章推荐
数据库的高级查询六:表连接查询:外连接(左外连接,右外连接,UNION关键字,连接中ON与WHERE的不同)
Apr 05 MySQL
MySQL 使用SQL语句修改表名的实现
Apr 07 MySQL
详解MySQL 用户权限管理
Apr 20 MySQL
如何用Navicat操作MySQL
May 12 MySQL
MySQL中使用or、in与union all在查询命令下的效率对比
May 26 MySQL
浅谈MySQL 亿级数据分页的优化
Jun 15 MySQL
解决mysql问题:由于找不到MSVCR120.dll,无法继续执行代码
Jun 26 MySQL
MySQL系列之十四 MySQL的高可用实现
Jul 02 MySQL
MySQL数据库超时设置配置的方法实例
Oct 15 MySQL
Mysql忘记密码解决方法
Feb 12 MySQL
MySQL提取JSON字段数据实现查询
Apr 22 MySQL
MySQL 执行数据库更新update操作的时候数据库卡死了
May 02 MySQL
MySQL 数据库范式化设计理论
Apr 22 #MySQL
MySQL提取JSON字段数据实现查询
mysql使用FIND_IN_SET和group_concat两个方法查询上下级机构
Apr 20 #MySQL
在MySQL中你成功的避开了所有索引
Apr 20 #MySQL
mysql中如何用命令创建联合唯一索引
Apr 20 #MySQL
mysql 8.0.27 绿色解压版安装教程及配置方法
MySQL去除密码登录告警的方法
Apr 20 #MySQL
You might like
PHP 快速排序算法详解
2014/11/10 PHP
3种方法轻松处理php开发中emoji表情的问题
2016/07/18 PHP
PHP编写daemon process详解及实例代码
2016/09/30 PHP
手把手教你自己写一个js表单验证框架的方法
2010/09/14 Javascript
javascript 学习笔记(一)DOM基本操作
2011/04/08 Javascript
JQuery+CSS提示框实现思路及代码(纯手工打造)
2013/05/07 Javascript
浅析offsetLeft,Left,clientLeft之间的区别
2013/11/30 Javascript
通过onmouseover选项卡实现img图片的变化
2014/02/12 Javascript
分享一个自己动手写的jQuery分页插件
2014/08/28 Javascript
js css 实现遮罩层覆盖其他页面元素附图
2014/09/22 Javascript
js实现Select列表内容自动滚动效果代码
2015/08/20 Javascript
原生JS实现拖拽图片效果
2020/08/27 Javascript
动态的9*9乘法表效果的实现代码
2016/05/16 Javascript
微信小程序 图片加载(本地,网路)实例详解
2017/03/10 Javascript
JS触摸与手势事件详解
2017/05/09 Javascript
详解操作虚拟dom模拟react视图渲染
2018/07/25 Javascript
简单学习5种处理Vue.js异常的方法
2019/06/17 Javascript
vue中对象数组去重的实现
2020/02/06 Javascript
Vue3新特性之在Composition API中使用CSS Modules
2020/07/13 Javascript
通过实例解析js可枚举属性与不可枚举属性
2020/12/02 Javascript
vue实现禁止浏览器记住密码功能的示例代码
2021/02/03 Vue.js
[02:53]2018年度DOTA2最佳战队-完美盛典
2018/12/17 DOTA
python中的hashlib和base64加密模块使用实例
2014/09/02 Python
Python装饰器用法实例总结
2018/02/07 Python
python中验证码连通域分割的方法详解
2018/06/04 Python
pytorch 转换矩阵的维数位置方法
2018/12/08 Python
基于python 微信小程序之获取已存在模板消息列表
2019/08/05 Python
Python异步编程之协程任务的调度操作实例分析
2020/02/01 Python
Python图像读写方法对比
2020/11/16 Python
纽约家具、家居装饰和地毯店:ABC Carpet & Home
2017/06/21 全球购物
Java工程师面试集锦之Spring框架
2013/06/16 面试题
科级干部考察材料
2014/02/15 职场文书
学雷锋的心得体会
2014/09/04 职场文书
结婚老公保证书
2015/02/26 职场文书
教师节主持词开场白
2015/05/29 职场文书
如何优化vue打包文件过大
2022/04/13 Vue.js