如何加快MYSQL更新?

时间:2015-10-05 11:57:11

标签: mysql sql optimization innodb

我有一个last_seen_at表,其中包含日期时间字段update users set last_seen_at = '2015-10-05 12:34:45' where id = 1182;。更新此字段大约需要120毫秒,我希望它能在我网站上的每个页面加载上快得多。我无法弄清楚为什么它如此缓慢:大约有55,000条记录不应该是大问题(我已经想到了)。

这是表信息:

mysql> show table status like 'users';
+-------+--------+---------+------------+-------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+
| Name  | Engine | Version | Row_format | Rows  | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time         | Update_time | Check_time | Collation       | Checksum | Create_options | Comment |
+-------+--------+---------+------------+-------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+
| users | InnoDB |      10 | Compact    | 55609 |            954 |    53051392 |               0 |     43352064 |  26214400 |          67183 | 2015-09-22 13:12:13 | NULL        | NULL       | utf8_general_ci |     NULL |                |         |
+-------+--------+---------+------------+-------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-----------------+----------+----------------+---------+

mysql> desc users;
+---------------------------------+--------------+------+-----+-----------------+----------------+
| Field                           | Type         | Null | Key | Default         | Extra          |
+---------------------------------+--------------+------+-----+-----------------+----------------+
| id                              | int(11)      | NO   | PRI | NULL            | auto_increment |
| last_seen_at                    | datetime     | YES  | MUL | NULL            |                |
+---------------------------------+--------------+------+-----+-----------------+----------------+

mysql> show indexes from users;
+-------+------------+------------------------------------------------+--------------+---------------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name                                       | Seq_in_index | Column_name                     | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------------------------------------------+--------------+---------------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| users |          0 | PRIMARY                                        |            1 | id                              | A         |       57609 |     NULL | NULL   |      | BTREE      |         |               |
| users |          1 | index_users_on_last_seen_at                    |            1 | last_seen_at                    | A         |       57609 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+------------------------------------------------+--------------+---------------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

正如您所看到的,我已经在last_seen_at列上获得了索引。为了清楚起见,我已经省略了所有其他列(除了id)。

当我更新last_seen_at时,我这样做:

Server version: 5.5.44-0ubuntu0.12.04.1 (Ubuntu)

MySQL服务器信息: {{1}}

我有什么办法可以加快更新速度吗?

编辑 - 我之前说过查询耗时700毫秒。它实际上更像120毫秒,对不起,我正在查看错误的查询。这仍然感觉有点太长了。毕竟这实际上是一个合理的写作时间吗?

编辑 - 我的所有时间都来自在mysql shell客户端中手动输入sql查询。我在Ruby on Rails网络应用程序中使用MySQL,但是这个应用程序不涉及这个问题的目的:我纯粹关注数据库级别。

5 个答案:

答案 0 :(得分:3)

好吧,您似乎以最有效的方式执行更新 - 即使用表上的主键,因此在那里可以做的事情并不多。 假设更新的120ms纯粹是数据库服务器所花费的时间(而不是网页中的往返),我只能想到一些可能有用的东西:

  • 您已将要更新的列编入索引 - 这通常会为更新添加一点时间,因为必须维护索引。我看到你需要使用那个列,所以你无法摆脱索引;但如果可以的话,你可能会看到更好的表现。

  • 批量更新有时是避免实时性能影响的好方法,但仍可实现您的目标。
    您可以让Web触发的插入进入带有时间戳字段的保留表,然后(离线)批量更新实际数据。有关示例批量更新语句,请参阅https://dba.stackexchange.com/questions/28282/whats-the-most-efficient-way-to-batch-update-queries-in-mysql

  • 数据库优化可能会有所帮助,但前提是数据库已经处于良好状态 - 例如内存分配,表空间碎片,缓冲池等等。

祝你好运!

答案 1 :(得分:2)

你无能为力。您的列上已经有索引,只需要一些时间就可以使用索引查找行并更新它。

索引可能会碎片化,这会降低查找速度。您可以使用analyze重建索引。

一个选项可能是延迟update或阻止它通过在您正在使用的编程环境中使用某些异步/后台任务来阻止页面构建(也就是“即发即忘”)。

答案 2 :(得分:2)

编写用户事件(id,now()等效于日志文件)。从另一个进程(如Create Event)处理日志文件,或者完全用另一种编程语言(如Java)处理日志文件。让我们称之为工作进程(nextLine())。

因此,用户在活动发生的环境中运行,但不会忍受更新调用的阻止开销,从而降低他/她的用户体验(用户体验)。阻止意味着他们等待。相反,活动记录得更快,例如fwrite(特定于语言)到日志文件。

日志文件(Open for Append)概念可以部署到一个专用目录,该目录要么包含1个文件中的所有用户活动,要么每个用户有1个文件。在后一种情况下,wp有一个简单的任务,只需记录单个更新语句的最后一行。例如,如果有11行,则有1个更新调用,而不是11个。

wp在后​​台,cron作业,创建活动,任何事情中运行。它会根据需要更新。拥有55k用户,这个系统相对较小。无论如何,每隔10分钟,每10秒钟就会发射一次。

至于要考虑的mysql wp存根:

Create Event

或其他一些CREATE EVENT userUpdateActivity ON SCHEDULE EVERY 10 SECOND DO (something) 策略。

wp处理并删除open for append日志文件。可以设想定期(每日?)日志文件的锁定和删除策略。

单个日志文件的问题是wp必须:

  • 阅读所有行并手动更新每一行
  • 或者,读取所有行并获取给定用户的最后一行并仅更新该行

在用户级别清理,删除更加困难

单个日志文件的好处是它是自包含的,不需要进行目录搜索。

Mysql Create Event手册页。如果完全在mysql中完成,我们仍然需要做一个Load Data Infile来获取数据。

我会选择一种非常适合这种日志文件处理的编程语言,例如java,c#,python,几乎任何东西,而不是一个笨重的Create Event到处理表中。

这里的主要外卖是让它异步。

答案 3 :(得分:1)

如果记录很宽且表格繁忙,最好将此列(加上int mat(cell **arr, int m, int n);)移动到" parallel"表

更新行时,会对该行的另一个副本进行更新,直到事务(以及可能的其他事务)完成为止。这涉及复制整个记录,可能涉及块拆分。另外还有REDO日志和UNDO日志的问题。如果您使用的是Replication,那么就有binlog。一排狭窄将减轻所有这些问题。

120ms听起来很高,所以我猜这个表中还有很多其他的东西。因此,拆分表可能会减少争用。

此外,这是id更大交易的一部分吗?或者在事务外部完成但是使用autocommit = 1?后者更有意义。

答案 4 :(得分:1)

在每个页面视图上发出db写入非常糟糕的设计,非常糟糕。在GET请求期间不发布任何写入被认为是好的风格 - 虽然您不一定需要对它进行宗教信仰,但这是一种非常好的扩展实践。

如果您绝对需要这些时间戳,一个简单的方法就是将它们转储到键值存储中 - memcached,redis,等等 - 并不时写入db。

增加吞吐量的一种非常简单的方法是,只有当更新的值与之前的值相差至少一小时(或一天)时才会写入更新的值 - 这将保证每个用户基本上每次浏览会话都会写一次,根据您的网站使用模式,将您的写入次数减少10到100次。