database-design redis geolocation geospatial nosql

database design - Sugerencias de diseño del programa Geo usando Redis



redis documentation (5)

Estoy en el proceso de aprender Redis y estoy construyendo un programa geográfico para propósitos de aprendizaje. Me gustaría usar solo Redis para almacenar los datos y estoy tratando de evitar cualquier base de datos relacional. Mi pregunta es cómo diseñar mejor la base de datos para el programa. Así es como va el programa:

1) Crearé millones de robots aleatorios en todo el mundo que deambulan para que puedan tener diferentes coordenadas geográficas (algunos robots pueden estar en el mismo espacio).

2) Cada robot enviará aleatoriamente una publicación al servidor (cada pocas horas en promedio) que contendrá: a) la ubicación desde donde el robot envió estos datos (en coordenadas o geohash, según la mejor idea de implementación) ) algún texto pequeño

3) Tendré un mapa con todos los robots y me gustaría poder hacer clic en un robot y obtener esta información: a) todas las publicaciones que se publicaron cerca del robot que acabo de hacer clic

4) Debido al hecho de que alojaré esto en AWS, tendré que eliminar las publicaciones cada dos horas para mantener el uso de memoria bajo, por lo que es obligatorio algún tipo de caducidad.

Mi principal preocupación es el rendimiento y estoy interesado en cómo diseñar la base de datos Redis.

En un solo día (trabajaré las matemáticas para publicaciones aleatorias para hacer esto) se generarán alrededor de ~ 500,000,000 publicaciones.

Mis ideas incompletas hasta ahora:

Idea 1

1) una publicación sería almacenada como tal:

