求各位大佬给些报表优化的思路?

2023-01-02 09:45:59 +08:00
 sunmoon1983

现在客户要求这么一张报表 横向为年龄 竖向为项目名称 有各种条件每次客户进入页面都要点击一下开始分析,报表生成起来很慢,现在要求优化,加上了缓存,但是第一次生成报表的时候还是需要等待,想用计划任务跑一下,但是想到了各种搜索条件组合起来有很多情况,没有实施,求优化思路!

当前表结构

CREATE TABLE `xx_table1` (
  `id` bigint NOT NULL,
  `code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '编号',
  `full_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '姓名',
  `gender` tinyint NOT NULL DEFAULT '0' COMMENT '性别',
  `age` smallint DEFAULT '0' COMMENT '年龄',
  `idcard` varchar(33) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '身份证号',
  `resident_address` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '常驻地址',
  `resident_address_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '常驻地址行政编码',
  `registered_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '户籍地址',
  `registered_address_code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '户籍地址行政编码',
  `birthday` date DEFAULT '0000-00-00' COMMENT '生日',
  `dead_date` datetime DEFAULT NULL COMMENT '死亡日期',
  `dead_place_code` bigint NOT NULL DEFAULT '0' COMMENT '死亡地点',
  `family_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '家属姓名',
  `family_tel` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '家属联系电话',
  `created_at` datetime DEFAULT NULL COMMENT '创建时间',
  `created_by` bigint NOT NULL COMMENT '创建人',
  `is_audit` tinyint NOT NULL DEFAULT '1' COMMENT '是否审核',
  `audit_by` bigint DEFAULT NULL COMMENT '审核人',
  `audit_at` datetime DEFAULT NULL COMMENT '审核时间',
  `year` int NOT NULL DEFAULT '0' COMMENT '年份',
  `item_id` int NOT NULL DEFAULT '0' COMMENT '项目 id',
  `area_code` bigint DEFAULT '0' COMMENT '地区代码',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `p_fi_idx` (`full_name`,`idcard`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='XX 表';

索引还没加

数据量大于 100W!

想到了把数据跑成单表,但是没做过,不知道要怎么写,不知道单表要怎么建,求大老指点

4821 次点击
所在节点    MySQL
44 条回复
Chad0000
2023-01-02 09:53:53 +08:00
如果是我的话,我就让客户提前建立报表(即定义每个报表的条件),然后这个数据改变后,定时后台重新统计写入报表。这样客户再进来读的是已经统计好的表,虽然有一点延迟,但考虑到这个量级的数据,有延迟并不影响。
Chad0000
2023-01-02 09:58:44 +08:00
但考虑到非必要勿增实体,能加资源解决的事情就不要搞得太麻烦。先加索引试试呗。
agui2200
2023-01-02 10:14:05 +08:00
上 clickhouse
sunmoon1983
2023-01-02 10:44:04 +08:00
@Chad0000 现在的想法就是,先把各个条件组合一下,然后用定时任务去跑
sunmoon1983
2023-01-02 10:44:20 +08:00
@agui2200 没有接触过,不太敢上呀
BugCry
2023-01-02 10:50:22 +08:00
每个指标单独成表,最后写个 view 拼到一起
c6h6benzene
2023-01-02 11:00:09 +08:00
一般的报表工具的话,可能都是先把底层的数据全部缓存之后( dataset )然后再以这个为基础去做筛选之类。如果筛选条件不固定的话,大概只能缓存整个表?
sunmoon1983
2023-01-02 11:19:56 +08:00
@c6h6benzene 现在的方案就是先把报表生成出来,然后再把整个报表缓存,但是生成的过程有点慢,客户无法忍受
Chad0000
2023-01-02 11:27:23 +08:00
@sunmoon1983 #4
我之前优化的一种方案是将允许统计的条件提前算出来,形成中间表。比如:
主键:[性别]_[出生年月]_[省市]_[死亡年月],再加一个数量字段。这样所有查询都可以在这个中间表进行,你再 Sum 一下。但这样做还是改逻辑,所以不如本地对比一下加索引后的影响,如果现有数据库资源可承受,就没必要改代码了。
yasea
2023-01-02 11:27:44 +08:00
这种就只有用空间换时间,每天、每小时跑批, 根据所有需要使用的的维度(过滤条件)分组将相应指标定时写入报表即可。如果数据量大, 可以再做索引、分区存储、分表、缓存等优化。
liprais
2023-01-02 11:31:39 +08:00
除了 mysql 一百万数据都不是啥问题
colvv
2023-01-02 11:54:56 +08:00
换 clickhouse
sunmoon1983
2023-01-02 13:19:01 +08:00
@colvv 没有接触过,不太敢换呀
sunmoon1983
2023-01-02 13:23:34 +08:00
@yasea 嗯,现在想的就是要空间换时间,但是我一直没想明白,要怎么跑数据,是要把我每种条件都组合一次,跑一次吗?比如,上面图中的条件有户籍,性别,年份,那个,我就要`户籍+男+2022`跑一次,下面再用`户籍+女+2022`跑一次,然后`户籍+性别(男、女)+每一年(从今年到 2010 年)`都跑一次存到库中,是这样吗?后面还要加上地区,好多条件呀,不敢想象
wxf666
2023-01-02 13:34:24 +08:00
@sunmoon1983 你查询的 SQL 是怎么写的呢?
agui2200
2023-01-02 13:38:43 +08:00
@sunmoon1983 ck 是最佳选择,多条件,而且支持亿级查询,而且轻量运维小,统计上比 mysql 强多了,你可以 写个 定时任务把所有表拍平扔 ck ,如果担心用不好,就做好回退,当 ck 不可用的时候用 mysql 强查就是了
jones2000
2023-01-02 13:46:32 +08:00
数据放入内存,自己统计计算不就可以。 才几百万的数据, 都是数值计算, 也不涉及到字符串检索。
wxf666
2023-01-02 13:50:44 +08:00
@yasea @agui2200 能不能抽出可能用到的字段,建个小小的覆盖索引呢?

比如,(行政编码, 性别, 年份, 年龄, 项目 ID ),看起来一行只需 5+10+1+2+1+4+8 = 31 字节,100W 行索引只需 30 MB

如果不指定『当前地区』,扫全部索引也能满足各种查询需求。
如果指定了『当前地区』,可能只需扫几百上千行,几百 KB 的索引即可统计出来。
wxf666
2023-01-02 13:52:51 +08:00
@jones2000 我也感觉是,就算一行需要 1KB ,全部放进内存也才不到 1GB ,for 一百万次实际就连 Python 都不会很慢

如果只挑可能用到的字段出来,一百万行可能也只用到几十 MB 内存即可
14104chk
2023-01-02 14:08:47 +08:00
@sunmoon1983 可以只用一个 select * from table ,然后用代码生成粒度比较细的统计数据存在缓存或者内存表里面,例如分成户籍、性别、年份。如果只查 2010 年的数据,没有户籍性别限制,就把 2010 年的各种户籍或者性别的数据加起来

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/906010

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX