本篇文章总结自【bilibili 狂神说Java Redis】【狂神说Java】Redis最新超详细版教程通俗易懂
在需求愈来愈多元化的今天,来自客户端的大量请求如果再直接访问关系型数据的话一定会消耗过多的资源,而且大多数据也没有必要进行持久化,因由等等原因,非关系型数据库被创造出来,例如存储键值对数据的 Redis、专门存储文档型数据的 MongoDB,非关系型数据库最大的特点之一就是将数据存储在内存中,就像是挡在关系型数据库前面的一道屏障,不仅降低了资源开销,也节约了查询的时间。
官网下载 Windows 版本或 Linux 版本,Windows 版本解压双击 Redis-server.exe 即可启动,Linux 版本先解压在输入命令( redis-server 文件位置 + redis 配置文件位置):redis-server redis.conf
即可启动
Redis有16个数据库,默认使用第0个数据库
Redis是单线程的,redis将所有数据存储到内存中,使用单线程操作效率最高,redis可达到QPS(每秒查询率)10万+
以下命令在 redis 客户端中测试,Windows 双击运行 Redis-Client.exe,Linux 通过命令redis-client -p 6379
启动客户端
01.使用select【num】命令可切换数据库
02.使用keys *命令获取数据库中所有的键
03.Flushdb命令清除当前数据库数据
04.Flushall命令清除数据库所有数据
05.Exists【key】判断当前key是否存在
06.Move【key】移除当前key
07.Expire 【key】【seconds】设置该key的过期时间,单位为秒
08.ttl【key】查看当前key的剩余时间
09.type【key】查看当前key的类型
redis-benchmark是一个压力测试工具,可使用命令redis-benchmark -h localhost -p 6379 -c 100 -n 100000
去测试redis的性能
01.Set【key】【value】设置值,当该key存在时则覆盖value
02.Get【key】获取值
03.Append【key】【字符串】追加字符串,如果当前key不存在相当于set该key
04.Strlen【key】获取字符串的长度
05.Incr【key】使该key自增1,如果value不是数字则会报错:ERR value is not an integer or out of range(值不是数字类型或值超过范围)
06.Decr【key】使该key自减1
07.Incrby / decrby【key】【步长】可设置该key的步长,指定增量
08.Getrange【key】【start】【end】截取字符串,例如hello截取[0,3]为hell,[0,-1]则获取全部字符串
09.Setrange【key】【start】【字符串】替换指定位置开始的字符串,例如setrange abcdef 1 xx,结果为:axxdef
10.Setex【key】【seconds】【value】设置key的过期时间,相当于set with expire
11.Setnx【key】【value】如果该key不存在就创建,相当于set if not exist
12.Mset【key1】【value1】【key2】【value2】…同时设置多个k-v
13.Mget【key1】【key2】…同时获取多个值
14.Msetnx【key1】【value1】【key2】【value2】设置多个值,这是一个原子性的操作,要么一起成功,要么一起失败
15.Set user:1 {name:zhangsan,age:3} 设置一个user:1对象,值为json字符来保存一个对象
16.Getset【key】【value】先get值,如果该key不存在则创建该k-v,如果存在则覆盖原value
01.Lpush【key】【value】插入一个值,插入到列表左边(头插法)
02.Rpush【key】【value】插入值,插入到列表右边(尾插法)
03.Lrange【key】【start】【end】获取列表中区间的值,[0,-1] 是获取所有值
04.Lpop【key】移除列表中最头上(左边)的一个元素
05.Rpop【key】移除列表中最后(右边)的一个元素
06.Lindex【key】【index】通过index下标获取某一个值
07.Llen【key】返回列表的长度
08.Lrem【key】【count】【value】移除key中指定count数量的value,精确匹配
09.Ltrim【key】【start】【end】截断方法,返回通过下标获取的值,其余未被返回的k-v被删除
10.Rpoplpush【key】【newkey】移除key中的最后一个元素,将其移动到newkey当中
11.Lset【key】【index】【newValue】将key中指定下标的值替换为newValue,如果该下标对应的值不存在则报错
12.Linsert【key】before/after【value】【newValue】将newValue插入到key中value的前面或后面
01.Sadd【key】【value】添加元素
02.Smembers【key】获取key集合中所有值
03.Sismember【key】【value】判断某一个值是不是在set集合中
04.Scard【key】获取该key集合中的元素个数
05.Srem【key】【value】移除该key集合中指定的value
06.Srandmember【key】随机抽取key集合中的一个元素
07.Srandmember【key】【count】随机抽选出key集合中指定个数的元素
08.Spop【key】随机删除key集合中的一个元素
09.Smove【key】【newKey】【value】将一个指定的值移动到另外一个set集合
10.Sdiff【key1】【key2】取两个集合的差集
11.Sinter【key1】【key2】取两个集合交集
12.Sunion【key1】【key2】取两个集合并集
01.Hset【hkey】【key】【value】插入一个具体的k-v
02.Hget【hkey】【key】获取hash中key的值
03.Hmset【hkey】【key1】【value1】【key2】【value2】…set多个k-v
04.Hmget【hkey】【key1】【key2】获取多个字段值
05.Hgetall【hkey】获取全部的k-v数据
06.Hdel【hkey】【key】删除hash中指定的key
07.Hlen【hkey】获取hash中字段(key)数量
08.Hexists【hkey】【key】判断hash中指定字段是否存在
09.Hkeys【hkey】只获得hash中所有key
10.Hvals【hkey】只获得hash中所有value
11.Hincrby【hkey】【key】【count】使key对应得value增加count
12.Hdecrby【hkey】【key】【count】使value减去count
13.Hsetnx【hkey】【key】【value】如果该k-v不存在则设置,存在则不能设置
01.Zadd【key】【score】【value】zset在set集合基础上新增了score值,score相当于当前value的排序值,zset可根据score来排序
02.Zadd【key】【score1】【value1】【score2】【value2】…添加多个值
03.Zrange【key】【start】【end】获取范围内的值,[0,-1]获取所有值
04.Zrangebyscore【key】-inf +inf根据score值从小到大显示所有的元素
05.Zrangebyscore【key】【down level】【up level】获取level区间的元素
06.Zrangebyscore【key】-inf +inf withscores从小到大显示全部的元素并附带score值
07.Zrevrange【key】0 -1从小到大显示所有元素
08.Zrem【key】【value】移除zset集合中指定元素
09.Zcard【key】获取zset中元素个数
10.Zcount【key】【start】【end】获取指定区间内的元素数量
01.geoadd 通过传入经纬度添加某个地点的位置
02.geopos 获取某个地点的经纬度坐标
03.geodist 算出两个地点间直线距离,可以换算为米、千米之类的单位
04.georadius 以某经纬度为中心找出方圆XX千米内的地点
05.georadiusbymember 找出指定地点周围的地点
06.geohash 返回一个或多个位置的 Geohash 字符串
07.geo 底层的实现原理就是 Zset,可使用 Zset 命令来操作 geo
01.Pfadd【key】【value…】创建多个元素
02.Pfcount【key】统计key的基数数量
03.Pfmerge【newKey】【key1】【key2】合并key1和key2中的元素至newKey,同时做去重操作
Redis 在开启事务后,后续对数据的操作会存放入队列中,直到进行执行事务操作,队列中的任务会按顺序依次执行,Redis 事务具有一致性、顺序性、排他性。
Redis事务没有隔离级别的概念,而且不保证原子性,例如某一操作在事务执行中失败,之后的操作依然执行,之前的操作也不会回滚。
Redis事务中分为编译型异常与运行时异常,编译型异常例如输入错误的命令,事务执行直接报错,所有命令不会执行;运行时异常例如对某个不是数字的字符串 +1 ,该操作报错但其他操作执行。
01.Multi开启事务
02.Exec执行事务
03.Discard取消事务
04.Watch【key】监控key,相当于记录当前key的版本号,在事务exec时拿之前的version与现在的version做判断,检查是否有其他线程修改了key。因此watch可以当做Redis的乐观锁操作。
例如执行watch money
,再开启一个事务操作,将 money 减少 20,在事务exec
之前,让第二个线程去修改 money,则exec
操作时事务不会执行,报异常。
05.Unwatch【key】解除该key的操作
01.save 900 1在900s内,至少有一个1个key被修改,则进行持久化操作
02.save 300 10在300内,10个key被修改
03.save 60 10000在60秒内,10000个key被修改
04.stop-writes-on-bgsave-error yes 持久化如果出错,是否还需要继续工作
05.rdbcompression yes 是否压缩rdb文件,压缩需消耗cpu资源
06.rdbchecksum yes 保存rdb文件的时候,进行错误的检查校验!
07.dir ./ 指rdb文件保存的目录
01.满足三个save条件之一就触发
02.执行flushall命令
03.退出redis触发
只要rdb文件位置与配置文件中dir描述的位置一致,开启redis时自动恢复rdb中数据
优点:
适合大规模数据恢复
对数据完整性要求不高
缺点:
如果 redis 意外宕机,例如在 save 条件的前一秒宕机,则最后修改的数据未被持久化
主进程创建子进程持久化时,会占用一定内存空间
官方解释:Append Only File 是另一种持久性模式,它提供了更好的持久性。例如,如果使用默认的数据 fsync 策略(请参阅配置文件后面的部分),Redis 在服务器断电等戏剧性事件中可能只会丢失一秒钟的写入操作,或者如果 Redis 进程本身发生了问题,但操作系统仍在正常运行,则只会丢失一次写入操作。
AOF 是 redis 持久化的第二种方式。redis 默认开启 rdb 模式,AOF 模式需修改配置文件开启。AOF 是以日志形式将用户所有的写或修改操作全部记录下来(不记录读操作),当 redis 因为宕机或其他因素自启时,会自动加载 AOF 文件从上至下依次执行原操作,以完成数据的恢复。
01.appendonly no 默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下,rdb完全够用,appendfilename
02.appendfilename “appendonly.aof” 持久化的文件的名字
03.appendfsync always 每次修改都会fsync()。Fsync()方法就是进行持久化操作,该配置比较消耗性能,默认关闭。
04.appendfsync everysec (Redis 开启 AOF 后默认为该操作)每秒执行一次fsync(),可能当用户在修改数据时redis突然宕机,可能会丢失这1s的数据,但也仅仅丢失这一秒的数据。
05.appendfsync no 不执行fsync(),这个时候操作系统决定持久化,速度最快。
06.no-appendfsync-on-rewrite no 默认aof文件的无限追加,文件会越来越大
07.auto-aof-rewrite-percentage 100 设置aof日志大小以指定的百分比增长
08.auto-aof-rewrite-min-size 64mb 如果aof日志大于64M,会 fork 一个新的进程来将文件进行重写
优点:
1、每一次修改都同步到日志中,文件的完整性好
2、每秒同步一次,仅可能丢失最后一秒的数据
3、从不同步,效率最高
4、如果 appendonly.aof 文件遭到恶意修改,可以使用 redis 根目录下的 redis-check-aof 程序进行修复,数据仍能恢复
缺点:
1、相对于数据文件来说,aof 远远大于 rdb,修复的速度也比 rdb 慢
2、AOF 运行效率也要比 RDB 慢,所以我们 redis 默认的配置就是 rdb 持久化
01.如果只做缓存的话,可以关闭任何持久化
02.同时开启两种持久化方式时,redis 重启的时会优先载入 AOF 文件来恢复数据,因为在通常情况下AOF 文件保存的数据集要比 RDB 文件保存的数据集完整。RDB 的数据不实时,同时使用两者时服务器重启也只会找 AOF 文件,但也不要只使用 AOF,因为 RDB 更适合用于备份数据库(AOF 在不断变化不好备份),快速重启,而且不会有 AOF 可能潜在的 Bug,留一个后手。
03.因为 RDB 文件只用作后备用途,建议只在 Slave(从机)上持久化 RDB 文件,而且只要 15 分钟备份一次就够了,只保留 save 900 1 这条规则。
04.如果开启 AOF,好处是在恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只 load AOF 文件就可以了,代价一是带来了持续的 IO,二是 AOF rewrite 的后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少 AOF rewrite 的频率,AOF 重写的基础大小默认值 64M 太小了,可以设到 5G 以上,默认超过原大小 100% 大小重写可以改到适当的数值。
05.如果不 Enable AOF,仅靠 Master-Slave Repllcation(主从复制)实现高可用性也可以,能省掉一大笔 IO,也减少了 rewrite 时带来的系统波动。代价是如果 Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的RDB文件,载入较新的文件。
redis-server.exe redis.windows.conf
、redis-server.exe redis.windows.6380.conf
依次开启主机与从机下图是主机与从机的各自信息,role 获知是主是从。如果需要第二台从机依旧新建一个配置文件并修改再开启。
SLAVEOF【主机ip】【主机端口】
来连接主机即可,但这种方式是暂时的,永久配置使用第一种方式。redis-sentinel sentinel.conf
来开启哨兵。#Example sentinel.conf
#哨兵sentinel实例运行的端口 默认26379
port 26379
#哨兵sentinel的工作目录
dir /tmp
#哨兵sentinel监控的redis主节点的 ip port
#master-name可以自己命名的主节点名字,只能由字母A-z、数字0-9、这三个字符".-_"组成。
#quorum 配置多少个sentinel哨兵统一认为master主节点失联 那么这时客观上认为主节点失联了
#sentinel monitor
sentinel monitor mymaster 127.0.0.1 6379 2
#当在Redis实例中开启了requirepass foobared授权密码,这样所有连接Redis实例的客户端都要提供密码
#设置哨兵sentinel连接主从的密码,注意必须为主从设置一样的验证密码
# sentinel auth-pass
sentinel auth-pass mymaster XXX
#指定多少毫秒之后主节点没有应答哨兵,此时哨兵主观上认为主节点下线,默认30秒
# sentinel down-after-milliseconds
sentinel down-after-milliseconds mymaster 30000
# 这个配置项指定了在发生failover主备切换(选举)时多可以有多少个slave同时对新的master进行同步,数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为1来保证每次只有一个slave处于不能处理命令请求的状态。
# sentinel parallel-syncs
sentinel parallel-syncs mymaster 1
#故障转移的超时时间failover-timeout可以用在以下这些方面:
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。
#4.当进行failover时,配置所有slaves指向新的master所需的大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了#默认三分钟
# sentinel failover-timeout
sentinel failover-timeout mymaster 180000
#SCRIPTS EXECUTION
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。#一个脚本的大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配 置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无 法正常启动成功。
#通知脚本
# sentinel notification-script
sentinel notification-script mymaster /var/redis/notify.sh
#客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
#以下参数将会在调用脚本时传给脚本:
#
# 目前总是“failover”, # 是“leader”或者“observer”中的一个。
#参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
布隆过滤器
缓存空对象
设置热点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题。
加互斥锁
使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
Redis 高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,也就是搭建集群。
限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。