MongoDB,复杂查询和性能

6

背景

在当前的项目中,我正在使用MySQL和SQLite相结合。我目前为每个用户提供自己的SQLite数据库,以规避我的提供商1GB MySQL数据库的限制。这种方法效果还不错,性能也很好,但是我知道未来对这些平面文件数据库进行持续维护将是一场噩梦。

SQLite令人惊讶地强大,并支持一些相当复杂的SQL查询。然而,我正在考虑使用MongoDB来使用一些NoSQL技术处理用户的大量数据。每个用户可能会生成超过60,000行数据。随着用户数量不断增加,我需要担心未来的性能问题。

-

复杂性

我对MongoDB和其他NoSQL数据库的担忧是它们似乎更受限于支持什么类型的查询操作。如果您只需要简单的批量查询,那就没什么大问题了,但是我需要执行一些更复杂的联接和过滤操作(联合、区分大小写、分组、偶尔联接等)。

我的示例查询尝试选择艺术家的曲目列表。主要问题是这些艺术家名称可能不匹配。例如,有些人标记为“A Day to Remember”,而有些人标记为“A Day To Remember”。对于区分大小写的查询,这会导致返回多个“不同”的记录,但实际上它们是相同的。通常我通过修剪和LOWER()函数将字段正确地组合在一起。

-

性能

我在本地机器上创建了两个全新的数据库。一个用于MongoDB,一个用于MySQL。由于最终结果必须使用PHP进行交互,因此我正在使用PHP与它们交互。每个数据库目前只有大约9,000条记录,因此在这一点上并不是特别大。

我在我的机器上运行了一些测试,并得出了令人失望的MongoDB结果。让我们考虑以下三个查询...

#1 - MongoDB:约14毫秒,结果不正确

$query = array('artist' => 'A Day to Remember');
$cursor = $collection->find($query);
foreach ($cursor as $row) {
    echo $row['artist'] . ' - ' . $row['album'] . ' - #'. $row['track'] . ' ' . $row['title'] . "\r\n";
}

#2 - MongoDB: ~170毫秒,正确的结果

$query = array('$where' => "this.artist.toLowerCase() == 'a day to remember'");
$cursor = $collection->find($query);
foreach ($cursor as $row) {
    echo $row['artist'] . ' - ' . $row['album'] . ' - #'. $row['track'] . ' ' . $row['title'] . "\r\n";
}

#3 - MySQL: ~18毫秒,正确结果

$sql = "select artist, album, track, title from radio_files where lower(artist) = 'a day to remember'";
$stmt = $mysqldb->prepare($sql);
$stmt->execute();
while($row = $stmt->fetch(PDO::FETCH_ASSOC))
{
    echo $row['artist'] . ' - ' . $row['album'] . ' - #'. $row['track'] . ' ' . $row['title'] . "\r\n";
}

讨论

也许我没有正确地查询#2,但是看看JavaScript查询引擎的效果。总的来说,这里的记录数量甚至不是很多:数据库中仅有不到9,000条。

我的主要问题是:最终哪种方式更可靠、性能更好,同时满足我的需求?随着我的项目用户群的增长,我计划离开我的受限服务器并获得一些专用设备。通过自己的MySQL安装,我应该能够维护自己的大型MyISAM表格,其中包含很少的关系数据和适当的索引。

但是,如果数据库中有数百万条记录,MySQL的性能会怎样呢?鼓励大家思考、评论和进行一般性讨论。谢谢!


你在MongoDB上的'artist'字段上添加了索引吗? - mellowsoon
一个更好的问题可能是为什么不寻找一个没有1GB数据库大小限制的新主机,并在您知道它可以工作的地方实施呢?(别误会,我喜欢NoSQL,但它并不适用于所有(甚至大多数)用例)。MySQL的性能将直接与您可以给它多少RAM(以及硬件总体性能)相关。我有一个生产数据库约100GB,总共不到10亿行,即使对于简单的连接,它也可以在几十毫秒内返回结果。当然,您需要优化查询,但它可以处理这个大小... - ircmaxell
我猜测1GB的限制是因为你正在与其他一千个客户共享一个数据库服务器。如果你可以在托管账户上安装MongoDB,那么有什么阻止你在自己的空间内安装MySQL,并且不使用共享数据库呢?这样就可以避免1GB的限制。 - mellowsoon
3个回答

