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


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 相关文章推荐
MySQL的join buffer原理
Apr 29 MySQL
教你解决往mysql数据库中存入汉字报错的方法
May 06 MySQL
超详细教你怎么升级Mysql的版本
May 19 MySQL
mysql 如何获取两个集合的交集/差集/并集
Jun 08 MySQL
Mysql数据库值的添加、修改、删除及清空操作实例
Jun 20 MySQL
MySQL系列之十 MySQL事务隔离实现并发控制
Jul 02 MySQL
详细聊聊关于Mysql联合查询的那些事儿
Oct 24 MySQL
MYSQL 表的全面总结
Nov 11 MySQL
mysql5.7的安装及Navicate长久免费使用的实现过程
Nov 17 MySQL
MySQL日期时间函数知识汇总
Mar 17 MySQL
MySQL创建表操作命令分享
Mar 25 MySQL
Mysql查询时间区间日期列表,不会由于数据表数据影响
Apr 19 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脚本的10个技巧(2)
2006/10/09 PHP
PHP,ASP.JAVA,JAVA代码格式化工具整理
2010/06/15 PHP
ThinkPHP结合ajax、Mysql实现的客户端通信功能代码示例
2014/06/23 PHP
支付宝接口开发集成支付环境小结
2015/03/17 PHP
ubutu 16.04环境下,PHP与mysql数据库,网页登录验证实例讲解
2017/07/20 PHP
JavaScript中的isXX系列是否继续使用的分析
2011/04/16 Javascript
使用JQ来编写最基本的淡入淡出效果附演示动画
2014/10/31 Javascript
整理Javascript函数学习笔记
2015/12/01 Javascript
jQuery绑定事件-多种实现方式总结
2016/05/09 Javascript
JavaScript中boolean类型之三种情景实例代码
2016/11/21 Javascript
JavaScript+Html5实现按钮复制文字到剪切板功能(手机网页兼容)
2017/03/30 Javascript
AngularJS中控制器函数的定义与使用方法示例
2017/10/10 Javascript
详解vue-cli3使用
2018/08/14 Javascript
vue中如何实现后台管理系统的权限控制的方法示例
2018/09/19 Javascript
angularJs select绑定的model取不到值的解决方法
2018/10/08 Javascript
浅谈让你的代码更简短,更整洁,更易读的ES6小技巧
2018/10/25 Javascript
Vue开发之watch监听数组、对象、变量操作分析
2019/04/25 Javascript
Vue 实现v-for循环的时候更改 class的样式名称
2020/07/17 Javascript
vue实现多个echarts根据屏幕大小变化而变化实例
2020/07/19 Javascript
[44:15]国士无双DOTA2 6.82版本详解(上)
2014/09/28 DOTA
Python使用win32com实现的模拟浏览器功能示例
2017/07/13 Python
python网络爬虫之如何伪装逃过反爬虫程序的方法
2017/11/23 Python
EM算法的python实现的方法步骤
2018/01/02 Python
CSS3制作缩略图的详细过程
2016/07/08 HTML / CSS
匡威德国官网:Converse德国
2019/01/26 全球购物
自我评价是什么
2014/01/04 职场文书
实习生的自我评价
2014/01/08 职场文书
文明礼貌演讲稿
2014/05/12 职场文书
公司承诺书范文
2014/05/19 职场文书
优秀毕业生求职信
2014/06/05 职场文书
机关党建工作汇报材料
2014/08/20 职场文书
工作经常出错的检讨书
2014/09/13 职场文书
2014年幼儿园园长工作总结
2014/12/17 职场文书
质量保证书格式
2015/02/27 职场文书
十二生肖观后感
2015/06/12 职场文书
你真的了解redis为什么要提供pipeline功能
2021/06/22 Redis