目录
现状分析:
分表方案:
Mycat1.5.1到1.6.7升级方案:
分表规则:
以task_id/task_item_id分表:
以日期分表(实际采用的方案):
csc_task_item表日期分表操作示例
旧数据迁移
mysqldump方式
load data方式(本地导入时不成功)
分表前后性能对比
结论
目前平台是基于mycati1.5做的一个垂直分库方案(各个模块有自己的库),mycat的配置如下图所示:
目前mycati1.5配置片段
如果平台目前没有过大并发访问压力,单节点可以支撑,只需要做好数据备份,如上图中的单节点。
以后的扩展,可以加一个读节点,实现读写分离。
巡检任务涉及到的表有:
csc_task (巡检任务),csc_task_station(巡检任务站点), csc_task_item(巡检任务测项)
csc_task (巡检任务)与csc_task_station为主子表,一对多。
csc_task_station(巡检任务站点)与csc_task_item(巡检任务测项) 为主子表,一对多。
三个表为主子孙表关系,都在csc库里。
以格尔木的数据为例:
每天任务数据大概是500条。
每个任务下的站点平均为4个,那么每天的站点数据是2000条。
每个站点下的测项数据是5个,那么每天的站点数据是10000条。
一年数据预估:
一天任务数据*400=20万
一天站点数据*400=80万
一天测项数据*400=400万
格尔木线上数据截图:
线上实际数据显示,自2018年8月24日起,到现在差不多一年时间,任务数据为20万,站点数据为120万,测项数据为1000万。
上边预估偏小,应调整为:
每天任务数据大概是500条。
每个任务下的站点平均为6个,那么每天的站点数据是3000条。
每个站点下的测项数据是10个,那么每天的站点数据是30000条。
一年实际数据:
(一天任务数据)*400=20万
(一天站点数据)*400=120万
(一天测项数据)*400=1200万
目前csc_task_item表1200万的数据表空间是8G左右,10年后数据为1.2亿,80G。
结论:任务表和站点表暂不需要分表,测项表需要分表。
以上数据取自格尔木项目,对格尔木一个项目来讲,分表即可。
对于平台来讲,情况较为复杂,比如10个租户时,数据应乘以10;那么一年任务数据为200万,一年站点数据为1200万,一年测项数据为1.2亿,10年为12亿,表空间(对于mysql来讲是.idb文件)10年为800G。
平台应在垂直分库基础上根据租户id的做水平分库,然后对数据了过于大的表,比如巡检任务测项表再做水平拆分。
对任务测项表进行水平拆分
由于原来已经做了垂直分库,所以对格尔木项,10年后巡检任务测项数据为1.2亿,80G,暂不需要再分库(硬盘容量需要扩容时,第一步可以增加服务器硬盘,目前所有的库都放在一个节点上,以后可以每个库放一个节点,这样对数据容量和并发访问性能都可以明显提升),因此在巡检库hap_csc_gem中对任务测项表csc_task_item进行水平拆分即可。
要实现巡检任务表的拆分(水平拆分),需要对mycat升级,目前用的mycat为1.5.1。Mycat的单库分表1.6版本开始支持,且1.6版本有个bug,需要替换lib下一个jar文件。
参考http://songwie.com/articlelist/202。
本地测试1.6.7版本已修复这个bug,也可以升级mycat到1.6.7,下载地址:
http://dl.mycat.io/1.6.7.1/。
Mycat官方说明截图
暂无
任务表的task_id为其主键,站点表和测项表也都有task_id,三个表的task_id为整数类型int,task_id%n,可以作为分表方案之一,n为分表数量。
以支撑10年为例,测项表单表数据:
n=50时,1.2亿/50=240万
n=100时,1.2亿/100=120万
n=200时,1.2亿/200=60万
Mycat配置文件schema.xml中:
该分表规则的限制:n需要确定,这就对数据总量有一个上限的要求,否则在扩容时,n个表的数据都需要迁移;
结论:对于格尔木如果支撑10年,以巡检测项表单表数据120万左右为标准,需要分表数最小为100。10年后,如果要继续支撑,需要增加分表数,增加时就需要迁移数据。该方案可行性一般。
任务测项表主键的task_item_id,也可以作为分表方案之一,同上。
分片字段的选择,check_time字段有null值,而created_dt没有,应该选择后者。
以上为分表规则配置文件rule.xml中的片段
以上为mycat配置文件schema.xml中分表规则的配置,需要注意的是,已配置的表必须事先创建好,目前创建了自2018年1月起到2020年12年的表,一共36张表,可以支撑业务到2020年结束。
说明:
目前格尔木一年的任务测项表数据为1200万,以月为单位,1200万数据分成12个表,单表数量降为100万。
需要用到的表,在mycat中配置即可,比如可以暂时配置3年的表(2018-2020),
2020年之后的表,可以动态添加:
该分表规则的限制:数据增长频率需要稳定,否则当变化时(1个月1张表变为10天一张表),也需要迁移数据;
结论:对分表数不固定但一定时间内数据量稳定可以预估的的业务比较适合,不需要迁移数据,比较适合目前场景,该方案可行性较好。
先在数据库建好需要的表:
连上mycat,用explain查看相应sql的数据来自哪些表,以确定无误:
explain select * from csc_task_item;
在mycat配置文件中增加2021年需要的表:
在在数据库中创建相应的表后,用命令reload @@config;让mycat加载最新配置(注意是连接上9066管理端口,上边的命令都是8066服务端口):
explain查看相应sql的数据来自哪些表,以确定无误:
explain select * from csc_task_item;
SELECT check_time,
check_value FROM
csc_task_item
WHERE tenantid =?
AND item_id=?
AND check_time IS NOT NULL
AND date_format(check_time, '%Y-%m-%d') >= ?
AND date_format(check_time, '%Y-%m-%d') <= ? ORDER BY check_time;
导出:
mysqldump -u root -p -t -c -–skip-add-locks hap_csc_gem csc_task_item > d:\\source.sql
-t忽略表结构,-c导出的数据带上列名——这个是必须的,否则mycat会报错,因为导入mycat中需要有分表字段。
导入:
mysql -uroot -p -h172.17.xxx.xxx -P8066
切换到指定的数据库:
use databaseName;
导入脚本:
source d:\\source.sql;
以下是参考mycat权威指南写的导出导入sql
导出:
select * from csc_task_item into outfile 'd:\\csc_task_item.txt' FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '' ESCAPED BY '\\' LINES TERMINATED BY '\n';
导入:
load data local infile 'd:\\csc_task_item.txt ' INTO TABLE csc_task_item FIELDS TERMINATED BY '\t' OPTIONALLY ENCLOSED BY '' ESCAPED BY '\\' LINES TERMINATED BY '\n' (task_item_id, task_station_id, task_id, task_code, task_name, line_id, line_name, station_id, station_name, item_id, item_name, item_type, item_remark, check_obj_type, eq_id, eq_name, unit_id, unit_symbol, item_index_name, org_name, org_id, check_value, standard_value, upper_limit_value, lower_limit_value, ulti_up_limit_value, ulti_low_limit_value, seq, check_result, check_last_value, last_task_id, check_result_last, remark, check_per_id, check_per_name, check_time, upload_time, upload_per_id, upload_per_name, upload_phone, check_gps_x, check_gps_y, created_dt, created_by, updated_by, updated_dt, created_by_name, updated_by_name, tenantid, ver, eq_is_not_work, eq_status, task_item_img, lj_remark, remark_user, remark_time, remark_username);
以下所有测试均为在本地测试(win10,8G,4核,8逻辑处理器)。
测试sql语句为常用sql,以及平台涉及到任务测项表功能点sql(报表统计)。
select count(*)
如下图所示,执行sql为select count(*) from csc_task_item;分表前执行时间2.18秒,分表后1.54秒
order by limit
select * from csc_task_item order by created_dt limit 2\G;
分表前为10.03秒,分表后为7.15秒。
都比较慢,如果有排序字段,最好建立索引,索引提升查询性能是最明显的。
根据task_id查询
Select * from csc_task_item where task_id = 2000000043148\G;
分表前为24.09秒,分表后为9.35秒
SELECT
check_time,
check_value
FROM
csc_task_item
WHERE
tenantid =1
AND item_id=2000000023511
AND check_time IS NOT NULL
AND date_format(check_time, '%Y-%m-%d') >= '2019-01-01'
AND date_format(check_time, '%Y-%m-%d') <= '2020-01-01'
ORDER BY
check_time;
分表前为41.47秒,分表后为0.21秒。
(两个查询执行的情况均在未为加索引csc_task_item (item_id,check_time);下执行)
加完索引后
SELECT task_name,station_name,item_name,check_result,check_value,remark,item_type, eq_is_not_work ,unit_symbol
FROM csc_task_item
WHERE tenantid=1 AND task_id=2000000023622 AND check_result='EXCEPTION'\G;
数据集为空,用时均为0.01秒
SELECT task_name,station_name,item_name,check_result,check_value,item_type,eq_is_not_work,unit_symbol
FROM csc_task_item
WHERE tenantid=1 AND task_id= 2000000043148\G;
时间一样,都执行的这么快,因为有索引
KEY `I_TENANTID_TASKID_TASK_STATION_ID` (`tenantid`,`task_id`,`task_station_id`)
SELECT
item.task_id,
item.task_name,
count(item.check_result) items
FROM
csc_task_item item
WHERE
item.tenantid =1
and item.task_id = 2000000148172
GROUP BY
item.task_id,
item.task_name\G;
SELECT
item.station_id,
item.station_name,
item.item_id,
item.item_name
FROM
csc_task_item item
WHERE
item.tenantid =1
and item.task_id = 2000000107904
GROUP BY
item.station_id,
item.station_name,
item.item_id,
item.item_name
ORDER BY
item.station_id \G;
分表前后几乎一样
根据本地测试的结果,分表后性能有一定提升。
查询不走索引时,性能提升比较明显。走索引时,分表前后没太大区别。
如果要充分利用分表后的优势,sql语句中最好加分表字段created_dt,这样可以路由到具体的表,而不是由mycat查所有表然后汇总。
巡检任务测项表最好添加索引。
CREATE INDEX I_ITEMID_CHECKTIME ON csc_task_item (item_id,check_time);