3

我有一个有 9 列和 1200 万行的大表,如下所示:

col1  col2  col3  col4  col5  col6  col7  col8  col9
12.3  37.4  7771  -675  -23   23.8  78.8  -892  67.5
79.3  -6.3  6061  -555  -24   28.1  77.1  -889  32.6
55.6  -7.3  8888  -921  -56   78.3  22.3  -443  22.9
....  ....  ....  ....  ....  ....  ....  ....  ....

目前该表以 TSV(制表符分隔矢量)格式保存在我的硬盘中,大小为 432MB。我想将表填充到 Redis 中以便最有效地完成这种查询:给定每列的最小值和最大值,计算给定范围内的行数,即

(min_col1 <= col1 <= max_col1) &&
(min_col2 <= col2 <= max_col2) &&
(min_col3 <= col3 <= max_col3) &&
(min_col4 <= col4 <= max_col4) &&
(min_col5 <= col5 <= max_col5) &&
(min_col6 <= col6 <= max_col6) &&
(min_col7 <= col7 <= max_col7) &&
(min_col8 <= col8 <= max_col8) &&
(min_col9 <= col9 <= max_col9)

所以我的问题是:

1)如何将表填充到 Redis 中?我应该使用什么样的键/值数据结构?散列、列表、集合、排序集合或其他什么?

2) 填充表后,给定 9 列的 9 最小值和最大值,如何编写查询以获得计数,即落在 9 个范围内的行数?我能想到的一种方法是,首先找出1到9中每个X满足(min_colX <= colX <= max_colX)的行,然后计算它们的交集。但我想这不是最有效的方法。我只想尽快检索计数。

顺便说一句,我尝试过 MongoDB。使用 mongoimport 填充表很简单,但是完成我的查询需要 10 秒,这太慢了,对于我的实时应用程序来说是不可接受的。相比之下,Redis 将数据保存在内存中,所以希望 Redis 可以将查询时间缩短到 1 秒。

供您参考,这是我在 MongoDB 中所做的。

mongoimport -u my_username -p my_password -d my_db -c my_coll --type tsv --file my_table.tsv --headerline
use my_db
db.my_coll.ensureIndex({col1:1, col2:1, col3:1, col4:1, col5:1, col6:1, col7:1, col8:1, col9:1 }).
db.my_coll.count({ col1: {$gte: min_col1, $lte: max_col1), col2: {$gte: min_col2, $lte: max_col2}, col3: {$gte: min_col3, $lte: max_col3}, col4: {$gte: min_col4, $lte: max_col4}, col5: {$gte: min_col5, $lte: max_col5}, col6: {$gte: min_col6, $lte: max_col6}, col7: {$gte: min_col7, $lte: max_col7}, col8: {$gte: min_col8, $lte: max_col8}, col9: {$gte: min_col9, $lte: max_col9} }).

我使用 explain() 来确保实际使用了 Btree 索引而不是表扫描。

我还尝试创建一个 ram 磁盘并将我的 MongoDB 数据库保存到 ram 磁盘中,它将查询时间从 10 秒缩短到 9 秒,这对于我的实时应用程序来说远远不能接受。

mkdir ~/ram
chmod -R 755 ~/ram
mount -t tmpfs none ~/ram -o size=8192m
mongod --dbpath ~/ram --noprealloc --smallfiles
4

2 回答 2

3

NoSQL 领域有(有点)新玩家:Tarantool

它具有内置的二级索引和一些对它们的范围查询的支持。现在,AFAIK,只能进行>=查询。

用户指南

box.select_range(space_no, index_no, limit, key, ...)

选择一系列元组,从 key 指定的偏移量开始。密钥可以是多部分的。使用最多限制元组限制选择。如果没有指定键,则从索引中的第一个键开始。

似乎它是这项工作的好工具。但是,它需要一些额外的工作(编写代码来限制这些查询)。

于 2012-06-10T09:04:51.823 回答
3

使每个col排序集,然后ZRANGEBYSCORE在每个键上使用,并在应用程序中进行交集和计数。我使用phpredis,我在内存中做了很多,使用array_intersect.

性能问题在 中ZADD,您将使用它来创建排序集。

一旦你在 Redis 的内存中创建了所有排序集,剩下的就真的很快了。


创建排序集(Redis 示例)

ZADD col1 12.3 line1
ZADD col1 79.3 line2
ZADD col1 55.6 line3

ZADD col2 37.4 line1
ZADD col2 -6.3 line2
ZADD col2 -7.3 line3

PHP,查找范围,交叉点和计数

$COL1 = $redis->zrangebyscore('col1', -10, 10);
$COL2 = $redis->zrangebyscore('col2', 2010, 2012);
$count = count(array_intersect($COL1, $COL2));

希望有帮助。

于 2012-06-11T00:18:02.837 回答