我试图比较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个线程进行读取。
答案 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_size
和innodb_buffer_pool_size
的不正确设置会对性能造成破坏性影响。你把它们设置为什么,以及你有多少RAM?
<强> OFFSET 强>
LIMIT 1000 OFFSET 100000
非常不切实际。它说要一次超过100000行;然后获取1000.几乎没有“真实”代码使用OFFSET
。
此外,OFFSET
保证读取到表的末尾,然后没有任何行实际读取。再次,不切实际。
基准是产生假新闻的一种方式。