我一直在寻找Web应用程序的瓶颈,发现INSERT查询的运行速度明显慢得多,如下所示:
MariaDB [myforum]> insert into tag set tagtext='abc12345',dateline=unix_timestamp(),canonicaltagid=1234;
Query OK, 1 row affected (0.24 sec)
这是通过在应用程序容器中安装mysql-client
进行测试来完成的。对于这样一个简单的查询,240ms似乎很长。我假设有一些DNS /网络问题。但是当直接在使用-h 127.0.0.1
建立连接的MariaDB容器中运行查询时,我会看到类似的结果:
MariaDB [myforum]> insert into tag set tagtext='abc123',dateline=unix_timestamp(),canonicaltagid=1234;
Query OK, 1 row affected (0.251 sec)
仅执行INSERT查询。 SELECT的速度非常快。奇怪的是,这似乎与MariaDB Docker安装有关:我在本地XAMPP安装上具有相同的数据库,其中相同的查询速度很快:
MariaDB [myforum]> insert into tag set tagtext='abc123',dateline=unix_timestamp(),canonicaltagid=123;
Query OK, 1 row affected (0.00 sec)
我只能对表本身进行有限的更改,因为这些更改来自vBulletin(旧的专有论坛CMS)。
我想知道为什么那些简单的查询是如此之慢?
slice_idle
and group_idle
to zero (default value was 8) CREATE TABLE `tag` (
`tagid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`tagtext` VARCHAR(100) NOT NULL DEFAULT '',
`dateline` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`canonicaltagid` INT(10) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`tagid`),
UNIQUE INDEX `tagtext` (`tagtext`),
INDEX `canonicaltagid` (`canonicaltagid`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=4112
;
version: '2'
volumes:
mysql-data:
services:
# Here is another service that access the db using dns name 'mariadb'
mariadb:
container_name: mariadb
image: mariadb:10.3
mem_limit: 3GB
restart: always
env_file:
- mariadb.env
volumes:
- ../dump.sql:/docker-entrypoint-initdb.d/dump.sql
- mysql-data:/var/lib/mysql
# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.3 LTS
Release: 18.04
Codename: bionic
# docker --version
Docker version 19.03.1, build 74b1e89
# docker-compose --version
docker-compose version 1.24.1, build 4667896b
该服务器的软件RAID 1具有两个企业硬盘,足够的CPU能力和内存(32GB)。而且它目前尚未用于任何其他应用程序,也没有用户的负担。因此,我可以排除其负载问题。
答案 0 :(得分:2)
经过一些研究,我发现了有关InnoDB刷新参数的信息。特别是innodb_flush_log_at_trx_commit
,它默认设置为1:
MariaDB [myforum]> show variables like '%innodb_flush%';
+--------------------------------+----------+
| Variable_name | Value |
+--------------------------------+----------+
| innodb_flush_log_at_timeout | 1 |
| innodb_flush_log_at_trx_commit | 1 |
| innodb_flush_method | O_DIRECT |
| innodb_flush_neighbors | 1 |
| innodb_flush_sync | ON |
| innodb_flushing_avg_loops | 30 |
+--------------------------------+----------+
值1表示每次提交都进行写入和刷新。因此,我认为这可能会增加开销。通过将posted here更改为2,可以解决这个问题。这将导致在每次提交后写入日志文件,但每秒仅将日志刷新一次到磁盘。
对我来说,这可以大大提高写入性能:MySQL cli显示0.000秒,而不是以前的300秒。受影响的Web应用程序的HTML呈现时间也从300-700ms减少到了90-120ms。
其后果:在失败的最坏情况下,一秒钟的交易可能会丢失。对于非常敏感/重要的数据(例如财务交易)和/或存在大量写入操作,这可能是不可接受的。我认为在像我这样的最常见网络案例中,这是一个适合的解决方案,我在全球范围内更改了该值:
set global innodb_flush_log_at_trx_commit = 2;
here的技术文档证实了我的发现:
innodb_flush_log_at_trx_commit
难道Innodb比MyISAM慢100倍?您可能忘记调整此值。默认值1表示每次更新事务提交(或事务外部的每个语句)都需要将日志刷新到磁盘上,这非常昂贵,尤其是在没有电池备份缓存的情况下。许多应用程序,尤其是那些从MyISAM表中移出的应用程序,都可以使用值为2的值,这意味着不刷新日志到磁盘,而仅刷新到操作系统缓存。日志仍然每秒刷新到磁盘,因此您通常不会丢失超过1-2秒的更新时间。值0会快一些,但安全性会降低一些,因为即使MySQL Server崩溃,您也可能丢失事务。值2仅在完全操作系统崩溃的情况下导致数据丢失。
但是,我仍然愿意接受其他/更好的解决方案。 This post列出了其他一些提示,但其中大多数都不适合我(服务器拥有足够的资源,专有软件等)。但是他们可能会帮助其他有类似问题的人。