Предложения по разработке геопроектов с использованием Redis

Я участвую в процессе обучения Redis и создаю гео-программу для обучения. Я хотел бы использовать Redis только для хранения данных и пытаюсь избежать реляционных баз данных. Мой вопрос заключается в том, как лучше всего создавать базу данных для программы. Вот как работает программа:

1) Я создам миллионы случайных роботов по всему миру, которые блуждают, чтобы они могли иметь разные гео-координаты (некоторые роботы могут находиться в одном и том же пространстве).

2) Каждый робот будет случайным образом отправлять сообщение на сервер (каждые несколько часов, возможно, в среднем), который будет содержать:   a) местоположение того места, где робот отправил эти данные (в любой из координат или geohash в зависимости от лучшей идеи реализации)   б) небольшой текст

3) Я буду иметь карту со всеми роботами и хотел бы иметь возможность нажимать на робота и получать эту информацию:   а) все сообщения, которые были размещены рядом с роботом, который я только что нажал

4) В связи с тем, что я буду размещать это на AWS, мне нужно будет удалять сообщения каждые пару часов, чтобы поддерживать низкий уровень использования памяти, поэтому какой-либо тип истечения является обязательным.

Моя основная забота - производительность, и меня интересует, как создать базу данных Redis.

В течение одного дня (я разработаю математику для случайных сообщений, чтобы сделать это) будет создано около 500 000 000 сообщений.

Мои незавершенные идеи:

Идея 1

1) сообщение будет храниться как таковое:

