4

我正面临着一个快速增长的表格的一些问题(目前有 4mio 行,每天 300k 插入)。我希望我能在这里得到一些想法和建议,以改进我的设置并从我的盒子里挤出最后一点,在不久的将来它会关闭我的网站之前。

设置:

    Intel i7 720 
    8GB RAM
    2x750GB SATA RAID 0
    CentOS
    MySQL 5.5.10
    Node.js + node-lib_mysql-client

表定义:

CREATE TABLE IF NOT EXISTS `canvas` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`x1` int(11) NOT NULL,
`y1` int(11) NOT NULL,
`x2` int(11) NOT NULL,
`y2` int(11) NOT NULL,
`c` int(4) unsigned NOT NULL,
`s` int(3) unsigned NOT NULL,
`m` bigint(20) unsigned NOT NULL,
`r` varchar(32) NOT NULL,
PRIMARY KEY (`id`,`x1`,`y1`) KEY_BLOCK_SIZE=1024,
KEY `x1` (`x1`,`y1`) KEY_BLOCK_SIZE=1024,
KEY `x2` (`x2`,`y2`) KEY_BLOCK_SIZE=1024
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT KEY_BLOCK_SIZE=4
/*!50100 PARTITION BY HASH ( (
(
x1 MOD 10000
)
) + y1 MOD 10000)
PARTITIONS 10 */ AUTO_INCREMENT=13168904 ;

查询:

SELECT x1,y1,x2,y2,s,c,r,m FROM canvas
WHERE 1 AND ((
 x1 >= 0
 AND x1 <= 400
 AND y1 >= 0
 AND y1 <= 400
 ) OR ( 
 x2 >= 0
 AND x2 <= 400
 AND y2 >= 0
 AND y2 <= 400
 ) )
  ORDER BY id desc

这是我正在执行的唯一查询,除了 x1、y1、x2 和 y2 的值​​每次查询都会发生变化。这是一个 2D 画布,每一行代表画布上的一条线。猜猜知道为 1 个字段选择的最大范围永远不会大于 1200(像素)也很重要。几周前,我升级到 MySQL 5.5.10 并开始使用分区。'x1 % 10000' hashw 作为我进入分区主题的第一个也是不知道的方法。它已经在 SELECT 速度方面给了我相当大的提升,但我确信仍有优化的空间。

哦,在你问之前......我知道我正在使用 MyISAM 表。我的一个朋友建议使用 innoDB,但已经尝试过,结果是表大了 2 倍,SELECT 性能大幅下降。我不需要花哨的事务和东西……我需要的只是最好的 SELECT 性能和 INSERT 的良好性能。

你会改变什么?我可以以某种方式调整我的索引吗?我的分区设置是否有意义?我应该增加分区文件的数量吗?

欢迎所有建议...我还与朋友讨论了将本地复制到内存表中,但我敢肯定,表大小超过我的 RAM 只是时间问题,而交换盒是一件相当丑陋的事情看。

当您考虑我的问题时,请记住它正在快速且不可预测地增长。万一它因为某种原因在某个地方传播开来,我预计每天会看到超过 1mio INSERTS。

感谢您阅读和思考。:)

编辑:请求的解释结果

select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
SIMPLE  canvas  index_merge     x1,x2   x1,x2   8,8     NULL    133532  Using sort_union(x1,x2); Using where; Using fileso...

EDIT2:请求的 my.cnf

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

innodb_buffer_pool_size = 1G
sort_buffer_size = 4M
read_buffer_size = 1M
read_rnd_buffer_size = 16M
innodb_file_format = Barracuda

query_cache_type = 1
query_cache_size = 100M

# http://dev.mysql.com/doc/refman/5.5/en/performance-schema.html
;performance_schema


