一个简单的 MySQL 插入/更新怎么会比外部 Web 请求慢?

Posted

技术标签:

【中文标题】一个简单的 MySQL 插入/更新怎么会比外部 Web 请求慢?【英文标题】:How can a simple MySQL insert/update be slower than an external web request? 【发布时间】:2016-03-01 16:32:09 【问题描述】:

由于一些性能问题,我一直在优化几个 SQL 查询并向某些表/列添加索引以加快速度。

php 中使用 microtime() 运行了一些时间测试(循环查询几百次并在每个循环中调用 RESET QUERY CACHE)。我对执行 3 件事的函数之一的结果感到有些困惑:

    sessions 表 (InnoDB) 中插入一行。 更新users 表 (InnoDB) 中的一行。 将会话 ID 发送到远程服务器,远程服务器将会话 ID 插入它自己的会话表 (MongoDB)。

步骤 1. 一般需要 30 - 40 ms,步骤 2. 20 - 30 ms,步骤 3. 7 - 20 ms。

我尝试查找 mysql 的一些预期查询时间,但没有发现任何有用的信息,所以我不知道会发生什么。话虽如此,这些查询时间似乎有点长,我肯定期望 Web 请求完成的速度比 MySQL 查询本地数据库的速度快。

知道这些查询时间与网络请求相比是否合理?

SQL/系统信息

两台服务器(远程服务器和带有 MySQL 数据库的服务器)都是在具有共享存储(多个 SSD RAID 目标)的同一物理服务器上运行的虚拟服务器。远程服务器分配了一个 CPU 和 2 GB RAM,MySQL 服务器分配了 8 个 CPU 和 32 GB RAM。两台服务器都在同一个局域网上。

sessions 插入查询:

 INSERT INTO sessions (
  session_id,
  user_id,
  application,
  machine_id,
  user_agent,
  ip,
  method,
  created,
  last_active,
  expires
)
VALUES (
  string, // session_id
  int, // user_id
  string, // application
  string, // machine_id
  string, // user_agent
  string, // ip
  string, // method
  CURRENT_TIMESTAMP, // created
  CURRENT_TIMESTAMP, // last_active
  NULL / FROM_UNIXTIME([PHP timestamp]) // expires
)

sessions 表(包含 ~500'000 行);

+-------------+---------------+------+-----+---------+----------------+
| Field       | Type          | Null | Key | Default | Extra          |
+-------------+---------------+------+-----+---------+----------------+
| sessions_id | int(11)       | NO   | PRI | NULL    | auto_increment |
| session_id  | char(32)      | NO   | UNI | NULL    |                |
| user_id     | int(11)       | NO   | MUL | NULL    |                |
| application | varchar(128)  | NO   |     | NULL    |                |
| machine_id  | varchar(36)   | NO   |     | NULL    |                |
| user_agent  | varchar(1024) | NO   |     | NULL    |                |
| ip          | varchar(15)   | NO   |     | NULL    |                |
| method      | varchar(20)   | NO   |     | NULL    |                |
| created     | datetime      | NO   |     | NULL    |                |
| last_active | datetime      | NO   |     | NULL    |                |
| expires     | datetime      | YES  | MUL | NULL    |                |
+-------------+---------------+------+-----+---------+----------------+

users 更新查询:

UPDATE users
SET last_active = string // For example '2016-01-01 00:00:00'
WHERE user_id = int

users 表(包含 ~200'000 行):

+------------------------+---------------------+------+-----+---------+----------------+
| Field                  | Type                | Null | Key | Default | Extra          |
+------------------------+---------------------+------+-----+---------+----------------+
| user_id                | int(11)             | NO   | PRI | NULL    | auto_increment |
| username               | varchar(64)         | NO   | MUL | NULL    |                |
| first_name             | varchar(256)        | NO   |     | NULL    |                |
| last_name              | varchar(256)        | NO   |     | NULL    |                |
| info                   | varchar(512)        | NO   |     | NULL    |                |
| address1               | varchar(512)        | NO   |     | NULL    |                |
| address2               | varchar(512)        | NO   |     | NULL    |                |
| city                   | varchar(256)        | NO   |     | NULL    |                |
| zip_code               | varchar(128)        | NO   |     | NULL    |                |
| state                  | varchar(256)        | NO   |     | NULL    |                |
| country                | varchar(128)        | NO   |     | NULL    |                |
| locale                 | varchar(5)          | NO   |     | NULL    |                |
| phone                  | varchar(128)        | NO   |     | NULL    |                |
| email                  | varchar(256)        | NO   | MUL | NULL    |                |
| password               | char(60)            | NO   | MUL | NULL    |                |
| permissions            | bigint(20) unsigned | NO   |     | 0       |                |
| created                | datetime            | YES  |     | NULL    |                |
| last_active            | datetime            | YES  |     | NULL    |                |
+------------------------+---------------------+------+-----+---------+----------------+

【问题讨论】:

远程服务器数据库有多大? 整个数据库大约 45 GB,但会话表大约有 600'000 行。我只是希望 Web 请求的套接字和 TCP 开销比将几百字节写入/更新到本地 MySQL 慢。 在这两种情况下,打开套接字、写入套接字和取回数据的开销是相同的(因为 MySQL 也作为服务运行)。唯一的区别是查询执行时间和数据的传输时间。如果传输时间非常短(很可能是现代连接),那么归结为哪种类型的数据库可以更快地完成。也许 Mongo 只是在这种事情上更快。 从个人测试来看,我可以说是的,Mongo 在插入查询中几乎总是胜过 MySQL。不过,更新查询在主键上写入带有 WHERE时间戳的 20-30 毫秒似乎仍然很慢。 【参考方案1】:

似乎问题只是我们的 MySQL 设置(它们都是默认设置)。

我在 users 更新查询上运行 MySQL 配置文件,发现步骤 query end 占用了执行查询的大部分时间。

谷歌搜索导致我找到https://***.com/a/12446986/736247 - 而不是直接使用所有建议的值(不推荐,因为其中一些会对数据完整性产生不利影响)我找到了更多信息,包括 Percona 上的这个页面: https://www.percona.com/blog/2013/09/20/innodb-performance-optimization-basics-updated/.

InnoDB 启动选项和系统变量也很有用:http://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html 也很有用。

我最终为以下设置设置了新值:

innodb_flush_log_at_trx_commit innodb_flush_method innodb_buffer_pool_size innodb_buffer_pool_instances innodb_log_file_size

这导致查询时间显着缩短(测量方式与我在问题中所做的相同):

    sessions 表中插入一行:~8 ms(从 30-40 ms 减少)。 更新users 表中的一行:~2.5 ms(从 20-30 ms 下调)。

【讨论】:

以上是关于一个简单的 MySQL 插入/更新怎么会比外部 Web 请求慢?的主要内容,如果未能解决你的问题,请参考以下文章

MySql批量插入时,如何不插入重复的数据

MySQL 数据表的插入更新删除数据

在一个查询 MYSQL 中插入和更新

mysql批量更新,数据存在则更新,不存在则插入

如果 COUNT = 0 [重复],如何在 mySQL 中插入或更新查询

MySql学习之插入删除和更新