2015-10-05 69 views
1

我有一个users表与日期时间字段last_seen_at。更新这个字段大约需要120ms,而且我希望它快很多,因为我在我的网站上几乎每个页面上都做了这个。我无法弄清楚为什么它如此缓慢:大约有55,000条记录不应该有问题(我想过)。如何加快MYSQL更新?

这里的表信息:

 
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我做它像这样:

update users set last_seen_at = '2015-10-05 12:34:45' where id = 1182;

MySQL服务器信息: Server version: 5.5.44-0ubuntu0.12.04.1 (Ubuntu)

有什么我可以做,以加快更新?

编辑 - 我以前说过的查询正在采取700毫秒。它实际上更像是120ms,对不起,我正在查看错误的查询。尽管如此,这仍然感觉有点过长。毕竟,这实际上是一个合理的写入时间吗?

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

+4

分享您的更新声明 – lad2025

+1

UPDATE如何查看? – jarlh

+0

对不起,现在补充。 –

回答

2

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

索引可能被分割,这会减慢查找速度。您可以使用analyze重建索引。

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

+0

感谢 - 我只是试过'优化表用户',它说'表不支持优化,重新创建+分析',但之后的执行时间似乎并不快。 –

+0

(谢谢你的实验是另一个反对做'优化表'的论点。) –

3

好了,你出现在最有效的方式进行更新 - 即,使用上表的主键,所以没有太多可以有工作要做。 假设120ms的更新是纯粹由DB服务器取(而不是在网页中往返)的时候,我只能想到的一些东西,可能会帮助:

  • 您已经索引该列正在更新 - 通常会增加一点时间来更新索引必须保留。我看到你需要使用该列,所以你不能摆脱索引;但如果可以的话,你可能会看到更好的表现。

  • 分批更新有时是避免了实时性能命中,但仍然达到你想要什么的好方法。
    您可以将Web触发式插入带有时间戳字段的保留表中,然后(离线)批量更新实际数据。有关示例批量更新语句,请参阅https://dba.stackexchange.com/questions/28282/whats-the-most-efficient-way-to-batch-update-queries-in-mysql

  • DB优化可能会有帮助,但只有当数据库不是已经好身材 - 这样的事情,如内存分配,表空间碎片,缓冲池等

祝你好运!

2

写入用户事件(id,now()相当于一个日志文件)。从另一个进程(如Create Event)处理日志文件,或者完全使用另一种编程语言(如Java)处理日志文件,然后将其命名。我们称之为工作进程(wp)。

因此,用户正在发生活动的环境中运行,但不会忍受阻止更新调用的开销,从而减慢其UX(用户体验)。阻挡意味着他们在等待。相反,活动记录更快,例如对日志文件进行fwrite(语言特定)。

日志文件(开放为追加)的概念可以被部署到一个专用的目录,要么具有在1个文件,或每用户1个文件中的所有用户活动。在后一种情况下,wp有一个简单的任务,只需获取为单个更新语句记录的最后一行。例如,如果有11行,那么有1个更新呼叫,而不是11.

wp在后台运行,在cron作业,创建事件,任何东西。它根据需要进行更新。拥有55k用户,该系统相对较小。无论如何,每隔nnn分钟,每10秒钟就能发射一次。

对于一个mysql Create Event存根考虑:

CREATE EVENT userUpdateActivity 
    ON SCHEDULE 
     EVERY 10 SECOND 
    DO 
(something) 

或其他一些wp策略。

wp进程和删除打开附加日志文件。定期(每天?)锁定和删除日志文件的策略是可以想象的。

有一个日志文件的问题是,wp一个必须:

  • 读取所有的行,并更新各行手动
  • 或读取所有的行,并得到给定用户最后一次和更新只是一个

更难以收拾,删除是,在用户级

单个LO的好处g文件是它是独立的,不需要目录搜索。

Mysql的Create Event手册页。如果完全在mysql中完成,仍然需要执行Load Data Infile以获取数据。

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

主要外卖在这里,虽然,是让异步的。

+0

Thanks @Drew - 我使用Ruby on Rails,并且我们有异步更新的解决方案,因此这120ms不会被添加到实际的响应时间。不过,我现在只关注数据库优化,我想知道这个简单的查询是否可以在数据库级别进行改进。它听起来像它不能。 –

+1

@MaxWilliams是的,我会说它不会比你做得更好。如果你想要更快的插入,升级你的数据库硬件。我会想象还有更多的事情可以做,以减少整个页面加载120毫秒,而不是使这个更新真的很复杂。 –

+0

谢谢@MattGibson –

1

如果记录很宽且表格繁忙,则最好将此列(加上id)移动到“并行”表格中。

当更新一行时,该行的另一个副本被创建,直到事务(以及可能的其他事务)完成。这涉及复制整个记录,可能涉及块拆分。另外还有REDO日志和UNDO日志的问题。如果您正在使用复制,则有二进制日志。窄行将减轻所有这些问题。

120ms听起来非常高,所以我猜这个表中有很多其他的东西正在发生。所以,拆分表格可能会减少争用。

此外,这是UPDATE更大的交易的一部分?或者在交易之外完成,但autocommit = 1?后者更有意义。

1

这只是一个非常糟糕的设计,并且在每个页面视图上执行db写入操作,并且缩放非常糟糕。在GET请求期间不要发出任何写入被认为是一种很好的风格 - 虽然你不一定需要对它有所信任,但这是一个非常好的缩放实践。

如果您绝对需要这些时间戳,一个简单的方法是将它们转储到键值存储中 - memcached,redis,不管 - 然后不时写入数据库。

超级简单的增加吞吐量的方法是,只有当它们与前一个不同时间至少一个小时(或一天)不同时,才能写入更新后的值 - 这样可以保证每个用户每次浏览会话都会获得一次写入根据您的网站使用模式,将您的写入次数减少10-100次。