目标:
针对邮件发送功能进行200W用户的发送目标进行发送时长的性能评估
约束:
1. 不能使用直接在邮件服务器上插入发送任务记录的方式创建200W发送任务
2.不能使用重复的邮箱/每个都必须不一样
3.发送邮箱有具体的后缀格式([email protected])
4.发送对象的邮箱/电话必须为加密后的字符串(满足业务可以真实解密, 解密通过第三方部件实现)
现有条件:
1. 系统已经存在完整的用户记录表(user),包括字段(ID,name, emailaddr, phone),记录数
2. 已经存在单独的用户邮箱信息加密/解密代码
实现思路1:
通过现有的用户记录表user, 使用自关联生成笛卡尔积, 不断重复进行关联操作扩充表数据到200W, 然后替换掉emailaddr, phone 列的值。
实现思路2:
1. 先创建一个user表的备份(使用语句create tables userbak as select * from user)
2. 使用存储过程定义循环变量n, 插入数据到user表中(从userbak中去除数据, 修改id为select (id+n) 生成新的记录, 生成200W条记录
3. 导出user表, 修改email 和phone信息,重新导入
实现思路3:
1. 先创建一个user表的备份(使用语句create tables userbak as select * from user)使用存储过程定义循环变量n, 插入数据到user表中(从userbak中去除数据, 修改id为select (id+n) 生成新的记录, 生成200W条记录
2. 新建临时表temp(id, emailaddr,phone), 使用user表的id填充temp表的id
3. 导出为文件, 填充emailaddr,phone数据到200W的数量, 重新导入到temp(创建出200W的email和phone数据)
4. 使用user表的ID和temp表的id 进行关联,为user表扩充出emailaddr1和phone1字段,值来自于temp表(由于user的id和temp的id字段值是一一对应的所以2表关联的结果相当于是对user表进行字段扩展,不会有额外的无效关联元组的出现)
5. 删除原表email 和 phone 字段,使用emailaddr1和phone1 代替
以上3种方案从设计角度核心是一样,希望通过利用SQL的现有能力(关联扩展表,基于现表创建新表,存储过程)简化新数据的创建过程, 但是也均存在以下问题:
1. User表的id 字段是主键唯一的, 同时id的大小并不是顺序递增的, 实际存在不用的跳跃(无规律),即存在id(n)+M=id(m)的情况. 所以使用单纯的数值循环累加id值会导致新数据插入失败(id重复)
PS: 可以通过增加随机函数/构建一个不会重复的值作为循环参数, 但是这个数据的产生规则本身就要花费一定时间和技术经验。
2. 插入的email和phone信息需要先创建原始数据, 再进行加密, 然后再更新到数据文件,导入到数据库中。但是目前加密工具并不支持一次处理200W的原始数据,另外这个过程存在多次200W数据的复制过程,耗时较多(email和phone2个字段共400w字符(原始数据400W->复制加密400W->加密后复制进数据库文件))
3. 现有数据库操作工具,导入200W条数据,存在进程崩溃的情况
综合以上, 以上3种方式理论上可行,但是实际操作性差。
那么换一个思路:
从上面的问题1(id做了主键约束),于其花时间解决新增数据主键冲突,不如新建一张新表,从1开始设置主键(递增即可), 一次性解决200W信息的加密,不如每次处理一条, 动态处理200W次, 避免了大数据量的拷贝,从文件导入到数据库收到客户端工具的限制, 换思路为使用文本文件(insert 语句),使用mysql的source命令导入应该会快很多.
综合以上, 解决方案需要具备如下条件:
1. 需要一个实现方式可以动态按规则生成200W的id值
2. 需要具备对原始信息具备加密功能
3. 生成结果是保存insert语句的xx.sql文本文件(用于source导入)
具体使用JAVA实现:
涉及使用xx个类(Main入口类, User类对user表进行建模同时使用inituser() 生成模板user对象, Util类实现了字符加密功能,MailList类用于动态生成user对象数组,使用object.clone方法通过模板user对象生成新的对象简化对象生成过程, MakeSQL类根据user对象生成insert语句,并使用自有方法(makefile)生成xx.sql文件)
功能实现过程:
1. 使用User类对user进行建模
2. User类自带init方法生成模板user对象(通过预制数据)
3. MakeList类提供设置id编号起始值,mailaddr格式字符串,user数量属性,生成具体的mailaddr字符串
4. MakeList使用encrpy调用Util类的方法对mailaddr进行加密
5. MakeList使用user类的init方法获得模板user对象,并clone生成新的user对象user1
6. 使用mailaddr替换user1的对应属性,并将user1添加到arraylist users中
7. MakeSQL接收users, 使用for(user u:users)遍历列表,并依次生成insert语句
8. 最后将insert语句保存进xxx.sql文件
9. 上传XXX.sql文件,使用SOURCE命令导入
到此问题圆满解决。
总结:解决问题的过程其实是分析解决方案的过程,一般下意识会使用我们所熟悉的方法,但是却不一定是有效的,路走不通的时候就回到问题的原点重新出发 ,解决问题先考虑是否有效,在考虑是否是最好的方法。
PS: 由于目标明确, 设计过程中并未使用继承和接口特性, 后续可以将util类和makesql 类设计成抽象类, 将makelist 设计成工厂类, 实现支持不同的表类型和SQL语句的扩展。
--- 个人原创,版权所有,转载请注明出处