提醒单对接ES与分库分表分析
1、问题及数据
1.1、涉及到提醒单的SQL查询优化无力。
1 | select count(*) |
1.2、数据
1.2.1、服务提醒
- 每周慢查询249条,平均耗时1.3s。
- 10545724654284683189(兔师傅)、10546050787360507919(西安恒泰汽车服务有限公司)、10545511425563128304(一番车道) 这三家公司触发慢查询次数最多。
- RemindBillReadFacade@queryRemindBillCount 近三个月平均响应时间:12.7ms 请求数:928.3K
- RemindBillReadFacade@queryRemindBillUnCompletedCount 近三个月平均响应时间:68.3ms 请求数:374.8K
- ServiceRemindBillReadFacade@queryServiceRemindBillList 近三个月平均响应时间:28.7ms 请求数:101.05K
- 服务提醒单现有数量:2464000条(百万级别)、服务提醒单操作日志现有数量:2371677条(百万级别)
- 服务提醒单每日新增数量:5096条(千级别)、服务提醒单操作日志每日新增数量:10022条(万级别)
从上面数据可以看出,本次优化主要解决兔师傅等门店较多的大客户的用户体验问题。
1.2.2、未迁移提醒数据
- 保险提醒现有数据:2678857条(百万级别),每日增量:10246条(万级别)
- 特殊人群提醒现有数据:9695180条(近千万级别),每日增量:100354条(十万级别)
如果所有提醒都迁移完成后,数据量剧增,到时候必须要做分库分表和对接ES,否则可能会影响正常的功能使用。
1.2.3、根据groupId分表后提醒单数据
group_id | 服务提醒单数量 |
---|---|
10545724654284683189 | 327544 |
10546172455174347299 | 74656 |
10545511425563128304 | 51467 |
10546050787360507919 | 42275 |
10545360219000198060 | 37992 |
10546172455173835772 | 32988 |
10546443563683618514 | 28356 |
10546050787353579790 | 22154 |
10545511425564218169 | 17932 |
10546172455150344939 | 12469 |
id_own_group | 保险提醒单数量 |
---|---|
10546050787360507919 | 149208 |
10546443563846901705 | 147508 |
10545511425563128304 | 101822 |
10546172455173835772 | 88605 |
10545360219000198060 | 73165 |
10546172455216487588 | 63005 |
10546172455174347299 | 59283 |
10546443563834764203 | 38264 |
10546050787353579790 | 28588 |
id_own_group | 特殊人群提醒单数量 |
---|---|
10545055917999668983 | 2174549 |
10546443563846901705 | 1884708 |
10545360219000198060 | 1078077 |
10545435426611755185 | 511770 |
10546443563710234093 | 356497 |
10546443563713262850 | 247156 |
10546443563754919092 | 243911 |
10546443563959457579 | 209337 |
10546172455194190154 | 182971 |
2、问题解决价值
1.1、实际客户价值
- 优化兔师傅等门店较多客户的用户体验,进入营销看板及服务提醒页面速度快,体验好。
- 所有提醒数据模型统一后,可以深挖数据,创造对客户更有价值的信息。
- 提醒单数据量特别多时,不影响客户正常使用提醒功能
统一模型后深挖数据例子:
客户快过生日了,且有个保养需要提醒,这时候可以给客户定制化提醒:比如因为客户生日快到了,专门为他定了了一个活动,来保养送xxx套餐卡之类。
1.2、研发团队价值
- 解决慢查询问题,提升mysql巡检得分
- 所有提醒数据模型统一,便于管理
- 提醒数据量增大后,不用因为优化接口问题而浪费开发人力资源
3、两个解决方案对比
3.1、分库分表
3.1.1、优点
- 代码侵入性小,改动小,测试范围小。
- 提醒单数量查询效率提升
- 提醒单列表查询效率提升
3.1.2、缺点
- 按照公司分表,门店多的公司数据过多,数据倾斜。
- 所有提醒迁移完成后,数据量剧增,查询提醒单会有性能风险。
- 数据迁移问题
- 兔师傅等拥有门店比较多的客户,对于标题1的问题,依然解决不了。
3.2、对接ES
3.2.1、优点
- 所有提醒迁移完成后,数据量剧增,查询提醒单性能不会影响太大
- ES大数据量数据统计性能较好
- 提醒单查询时,附加信息不必重新查询操作日志获取,可以直接走es,查询效率应有明显提升。
- 对于问题1,对于or查询支持较好,可解决问题。
3.2.2、缺点
- 学习成本高
- 代码侵入性大,改动大,测试范围大。
- 查询接口改造
3.2.3、mapper查询语句改造分析
3.2.3.1、分库分表
不做service改动 只修改mapper查询语句,在mapper层切换分库分表数据源
需排查提醒单相关sql,Sharding-JDBC的语法并不是支持所有普通JDBC的语法,参考:https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/sharding/use-norms/sql/
3.2.3.2、对接ES
首先根据条件查询es,获取返回的提醒单主键id等,批量查询信息。原有接口出入参不变,需要改动service。
3.2.3.3、分析
1)如果先分库分表,修改mapper,切换代理。比如:
还需修改Sharding-JDBC不支持的语法。
1 | select a,b,c from reming_bill where id_own_org in (xxx) //走分库分表逻辑 |
再对接es,需要新增mapper,原有的sql废弃,比如:
1 | select c from reming_bill where id in (xxx) //走分库分表逻辑 |
其中查询的a,b从搜索中获取,c从数据库获取。
2)如果先接es,新增mapper。比如:
1 | select c from reming_bill where id in (xxx) //走单表逻辑 |
再分库分表,切换代理,sql不必修改。
4、优化思路
根据第3步分析,短期内,先做分库分表可以提升各种查询的效率,且改动范围小,影响范围也小。
但是不能解决标题1的问题,目前服务提醒单每天增量数据5千,分表意义不是很大。
对接ES虽然耗时长,但是能解决现有问题(兔师傅查询耗时太长)。
对于代码改造,如果先分库分表再对接ES,mapper层需要改两次,如果先对接es,再分库分表,mapper层只需要改一次。
因为马上要在提醒的基础上做商机,所以表结构难免会修改,商机做完,预计提醒单数据也就达到300万,所以分库分表可以先不做。
对接搜索应该提前做,否则做商机的时候开发工作量太大,对接搜索时间太紧张。
所以提醒单优化计划分两步走:首先对接ES,之后分库分表。
如果先对接ES,写新的mapper时,需注意Sharding-JDBC的语法并不是支持所有普通JDBC的语法,要提前了解,规避。