MyISAM读取比innodb慢得多

时间:2017-08-07 08:15:27

标签: mysql innodb myisam

我试图比较myisam和innodb写/读性能,但我很惊讶myisam的读取比innodb慢得多,而它的写入速度要快得多,这与我学到的相比完全相反。

mysql版本是5.7.18-0ubuntu0.16.04.1。

这是我的两张桌子:

mysql> show create table inno_1;
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                                                                                                                                                                                                      |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| inno_1 | CREATE TABLE `inno_1` (
  `id` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+



mysql> show create table isam_1;
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                                                                                                                                                                                                      |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| isam_1 | CREATE TABLE `isam_1` (
  `id` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

我试图向两个表写两次50000行

[2017-08-07 15:57:12]  [0.86ms]  INSERT INTO `inno_1` (`id`,`name`,`created_at`,`updated_at`) VALUES ('11e77b46-0576-5c30-8e53-1c1b0d1700f9','xxx','2017-08-07 15:57:12','2017-08-07 15:57:12')  
[1 rows affected or returned ] 
insert time : 1m23.905679587s



[2017-08-07 15:55:49]  [0.11ms]  INSERT INTO `isam_1` (`id`,`name`,`created_at`,`updated_at`) VALUES ('11e77b45-d416-79b0-8804-1c1b0d1700f9','xxx','2017-08-07 15:55:49','2017-08-07 15:55:49')  
[1 rows affected or returned ] 
insert time : 5.500709602s

而读:

[2017-08-07 15:57:17]  [20.95ms]  SELECT * FROM `inno_1`   ORDER BY id asc LIMIT 1000 OFFSET 100000  
[0 rows affected or returned ] 
select time : 2.076151355s


[2017-08-07 15:56:24]  [353.06ms]  SELECT * FROM `isam_1`   ORDER BY id asc LIMIT 1000 OFFSET 100000  
[0 rows affected or returned ] 
select time : 32.030940358s

我不明白为什么它与官方建议如此不同,如果我将id改为int,myisam的读取会得到很大改善。

mysql> show create table inno;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                     |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| inno  | CREATE TABLE `inno` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=50001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> show create table isam;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                     |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| isam  | CREATE TABLE `isam` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=50001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

的myisam

[2017-08-07 16:08:37]  [0.09ms]  INSERT INTO `isam` (`name`,`created_at`,`updated_at`) VALUES ('xxx','2017-08-07 16:08:37','2017-08-07 16:08:37')  
[1 rows affected or returned ] 
insert time : 4.745437221s


[2017-08-07 16:08:41]  [12.55ms]  SELECT * FROM `isam`   ORDER BY id asc LIMIT 1000 OFFSET 50000  
[0 rows affected or returned ] 
select time : 1.105638295s

innodb的:

[2017-08-07 16:09:26]  [0.87ms]  INSERT INTO `inno` (`name`,`created_at`,`updated_at`) VALUES ('xxx','2017-08-07 16:09:26','2017-08-07 16:09:26')  
[1 rows affected or returned ] 
insert time : 1m28.577975501s


[2017-08-07 16:09:30]  [9.58ms]  SELECT * FROM `inno`   ORDER BY id asc LIMIT 1000 OFFSET 50000  
[0 rows affected or returned ] 
select time : 739.580336ms

任何人都可以解释一下吗?

更新

我首先使用10个线程进行写入,然后使用1个线程进行读取。

1 个答案:

答案 0 :(得分:0)

黑天鹅确实存在;你还没有看到任何人。

<强>写入

  • 交易:MyISAM没有这样的概念。默认情况下,InnoDB会立即“自动提交”您的每个INSERTs。这基本上需要额外的磁盘命中;这就是使InnoDB变慢的原因。另一方面,如果您在单个事务中完成了所有插入操作(这对于您的伪造测试来说是合乎逻辑的),它将运行得更快,可能与InnoDB一样快。

  • innodb_flush_log_at_trx_commit可以部分控制上述开销 - 存在潜在的安全风险。

  • 批处理:包含100行的单个INSERT语句的运行速度约为100行INSERTs的10倍。

  • 两个引擎都有一些优化插入表的“结尾”。请记住,这只是一种写入模式,因此在此处得出结论是有风险的。

  • 您的写入时间相差约10:1。当您需要阅读而不是缓存块时,这与旋转HDD一致。使用SSD(Flash)会显着缩小该比率。

<强>读取

  • MyISAM索引(包括PRIMARY KEY)位于单独的BTree中。它们通过字节偏移(或记录号)“指向”数据。这种间接性导致您的特定读取运行速度变慢。其他基准可能运行得更快。

  • InnoDB使用数据“聚集”PK,因此可以避免额外的查找。

<强>编号

  • 如果id单调递增,数据将被写入'end'。 (MyISAM的数据文件结尾(.MYD)和PK BTree结尾(.MYI); InnoDB的数据结束+ PK BTree。)

  • 如果id跳过,就像你显然正在使用UUID一样,那么其他事情正在发生。 MyISAM的数据被附加到,但PK的BTree是随机更新的。 InnoDB只需要随机更新数据+ PK BTree。

  • UUID很糟糕,缓存被烧毁了。一旦UUID(PK和/或数据)的BTree大于可缓存的BTree,性能就会下降。最终,缓存是无用的,每个INSERT都是读取 - 修改 - 写入磁盘命中以更新PK。这会影响两个引擎,但方式略有不同。由于你的基准测试只涉及几兆字节,你可能没有达到这个目的。

<强>缓存

  • MyISAM使用key_buffer缓存1KB索引块。它允许操作系统缓存数据块。

  • InnoDB使用buffer_pool为数据和索引块缓存16KB块。

  • 由于操作系统可能在磁盘上使用4KB作为分配单元,因此一个引擎占用了磁盘块的一小部分;另一个是一次抓取多个磁盘块。根据块访问的随机性等,这种差异转化为稍快一些的更快的基准测试。

  • key_buffer_sizeinnodb_buffer_pool_size的不正确设置会对性能造成破坏性影响。你把它们设置为什么,以及你有多少RAM?

<强> OFFSET

  • LIMIT 1000 OFFSET 100000非常不切实际。它说要一次超过100000行;然后获取1000.几乎没有“真实”代码使用OFFSET

  • 此外,OFFSET保证读取到表的末尾,然后没有任何行实际读取。再次,不切实际。

基准是产生假新闻的一种方式。