具有 550M 行和 128MB 内存的 mysql 表

标签 mysql innodb query-performance

如果有人能解释 MySQL 为何不在默认配置下使用大表,我将不胜感激。

note: I don't need advice how to increase the memory, improve the performance or migrate etc. I want to understand why it is working and performing well.

我有下表:

CREATE TABLE `daily_reads` (
  `a` varchar(32) NOT NULL DEFAULT '',
  `b` varchar(50) NOT NULL DEFAULT '',
  `c` varchar(20) NOT NULL DEFAULT '',
  `d` varchar(20) NOT NULL DEFAULT '',
  `e` varchar(20) NOT NULL DEFAULT '',
  `f` varchar(10) NOT NULL DEFAULT 'Wh',
  `g` datetime NOT NULL,
  `PERIOD_START` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `i` decimal(16,3) NOT NULL,
  `j` decimal(16,3) NOT NULL DEFAULT '0.000',
  `k` decimal(16,2) NOT NULL DEFAULT '0.00',
  `l` varchar(1) NOT NULL DEFAULT 'N',
  `m` varchar(1) NOT NULL DEFAULT 'N',
  PRIMARY KEY (`a`,`b`,`c`,`PERIOD_START`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

它在具有 1 个 CPU 内核、6GB RAM、CentOS 7 的虚拟机上运行(对该虚拟机的访问权限非常有限)。

它在具有 128MB 缓冲池的默认 MySQL 配置上运行 (SELECT @@innodb_buffer_pool_size/1024/1024)

数据库大小约为 96GB,“读取”表中有约 5.6 亿行,其他表中有约 7.1 亿行。

select database_name, table_name, index_name, stat_value*@@innodb_page_size
from mysql.innodb_index_stats where stat_name='size';

PRIMARY:83,213,500,416(没有其他索引)

我每月读取约 50 万次,写入仅作为 ETL 过程的一部分直接从 Informatica 到数据库(每月约 7500 万次写入)。

只通过存储过程调用读取查询:

CALL sp_get_meter_data('678912345678', '1234567765432', '2017-01-13 00:00:00', '2017-05-20 00:00:00');

// striped out the not important bits:
...
SET daily_from_date = DATE_FORMAT(FROM_DATE_TIME, '%Y-%m-%d 00:00:00');
SET daily_to_date = DATE_FORMAT(TO_DATE_TIME, '%Y-%m-%d 23:59:59');
...
SELECT
    *
FROM
    daily_reads
WHERE
    A = FRIST_NUMBER
    AND
    B = SECOND_NUMBER
    AND
    daily_from_date <= PERIOD_START
    AND
    daily_to_date >= PERIOD_START
ORDER BY
    PERIOD_START ASC;

我对 InnoDB 的理解非常有限,但我认为我需要将所有索引放入内存中以进行快速查询。读取过程仅需几毫秒。我认为在默认的 MySQL 配置上查询 500M+ 表的速度在技术上是不可能的...?

我错过了什么?

注意:我不需要如何增加内存、提高性能或迁移等方面的建议。我想了解它为何运行良好且性能良好。

最佳答案

长答案:您的主键是多个以 ab 开头的列的组合。

您的 WHERE 子句说明了这一点。

 WHERE a = FRIST_NUMBER
   AND b = SECOND_NUMBER
   AND etc etc.

这个 WHERE 子句确实非常有效地利用了与您的主键关联的索引。它随机访问索引到它需要的第一行,然后按顺序扫描它。因此,它实际上不必分页您的索引或表中的大部分内容来满足您的查询。

简短回答:当查询利用索引时,MySQL 快速且便宜。

如果您想要一个完美 用于此查询的索引,它将是 (a, b, daily_from_date) 上的复合索引。这将使用相等匹配来命中索引中的第一个匹配行,然后范围扫描您选择的日期范围的索引。但是你现在的表现已经很不错了。

您询问索引是否必须完全适合内存。不是。DBMS 软件的全部目的 是处理内存中不可能一次容纳的大量数据。好的 DBMS 实现可以很好地维护内存缓存,并在需要时从大容量存储中刷新这些缓存。 innodb 缓冲池就是这样一种缓存。请记住,对表的任何插入或更新最终都需要将表数据和索引数据写入大容量存储。

关于具有 550M 行和 128MB 内存的 mysql 表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45485999/

相关文章:

如果 SQL 条目是新的,PHP 电子邮件,如果更新则没有电子邮件

mysql - 将另一个数据库表中的整列更新到mysql中的当前数据库表

mysql - 我以某种方式设法在 MySQL 表中获得两个具有相同名称的索引,这是一个问题吗?

MySQL 使用具有新值的索引发生死锁

MySQL 执行 "No impact"临时 INSERT 并避免复制锁定

java - 从 MySql 检索并填充 .jsp 页面

mysql - 与 InnoDB 表的 PDO 事务

sql - SQL连接中约束放置的性能差异

elasticsearch - 非常慢的 elasticsearch 术语聚合。如何提高?

MySQL:char(4) 比smallint(5,无符号) 更快的性能差异