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

Redis diseño clave para la aplicación de stock en tiempo real

Mi sugerencia es almacenar min/max/total para todos los intervalos que le interesen y actualizarlo para los actuales con cada punto de datos que llega. Para evitar la latencia de la red al leer datos anteriores para compararlos, puede hacerlo completamente dentro del servidor de Redis mediante secuencias de comandos de Lua.

Una clave por punto de datos (o, peor aún, por campo de punto de datos) consumirá demasiada memoria. Para obtener los mejores resultados, debe agruparlo en pequeñas listas/hashes (consulte http://redis.io/topics/memory-optimization). Redis solo permite un nivel de anidamiento en sus estructuras de datos:si sus datos tienen varios campos y desea almacenar más de un elemento por clave, debe codificarlo usted mismo de alguna manera. Afortunadamente, el entorno estándar de Redis Lua incluye compatibilidad con msgpack, que es un formato binario similar a JSON muy eficiente. Las entradas JSON en su ejemplo codificadas con msgpack "tal cual" tendrán una longitud de 52-53 bytes. Sugiero agrupar por tiempo para que tenga 100-1000 entradas por clave. Supongamos que el intervalo de un minuto se ajusta a este requisito. Entonces el esquema de claves sería así:

YYmmddHHMMSS — un hash de tid a puntos de datos codificados con msgpack para el minuto dado.5m:YYmmddHHMM , 1h:YYmmddHH , 1d:YYmmdd — hashes de datos de ventana que contienen min , max , sum campos.

Veamos un script Lua de muestra que aceptará un punto de datos y actualizará todas las claves según sea necesario. Debido a la forma en que funcionan las secuencias de comandos de Redis, debemos pasar explícitamente los nombres de todas las claves a las que accederá la secuencia de comandos, es decir, los datos en vivo y las tres claves de ventana. Redis Lua también tiene una biblioteca de análisis JSON disponible, por lo que, en aras de la simplicidad, supongamos que solo le pasamos el diccionario JSON. Eso significa que tenemos que analizar los datos dos veces:en el lado de la aplicación y en el lado de Redis, pero los efectos de rendimiento no están claros.

local function update_window(winkey, price, amount)
    local windata = redis.call('HGETALL', winkey)
    if price > tonumber(windata.max or 0) then
        redis.call('HSET', winkey, 'max', price)
    end
    if price < tonumber(windata.min or 1e12) then
        redis.call('HSET', winkey, 'min', price)
    end
    redis.call('HSET', winkey, 'sum', (windata.sum or 0) + amount)
end

local currkey, fiveminkey, hourkey, daykey = unpack(KEYS)
local data = cjson.decode(ARGV[1])
local packed = cmsgpack.pack(data)
local tid = data.tid
redis.call('HSET', currkey, tid, packed)
local price = tonumber(data.price)
local amount = tonumber(data.amount)
update_window(fiveminkey, price, amount)
update_window(hourkey, price, amount)
update_window(daykey, price, amount)

Esta configuración puede realizar miles de actualizaciones por segundo, sin demasiada memoria, y los datos de la ventana se pueden recuperar al instante.

ACTUALIZACIÓN:en la parte de la memoria, 50-60 bytes por punto sigue siendo mucho si desea almacenar más de unos pocos millones. Con este tipo de datos, creo que puede obtener tan solo 2-3 bytes por punto utilizando un formato binario personalizado, codificación delta y la posterior compresión de fragmentos utilizando algo como Snappy. Depende de sus requisitos, si vale la pena hacer esto.