7

尝试使用正则表达式:

$regex = new MongoRegex('/^' . preg_quote('a day to remember'). '$/i');
$query = array('artist' => $regex);
$cursor = $collection->find($query);

哇,好棒的调用!这个只需要7毫秒就能运行。这是目前为止最快的一个! - jocull

6

如果您想在Mongodb中对某个值进行不区分大小写的搜索,那么您需要将该值存储两次。一次是正常的,另一次是小写的用于索引和搜索。

Mongodb拥有丰富的查询语言(与其他nosql系统相比),您可以对每个列(组合)进行索引。但我发现mapreduce很慢,但只要您能够不使用mapreduce解决问题,就可以了。


这真的是首选方法吗?看起来随着时间的推移会消耗大量额外的数据。 - jocull
我也这样做。为了避免执行多个查询(因为没有JOINs),我选择重新存储我的数据。例如,在保存博客评论的作者姓名时,我除了用户的_id之外,还存储了用户名。如果我必须循环遍历记录然后从用户集合中提取用户名,这将节省一次额外的数据库访问。 - sdot257
@jocull,但在sqlite中也是同样的情况。Sqlite无法对Unicode字符串执行ToUpper()操作,因此我需要将值存储两次。 - TTT
奇怪吗?我一直在SQLite中使用lower(x)函数,并取得了相当不错的成功。它可能对Unicode的处理不够正确,但文档说您可以加载扩展来解决这个问题。http://www.sqlite.org/lang_corefunc.html - jocull
@jocull,我不知道这个扩展。也许我会测试一下,例如使用土耳其的无点i进行测试。 - TTT

4
不同的NoSQL解决方案之间的差异比传统的SQL数据库之间的差异要大得多,但MongoDB实际上是其中功能最丰富的解决方案之一,特别是在查询复杂性方面。
然而,你不应该仅仅因为期望每个用户有60000行数据就盲目选择NoSQL解决方案。MySQL和其他流行的关系型数据库管理系统可以处理数十亿行数据而没有问题。
关系型数据库具有许多重要的特性(例如ACID保证和复杂的查询),如果你需要这些特性,最好使用SQL数据库。NoSQL通常是在这些特性(或全部特性)与水平扩展的易用性之间进行权衡。如果你可以预期使用关系型DBMS来管理系统的可扩展性问题,那么我会认真考虑坚持使用SQL。
“我目前正在为每个用户提供自己的SQLite数据库,以避免我的提供商限制了1GB的MySQL DB限制。”
你也可以考虑更换提供商。施加此类限制的主机最终可能会以某种其他方式限制你。

1
如果他已经到了每个用户可以生成60k行的地步,那么建议切换到一个真正的托管服务提供商。这样可以升级您的服务。+1 - mellowsoon
我的提供者是 GoDaddy,是的,它有很多限制。目前我正在使用我所拥有的资源来启动,因为它适合我的预算并且我可以让它正常运作。它比专用主机便宜1200%。当我的业务增长时,改进托管是我要考虑的首要事情之一。 - jocull
@jocull:它可能比专用主机便宜,但它并不比虚拟服务器便宜得多,你可以以每月5美元的低价获得虚拟服务器(此外,你几乎可以获得所有专用服务器的好处)。首先改善托管服务,这是值得的... - ircmaxell
1
是的,在尝试虚拟专用服务器之前,绝对不要选择独立服务器。如今它们非常便宜,几乎可以说是一种福音,而在初始启动时,独立服务器(几乎)总是过度的。如果您还没有找到任何公司,请首先查找(谷歌)这些公司:Webbynode、Linode、Slicehost、6Sync、Rackspace(云服务器)。Webbynode和Rackspace(云服务器)从256mb ram开始(在撰写本文时),每月约10美元。 - Michael van Rooijen
谢谢您提供关于Webbynode的提示,这是我看过最便宜的VPS! - jocull
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接