redis新特性研究

 

目录

 Redis Set

Options

History

Redis HINCRBY

Redis SetEX

Redis Keyspace Notifications

Redis分布式锁

Redis的原子性

单实例Redis

redis分布式锁

Red-lock分布式锁算法

Redis GeoHash

Redis处理Json

Redis Search

ACL



 Redis Set

127.0.0.1:6379> help set

  SET key value [EX seconds|PX milliseconds|KEEPTTL] [NX|XX]
  summary: Set the string value of a key
  since: 1.0.0
  group: string

Set key to hold the string value. If key already holds a value, it is overwritten, regardless of its type. Any previous time to live associated with the key is discarded on successful SET operation.

Options

The SET command supports a set of options that modify its behavior:

  • EX seconds -- Set the specified expire time, in seconds.
  • PX milliseconds -- Set the specified expire time, in milliseconds.
  • EXAT timestamp-seconds -- Set the specified Unix time at which the key will expire, in seconds.
  • PXAT timestamp-milliseconds -- Set the specified Unix time at which the key will expire, in milliseconds.
  • NX -- Only set the key if it does not already exist.
  • XX -- Only set the key if it already exist.
  • KEEPTTL -- Retain the time to live associated with the key.
  • GET -- Return the old string stored at key, or nil if key did not exist. An error is returned and SET aborted if the value stored at key is not a string.

Note: Since the SET command options can replace SETNXSETEXPSETEXGETSET, it is possible that in future versions of Redis these commands will be deprecated and finally removed.

History

  • >= 2.6.12: Added the EXPXNX and XX options.
  • >= 6.0: Added the KEEPTTL option.
  • >= 6.2: Added the GETEXAT and PXAT option.
  • >= 7.0: Allowed the NX and GET options to be used together.
redis> SET mykey "Hello"
"OK"
redis> GET mykey
"Hello"
redis> SET anotherkey "will expire in a minute" EX 60
"OK"

Redis HINCRBY

参考:HINCRBY – Redis

redis> HSET myhash field 5
(integer) 1
redis> HINCRBY myhash field 1
(integer) 6
redis> HINCRBY myhash field -1
(integer) 5
redis> HINCRBY myhash field -10

Redis SetEX

参考:SETEX – Redis 

SET mykey value
EXPIRE mykey seconds

Redis Keyspace Notifications

参考:Redis Keyspace Notifications – Redis

想一想一个需求:

  1. 设置了生存时间的Key,在过期时能不能有所提示?
  2. 如果能对过期Key有个监听,如何对过期Key进行一个回调处理?
  3. 如何使用 Redis 来实现定时任务?
 #notify-keyspace-events Ex
1466 #
1467 #  By default all notifications are disabled because most users don't need
1468 #  this feature and the feature has some overhead. Note that if you don't
1469 #  specify at least one of K or E, no events will be delivered.
notify-keyspace-events ""

这里需要配置 notify-keyspace-events 的参数为 “Ex”。x 代表了过期事件。notify-keyspace-events "Ex" 保存配置后,重启Redis服务,使配置生效。

在 Redis 的 2.8.0 版本之后,其推出了一个新的特性——键空间消息(Redis Keyspace Notifications),它配合 2.0.0 版本之后的 SUBSCRIBE 就能完成这个定时任务的操作了,不过定时的单位是秒。

(1)Publish / Subscribe 

     Redis 在 2.0.0 之后推出了 Pub / Sub 的指令,大致就是说一边给 Redis 的特定频道发送消息,另一边从 Redis 的特定频道取值——形成了一个简易的消息队列。

(2)Redis Keyspace Notifications

      在 Redis 里面有一些事件,比如键到期、键被删除等。然后我们可以通过配置一些东西来让 Redis 一旦触发这些事件的时候就往特定的 Channel 推一条消息。大致的流程就是我们给 Redis 的某一个 db 设置过期事件,使其键一旦过期就会往特定频道推消息,我在自己的客户端这边就一直消费这个频道就好了。
     以后一来一条定时任务,我们就把这个任务状态压缩成一个键,并且过期时间为距这个任务执行的时间差。那么当键一旦到期,就到了任务该执行的时间,Redis 自然会把过期消息推去,我们的客户端就能接收到了。这样一来就起到了定时任务的作用。

