【实战】多属性校验唯一性解决方案

3 篇文章 0 订阅

【前言】最近在优化一个导入功能,导入6w条数据居然花了1天的时间,当时我就惊呆了O.O

后面检查了一下代码,主要的问题在于for循环走创建接口,没用到多线程,所以耗时比较久,这还只是校验数据可用性的阶段,后面还有一个校验唯一性的阶段,这个阶段需要21个属性均不重复来校验唯一性(原来有25个属性,有几个属性的校验是多余的,被优化了),旧表没有索引且所有属性列均没有设置非空和默认值,导致sql里有一堆的判空处理

<choose>
    <when test="attr == null or attr == ''">
        and (attr is null or attr = '')
    </when>
    <otherwise>
        and attr = #{attr}
    </otherwise>
</choose>

作为一只有代码洁癖的程序猿,下巴都给我惊掉了,然后这样的判断多达9处

好在这张表只有6条数据,不然1天估计跑得够呛

由于优化的项目没集成消息队列,且对redis缓存的使用等于零(单机得不能再单机的单机应用),这时候我想去依靠这2个中间件几乎是不可能的事情,因为很多表都没做缓存处理,如果我用Redis的话就还需要去维护缓存,由于工期比较赶(懒),所以直接全部拉到JVM内存里操作

【缺点总结】

1. for循环走创建接口,即使只有6w数据,性能依然极其底下

2. 数据表属性列没设置非空和默认值,导致需要is null 和 空字符判断,如果设置默认值为空字符串,则只需要判断 attr = '' 即可

3. 多属性校验过程太过粗糙,且未排除多余属性,比如 [A,B,C,D.....]属性,通过A就可以确定对象时,又传入B,C,D....属性进行确认,这操作没必要

【推荐解决方案】

① 直接丢消息队列/redis队列,慢慢消费

② 多线程消费

本文主要介绍多属性唯一性校验的解决方案

下面开始我解决这个问题的过程

当时看到sql那边拼接25个属性校验,我立马想到的就是过滤冗余判断属性,并计算这21个属性的MD5值来校验唯一性,因为当某一个属性变动时,这个对象就是新的对象,也就是MD5值也会变成新的

一开始想到的是用mysql的json_object来拼接对象,当我拼完后并且计算出MD5后感觉一切都很顺利,但让我用Java写一遍计算MD5过程后却发现计算得出的MD5值不一样

检查后发现json_object是根据max_sort_length进行排序的,即先比较字符串长度,当长度一样时再比较字符串大小

OK,那我改用TreeMap来填充可以吧,然后重写Comparator比较器

打印出的字符串比较后,可以的,这时候顺序是对的

但是!!!打印的MD5还是不一致! 摔碗.gif

原来json_object转字符串后会在key:value的冒号后面多个空格,然后key1和key2之间还有个空格

而Java的JSON工具转成字符串后都会把多余的空格去掉 嗯,我已经不知道摔了几只碗了

一开始解决时,用了mysql的REPLACE函数,但是要真对字符串和数字,所以至少得4次使用REPLACE函数

REPLATE(REPLACE(REPLACE(REPLACE(json, '": "', '":"'), '": ', '":'), '", "', '","')))

是不是觉得头皮发麻?所以我放弃用mysql处理,转用Java处理拼接空格,而不是mysql去除空格

但是这个方法还是不够好,又想到一个更好的

mysql处理

用concat去拼接字符串, 格式: key1=value1,key2=value2,key3=value3

SELECT MD5(CONCAT('key1=', value1, ',key2=', value2, ',key3=', value3))

java处理

用Arrays.asList('key1', 'key2', 'key3')存储顺序,然后将对象转Map<String, Object>, 直接遍历list去封装字符串就行了

Map<String, Object> map = new HashMap<>();
map.put("key1", value1);
map.put("key2", value2);
map.put("key3", value3);
List<String> list = Arrays.asList("key1", "key2", "key3");
String str = list.stream().map(key -> {
    Object value = map.get(key);
    return key + "=" + value;
}).collect(Collectors.joining(","));
// 我这边用的hutool工具的MD5工具,可以自行选择
String md5 = MD5.create().digestHex(str);

数据库表只要新增一个MD5属性用来存储并加上索引就行了,第一次初始化之后,后续校验唯一只需要在代码里拼接好字符串后再进行MD5计算之后根据md5值查询数据库就可以快速判断是否已存在

注意事项

value的值要注意默认值的处理

当字段变动时(字段名修改,删除字段,增加字段),MD5的值得重新计算

因为这边的数据只有6w,所以用的32位MD5
如果怕重复的话,可以参考我的另一篇文章 千万级别数据去重思路

核心思想就是拼接字符串,然后做加密,MD5只是其中一种方式

如果帮到你,请点个赞吧 O(∩_∩)O~ 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值