Redis, как хранить ассоциативный массив? Набор или хэш или список?

Я немного запутался со всеми доступными вариантами хранения Redis. Я хочу сделать что-то простое и не хочу переусердствовать. Я работаю с phpredis и Redis 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',
    )
);

Итак, что я делал до сих пор, так это использовал тип hash. хранение моего массива следующим образом:

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 в другом списке, чтобы иметь возможность перебирать все сообщения?

Итак, мой вопрос заключается в том, какие типы данных я должен использовать для хранения списка сообщений, что позволяет мне получать одно сообщение по его идентификатору и перебирать все сообщения?

Спасибо, Максим


person maxwell2022    schedule 24.02.2014    source источник


Ответы (3)


Вы можете использовать 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"

Если вы не хотите использовать сортировку, вы можете использовать команду «Выбрать все имена ключей из SET» с помощью SMEMBERS, а затем использовать Redis Pipeline для получения всех ключей.

person Jack Daniel's    schedule 25.02.2014
comment
Значит, нет другого решения, кроме как хранить хэш post в SET? Каковы наилучшие варианты SORT или SMEMBERS, если у меня есть миллионы записей в SET? - person maxwell2022; 25.02.2014
comment
Если вы заметили, что для сортировки я использую nosort, так что на самом деле он не будет сортироваться. Кроме того, идеальной структурой, по моему мнению, является HASH для хранения пары ключ-значение, я уверен, что вы не будете получать миллионы значений за один раз, вы получите только несколько, скажем, 50, тогда вы можете использовать Limit с sort . redis.io/commands/SORT - person Jack Daniel's; 25.02.2014
comment
Что делать, если я хочу удалить весь хэш из этого набора? Должен ли я перебирать их все, чтобы удалить их? - person maxwell2022; 25.02.2014
comment
Вы имеете в виду, что хотите очистить SET или также хотите удалить HASH? - person Jack Daniel's; 25.02.2014
comment
Оба из них. Я хочу удалить SET и все содержащиеся в нем HASH. Я просто использую Redis в качестве буфера при выполнении вычислений, после вычислений я сохраняю результат в MySQL и хочу удалить Redis сохраненных объектов, которые я больше не буду использовать. - person maxwell2022; 25.02.2014
comment
В этом случае перераспределите базу данных с 0 на 12. Вы можете выбрать базу данных с помощью команды SELECT (например, Select 10). Загрузите свои данные в одну базу данных, а затем используйте FLUSHDB, чтобы удалить все ключи из этой базы данных. Убедитесь, что вы используете FLUSHDB, а не FLUSHALL, потому что FLUSHALL удалит все ключи из ВСЕХ БАЗ ДАННЫХ, а FLUSHDB удалит все ключи из ВЫБРАННОЙ БАЗЫ ДАННЫХ. - person Jack Daniel's; 25.02.2014

Просто для тех, кто ищет 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);

Надеюсь, это поможет некоторым из вас ;)

person maxwell2022    schedule 26.02.2014
comment
Вы должны проверить igbinary: $igbinary = function_exists('igbinary_serialize'); $encoded = $igbinary ? igbinary_serialize($data) : serialize($data); Это должно быть немного быстрее, чем обычная сериализация. - person Seb; 29.11.2017

В PHP вы можете просто сделать

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

затем

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

Или используйте любую технику сериализации, которую вы предпочитаете. Кодирование/декодирование JSON было достаточно эффективным, чтобы мне было все равно.

person Darth Egregious    schedule 23.10.2014
comment
Но когда вам нужно сделать это для тысяч результатов, это, вероятно, потребует больше ресурсов, чем необходимо. Использование решения только для redis с меньшей вероятностью приведет к проблемам с производительностью. - person halfpastfour.am; 15.04.2017
comment
@BobKruithof Да, черт возьми. Этот ответ немного грязный, и мне немного стыдно за него. Тем не менее, для того, что я делал в то время, это отлично сработало. - person Darth Egregious; 23.10.2017