redis-cli -h 127.0.01.4 -p 63789
127.0.0.1:63789> psubscribe __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "__keyevent@0__:expired"
3) (integer) 1

Redis分布式锁

Redis的原子性

同一个Redis实例,它只以单个进程运行,并可以确保所有请求都是在同一个序列中执行的,因此可以保证Redis执行的语句是原子性的。 对于使用EVAL,通过LUA运行的多条语句,也可以保证像数据库事务一样具有原子性。

单实例Redis

一个Go的实现:https://github.com/bsm/redis-lock

单实例Redis只需借助SETNX(2.6.12后续版本只需SET key value NX也可以做到)即可:

LOCK(lockKey, ownerID):
SET lockKey   ownerID  NX PX  expirationInMilliseconds

Unlock(lockKey, ownerID):
if GET(lockKey) == ownerID:
    DEL(lockKey)

redis分布式锁

为lockKey设置一个独一无二的值ownerID,这样在Unlock时,就不会出现lockKey正好被自动Expire删除后,原拥有者误将别人的锁释放掉的情况。

如果一系列操作需要多个Redis操作,那么应当EVAL将多个操作封装到同一段LUA代码中,否则可能导致多次通讯时差中出现意外。

这种情况仅适用于同一条key存在于同一个Redis实例的情况,例如Redis只有一个,或者不使用Master-Slave的Redis集群,例如无slave的hashring集群(利用类似一致性环形哈希计算key,最终请求落到特定节点上)

如果是使用Master-Slave的Redis集群,同一个key可以存在若干个备份,写入master的数据同步到slave中需要一段时间。考虑以下情形:

  1. Client A在master上获取了对key的锁: key:A
  2. master短暂故障(网络故障,重启等),但key:A尚未同步到slave
  3. Client B向slave请求获得锁 key:B成功
  4. master恢复运行,现在Client A、B都认为自己获得了锁

Red-lock分布式锁算法

一个Go的实现:https://github.com/go-redsync/redsync
在使用master-slave集群时,上述锁的问题在于同步过程中发生了冲突,因此一种解决方案是同时在多个节点上获取锁,当在多数节点成功时,就意味着其它client必然只能获得少数成功,该算法来自https://redis.io/topics/distlock,根据Redis文档的说法,并未在生产环境全面验证,但理论上是行的通的。算法如下:

假定我们想要锁定的时间为T,

  1. 记录下当前时间start,以毫秒(ms)为单位
  2. 借助多线程/协程同时向所有Redis实例请求获得锁 K:V, px=T,设置一个请求的超时时间,例如如果T为10s,那么我们可以设置请求超时为5-50ms,这样就可以避免在一个已经死掉的client上花费过多时间,但该值不应当低于网络通讯时间
  3. 不论成功与否,所有过程结束之后,计算剩余锁的时间 t=now-start
  4. 如果t<T,或者成功获得锁的数量不超过集群的半数,则认为失败
  5. 如果获取锁失败,那么释放掉所有集群上的锁(仅限于K:V一致)。为什么不只释放自己成功获得锁的实例呢?考虑到集群中存在Master-Slave的同步机制,以及我们设置的请求超时,最终存有我们的锁的实例将不限于曾经成功的那些,因此必须对所有实例释放我们的锁。

Redis GeoHash

redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEORADIUS Sicily 15 37 200 km WITHDIST
1) 1) "Palermo"
   2) "190.4424"
2) 1) "Catania"
   2) "56.4413"
redis> GEORADIUS Sicily 15 37 200 km WITHCOORD
1) 1) "Palermo"
   2) 1) "13.36138933897018433"
      2) "38.11555639549629859"
2) 1) "Catania"
   2) 1) "15.08726745843887329"
      2) "37.50266842333162032"
