Redis如何存储关联数组?使用Set、Hash还是List?

33

对于Redis的所有可用存储选项,我有些困惑。 我想做一些简单的事情,我不想过度工程化。 我正在使用phpredisRedis v2.8.6

我有一个简单的关联数组需要存储。我还需要能够通过键检索项目并循环遍历所有项目。

$a = array(
    '12345' => array(
        'name' => 'Post A',
        'val2' => 'blah blah',
        'val3' => 'blah blah blah',
    ),
    '54321' => array(
        'name' => 'Post B',
        'val2' => 'blah blah',
        'val3' => 'blah blah blah',
    ),
    '998877' => array(
        'name' => 'Post C',
        'val2' => 'blah blah',
        'val3' => 'blah blah blah',
    )
);

到目前为止,我一直在使用哈希类型。像这样存储我的数组:

foreach ($a as $key => $value) {
    $this->redis->hSet('posts', $key, json_encode($value));
}

那样我就能够轻松地获取密钥,就像这样:

public function getPost($postId)
{
    return json_decode($this->redis->hGet('posts', $postId), true);
}

// This is returning the information of Post A
$post = getPost(12345);

现在我需要遍历所有的帖子,但我不知道该如何做,也不确定是否可以使用当前的结构来完成。我不知道是否需要将所有的 post_id 存储在另一个列表中,以便能够遍历所有的帖子?

因此,我的问题是,我应该使用哪些数据类型来存储我的帖子列表,使我能够通过 ID 获取单个帖子并循环遍历所有帖子?

谢谢, Maxime

3个回答

32

你可以将 SET、Hash 和 SORT 结合使用。

redis 127.0.0.1:6379> HMSET TEST_12345 name "Post A" val2 "Blah Blah" val3 "Blah Blah Blah"
OK
redis 127.0.0.1:6379> HMSET TEST_54321 name "Post B" val2 "Blah Blah" val3 "Blah Blah Blah"
OK
redis 127.0.0.1:6379> HMSET TEST_998877 name "Post C" val2 "Blah Blah" val3 "Blah Blah Blah"
OK
redis 127.0.0.1:6379> SADD All_keys TEST_12345 TEST_54321 TEST_998877
(integer) 3
redis 127.0.0.1:6379> HGETALL TEST_12345

获取一个哈希值:

redis 127.0.0.1:6379> HGETALL TEST_12345
1) "name"
2) "Post A"
3) "val2"
4) "Blah Blah"
5) "val3"
6) "Blah Blah Blah"

获取所有哈希值

redis 127.0.0.1:6379> SORT All_keys BY nosort GET *->name GET *->val2 GET *->val3
1) "Post A"
2) "Blah Blah"
3) "Blah Blah Blah"
4) "Post B"
5) "Blah Blah"
6) "Blah Blah Blah"
7) "Post C"
8) "Blah Blah"
9) "Blah Blah Blah"

如果您不想使用 sort,可以使用 SMEMBERS 从 SET 中获取所有键名,然后使用 Redis 管道获取所有键。


1
那么存储post的哈希值在SET中是唯一的解决方案吗?如果在SET中有数百万条目,最好的选项是SORT还是SMEMBERS - maxwell2022
如果您注意到我使用nosort进行排序,实际上它不会进行排序。此外,在我看来,理想的数据结构是哈希表,以保持键值对。我相信您不会一次获取数百万个值,您只会获取其中的几个,比如说50个,然后您可以使用sort命令的Limit参数进行排序。了解更多:http://redis.io/commands/SORT - Jack Daniel's
如果我想从这个集合中删除所有哈希,我该怎么办?我必须循环遍历它们以删除它们吗? - maxwell2022
你的意思是要清空SET,还是也要删除HASH? - Jack Daniel's
我想要删除它们两个。我要删除SET和其中包含的所有HASH。我只是在进行计算时使用Redis作为缓存,完成计算后将结果保存在MySQL中,并且我想要删除那些不再使用的Redis存储对象。 - maxwell2022
在这种情况下,Redis数据库为0至12。您可以使用SELECT命令选择数据库(例如选择10)。将数据加载到一个数据库中,然后使用FLUSHDB从该数据库中删除所有键。确保使用FLUSHDB而不是FLUSHALL,因为FLUSHALL会从所有数据库中删除所有键,而FLUSHDB将从选择的数据库中删除所有键。 - Jack Daniel's

13

仅供寻找PHP代码的人参考,这是我最终使用的代码:

// Create a post hash
$key = 'post:'.$post->getId();
$this->redis->hSet($key, 'data', serialize($post->toArray()));

// Add a post in the account posts SET
$this->redis->sAdd($account->getId().':posts', $post->getId());

// You can execute the above code as many time as you need 
// to add an object in a SET

// Fetch the first $limit posts for this account
// SORT <account_id>:posts BY nosort GET <account_id>:post:*->data
$key = $account->getId().':posts';
$keys = $this->redis->sort($key, array(
    'by' => 'nosort',
    'limit' => array($offset, $limit),
    'get' => 'post:*->data'
));

// All Good !
var_dump($keys);

希望这能对你们中的一些人有所帮助 ;)


你应该检查一下 igbinary:$igbinary = function_exists('igbinary_serialize'); $encoded = $igbinary ? igbinary_serialize($data) : serialize($data);它应该比常规的 serialize 稍微快一些。 - Seb

11

在PHP中,您只需执行以下操作:

$redis->set($key, json_encode($value));

那么

$value = json_decode($redis->get($key));

或者使用您喜欢的任何序列化技术。对我而言,JSON编码/解码足够高效,使我不必关心。


1
但是当您需要针对数千个结果执行此操作时,它可能会使用比必要更多的资源。使用仅基于 Redis 的解决方案很少会让您陷入性能问题。 - halfpastfour.am
@BobKruithof 是的,绝对没错。这个答案有点像肮脏的黑客技巧,我有点为此感到羞耻。不过当时它确实很好地完成了我的工作。 - Darth Egregious

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