【问题标题】:Mysql Queries in big data table大数据表中的Mysql查询
【发布时间】:2018-06-27 08:15:05
【问题描述】:

我的 mysql 数据库表有问题。我在表中有超过 2000 万行。表结构如下所示。主要问题是查询需要很长时间才能执行(有些查询需要超过 20 秒)。我尽可能使用索引,但是许多查询使用日期范围,并且在日期范围内我的索引不起作用。同样在查询中,我几乎使用每一列。我需要更改我的数据表以提高效率吗?

`history` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `barcode` varchar(100) DEFAULT NULL,
  `bag` varchar(100) DEFAULT NULL,
  `action` int(10) unsigned DEFAULT NULL,
  `place` int(10) unsigned DEFAULT NULL,
  `price` decimal(10,2) DEFAULT NULL,
  `old_price` decimal(10,2) DEFAULT NULL,
  `user` int(11) DEFAULT NULL,
  `amount` int(10) DEFAULT NULL,
  `rotation` int(10) unsigned DEFAULT NULL,
  `discount` decimal(10,2) DEFAULT NULL,
  `discount_type` tinyint(2) unsigned DEFAULT NULL,
  `original` int(10) unsigned DEFAULT NULL,
  `was_in_shop` int(10) unsigned DEFAULT NULL,
  `cate` int(10) unsigned DEFAULT NULL COMMENT 'grupe',
  `sub_cate` int(10) unsigned DEFAULT NULL,
  `comment` varchar(255) DEFAULT NULL,
  `helper` varchar(255) DEFAULT NULL,
  `ywd` varchar(255) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `deleted_at` timestamp NULL DEFAULT NULL
)

PRIMARY KEY (`id`),
KEY `barcode` (`barcode`) USING BTREE,
KEY `action` (`action`) USING BTREE,
KEY `original` (`original`) USING BTREE,
KEY `created_at` (`created_at`) USING BTREE,
KEY `bag` (`bag`) USING BTREE

ENGINE=InnoDB

我的一些疑问:

select SUM(amount) as amount, 
       SUM(comment) as price, 
       cate 
  from `history` 
 where (    `action` = '4' 
        and `place` = '28' 
        and `created_at` >= '2018-04-01 00:00:00'
        and `created_at` <= '2018-04-30 23:59:59'
       ) 
   and `history`.`deleted_at` is null 
group by `cate`;

select cate, 
       SUM(amount) AS kiekis, 
       SUM(IF(discount>0,(price*amount)-discount,(price*amount))) AS suma, 
       SUM(IF(discount>0,IF(discount_type=1,(discount*price)/100,discount),0)) AS nuolaida 
  from `history` 
 where (    `history`.`action` = '4' 
        and `history`.`created_at` >= '2018-01-01 00:00:00'
        and `history`.`created_at` <= '2018-01-23 23:59:59'
       ) 
   and LENGTH(barcode) > 7
   and `history`.`deleted_at` is null 
 group by `cate`;

【问题讨论】:

  • 请。在帖子中也显示查询
  • 我们需要知道您遇到的问题类型,以便能够提供任何有意义的指导。我猜你从不在查询中使用id,这只是一个代理键?如果是这种情况,那么您可能会考虑将 id 改为唯一约束,并将主键更改为在提取数据时更有帮助的东西。主键定义了数据物理存储的顺序(聚集索引),所以如果您总是按条形码查询,那么将其用作主键可能更有意义?
  • 添加了我的一些查询以发布。我不能使用条形码作为我的主要,因为我有多行具有相同的条形码。
  • 向我们展示explain 计划,尝试其他索引、LENGTH(barcode) 上的虚拟列索引或 deleted_at 列上的索引,或者添加一个标志列 IsDeleted 作为布尔值而不是测试 null /not null 值并且有一个包含很多值的索引

标签: mysql sql mariadb innodb


【解决方案1】:

您的第一个查询最好写成:

select SUM(h.amount) as amount, 
       SUM(h.comment) as price, 
       h.cate 
from history h
where h.action = 4 and 
      h.place = 28 and
      h.created_at >= '2018-04-01' and
      h.created_at < '2018-05-01' and
      h.deleted_at is null 
group by h.cate;

为什么?

  • placeaction 是数字。比较应该是一个数字。混合类型可以防止使用索引。
  • 时间组件对于日期比较没有用处。
  • 限定所有列名只是一个好主意。

那么,对于这个查询,一个合理的索引是history(action, place, created_at, deleted_at)

所以,我将从多列索引开始。

如果您仍然遇到性能问题,则应考虑根据 created_at 日期对数据进行分区。

【讨论】:

    【解决方案2】:

    INDEX(a), INDEX(b) 用于某些用途,但“复合”INDEX(a,b) 更好地用于某些查询。

     where (    `action` = '4' 
            and `place` = '28' 
            and `created_at` >= '2018-04-01 00:00:00'
            and `created_at` <= '2018-04-30 23:59:59'
           ) 
       and `history`.`deleted_at` is null 
    

    需要

    INDEX(action, place, -- first, but in either order
          deleted_at,
          created_at)    -- last
    

    我更喜欢这样写日期范围:

            and `history`.`created_at` >= '2018-04-01'
            and `history`.`created_at`  < '2018-04-01' + INTERVAL 1 MONTH
    

    这比处理闰年、年末等要容易得多。它对于DATEDATETIMEDATETIME(6)TIMESTAMPTIMESTAMP(6) 都能“正确”工作。

    为此

     where (    `history`.`action` = '4' 
            and `history`.`created_at` >= '2018-01-01 00:00:00'
            and `history`.`created_at` <= '2018-01-23 23:59:59'
           ) 
       and LENGTH(barcode) > 7
       and `history`.`deleted_at` is null 
    

    我最有可能尝试这个:

    INDEX(action, deleted_at, created_at)  -- in this order
    

    有不同年份的单独表格。如果您要删除旧数据,请考虑使用PARTITION BY RANGE(TO_DAYS(...)) 以获得DROP PARTITION 的速度。 (但这是另一个讨论。)

    【讨论】:

      【解决方案3】:

      如果我遇到您的情况,我会考虑使用分页数据库名称。我的意思是有多个 history_X 表,其中 X 是与内容相关的 int。

      由于这是一个历史表,是否可以在名称中包含部分日期?

      你说你使用范围来搜索数据,所以如果你在表名中使用年份,你可以有

      • history_2014
      • history_2015
      • history_2016
      • history_2017
      • history_2018

      然后您可以使用适用于您的日期范围的表格进行搜索。

      如果您需要跨表的范围内的日期,那么您可以使用 UNION 查询将 2 个结果集合并为一个。

      【讨论】:

      • 我的历史表只包含一年的数据,我已经将旧数据分开到不同的表中
      • 这是一个非常糟糕的建议。将一个表拆分为多个不同的表是一个坏主意。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-11
      • 2012-07-17
      • 2012-01-13
      • 1970-01-01
      • 1970-01-01
      • 2017-03-15
      • 2021-05-07
      相关资源
      最近更新 更多