MySQL 海量数据操作处理
Posted
技术标签:
【中文标题】MySQL 海量数据操作处理【英文标题】:MySQL Massive Data Manipulation Processing 【发布时间】:2012-09-14 10:02:39 【问题描述】:我在处理数据库中的大数据时遇到以下问题:
基本上,来自数字传感器的所有计量每秒都存储在数据库中。 报告应该从所有数据中显示的只是发生的变化,例如在时间 X 寄存器 #1 的值从 0 更改为 1。
我创建了一个程序,它只能返回我需要的数据(更改),这为我节省了大量的 php 处理时间,但最大的问题是,对于 4 天的当前数据,查询需要6 * N 秒完成,其中 N 是所选寄存器的数量。
现在我想知道克服这个问题的最佳解决方案是什么。
另一个想法是在每次新插入数据计量时触发一次,但问题是这会更加复杂,因为我需要查看在另一个时间提交的先前计量。
所以我想创建可以在新数据以某种方式到达时自动更新的视图。这意味着当对报告发出请求时,数据将准备就绪并从视图中获取。
这会是一个好的解决方案吗?
【问题讨论】:
【参考方案1】:可以通过单个查询从现有数据中识别状态更改,但(正如您所发现的)非常昂贵。我会敦促您将每个状态更改存储在缓存中。
作为@Fluffeh explained,如果使用合适的索引,从现有表中查找最新状态不会很昂贵;所以触发方式应该是比较合理的。
因此:
定义一个合适的索引(如果它不存在的话):
ALTER TABLE existing_table ADD INDEX (register_id, timestamp);
为缓存创建一个表(并且可以选择设置用户权限,使其不能被您的应用程序直接修改):
CREATE TABLE status_changes VALUES (
register_id ...,
timestamp TIMESTAMP,
old_status ...,
new_status ...,
PRIMARY KEY (register_id, timestamp),
FOREIGN KEY (register_id, timestamp, old_status)
REFERENCES existing_table (register_id, timestamp, status),
FOREIGN KEY (register_id, timestamp, new_status)
REFERENCES existing_table (register_id, timestamp, status)
);
从有权修改新表的用户定义触发器:
DELIMITER ;;
CREATE TRIGGER record_change AFTER INSERT ON existing_table FOR EACH ROW
BEGIN
DECLARE _last_status ... ;
SELECT last.status
INTO _last_status
FROM existing_table AS last
WHERE last.register_id <=> NEW.register_id
AND last.timestamp < NEW.timestamp
ORDER BY last.timestamp DESC
LIMIT 1;
IF NOT NEW.status <=> _last_status THEN
INSERT INTO status_changes (
register_id,
timestamp,
old_status,
new_status
) VALUES (
NEW.register_id,
NEW.timestamp,
_last_status,
NEW.status
);
END IF;
END;;
DELIMITER ;
根据历史数据填充新表:
INSERT IGNORE INTO status_changes (
register_id,
timestamp,
old_status,
new_status
)
SELECT NEW.register_id,
NEW.timestamp,
(
SELECT last.status
FROM existing_table AS last
WHERE last.register_id <=> NEW.register_id
AND last.timestamp < NEW.timestamp
ORDER BY last.timestamp DESC
LIMIT 1
) AS _last_status,
NEW.status
FROM existing_table AS NEW
WHERE NOT NEW.status <=> (
SELECT last.status
FROM existing_table AS last
WHERE last.register_id <=> NEW.register_id
AND last.timestamp < NEW.timestamp
ORDER BY last.timestamp DESC
LIMIT 1
)
;
【讨论】:
对不起,最后一个查询需要什么?我目前正在努力在每次插入表后进行状态更改。这足以让我在短时间内获得每个寄存器的状态更改 @GeorgeNikolaides:我假设您有一个充满历史数据的数据库,您希望从中填充新表?这就是第 4 步所做的一切(作为一次性练习),然后触发器接管所有后续/新数据。 实际上,数据库每秒都充满了新的计量(传感器值 - 0 和 1),正如您建议的那样,我现在要做的是监控新表中 0 和 1 的变化.这将增加报告和趋势的检索时间。 @GeorgeNikolaides:如果您现在只对变化感兴趣,并且不想对历史变化进行任何分析,那么您可以省略第 4 步。【参考方案2】:我假设您的表已经很好地建立了索引,并且您的查询很好地使用了这些索引?
在这种情况下,您似乎可以从综合索引中获益最多 - 一个同时包含日期和注册的索引。每个索引都有帮助,但两者的综合索引会更有帮助。
添加复合索引的语法是:
alter table yourTableName add index yourIndexName(col1, col2);
mysql> select * from table1;
+---------+------+------+-------------+
| autonum | ID | name | metavalue |
+---------+------+------+-------------+
| 1 | 1 | Rose | Drinker |
| 2 | 1 | Rose | Nice Person |
| 3 | 1 | Rose | Runner |
| 4 | 2 | Gary | Player |
| 5 | 2 | Gary | Funny |
| 6 | 2 | Gary | NULL |
| 7 | 2 | Gary | Smelly |
+---------+------+------+-------------+
7 rows in set (0.01 sec)
mysql> alter table table1 add index autoNumID(autonum, ID);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
您可以研究的另一件事是制作一个更新的汇总表(每小时或每天等)。使用 CRON 或其他方式运行查询,该查询会将您的数据汇总到一个小得多的表格中,您的报告将用于该表格。
【讨论】:
不幸的是我没有使用任何索引,我认为这是最大的问题。这些值从二进制值(0 和 1)转换为单个十进制值。这意味着每秒在数据库中存储的唯一内容是十进制值。这意味着每次我获取数据时,我都必须执行以下操作:SUBSTRING(REVERSE(LPAD(BIN(DiValue
), 16, 0)) FROM alias_offset
FOR 1)
把下巴从地上捡起来哦,天哪,我的蝙蝠侠,我想我们有一个赢家。
没有内连接,如果你说的是这个
@GeorgeNikolaides 不,我是说如果您在这么多行上执行复杂的子串和其他计算(并且它们没有被索引),那么我们就遇到了您的问题。这就是为什么您的查询需要这么长时间。您的数据库正在执行如此多复杂的计算 - 在这种情况下,您真的需要制作汇总表。以上是关于MySQL 海量数据操作处理的主要内容,如果未能解决你的问题,请参考以下文章