问题背景
需求中需要消费kafka中的数据,将数据转存到mysql中。为了提高消费效率,采用mysql批量插入,并引入了多线程,
其中批量插入由list保存定量的数据一次性插入实现,由此引发kafka中重复的数据多线程批量插入mysql出现死锁问
题。报错描述是这样:Deadlock found when trying to get lock; try restarting transaction。这是因为
mysql数据表中定义了唯一主键,同时插入了多条相同的数据进入mysql数据表引起的死锁问题。
解决方案
经过漫长的查询资料、思考、编码、测试后,得到一个可靠的解决方案,并亲测有效。问题的原因就是同时插入多条
相同数据,所以解决问题的思路就是在多线程下给数据去重。方法就是定义一个全局的变量List集合uniqueList保存
数据的主键,保证数据的唯一性,因为在多线程下,这个uniqueList使用CopyOnWriteArrayList实现,这里面使用了
分段锁的方式提高了多线程下数据读取的速率。在将新数据放入批量插入mysql的list中之前,都通过uniqueList判断
新数据是否与已经保存的数据有重复情况,重复则丢弃,不重复则放进去。同时为了保证全局的uniqueList不出现
数据量过多,造成oom内存溢出问题,每插入一个批次的数据则将该批次的数据的主键从uniqueList中移除。至此,
该问题可以解决。代码太长不适合粘贴,这里只做思路记录。
注意:在之后的实践中有了新的问题, CopyOnWriteArrayList 确实可以保证数据的线程安全问题,但是它写时复制特性在数据量比较大的时候会出现OOM的问题。这里建议使用 ArrayBlockingQueue 作为代替,同时因为是队列,其长度是定义时就需要设置好,不能灵活长短变化,所以我采用了写入数据量控制,避免出现队列溢出的错误。