2011-03-16 19 views
6

Estoy empezando a utilizar Redis, y me he encontrado con el siguiente problema.Autoincremento en Redis

Tengo un montón de objetos, digamos Messages en mi sistema. Cada vez que un nuevo User conecta, hago lo siguiente:

  1. INCR alguna variable global, digamos g_message_id, y guardar el valor de retorno de INCR (el valor actual de g_message_id).

  2. LPUSH el nuevo mensaje (incluido el id y el mensaje real) en una lista.

Otros clientes utilizan el valor de g_message_id para comprobar si hay nuevos mensajes de conseguir.

El problema es que un cliente podría INCR la g_message_id, pero no tienen tiempo para LPUSH el mensaje antes de que otro cliente intenta leerlo, suponiendo que hay un mensaje nuevo.

En otras palabras, estoy buscando una manera de hacer el equivalente a agregar filas en SQL y tener un índice autoincrementado para trabajar.

Notas:

no puedo usar los índices de la lista, ya que a menudo tienen que eliminar partes de la lista, por lo que es válido.

Mi situación en realidad es un poco más compleja, esta es una versión más simple.

solución actual:

La mejor solución que he llegado con y lo que planeo hacer es utilizar WATCHTransactions y para tratar de realizar una "incremento automático" a mí mismo.

Pero este es un caso de uso tan común en Redis que me sorprende que no exista una respuesta, así que me preocupa que esté haciendo algo mal.

Respuesta

7

Si leo correctamente, está usando g_message_id como secuencia de identificación y como indicador para indicar que hay nuevos mensajes disponibles. Una opción es dividir esto en dos variables: una para asignar identificadores de mensajes y la otra como una bandera para indicar a los clientes que hay un nuevo mensaje disponible.

Los clientes pueden comparar el valor/previo actual de g_new_message_flag saber cuando nuevos mensajes están disponibles:

> INCR g_message_id 
(integer) 123 

# construct the message with id=123 in code 

> MULTI 
OK 
> INCR g_new_message_flag 
QUEUED 
> LPUSH g_msg_queue "{\"id\": 123, \"msg\": \"hey\"}" 
QUEUED 
> EXEC 

alternativa posible, si sus clientes pueden apoyarlo: Es posible que desee ver en el los comandos Redis publish/subscribe, p. ej. Los clientes pueden publicar notificaciones de nuevos mensajes y suscribirse a uno o más canales de mensajes para recibir notificaciones. Puede mantener el g_msg_queue para mantener una acumulación de N mensajes para clientes nuevos, si es necesario.

actualización basado en el comentario: Si desea que cada cliente para detectar hay mensajes disponibles, el pop de todos los que están disponibles, y cero fuera de la lista, una opción es utilizar una transacción para leer la lista:

# assuming the message queue contains "123", "456", "789".. 
# a client detects there are new messages, then runs this: 

> WATCH g_msg_queue 
OK 
> LRANGE g_msg_queue 0 100000 
QUEUED 
> DEL g_msg_queue 
QUEUED 
> EXEC 
1) 1) "789" 
    2) "456" 
    3) "123" 
2) (integer) 1 

actualización 2: Teniendo en cuenta la nueva información, esto es lo que yo haría:

  1. en tus clientes utilizan escritor RPUSH a anexar nuevos mensajes a la lista. Esto permite que los clientes de los lectores comiencen en 0 e iteren sobre la lista para recibir nuevos mensajes.
  2. Los lectores solo deben recordar el índice del último mensaje que extrajeron de la lista.
  3. Los lectores miran g_new_message_flag para saber cuándo sacar de la lista.
  4. Cada cliente de lector utilizará "Límite de índice de lista LRANGE" para recuperar los mensajes nuevos. Supongamos que un cliente lector ha visto un total de 5 mensajes, se ejecutará "LRANGE g_msg_queue 5 15" para obtener los próximos 10 mensajes. Supongamos que se devuelven 3, por lo que recuerda el índice . Puede establecer el límite tan grande como desee y puede recorrer la lista en pequeños lotes.
  5. El cliente debe establecer un segador RELOJ en la lista y eliminarlo dentro de una transacción, abortando cualquier cliente si está leyendo al mismo tiempo de la misma.
  6. Cuando un cliente lector intenta LRANGE y obtiene 0 mensajes, puede asumir que la lista se ha truncado y restablecer su índice a .
+0

Interesante. Esto lleva a un problema relacionado: cuando un cliente sondea el servidor con su copia de "g_new_message_flag", ¿cómo recupera el servidor los mensajes de la lista? Simplemente puede extraer una cantidad de mensajes basados ​​en la diferencia entre "g_new_message_flag" y el indicador de los usuarios, pero si se agregan nuevos mensajes entre la operación de resta y el LPOP, aparecerá una cantidad incorrecta de mensajes. –

+0

Veo lo que quiere decir: si se supone que un cliente debe mostrar todos los mensajes disponibles y poner a cero la lista, eso es más complicado. Actualizaré mi respuesta con algunos pensamientos. – samplebias

+0

En realidad, no quiero que el cliente elimine los mensajes, solo obtenga los últimos mensajes. Tengo varios clientes diferentes con (posiblemente) diferentes valores de g_msg_queue, y cada uno necesita leer los últimos mensajes que han llegado. La lista solo se elimina periódicamente mediante un proceso en segundo plano (por ejemplo, una vez al día). –

3

¿Realmente necesita identificadores secuenciales únicos? Puede usar UUIDs para la exclusividad y las marcas de tiempo para verificar si hay mensajes nuevos. Si mantiene los relojes sincronizados correctamente en todos sus servidores, las marcas de tiempo con una resolución de un segundo deberían funcionar bien.

Si realmente necesita identificadores secuenciales únicos, probablemente tendrá que configurar un Flickr style ticket server para administrar adecuadamente la lista central de ID. Esto, esencialmente, movería su g_message_id a una base de datos con el manejo adecuado de las transacciones.

+0

Resolvió mi problema ... Los UUID son perfectamente adecuados para mi caso – FloatingRock