场景一:数据缓存
在使用Redis时,利用Redis作为系统的分布式缓存组件是非常多的,主要解决两个问题:
数据量太大时,与关系型数据库的大量交互会产生一定的性能瓶颈问题。
本地缓存可能导致数据不一致的问题。
在之前使用Redis做数据缓存时,主要是两个应用方面:
大量的查询数据,利用Redis对这些数据做缓存
用户会话信息的保存,在用户登陆后的session会话信息,利用Redis做session的分布式缓存,例如,在前后端完全分离的系统中,利用Redis做用户权限数据的缓存非常方便。
场景二:统计与计数
在分布式系统中,对于计数的问题比较麻烦,本地利用加锁方式可以保证本地的计数正确,但是如果主机太多,在进行汇总时,可能还是会出现问题,利用redis的HINCRBY可以实现数据的计数统计,因为Redis是单进程单线程的,一次只能执行一个Redis命令,所以不会出现并发的问题。Redis命令
场景三:排序
在一些系统中可能需要对一些数据进行排序,例如,在消息中间件系统中,需要针对消息量对使用该消息中间件的系统排序,如果使用关系型数据库无法进行实时的排序,利用Redis的sorted set可以对数据进行排序。sorted set使用
场景四:分布式锁
针对单机的加锁方式很好处理,java中用synchronized和Locks都可以实现,但是针对多系统的分布式锁的处理方式相对复杂,在分布式系统中,可以用三种方式实现分布式锁:
关系型数据库,例如针对同一个主键,一次只能有一个插入成功,那么多个系统去竞争锁时相当于向这个表中插入同一个主键,插入成功的获得锁成功。释放的时候删除这条主键数据,插入的数据不是主键,对普通的数据做唯一性约束也可。
利用Redis的SET实现分布式锁
利用zookeeper的临时节点实现分布式锁
在开发mqtt的服务端时,有一部分功能需要用到分布式锁,具体的方式就是采用Redis的SET命令实现的分布式锁。网上很多是用SETNX命令实现,但是该实现是会有问题的。例如下面一段伪代码:
if(setnx(key,1) == 1){//成功获取到锁 try{ //逻辑处理 }catch(Exception ex){ //异常处理 }finally{ del(key) } }else{ //获取锁失败的处理 }
这样处理虽然可以保证在系统正常运行的情况下的分布式的锁的获取与释放,但是如果系统crash,例如在setnx后就crash却没有del,会导致其他主机不能获取到该锁。这里就需要利用Redis的expire命令来设置key的过期时间,但是setnx本身不支持设置超时时间,利用set(key,1,30,NX)就可以使用该命令了。并且设置了key的超时过期时间为30s。
有一种极端情况:如果A机器获得了锁但是执行业务超过30s,该锁自动释放(Redis过期了该key),B机器又获得了该锁,然后A执行完了业务逻辑del掉该key,那么A有可能del掉的是B机器的锁,所以需要堆锁进行判断,如果不是A的锁,A不能进行del。所以需要在del前需要判断key是不是A设置的。因为判断和del也不是原子性的,所以这里有两种处理:
利用Lua脚本来实现将判断操作和del操作作为原子性操作,
在每次执行业务逻辑时加时间戳,如果业务逻辑执行时间超过30s,说明该key已经不属于A了,不再进行del的操作。
场景五:服务注册与发现
在分布式集群里,基本都会用到服务注册与发现。redis也是可以实现的,大致实现过程是:
每台主机每隔一定时间向Redis汇报自己的服务情况,并写入当前的汇报时间戳
每台主机定时从Redis拉取集群主机信息,进行判断,如果某台主机长时间没有更新时间戳,则表示该主机已经crash,其余主机可以处理该主机上的一些业务,例如在mqtt中,mqtt的客户端设备会与一台主机保持长连接,如果某台主机挂掉了,需要连另一台主机,那么其余主机需要根据集群主机信息决定是否接收。
一、key的设计
key不要太长,由于redis是通过对key进行hash然后对数据进行存储和获取的,所以理论上key越短要越快一些,同时,key也是会占内存的,key太大,反而会挤压正常值的可用内存。
按模式匹配设计key,其实就是按数据类型设计key,例如现在要存储一类数据,apple,orange,pear,需要将这些作为key,那么在设计key时可以设计为:fruit:apple,fruit:orange,fruit:pear,fruit:实际上只是一个前缀,但是这样设计key,可以让该key-value的值的意义更明确,代表一类水果,同时利用keys命令也可以进行一些数据的统计,keys不要过多的使用,它的时间复杂度为O(n),在执行时如果redis中的Key太多,会阻塞很长的时间。
二、命令使用注意
在使用Redis的五种数据结构的功能时,特别是集合和列表时,需要注意该命令的时间复杂度。具体每个命令可以参考Redis命令参考,对时间复杂度太长的命令如果能用其它命令代替的话尽量用其它命令代替,同时,如果key太多,尽量用hash结构实现,这样可以利用hash结构的field作为key,减少key的数量。