我有一个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,但是这个应用程序不涉及这个问题的目的:我纯粹关注数据库级别。
答案 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次。