[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

innoDB 值用于我的 innoDB 尝试……猜想它们不再需要了。该服务器还运行 4 个其他网站,但它们相当小,不值得一提。不管怎样,我很快就会把这个项目转移到一个专用的盒子里。你的想法可能很激进——我不介意实验。

EDIT3 - 带索引的基准

好的,伙计们......我已经用不同的索引做了一些基准测试,到目前为止结果非常好。对于这个基准测试,我选择了一个 2000x2000 像素框内的所有行。

SELECT SQL_NO_CACHE x1,y1,x2,y2,s,c FROM canvas_test WHERE 1 AND (( x1 BETWEEN -6728 AND -4328 AND y1 BETWEEN -6040 AND -4440 ) OR (  x2 BETWEEN -6728 AND -4328 AND y2 BETWEEN -6040 AND -4440 ) )  ORDER BY id asc

使用我在上面发布的表/索引定义,平均查询时间是:1740ms

然后我删除了所有索引,除了主键-> 1900ms

为 x1 -> 1800ms 添加了一个索引

为 y1 -> 1700ms 添加了一个索引

为 x2 -> 1500ms 添加了一个索引

为 y2 -> 900ms 添加了一个索引!

到目前为止,这真是令人惊讶……出于某种原因,我认为为 x1/y1 和 x2/y2 制作组合索引会以某种方式有意义,但实际上看起来我错了。

EXPLAIN 现在返回:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  canvas_test     index_merge     x1,y1,x2,y2     y1,y2   4,4     NULL    263998  Using sort_union(y1,y2); Using where; Using fileso..

现在我想知道为什么它使用 y1/y2 作为键而不是全部四个?

但是,我仍在寻找更多的想法和建议,尤其是关于分区和正确的散列。

4

4 回答 4

2

首先,我将 SELECT 修改为

SELECT x1,y1,x2,y2,s,c,r,m FROM canvas
WHERE 
  x1 BETWEEN 0 AND 400 AND y1 BETWEEN 0 AND 400 OR
  x2 BETWEEN 0 AND 400 AND y2 BETWEEN 0 AND 400
ORDER BY id desc

并且还要确保对该表达式有一个索引:

CREATE INDEX canvas400 ON canvas(
  x1 BETWEEN 0 AND 400 AND y1 BETWEEN 0 AND 400 OR
  x2 BETWEEN 0 AND 400 AND y2 BETWEEN 0 AND 400
)
于 2011-05-18T23:09:31.930 回答
1
  1. 您的服务器当前使用了多少内存?
  2. 这是服务器上唯一的数据库/表吗?
  3. 您是否只使用 MyISAM?

只要您不更新行,MyISAM 就可以使用。当您更新 MyISAM 表上的一行时,MySQL 会锁定整个表,阻止任何 SELECT 和 INSERTS 执行,直到 UPDATE 完成。UPDATE 优先于 SELECT,因此如果您有很多 UPDATE 正在运行,您的 SELECTS 将等到它们全部完成后再返回任何行。

如果这对您来说没问题,那么请转到您的服务器配置。您的 my.cnf 文件是什么样的?您需要优化此文件以最大化可用于索引的内存量。如果这些 SELECT 速度变慢,那是因为您的表索引不适合内存。如果 MySQL 不能将您的表索引放入内存,那么它必须去磁盘并进行表扫描以获取您的数据。这会扼杀性能。

编辑 2011 年 5 月 18 日晚上 9:30 EST

查看您的 my.cnf 后,我注意到您的MyISAM 优化为零。你的起点将是key_buffer_size变量。根据经验,这个变量设置在系统总可用内存的 25% 到 50% 之间。您的系统有 8GB 可用内存,所以我想说 3GB 左右是最低起点。但是,如果您知道您可以控制系统上的其他变量,您可以估计您需要多少并根据需要对其进行优化。

您应该做的是 cd 到您的 mysql 数据目录(通常是/var/lib/mysql),这是您所有数据文件所在的位置。一种快速判断您有多少索引数据的方法是

 sudo du -hc `find . -type f -name "*.MYI"

此命令将查看所有 MyISAM 索引文件的大​​小并告诉您它们的总大小。如果你有足够的内存,你想让key_buffer_size你的 my.cnf 比你所有的 MYI 文件的总大小更大。这将确保您的 MyISAM 索引在内存中,因此 MySQL 不必为索引数据访问磁盘。

快速说明,不要随意增加你的key_buffer_size意愿。这只是 MySQL 的一个需要内存的区域,还有其他移动部分需要平衡内存使用。MySQL 连接占用内存,不同的表引擎为它们的索引使用不同的内存池,而 MySQL 为不同的事情使用其他位的内存。如果由于设置key_buffer_size太大而导致内存不足,您的服务器可能会开始分页(使用虚拟内存,这将进一步降低性能)或更糟,崩溃。如果您不确定,请从较小的值开始,检查您的内存使用情况,然后增加它,直到您对性能感到满意,并且您的服务器没有崩溃。

于 2011-05-18T23:21:52.747 回答
1

请记住,MySQL 每次查询只会使用每个表的一个索引。您的 SELECT 查询将无法在同一个查询中使用您的两个索引 - 它将使用一个或另一个。您可能会发现将两个 SELECT 查询联合起来更有效,这样每个查询都可以使用适当的索引,例如:

SELECT x1,y1,x2,y2,s,c,r,m FROM canvas
WHERE 
 x1 >= 0
 AND x1 <= 400
 AND y1 >= 0
 AND y1 <= 400
UNION
SELECT x1,y1,x2,y2,s,c,r,m FROM canvas
WHERE
 x2 >= 0
 AND x2 <= 400
 AND y2 >= 0
 AND y2 <= 400
;

或者您可以像建议的其他回复之一那样使用 BETWEEN ,例如:

SELECT x1,y1,x2,y2,s,c,r,m FROM canvas
WHERE x1 BETWEEN 0 AND 400 AND y1 BETWEEN 0 AND 400
UNION
SELECT x1,y1,x2,y2,s,c,r,m FROM canvas
WHERE x2 BETWEEN 0 AND 400 AND y2 BETWEEN 0 AND 400
;

自从我使用 UNION 以来已经有一段时间了,所以我不确定您将 ORDER BY 子句放在哪里,但您可以尝试一下。

作为提到的其他回复之一,使用 EXPLAIN 查看 MySQL 必须考虑多少行才能满足查询。

尽管我自己没有玩过这些,但 RTREE 索引可能也值得一看。

于 2011-08-03T17:09:31.530 回答
0

你得到什么样的速度?由于您不需要任何相关的东西,您应该考虑将数据移动到Redis,它应该可以轻松地在您的机器上执行 +100k 插入或读取/秒。

于 2011-05-19T00:17:51.427 回答