`HSET [Geohash of location] [timestamp] [small text] (<-- the value will be used in a later feature to increment the number of manual modification I make to a post).

2) Затем я смог бы получить все сообщения рядом с роботом, отправив место географии, в котором он находится. Крушение здесь - мне также нужно будет включить его 8 соседей-геохашей, для которых потребуется еще 8 запросов. Именно поэтому я также рассматриваю концепцию пространственной близости для этой функции.

HGETALL [GeoHash Location of robot] 

Затем будет возвращено поле ([timestamp]) и значение ( "0" );

3) Истечение старых сообщений. Поскольку я не могу использовать команду EXPIRE для удаления полей из hashset, мне тогда нужно будет периодически проверять все поля hashset и находить старые временные метки и удалять их. Поскольку Redis разрешает поиск шаблонов, это может быть затруднено, когда все временные метки отличаются.

Идея 2:

Использовать Redis-geo (https://matt.sh/redis-geo).

1) Чтобы сохранить сообщения, которые я запускал:

geoadd globalSet [posts_long] [posts_lat] "small text";

2) Чтобы получить всю информацию о записи для робота поблизости:

georadius globalSet [robots_long] [robots_lat] [X] km

Это вернет все сообщения рядом с роботом в пределах X км.

3) Затем я застрял, как удалить старые сообщения

Ответы

Ответ 1

Позвольте мне дать вам базовую идею о том, как я понял вашу проблему:

Вместо сохранения значений в хэш просто сохраните все в redis. Постройте ключ как GeoLocation: [Geohash location robot]: 1 [с указанием количества сообщений, это будет продолжать увеличиваться при каждом новом запросе]: отметка времени и значение будут отметкой времени. Аналогично для небольшого текста GeoLocation: [Местоположение Geohash робота]: 1 [с указанием номера сообщения]: smallText. Используйте set expire, чтобы установить значения и установить время истечения времени как ваше желание.

Пример: setex GeoLocation: 12.31939: 1: временная метка 1432423232 (временная метка) 14400 (4 часа)   setex География: 12.31939: 1: smalltext ronaldo 14400

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

Теперь, чтобы получить всю информацию, размещенную определенным роботом, используйте ключи GeoLocation: (расположение конкретного робота): * и получите значения каждого из них.

Таким образом, вам не нужно сканировать все ключи в redis. Вы получите информацию относительно быстрее, и ключи устареют сами.

Ответ 2

Одна идея, которую я убираю из описания, - это то, что вы будете знать текущее местоположение данного "робота", и вы хотели бы найти других мобильных пользователей рядом с ним в режиме реального времени, но вам также понравится некоторый бит исторической информации о местоположении.

Мне нравится думать о redis как об разоблачении исходных блоков для создания базы данных более высокого уровня. Имея это в виду, вы понимаете, что вам нужно создавать и поддерживать свои собственные функции базы данных более высокого уровня, такие как индексы и т.д.

Поскольку этот тип данных будет в первую очередь доступен, если у вас есть определенный робот, я бы рекомендовал хранить историю местоположений и метаданные для бота в ключе на основе уникального идентификатора бота, а не его местоположения.

Затем сохраните это относительное местоположение (или любую другую группировку) другим, управляя его идентификатором в наборах или хэшах, которые группируют ботов в определенном месте. Вы можете использовать несколько наборов или вложенных структур данных для своего уровня детализации.

Сохраняйте целостность данных, обновляя ботовую запись и информацию о местоположении как часть redis transaction. Используйте pipelining для эффективности.

Вам не нужно использовать expire для старых сообщений, так как вы можете управлять размером базы данных, ограничивая количество исторических записей в основной записи бота. Когда вы переходите к обновлению бота, просто выполняйте над ним какую-то операцию очистки, когда он преодолевает определенную длину (llen, slen, hlen и т.д.), Чтобы дать вам прогнозируемый/регулируемый размер совокупных данных.

Если есть какая-то надежда, что вы делаете, это может стать продукцией. Я настоятельно рекомендую рассмотреть partitioning вне ворот. Любой уровень успеха потребует его, поэтому он может также сделать это впереди. Доверьтесь мне. В этом случае я бы разбился функционально (местоположение по сравнению с состоянием робота... разными базами данных в разных группах репликации), а также с помощью ключа (хэш или что-то еще... чтобы разбить 500M на разумные куски).

Разделение делает транзакции жесткими, но для вашего случая использования я сомневаюсь, что это прерыватель транзакции. Использование redis messaging в сочетании с транзакциями позволяет вам сохранить целостность, выполняя различные обновления программно.

Наконец, я бы подумал о чем-то, кроме redis (эластик в вашем случае, который я предполагаю). В спектре поддержки concurrency и способности делать сложные запросы redis отлично подходит для первого. По этой причине он идеально подходит для отслеживания сеансов или подобного состояния.

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

Если вам нужно связать объекты друг с другом (запросы), уметь поддерживать аналитику и т.д., с пользователями 500M вы сможете позволить себе большой кластер с красным смещением, динамо или аналогичный. Можете поставить кинезис впереди, чтобы помочь с помощью concurrency, доставляя небольшие сообщения вверх для массовой загрузки. Как красное смещение, так и динамо-экшн выигрывают от специальных путей загрузки от кинезиса.

Определенно хочу держаться подальше от RDS, но есть и другие варианты, которые проще реализовать и помогут вам избежать этого неизбежного дня, когда вам придется перебирать кластер redis (для которого вы бы использовали scan).

(Старый пост, который я знаю, но интересный вопрос, и ответ относится ко многим ситуациям.)

Ответ 3

Обратите внимание, что с v3.2 Redis поддерживает Geosets с помощью команды GEOADD.

Ответ 4

Как системный джойк указал, что у search-redismodules.com есть модуль поиска. Он также имеет случайный лесной модуль для поиска ближайших соседей.

Ответ 5

Хорошо, отделим наши задачи:

  • Нам нужен индекс, содержащий все наши роботы, поэтому мы можем перебирать их
  • Возможно, нам нужно будет сохранить некоторую общую информацию о нашем роботе
  • Нам нужно хранить гео-историю для каждого робота
  • Нам нужно очистить старые данные каждые X

1) Позволяет сделать ZSET, который содержит идентификатор робота, и его SCORE будет последней меткой времени активности, в будущем мы сможем удалить неактивные роботы с помощью этого индекса.

ZADD ZSET:ROBOTS <timestamp> robot:17 или событие лучше просто 17 без robot:, потому что redis будет хранить целые числа как 4 байта в ОЗУ.

2) Позволяет хранить нашу общую информацию об роботе в HSET

HSET HSET:ROBOT:17 name "Best robot ever #17" model "Terminator T-800"

3) Обычно мы можем использовать несколько способов его сохранения, например, мы можем использовать регулярный ZSET с использованием метода многомерных индексов (https://redis.io/topics/indexes#multi-dimensional-indexes), но это очень сложно понять, поэтому позволяет использовать более простой redis GEO

GEOADD GEO:ROBOT:17 13.361389 38.115556 "<timestamp>:<message-data>"

Внутри GEO используется обычный ZSET, поэтому мы можем легко перебирать его с помощью команд ZRANGE или ZRANGEBYSCORE.

И, конечно же, мы можем использовать команды GEO, такие как GEORADIUS для наших нужд.

4) Процесс очистки. Я предлагаю очистить по времени, но вы можете сделать это так же по количеству записей, просто используйте ZRANGE вместо ZRANGEBYSCORE

Давайте найдем все наши неактивные роботы, которые были неактивны по крайней мере неделю.

ZRANGEBYSCORE ZSET:ROBOTS -inf <timestamp-of-week-before>

Теперь нам нужно перебрать эти идентификаторы и удалить ненужные ключи HSET, GEO и удалить их из нашего index

ZREM ZSET:ROBOTS 17
DEL HSET:ROBOT:17
DEL GEO:ROBOT:17

Теперь нам нужно удалить только старые записи GEO-истории, как я сказал выше. GEO в redis является регулярным ZSET под капотом, поэтому позволяет использовать ZRANGE

ZRANGE GEO:ROBOT:17 0 -1

Мы получим список записей, но он будет отсортирован странно из-за GEO, каждый score будет GEO location.

Наши записи отформатированы как ":", поэтому мы можем использовать split(':') и сравнивать временную метку, если она старая, мы удаляем ее. Например, наша метка времени 12345678, а сообщение hello

ZDEL GEO:ROBOT:17 1234567:hello

P.S. Я настоятельно рекомендую вам прочитать эту замечательную статью о ZSET в redis https://redis.io/topics/indexes

Короче: Redis сортирует элементы не только по счету, но и по имени ключа, это означает, что записи с одинаковой оценкой будут отсортированы по алфавиту, что очень полезно!

ZADD key 0 ccc 0 bbb 0 aaa
ZRANGE key 0 -1

вернет вам отсортированный набор:

  • "ааа"
  • "БББ"
  • "ссс"