`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) Entonces podría obtener todas las publicaciones cerca de un robot enviando la ubicación de geohash en la que se encuentra. El problema aquí es que también tendría que incluir a sus 8 vecinos de geohash que requerirán 8 consultas más. Es por eso que también estoy estudiando el concepto de proximidad espacial para esta característica.

HGETALL [GeoHash Location of robot]

Esto devolvería el campo ([timestamp]) y el valor ("0");

3) Vencimiento de los puestos antiguos. Ya que no puedo usar el comando EXPIRE para eliminar campos de un hashset, entonces necesitaría escanear todos los campos de hashset periódicamente y encontrar marcas de tiempo antiguas y eliminarlas. Dado que Redis solo permite la búsqueda de patrones, esto podría ser difícil cuando todas las marcas de tiempo son diferentes.

Idea 2:

Utilice Redis-geo ( https://matt.sh/redis-geo ).

1) Para almacenar las publicaciones que correría:

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

2) Para obtener toda la información de la publicación de un robot cercano:

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

Esto devolvería todos los mensajes cerca del robot dentro de X kms.

3) Ahora estoy atascado en cómo eliminar las publicaciones antiguas


Como señaló Systemjack sobre search- redismodules.com tiene un módulo de búsqueda. Tiene un módulo de bosque al azar para encontrar vecinos más cercanos también.


Ok, vamos a separar nuestras tareas:

  1. Necesitamos un índice que contenga todos nuestros robots, por lo que podemos iterar sobre ellos
  2. Probablemente necesitaremos almacenar información genérica sobre nuestro robot.
  3. Necesitamos almacenar geohistoria para cada robot.
  4. Necesitamos limpiar datos antiguos cada X

1) Permite hacer un ZSET que contenga un ID de robot y su puntuación (SCORE) será la última marca de tiempo de actividad, en el futuro podremos eliminar robots no activos utilizando este índice.

ZADD ZSET:ROBOTS <timestamp> robot:17 o, mejor aún, solo 17 sin robot: debido a redis almacenará enteros como 4 bytes en la RAM.

2) Permite almacenar nuestra información genérica robot en HSET

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

3) En general, podemos usar varias formas de almacenarlo, por ejemplo, podemos tomar ZSET regular utilizando la técnica de índices multidimensionales ( https://redis.io/topics/indexes#multi-dimensional-indexes ), pero es muy complicado de entender. , así que vamos a usar más simple redis GEO

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

Internamente, GEO utiliza ZSET regular, por lo que podemos iterarlo fácilmente con los comandos ZRANGE o ZRANGEBYSCORE.

Y, por supuesto, podemos usar comandos GEO como GEORADIUS para nuestras necesidades.

4) El proceso de limpieza. Le sugiero que realice una limpieza por tiempo, pero puede hacerlo de la misma manera por número de entradas, simplemente use ZRANGE lugar de ZRANGEBYSCORE

Permite encontrar todos nuestros robots no activos que no estaban activos por lo menos una semana.

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

Ahora necesitamos iterar sobre esas ID y eliminar las claves HSET, GEO no necesarias y eliminarlas de nuestro index

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

Ahora necesitamos eliminar solo las entradas antiguas del historial de GEO, como dije anteriormente. GEO en redis es un ZSET normal debajo del capó, así que vamos a usar ZRANGE

ZRANGE GEO:ROBOT:17 0 -1

Obtendremos una lista de entradas, pero se ordenará extraño debido a GEO, cada score será la GEO location .

Nuestras entradas tienen el formato ":", por lo que podemos usar split('':'') y comparar la marca de tiempo, si lo eliminamos con anterioridad. Por ejemplo, nuestra marca de tiempo es 12345678 y el mensaje es hello

ZDEL GEO:ROBOT:17 1234567:hello

PS Le recomiendo que lea este artículo impresionante sobre ZSET en redis https://redis.io/topics/indexes

En resumen: los elementos de clasificación de Redis no solo por puntuación, sino también por nombre de clave, esto significa que las entradas con la misma puntuación se ordenarán alfabéticamente, ¡lo cual es muy útil!

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

le devolverá ordenado conjunto:

  1. "aaa"
  2. bbb
  3. "ccc"

Permítame darle una idea base sobre cómo entendí su problema:

En lugar de almacenar valores en hash, simplemente almacene todo en redis. Construya la clave como GeoLocation: [Geohash location of robot]: 1 [indicando el número de publicaciones, esto seguirá aumentando cada vez que se presente una nueva solicitud]: la marca de tiempo y el valor serán la marca de tiempo. Similarmente para texto pequeño GeoLocation: [Geohash location of robot]: 1 [indicando el número de post]: smallText. Utilice set expire para establecer los valores y establecer el tiempo de caducidad como desee.

Ej: setex GeoLocation: 12.31939: 1: timestamp 1432423232 (timestamp) 14400 (4 hrs) setex GeoLocation: 12.31939: 1: smalltext ronaldo 14400

Por lo tanto, recibirá una cantidad ilimitada de publicaciones de todos los robots con una clave distinta para acceder y la configuración caducará también se ha vuelto fácil.

Ahora para obtener toda la información publicada por un robot en particular, use las teclas GeoLocation: (ubicación de un robot en particular): * y obtenga los valores de cada uno.

De esta manera, no es necesario escanear todas las teclas en redis. Obtendrá la información relativamente más rápido y las claves caducarán por sí mismas.


Tenga en cuenta que a partir de v3.2 Redis admite Geosets con el comando GEOADD .


Una idea que tomo de la descripción es que sabrás la ubicación actual de un "robot" determinado y te gustaría encontrar otros usuarios móviles cerca de ella en tiempo real, pero también te gustaría tener un poco de ubicación histórica información.

Me gusta pensar en redis como la exposición de componentes básicos para crear una base de datos de nivel superior. Teniendo esto en cuenta, se da cuenta de que necesita crear y mantener sus propias funciones de base de datos de nivel superior como índices, etc.

Dado que se tendrá acceso a este tipo de datos principalmente cuando tenga en mente un robot específico, recomendaría almacenar el historial de ubicación y los metadatos de un bot en una clave basada en el identificador único del bot y no en su ubicación.

Luego, mantenga su ubicación relativa (o cualquier otra agrupación) con otras personas mediante la gestión de su ID en conjuntos o hashes que agrupan los robots en una ubicación determinada. Puede usar conjuntos múltiples o estructuras de datos anidadas para un nivel de capacidad de nivel de detalle.

Mantenga la integridad de sus datos actualizando el registro del bot y la información de ubicación como parte de una transaction redis. Use pipelining para la eficiencia.

No es necesario que expire publicaciones anteriores, ya que puede administrar el tamaño de su base de datos al limitar el recuento de entradas históricas en el registro principal del robot. Cuando vaya a actualizar un bot, simplemente realice algún tipo de operación de limpieza cuando alcance una cierta longitud (llenador, slen, hlen, etc.) para darle un tamaño de datos agregados predecible / ajustable.

Si hay alguna esperanza de que lo que está haciendo pueda convertirse en producción, recomiendo encarecidamente considerar la partitioning fuera de la puerta. Cualquier nivel de éxito lo requerirá, así que también puede hacerlo por adelantado. Créeme. Para este caso, particionaría funcionalmente (ubicación frente a estado de robot ... diferentes bases de datos en diferentes grupos de replicación), así como por clave (hash o lo que sea ... para dividir sus 500M en partes razonables).

La partición hace que las transacciones sean difíciles, pero para su caso de uso, dudo que sea un factor decisivo. El uso de la mensajería de redis junto con las transacciones puede permitirle mantener su integridad al ejecutar varias actualizaciones de manera programática.

Finalmente, consideraría algo además de redis (supongo que hay elasticache en su caso). En el espectro de soporte para la concurrencia y la capacidad de realizar consultas complejas, redis es ideal para las primeras. Por esa razón, es perfecto para realizar un seguimiento de las sesiones o estado similar.

Requerirás mucha concurrencia, pero en su mayor parte es agregada y no se actualiza. Así que no como una máquina de estados en evolución. Y necesitas al menos alguna habilidad para buscar.

Si necesita relacionar objetos entre sí (consultas), ser capaz de admitir análisis, etc., con 500 millones de usuarios podría permitirse un gran clúster de corrimiento al rojo, dinamo o similar. Podría poner kinesis al frente para ayudar con la concurrencia al agrupar los mensajes pequeños para una carga masiva. Tanto el desplazamiento al rojo como el dínamo se benefician de las rutas de carga especiales de la kinesis.

Definitivamente, querrá mantenerse alejado de RDS, pero hay otras opciones que serían más sencillas de implementar y le ayudarían a evitar ese día inevitable cuando tenga que recorrer su grupo de redis (para el cual usaría el scan obviamente).

(Antiguo mensaje que conozco, pero una pregunta interesante y la respuesta se aplica a muchas situaciones).