diff --git a/中间件/redis/redis.md b/中间件/redis/redis.md index 2d11454..924cfa5 100644 --- a/中间件/redis/redis.md +++ b/中间件/redis/redis.md @@ -65,6 +65,12 @@ - [SREM](#srem) - [SPOP](#spop) - [SRANDMEMBER](#srandmember) + - [redis hashes](#redis-hashes) + - [对象表示](#对象表示) + - [counters](#counters) + - [Field Expiration](#field-expiration) + - [Common field expiration use cases](#common-field-expiration-use-cases) + - [Field Expiration examples](#field-expiration-examples) # redis @@ -950,3 +956,120 @@ SDIFF命令在`difference between all sets is empty`时,会返回一个empty a "bike:2" ``` +### redis hashes +redis hashes为记录`field-value pair`集合的数据结构,可以使用hashes来表示基本对象或存储counter的分组,示例如下: +#### 对象表示 +```redis-cli +> HSET bike:1 model Deimos brand Ergonom type 'Enduro bikes' price 4972 +(integer) 4 +> HGET bike:1 model +"Deimos" +> HGET bike:1 price +"4972" +> HGETALL bike:1 +1) "model" +2) "Deimos" +3) "brand" +4) "Ergonom" +5) "type" +6) "Enduro bikes" +7) "price" +8) "4972" +``` +通常来讲,可以存储在hash中的fields数量并没有限制。 + +命令`HSET`可用于向hash中设置多个fields,而命令`HGET`可以用于获取一个field,`HMGET`可以用于获取多个field。 + +```redis-cli +> HMGET bike:1 model price no-such-field +1) "Deimos" +2) "4972" +3) (nil) +``` + +同样的,hash结构支持对单个field进行操作,例如`HINCRBY` +```redis-cli +> HINCRBY bike:1 price 100 +(integer) 5072 +> HINCRBY bike:1 price -100 +(integer) 4972 +``` +#### counters +将hash用于存储counters分组的示例如下所示: +```redis-cli +> HINCRBY bike:1:stats rides 1 +(integer) 1 +> HINCRBY bike:1:stats rides 1 +(integer) 2 +> HINCRBY bike:1:stats rides 1 +(integer) 3 +> HINCRBY bike:1:stats crashes 1 +(integer) 1 +> HINCRBY bike:1:stats owners 1 +(integer) 1 +> HGET bike:1:stats rides +"3" +> HMGET bike:1:stats owners crashes +1) "1" +2) "1" +``` +#### Field Expiration +在`redis open source 7.4`中,支持为独立的hash field指定超时: +- `HEXPIRE`: set the remaining TTL in seconds +- `HPEXPIRE`: set the remaining TTL in milliseconds +- `HEXPIREAT`: set expiration time to a timestamp specified in seconds +- `HPEXPIREAT`: set the expiration time to a timestamp specified in milliseconds + +如上所示,在指定超时时,可以通过时间戳来指定,也可以通过TTL来指定。 + +同时,获取超时事件也可以通过`时间戳`和`TTL`来获取: +- `HEXPIRETIME`: get the expiration time as timestamp in seconds +- `HPEXPIRETIME`: get the expiration time as timestamp in milliseconds +- `HTTL`: get the remaining ttl in seconds +- `HPTTL`: get the remaining ttl in milliseconds + +如果想要移除指定hash field的expration,可以通过如下方式: +- `HPERSIST`: 移除hash field的超时 + +##### Common field expiration use cases +- `Event Tracking`:使用hash key来存储`最后一小时的事件`。其中,field为每个事件,而设置事件field的ttl为1小时,并可使用`HLEN`来统计最后一小时的事件数量。 +- `Fraud Detection`:通常,用户行为进行分析时,可按小时记录用户的事件数量。可通过hash结果记录过去48小时中每小时的操作数量,其中hash field代表用户某一个小时内的操作数。每个hash field的过期时间都为48h。 +- `Customer session management`: 可以通过hash来存储用户数据。为每个session创建一个hash key,并且向hash key中添加session field。当session过期时,自动对session key和session field进行expire操作。 +- `Active Session Tracking`: 将所有的active sessions存储再一个hash key中。每当session变为inactive时,将session的TTL设置为过期。可以使用`HLEN`来统计活跃的sessions数量。 + +##### Field Expiration examples +`对于hash field ixpiration的支持在官方client libraries中尚不可用`,但是可以在`python(redis-py)`和`java(jedis)`的beta版本client libraries中使用。 + +如下python示例展示了如何使用field expiration: +```py +event = { + 'air_quality': 256, + 'battery_level':89 +} + +r.hset('sensor:sensor1', mapping=event) + +# set the TTL for two hash fields to 60 seconds +r.hexpire('sensor:sensor1', 60, 'air_quality', 'battery_level') +ttl = r.httl('sensor:sensor1', 'air_quality', 'battery_level') +print(ttl) +# prints [60, 60] + +# set the TTL of the 'air_quality' field in milliseconds +r.hpexpire('sensor:sensor1', 60000, 'air_quality') +# and retrieve it +pttl = r.hpttl('sensor:sensor1', 'air_quality') +print(pttl) +# prints [59994] # your actual value may vary + +# set the expiration of 'air_quality' to now + 24 hours +# (similar to setting the TTL to 24 hours) +r.hexpireat('sensor:sensor1', + datetime.now() + timedelta(hours=24), + 'air_quality') +# and retrieve it +expire_time = r.hexpiretime('sensor:sensor1', 'air_quality') +print(expire_time) +# prints [1717668041] # your actual value may vary +``` +