劳动节后拿到一个需求要替换百万级的用户数据,涉及到了MySQL和Oracle两个数据库中的三张表。
场景分析:MySQL数据库中有一张表:USER_QKL_ACCOUNT_INFO
(用户区块链账户信息表-以下简称为A),Oracle数据库中有两张表:USER_INFO
(用户表-以下简称为I)和USER_QKL_MESSAGE
(用户区块链信息表-以下简称为Q);
首先需要从A表中取出原始未加密的区块链信息(区块链地址、用户私钥和用户公钥),A表中只包含手机号码和区块链信息,Q表结构和A表大致相似不同的地方在于Q表中只有用户ID和区块链信息,而I表中既有用户ID又有手机号码没有区块链信息。一共耗费三天时间进行试错,总结出全部的处理过程。
方案一:
首先,经过简单的思考,我推出了方案一:
1.先将MySQL数据库中的A表通过Navicat导出为json文件;
2.然后用MybatisPlus把Oracle数据库里面的两张表生成了entity、mapper、service、controller……
3.通过编写后端方法先解析json文件里面的内容然后遍历Q表中区块链信息为空的用户List,获取list.size()再for循环通过json文件(A表)中解析出的手机号码(mobile)调用相关接口去I表中查对应的用户ID(userId)。
通过用户ID获取到该对象,加密json文件中获取到的区块链信息并set给该对象,调用MybatisPlus自带的insertOrUpdate()方法更新用户信息。
PS:这个方案涉及到了两处查询的过程,消耗掉了大量时间,还需要校验手机号码格式是否正确并且是否存在与该号码匹配的用户信息。经过测试发现运行程序执行操作更新一万条数据大概需要1小时27分钟,如果将百万数据替换完成大概需要一百多个小时……
方案二:
后来,继续优化,拿出了方案二:
方案二是保留方案一中的json文件,并且把Q表导出为CSV文件。
这里起初是想导出为Excel文件,但是发现:单个sheet只能保存65536行数据,这无疑是加深了处理数据的难度,所以经过权衡之后确定了保存为CSV格式。
首先在网上搜索了一下处理CSV文件的工具类,找到了FastCsv和SuperCsv,结果发现貌似无法设置追加数据,每一次写出文件都会覆盖掉之前的内容。一时没有发现有较为详细的文档可以参考……所以最终是用了hutool的CsvUtil,这个工具类也是和上面一样的毛病,但是其他功能用到了hutool所以这里也就继续使用了。
1.解析json文件,过程看方案一。
2.通过SQL语句查询I表中每个用户所在行的记录(行号),Oracle数据库直接使用RowNum函数。
3.用hutool.core.text.csv.CsvUtil读取Q表的CSV文件,用第二步的行号查询CSV文件的所在行rows.get(rowNum).getRawList()
拿到Q表CSV文件中的用户ID。将json文件中解析出来的区块链信息和该用户ID进行组装生成新的字符串(user)。
4.BufferedWriter按照CSV格式写出刚才的user内容到新的txt文件中,这里可以设置new FileOutputStream(file, true)
为不覆盖(即追加内容)。
5.将txt文件重命名为*.csv然后用Navicat导入到数据库中。
PS:由于还是需要通过数据库查询该用户所在的行号依然不能压缩处理的时间,经过测试大概比之前的速度快了50%左右……
方案三:
速度慢的具体原因是什么呢?是查询数据的过程消耗了大量的时间,所以我又拿出了方案三:
1.解析json文件,过程看方案一。
2.通过SQL语句查询I表中每个用户所在行的记录(行号),Oracle数据库直接使用RowNum函数。
3.用hutool.core.text.csv.CsvUtil读取Q表的CSV文件,用第二步的行号查询CSV文件的所在行rows.get(rowNum).getRawList()
拿到Q表CSV文件中的用户ID。将json文件中解析出来的区块链信息和该用户ID进行组装生成新的字符串(content)。String content ="UPDATE USER_QKL_MESSAGE SET QKL_ADDRESS ="..."WHERE USER_ID = " + userId;
4.将上面拼接好的SQL语句逐行写入到txt文件中。
5.将txt重命名为*.sql,在Navicat运行SQL文件批量更新用户信息。
PS:感觉已经达到了我需要的效果,经过测试平均一秒钟可以写入十五六条数据,一个小时最多可以完成六万条,那我岂不是只需要十几个小时就能搞定?最后入库过程预计也就是一个小时的样子吧?纳尼?数据今天就要用?
方案五:
此处略过一些踩坑记录,直接推出终极版方案五:
1.将MySQL数据库中的A表导出为txt格式,在Oracle数据库中新建表USER_QKL_ACCOUNT_INFO
,并按照MySQL数据库中的字段逐一匹配,导入A表的数据到Oracle数据库。
2.用三表联查,取到区块链信息不为NULL的用户:
SELECT Q.USER_ID,A.QKL_ADDRESS,A.QKL_PRIVATEKEY,A.QKL_PUBLICKEY,Q.QKL_ID,A.CREATE_TIME,A.UPDATE_TIME
FROM "USER_INFO" I LEFT JOIN "USER_QKL_MESSAGE" Q ON I.USER_ID = Q.USER_ID LEFT JOIN "USER_QKL_ACCOUNT_INFO" A ON A.MOBILE=I.MOBILE
WHERE A.QKL_ADDRESS IS NOT NULL
3.导出查询结果为SQL文件,打开导出的SQL文件替换<table_name>
为USER_QKL_MESSAGE
(即Q表),然后运行SQL文件把数据写入到Q表中。
4.把Q表导出为CSV格式,现在拿到的是关联用户ID和区块链信息以后的记录了。用hutool.core.text.csv.CsvUtil读取这个CSV文件,然后获取CSV文件中每一行的数据。
5.分别拿到用户ID、区块链信息、创建时间、更新时间……然后加密区块链信息拼接为content,按照CSV格式写出刚才的content内容到新的txt文件中。
6.将txt文件重命名为*.csv然后用Navicat导入到数据库中。
PS:由于省略了和数据库的交互操作,避免了一系列的查询过程,这一次加密了一百一十九万条数据只用了惊人的九分钟十五秒。可能还有更好的方式可以达到秒级操作~比如多线程、比如kettle……不过目前这个处理速度我已经非常满意了。