redis> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD
1) 1) "Palermo"
   2) "190.4424"
   3) 1) "13.36138933897018433"
      2) "38.11555639549629859"
2) 1) "Catania"
   2) "56.4413"
   3) 1) "15.08726745843887329"
      2) "37.50266842333162032"

获取元素的 hash 值
geohash 可以获取元素的经纬度编码字符串,上面已经提到,它是 base32 编码。 你可
以使用这个编码值去 http://geohash.org/${hash}中进行直接定位,它是 geohash 的标准编码
值。
redis> geohash company ireader
1) "wx4g52e1ce0"
redis> geohash company juejin
1) "wx4gd94yjn0"

存储结构

Redis 在存储数据不同数据类型的数据时都有对应的编码方式。 Redis GEO是采用哪种编码方式进行存储的呢?在翻阅Redis GEO API时发现其并没有删除指令,因为其底层是使用 zset 进行实现的。 我们可以使用zrem 进行数据的删除。再尝试用zset的查询指令,查询上文中添加的GEO信息

redis> ZRANGE cars:locations  0 -1 WITHSCORES
1) "1"
2) "4054421060663027"
3) "2"
4) "4054421167795118"

发现车辆编号为1的位置信息为4054421060663027;车辆编号为2的位置信息为4054421167795118。 再回顾一下zset增加成员的指令

ZADD key score member [[score member] [score member] ...]
复制代码
至此可以推断出Redis GEO 添加经、纬度位置信息的指令的过程是

ZADD cars:locations 4054421060663027 1
4054421060663027为对经纬度进行编码后的值。使用4054421060663027作为score 可以快速实现对经纬度的索引。

Redis处理Json

1. redisJson module

redis-server --loadmodule ./target/release/librejson.so
127.0.0.1:6379> JSON.SET amoreinterestingexample . '[ true, { "answer": 42 }, null ]'
OK
127.0.0.1:6379> JSON.GET amoreinterestingexample
"[true,{\"answer\":42},null]"
127.0.0.1:6379> JSON.GET amoreinterestingexample [1].answer
“42”
127.0.0.1:6379> JSON.DEL amoreinterestingexample [-1]
1
127.0.0.1:6379> JSON.GET amoreinterestingexample
"[true,{\"answer\":42}]"


_, err = rejson.JSONSet(conn, "JohnDoeJSON", ".info.Major", "EE", false, false)
if err != nil {
    return
}

2. scan struct

// 使用 Do 方法调用命令
_, err = conn.Do("HMSET", Redis.Args{"simple_object"}.AddFlat(simpleObject)...)
if err != nil {
    return
}

value, err := Redis.Values(conn.Do("HGETALL", key))
if err != nil {
    return
}
object := SimpleStruct{}
err = Redis.ScanStruct(value, &object)
if err != nil {
    return
}

 利用redis库自带的Args 和 AddFlat对结构体进行转换。然后以hash类型存储。该方式实现简单,但存在最大的问题是不支持复杂结构(如:结构体中内嵌结构体、数组等)。

3.json 序列化存储

Redis Search

https://github.com/RediSearch/RediSearch
 

RediSearch is a Redis module that provides querying, secondary indexing, and full-text search for Redis. To use RediSearch, you first declare indexes on your Redis data. You can then use the RediSearch query language to query that data.

https://github.com/RediSearch/redisearch-go

ACL

 ACL LIST
1) "user default on nopass ~* +@all"

每一行最前面的两个词是”user”和用户名。之后的词是具体ACL定义的规则。 我们下面将会看到怎样使用这些规则,但是现在,可以简单理解为默认(default)用户的配置是开启的(active(on)),不需要密码(require no password(nopass)),可以访问所有键(to access every possible key(~*))和可以执行任何命令(call every possible command(+@all))的。

另外,对于默认(default)用户来说,没有配置密码规则意味着新的客户端连接会自动使用默认(default)进行验证而不用显式的执行AUTH命令。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

anssummer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值