【发布时间】:2015-10-09 12:19:48
【问题描述】:
我需要了解如何使用 mongo 来获得更好的性能。我有 2 个使用 mongo 的项目,其中一个有 1.4 亿行,每个查询几乎立即运行,数据以小块显示,因此通过一些索引 mongo 能够过滤 99% 的数据并快速返回选定的数据. Mongo 在这类项目上表现出色。
另一方面,我有另一个项目,如谷歌分析跟踪访问、点击等。目标是根据特定标准(使用表单)在时间范围内计算点击次数。我正在为同样的任务挑战 mysql。
第一次尝试
我使用传统的数据模式,逐行,类似于:
{
'user':'abc',
'date':'2015-07-20',
'hour':02,
[....]
'clicks':30
}
有 200+ 百万行(即使点击按小时打包),我有每个字段的索引和查询最多的组的一些复合索引。如果结果的行块足够大,尝试通过某个 $match 聚合和 $sum 点击真的很慢,更糟糕的是总行数,索引吃掉了服务器中 32gb 的内存。
第二次尝试
利用 mongo 的架构优势,设计了一个分组架构,尽可能减少重复数据,该架构中每种点击的属性由唯一的字段组合(具有唯一索引)确定,然后点击分组在按日期分布的树上,行示例:
{
"user" : "asd",
[....]
"date" : {
"total" : 5,
"years" : {
"2015" : {
"total" : 5,
"months" : {
"06" : {
"total" : 5,
"days" : {
"30" : {
"total" : 2,
"hours" : {
"16" : 1,
"22" : 1
}
},
"28" : {
"total" : 1,
"hours" : {
"6" : 1
}
},
"29" : {
"total" : 2,
"hours" : {
"14" : 1,
"20" : 1
}
}
}
}
}
}
}
}
}
由于这种策略,200+ 百万行减少了 10 倍,并且索引适合内存,因此插入速度变慢了,因为在插入新“行”之前,您必须检查是否有找到相同的特征并将其应用于日期数组的点击合并(如果之前存在)。
当我需要计算行数时,速度已经比传统模式有所提高,但我需要做这样的模糊聚合操作来计算数据:
['$sum'=>'$date.years.'.$year.'.months.'.$month.'.days.'.$day.'.total']
这比一般mysql的平均速度要低一些,但是差距太小了,即使在某些条件下mysql也赢了太多,考虑到mysql是2亿行,mongo是2000万行,这是不能接受的因为很多时候 mysql 在 16 秒内进行查询,而 mongo 在 120 秒内解决它。我想击败 mysql (myIsam) 来使用 mongo 作为替代品。我尝试了很多东西,从日期树上的稀疏索引到保存一些预处理结果并将它们混合的二级缓存。它不可能在某一天缓存所有数据排列,因为 [...] 字段很多。
分片可能是一种解决方案,但我认为不会神奇地将速度提高 2 倍。
请给我一些提示
更新
让我们搜索某个国家/地区的某些日子:
Mongo 压缩架构
Mongodb count rows where country = 'AD': 11389
聚合:
Array
(
[0] => Array
(
[$match] => Array
(
[country] => AD
)
)
[1] => Array
(
[$group] => Array
(
[_id] => Array
(
[country] => $country
)
[2015-07-01] => Array
(
[$sum] => $date.years.2015.months.07.days.01.total
)
[2015-07-02] => Array
(
[$sum] => $date.years.2015.months.07.days.02.total
)
[2015-07-03] => Array
(
[$sum] => $date.years.2015.months.07.days.03.total
)
[2015-07-04] => Array
(
[$sum] => $date.years.2015.months.07.days.04.total
)
[2015-07-05] => Array
(
[$sum] => $date.years.2015.months.07.days.05.total
)
[2015-07-06] => Array
(
[$sum] => $date.years.2015.months.07.days.06.total
)
[2015-07-07] => Array
(
[$sum] => $date.years.2015.months.07.days.07.total
)
[2015-07-08] => Array
(
[$sum] => $date.years.2015.months.07.days.08.total
)
[2015-07-09] => Array
(
[$sum] => $date.years.2015.months.07.days.09.total
)
[2015-07-10] => Array
(
[$sum] => $date.years.2015.months.07.days.10.total
)
[2015-07-11] => Array
(
[$sum] => $date.years.2015.months.07.days.11.total
)
[2015-07-12] => Array
(
[$sum] => $date.years.2015.months.07.days.12.total
)
)
)
[2] => Array
(
[$project] => Array
(
[_id] => $_id
[dates] => Array
(
[2015-07-01] => $2015-07-01
[2015-07-02] => $2015-07-02
[2015-07-03] => $2015-07-03
[2015-07-04] => $2015-07-04
[2015-07-05] => $2015-07-05
[2015-07-06] => $2015-07-06
[2015-07-07] => $2015-07-07
[2015-07-08] => $2015-07-08
[2015-07-09] => $2015-07-09
[2015-07-10] => $2015-07-10
[2015-07-11] => $2015-07-11
[2015-07-12] => $2015-07-12
)
)
)
)
结果:
Array
(
[data] => Array
(
[AD] => Array
(
[_id] => Array
(
[country] => AD
)
[dates] => Array
(
[2015-07-01] => 6080
[2015-07-02] => 6580
[2015-07-03] => 6178
[2015-07-04] => 6084
[2015-07-05] => 7085
[2015-07-06] => 7192
[2015-07-07] => 5672
[2015-07-08] => 6769
[2015-07-09] => 6370
[2015-07-10] => 6035
[2015-07-11] => 5513
[2015-07-12] => 6941
)
)
)
[time] => 17.0764780045
)
Mysql 传统模式
Mysql 计数行数:38515
Mysql查询:
SELECT date,sum(clicks) as clicks FROM table WHERE ( country = "AD" AND ( date > 20150700 AND date < 20150712 ) ) GROUP BY country,date;
结果:
Array
(
[0] => Array
(
[date] => 20150701
[clicks] => 6080
)
[1] => Array
(
[date] => 20150702
[clicks] => 6580
)
[2] => Array
(
[date] => 20150703
[clicks] => 6178
)
[3] => Array
(
[date] => 20150704
[clicks] => 6084
)
[4] => Array
(
[date] => 20150705
[clicks] => 7085
)
[5] => Array
(
[date] => 20150706
[clicks] => 7192
)
[6] => Array
(
[date] => 20150707
[clicks] => 5672
)
[7] => Array
(
[date] => 20150708
[clicks] => 6769
)
[8] => Array
(
[date] => 20150709
[clicks] => 6370
)
[9] => Array
(
[date] => 20150710
[clicks] => 6035
)
[10] => Array
(
[date] => 20150711
[clicks] => 5513
)
)
time: 0.25689506530762
Mongodb 传统架构
项目数:
聚合:
Array
(
[0] => Array
(
[$match] => Array
(
[country] => AD
[date] => Array
(
[$in] => Array
(
[0] => 20150701
[1] => 20150702
[2] => 20150703
[3] => 20150704
[4] => 20150705
[5] => 20150706
[6] => 20150707
[7] => 20150708
[8] => 20150709
[9] => 20150710
[10] => 20150711
[11] => 20150712
)
)
)
)
[1] => Array
(
[$group] => Array
(
[_id] => Array
(
[country] => $country
)
[count] => Array
(
[$sum] => $clicks
)
)
)
)
结果:
Array
(
[result] => Array
(
[0] => Array
(
[_id] => Array
(
[country] => AD
)
[clicks] => 76499
)
)
[ok] => 1
)
time: 27.8900089264
【问题讨论】:
-
分片可以极大地提高速度。但是,如果您真的想要/可以允许,问题仍然存在。从模式中,您可以尝试第三种方法。创建更多的集合,其中包含更少的 [...] 字段,从而对数据进行更多分组。如果大多数查询使用 2 个字段,则仅使用这两个字段再创建一个集合,并在需要超过这两个字段时使用大集合等。
-
@Endijs 实际上这是一个好主意,但很难找到最常用的字段组,因为查询是所有内容的混合,没有明确的赢家。真正的问题是,mysql 似乎比 mongo 快(不可扩展,不那么容易分发,但对于简单的扫描来说更快),即使您使用 mongo 可以提高 mysql 速度的功能,mysql 也会继续获胜 :/ 。是否有一些指南可以添加数据以在 mongo 中表现更好?
标签: php performance mongodb