sql >> Base de Datos >  >> NoSQL >> Redis

Redis `SCAN`:¿cómo mantener un equilibrio entre las nuevas claves que podrían coincidir y garantizar un resultado final en un tiempo razonable?

Primero algo de contexto, solución al final :

Desde comando SCAN> Garantía de terminación

Se garantiza que el algoritmo SCAN terminará solo si el tamaño de la colección iterada permanece limitado a un tamaño máximo dado; de lo contrario, iterar una colección que siempre crece puede dar como resultado que SCAN nunca termine una iteración completa.

Esto es fácil de ver intuitivamente:si la colección crece, hay más y más trabajo por hacer para visitar todos los elementos posibles, y la capacidad de terminar la iteración depende de la cantidad de llamadas a SCAN y su valor de opción COUNT en comparación con la tasa en que crece la colección.

Pero en la opción COUNT dice:

Importante:no hay necesidad de usar el mismo valor COUNT para cada iteración. La persona que llama es libre de cambiar el recuento de una iteración a la otra según sea necesario, siempre que el cursor pasado en la siguiente llamada sea el obtenido en la llamada anterior al comando.

Importante a tener en cuenta, desde Scan garantiza:

  • Un elemento dado puede devolverse varias veces. Depende de la aplicación manejar el caso de elementos duplicados, por ejemplo, usar solo los elementos devueltos para realizar operaciones que son seguras cuando se vuelven a aplicar varias veces.
  • Los elementos que no estuvieron constantemente presentes en la colección durante una iteración completa, pueden ser devueltos o no:no está definido.

La clave para una solución está en el propio cursor. Consulte Dar sentido al cursor ESCANEAR de Redis. Es posible deducir el porcentaje de progreso de su exploración porque el cursor es realmente los bits invertidos de un índice del tamaño de la tabla.

Usando DBSIZE o INFO keyspace comando puede obtener cuántas llaves tiene en cualquier momento:

> DBSIZE
(integer) 200032
> info keyspace
# Keyspace
db0:keys=200032,expires=0,avg_ttl=0

Otra fuente de información es el DEBUG htstats index no documentado. , solo para tener una idea:

> DEBUG htstats 0
[Dictionary HT]
Hash table 0 stats (main hash table):
 table size: 262144
 number of elements: 200032
 different slots: 139805
 max chain length: 8
 avg chain length (counted): 1.43
 avg chain length (computed): 1.43
 Chain length distribution:
   0: 122339 (46.67%)
   1: 93163 (35.54%)
   2: 35502 (13.54%)
   3: 9071 (3.46%)
   4: 1754 (0.67%)
   5: 264 (0.10%)
   6: 43 (0.02%)
   7: 6 (0.00%)
   8: 2 (0.00%)
[Expires HT]
No stats available for empty dictionaries

El tamaño de la mesa es la potencia de 2 según tu número de claves:Claves:200032 => Tamaño de la mesa:262144

La solución:

Calcularemos un COUNT deseado argumento para cada escaneo.

Digamos que llamará a SCAN con una frecuencia (F en Hz) de 10 Hz (cada 100 ms) y quieres que se haga en 5 segundos (T En s). Entonces quieres que esto termine en N = F*T llamadas, N = 50 en este ejemplo.

Antes de su primer escaneo, sabe que su progreso actual es 0, por lo que su porcentaje restante es RP = 1 (100%).

Antes de cada SCAN llamada (o cada número dado de llamadas que desea ajustar su COUNT si desea guardar el tiempo de ida y vuelta (RTT) de un DBSIZE llamada), llamas a DBSIZE para obtener el número de llaves K .

Usará COUNT = K*RP/N

Para la primera llamada, esto es COUNT = 200032*1/50 = 4000 .

Para cualquier otra llamada, debe calcular RP = 1 - ReversedCursor/NextPowerOfTwo(K) .

Por ejemplo, digamos que ya ha realizado 20 llamadas, ahora N = 30 (número restante de llamadas). Llamaste a DBSIZE y obtuve K = 281569 . Esto significa NextPowerOfTwo(K) = 524288 , esto es 2^19.

Tu siguiente cursor es 14509 en decimal =000011100010101101 en binario. Como el tamaño de la tabla es 2^19, la representamos con 18 bits.

Inviertes los bits y obtienes 101101010001110000 en binario =185456 en decimal. Esto significa que hemos cubierto 185456 de 524288. Y:

RP = 1 - ReversedCursor/NextPowerOfTwo(K) = 1 - 185456 / 524288 = 0.65 or 65%

Así que tienes que ajustar:

COUNT = K*RP/N = 281569 * 0.65 / 30 = 6100

Así que en tu próximo SCAN llamarte usa 6100 . Tiene sentido que haya aumentado porque:

  • La cantidad de claves ha aumentado de 200032 a 281569.
  • Aunque solo tenemos el 60% de nuestra estimación inicial de llamadas restantes, el progreso está retrasado ya que el 65% del espacio de claves está pendiente de escanear.

Todo esto suponiendo que está recibiendo todas las claves. Si estás haciendo coincidir patrones , debe usar el pasado para estimar la cantidad restante de claves que se encontrarán. Agregamos como factor PM (porcentaje de coincidencias) al COUNT cálculo.

COUNT = PM * K*RP/N

PM = keysFound / ( K * ReversedCursor/NextPowerOfTwo(K))

Si después de 20 llamadas, solo ha encontrado keysFound = 2000 llaves, entonces:

PM = 2000 / ( 281569 * 185456 / 524288) = 0.02

Esto significa que solo el 2 % de las claves coinciden con nuestro patrón hasta ahora, por lo que

COUNT = PM * K*RP/N = 0.02 * 6100 = 122

Este algoritmo probablemente se puede mejorar, pero entiendes la idea.

Asegúrese de ejecutar algunos puntos de referencia en el COUNT número que usará para comenzar, para medir cuántos milisegundos es su SCAN tomando, ya que es posible que deba moderar sus expectativas sobre cuántas llamadas necesita (N ) para hacer esto en un tiempo razonable sin bloquear el servidor, y ajuste su F y T en consecuencia.