php - 在 MySQL 集群中导入和报告较大的数据集
问题描述
我们有一个 MySQL (mariaDB/Galera) 集群,在一张表中包含数十亿个唯一数据点。我们需要将该表迁移到一个新的表,以整理出重复的条目,这需要很长时间,而且我们在这方面受到限制。下一步将是为给定的时间窗口和相应 NAS(现实世界中的路由器/位置)的 UUID 以及经常出现或切换 NAS 的用户的唯一 ID (MAC) 生成报告
MySQL (mariaDB/Galera) 数据库现在大小约为 25GB,这应该不是问题。但是查询用户的 UID/MAC 以及 UUID NASes/位置的报告需要很长时间。
表结构的布局如图所示。一个是实际的表,两个是可能的优化。但我真的不知道这是否会有所作为。
我们的数据库方法是正确的还是我们应该使用不同的方法(数据库、表结构、堆栈等等)(欢迎提出建议)
迁移查询(非常慢)如下:
INSERT INTO `metric_macs` m
(`uuid`,`shortname`,`mac`,`start`,`stop`,`duration`)
VALUES
SELECT uuid, shortname, mac, a, b, duration
FROM import i
ON DUPLICATE KEY update m.id = m.id
查询唯一用户:
SELECT DISTINCT mac FROM `metric_macs` WHERE uuid in ('xxxx','yyyyy') and ( start BETWEEN '2020-01-01' and '2020-02-01' or stop BETWEEN '2020-01-01' and '2020-02-01') ;
所有数据集的计数
查询经常性用户:
SELECT id FROM `metric_macs`
WHERE uuid in ('xxxx','yyyyy')
and ( start BETWEEN '2020-01-01' and '2020-02-01'
or stop BETWEEN '2020-01-01' and '2020-02-01')
GROUP BY `mac`, `uuid`
HAVING COUNT(*) > 1
所有数据集的计数
查询唯一位置切换用户:
SELECT uuid,mac FROM `metric_macs`
WHERE uuid in ('xxxx','yyyyy')
and ( start BETWEEN '2020-01-01' and '2020-02-01'
or stop BETWEEN '2020-01-01' and '2020-02-01')
GROUP BY `mac`, `uuid`
之后,php 用于计算具有两个以上不同 UUID 的所有用户。
该列表每 15 分钟更新一次,其中包含连接到 NAS 的 UID (MAC) 列表,该列表在过去 20 分钟内检查给定 UID(MAC) 的活动。如果有,我们更新最后一个条目的停止计数,增加 15 分钟并开始计算增益。
对不起,混乱。我们对这种报告生成方式相当陌生。有哪些可能的方法来优化数据库或查询以实现近乎即时的报告?
谢谢!
编辑:
CREATE TABLE `metric_macs` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`shortname` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`mac` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL,
`start` datetime NOT NULL,
`stop` datetime NOT NULL,
`duration` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `metric_macs_uuid_index` (`uuid`),
KEY `metric_macs_mac_index` (`mac`),
KEY `metric_macs_start_stop_index` (`start`,`stop`),
KEY `metric_macs_uuid_start_stop_index` (`uuid`,`start`,`stop`),
KEY `metric_macs_uuid_stop_index` (`uuid`,`stop`)
) ENGINE=InnoDB AUTO_INCREMENT=357850432 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
解决方案
摆脱 36 字节的 id 是件好事。但是,不要停留在 8 个字节;您可能不需要超过 4 个字节(INT UNSIGNED
最大或 40 亿)或 3 个(MEDIUMINT UNSIGNED
最大 16M)。
当您使用它时,删除破折号并取消 uuid 的十六进制,以便它们适合BINARY(16)
(16 个字节)。
我认为您在转换中有 3 个问题需要解决:
- 有效地将当前架构更改为更好的架构。假设这有旧的、不变的行,您可以在后台执行此操作。
- 快速完成最后一步。(实际上,我们将最后这样做。)
- 将摄取更改为新格式。
第 0 步:获取最新的时间戳,以便在花费时间执行第 1 步后知道在哪里执行第 2 步和第 3 步。
第 1 步:构建users
and stations
,它可能很简单
INSERT INTO users (user_id)
SELECT UUID2BIN(userID)
FROM ( SELECT DISTINCT userID ) FROM log;
(同样对于stations
)
看到这个转换uuid:http: //mysql.rjweb.org/doc.php/uuid
这可能需要一些时间,但它可以有效地进行重复数据删除。
在填写步骤 2 之前,让我讨论步骤 3。
第 3 步:如果摄取率“高”,请参阅此处了解有关 ping-ponging 暂存表和批量标准化等的详细信息:http: //mysql.rjweb.org/doc.php/staging_table
但是,您的摄取率可能不会那么快。不要将IODKU 与用于从和LAST_INSERT_ID
获取 id的技巧一起使用。它会“烧毁” id 并威胁要溢出您的 INT/MEDIUMINT 。相反,请参阅上面的链接。users
stations
id
插入time_table
,如果不超过每秒 100 个 (HDD) 或 1000 个每秒 (SSD),INSERT
在您获得必要的 ID 时可能很简单
INSERT INTO time_table (user_id, station_id, start_time, stop_time)
VALUES (
( SELECT id FROM users WHERE userID = uuid2bin('...') ),
( SELECT id FROM stations WHERE userID = uuid2bin('...') ),
'...', '...'
);
返回第 2 步。您在旧表中保存了一堆行。你保存了这些的开始日期。log
像我的链接中讨论的那样,就好像它是“临时表”一样进行批量标准化和批量插入。
当新表“不完整”时,这应该允许您以零停机时间和少量时间进行转换。
我没有解释为什么“报告需要很长时间”。我需要看看SELECTs
。同时,这里有两个想法:
如果您构建新的类似 INT 的 id,请按日期对它们进行排序,以便它们至少按时间顺序排序,因此对于某些类型的查询更好地聚集在一起。
通常,构建和维护“汇总表”可以使报表运行得更快。见http://mysql.rjweb.org/doc.php/summarytables
“查询经常性用户:”有多个查询性能问题。除非我的方法不够充分,否则我不想深入细节。
推荐阅读
- typescript - 'string | 类型的参数 RegExp' 不可分配给“字符串”类型的参数
- flutter - Flutter SliverAppBar 只有 TabBar
- java - Intellij IDEA 2020.3.3 中 Switch 的 Java 16 模式匹配问题(预期表达式)
- flutter - 颤振错误:未为“状态”类型定义“保存”方法
- javascript - 使用图像干预的多图像上传。我尝试上传多张图片但无法上传,因为我不知道如何,任何帮助将不胜感激
- node.js - 我在 Studio 3T 中构建了一个查询,它在 Studio 3T 中运行,但无法让它在 Node.js 项目中运行
- c++ - 在 C++ 中不使用 lambda 的函数组合
- c - 奇怪的 Malloc 错误(以及涉及此 shell 的其他问题)
- three.js - Three.js - Stencil 仅在某些对象上
- c# - 如何将属性中的值分配给 Linq 中的枚举