Ken*_*dde 5 mysql innodb database-design optimization
我是一名学生,他的任务是设计传感器数据数据库。我的大学目前有一个大型数据库,里面充满了这些数据,但存储的很多内容都是不必要的。他们希望我从现有数据库中提取一些字段,并将其插入到一个新数据库中,该新数据库只包含“必需品”。我需要从旧行中提取每一行,并每天获取一次新数据。
将执行的查询通常是在给定时间跨度之间选择一组传感器的传感器读数。
我最初对于大量数据带来的复杂性还很天真,所以我严重低估了这项任务所需的时间。因此,加上我无法从家里访问服务器,我在这里寻求帮助和意见。
最初的设计如下所示:
CREATE TABLE IF NOT EXISTS SENSORS (
ID smallint UNSIGNED NOT NULL AUTO_INCREMENT,
NAME varchar(500) NOT NULL UNIQUE,
VALUEFACETS varchar(500) NOT NULL,
PRIMARY KEY (ID)
);
CREATE TABLE IF NOT EXISTS READINGS (
ID int UNSIGNED AUTO_INCREMENT,
TIMESTAMP int UNSIGNED INDEX NOT NULL,
VALUE float NOT NULL,
STATUS int NOT NULL,
SENSOR_ID smallint UNSIGNED NOT NULL,
PRIMARY KEY (ID),
FOREIGN KEY (SENSOR_ID) REFERENCES SENSORS(ID)
);
Run Code Online (Sandbox Code Playgroud)
设计问题
我的第一个问题是我是否应该为读数保留自动递增密钥,或者在 TIMESTAMP(UNIX 纪元)和 SENSOR_ID 上使用复合密钥是否会更有利?
这个问题既适用于我每天必须插入 210 万行的事实,也适用于我想要针对上述查询进行优化的事实。
初始批量插入:
经过大量的试验和错误并在网上找到指南后,我发现使用 load infile 插入最适合此目的。我编写了一个脚本,该脚本将从旧数据库中选择 500 000 行,并将它们(全部 2.5 亿行)写入 csv 文件,如下所示:
TIMESTAMP,SENSOR_ID,VALUE,STATUS
2604947572,1399,96.434564,1432543
Run Code Online (Sandbox Code Playgroud)
然后我的计划是使用 GNU 排序对其进行排序,并将其拆分为包含 100 万行的文件。
在插入这些文件之前,我将删除 TIMESTAMP 上的索引,并运行以下命令:
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET SESSION tx_isolation='READ-UNCOMMITED';
SET sql_log_bin = 0;
Run Code Online (Sandbox Code Playgroud)
插入后,我当然会恢复这些更改。
这个计划到底可行吗?
如果我根据 SENSOR_ID 和 TIMESTAMP 而不是 TIMESTAMP 和 SENSOR_ID 对 csv 进行排序,可以加快插入速度吗?
批量插入后重新打开索引后,每天可以插入200万行吗?
是否可以使用常规插入语句进行每日插入,或者我必须使用 load infile 才能跟上
输入负载?
我的cnf
除以下配置外,所有配置均为默认配置:
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_size=5GB
innodb_flush_method=O_DIRECT
innodb_doublewrite = 0
Run Code Online (Sandbox Code Playgroud)
为了这个特定目的我还需要任何其他优化吗?
服务器有 8GB RAM。mysqld 版本 8.0.22 Ubuntu 20.04
任何想法、想法或意见将不胜感激。
“传感器”数据集的一般建议:
规格:
STATUS int NOT NULL - 4字节?它可能有什么价值?(如果可行,请将其缩小。)PRIMARY KEY(sensor_id, timestamp)将是独特且充分的。然后彻底摆脱id。结果是减少了一个需要更新的二级索引。我建议对列按此顺序。但真正的选择需要基于SELECTs将要执行的操作。INSERT. 或者使用LOAD DATA INFILE(除非这需要更多的磁盘命中)。也就是说,你INSERT每分钟就会有一个。单个线程跟上应该没有问题。“持续”加载,我认为等到一天结束没有任何好处。(或者我错过了什么?)FOREIGN KEY; FK 检查需要额外的努力。autocommit开启;这样每个块都会被提交。否则,重做日志将变得巨大,占用额外的磁盘空间并减慢速度。(timestamp);意志AUTO_INCREMENT会照顾自己。如果您删除 auto_inc,然后按 排序PRIMARY KEY(sensor_id, timestamp)(如果您接受我的建议)。PRIMARY KEY加载数据时将其放置到位。否则,在构建 PK 时需要复制该表。ALTER TABLE .. ADD INDEX ..则在初始加载后。innodb_doublewrite下去——这可以防止罕见但灾难性的数据损坏。与您提供的链接相关的评论:
PARTITION BY RANGE(): http: //mysql.rjweb.org/doc.php/partitionmaint 注意:我建议的 PK 是按天分区的正确选择(或周或月)。DELETEingvia时DROP PARTITION。没有SELECTs可能跑得更快(或更慢)。mysqldump,只需使用默认值即可。这将产生易于管理的INSERTs大量行。TSV/CSV 不是必需的,除非您来自其他来源。ADD INDEX在加载数据后使用,可以推迟费用。捕获一个损坏的 cron 作业...
如果您有一个 cron 作业根据源中的时间戳捕获“昨天”的数据,则仍然存在 cron 作业失败或什至不运行的风险(当您的服务器在作业应该运行时关闭时) )。
使用我推荐的 PK(sensor, ts),以下方法相当有效:
SELECT sensor, MAX(ts) AS max_ts
FROM dest_table GROUP BY sensor;
Run Code Online (Sandbox Code Playgroud)
效率来自于在桌子上跳来跳去,只跳到 1500 个位置。
在 cron 作业开始时,运行此命令(在目标上)以“找出您停止的位置”:
SELECT MAX(max_ts) AS left_off FROM
( SELECT sensor, MAX(ts) AS max_ts
FROM dest_table GROUP BY sensor ) AS x;
Run Code Online (Sandbox Code Playgroud)
然后从源中获取新行
WHERE ts > left_off
AND ts < CURDATE()
Run Code Online (Sandbox Code Playgroud)
通常情况下,left_off会在昨天早上午夜之前不久。有打嗝的时候就会提前一天。
您还可以使用该子查询来查看是否有任何传感器离线以及何时离线。
| 归档时间: |
|
| 查看次数: |
5750 次 |
| 最近记录: |