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 海量数据操作处理的主要内容,如果未能解决你的问题,请参考以下文章

mysql处理海量数据时的一些优化查询速度方法

MySQL 处理海量数据时的一些优化查询速度方法

mysql处理海量数据时的一些优化查询速度方法

mysql处理海量数据时的一些优化查询速度方法

mysql处理海量数据时的一些优化查询速度方法

Mysql处理海量数据时的一些优化查询速度方法