Redis学习笔记
redis数据类型
五种基本类型
string
- keys * 查看所有 的key
- select + 数字 选择 数据库(redis有16个数据库)
- flushall 删除所有数据
- exists + key 判断某个key 是否存在
- expire + key + time 设置某个key的过期时间 单位是 秒
- setex + key + time + value 设置一个key的过期时间和值setex(expire )
- ttl + key 查看key的过期时间还剩多少
- del + key 删除 key
- type + key 查看 key的数据类型
- move + key + 数字 把当前库的key转移的 下表为 数字 的库中
- append + key + value 在key后追加 value,如果 key不存在,就添加
- strlen + key 获取key下value的长度
- incr/decr + key key的value +/-1
- incrby/decrby + key + 数字 key的value+/- 数字长度
- getrange + key + startIndex + endIndex 查看下标为startIndex到end的key的值(查看所有的话为 0 -1)
- setrange + key + index +value 将kye下标为index的值替换成value,如果value有两个,则替换两位
- setnx + key + value 如果key不存在,key值为value,如果存在,则创建失败setnx(set if Not eXists)
- mset + [key,value] 一次性赋多个值 如 mset k1 v1 k2 v2
- mget + key1 +.. 一次性获取多个值 如 mget k1 k2 ..
- msetnx 原子性操作,设置多个值时,如果有一个值存在,则其他的也不会被设置成功
- getset + key + value 获取值然后设置值,如果不存在,则返回空并设置值,如果存在,则返回值并设置新的值
list
- lpush + list+ element 将元素放入list中 先进后出
- lrange +list+sindex+eindex 取出list中下表为 sindex到eindex中的值
- rpush 从右边(尾部)插入
- lpop + list 将list左边第一个元素移除 rpop移除右边第一个
- lindex +list+ index 通过下标获取
- llen + list返回数组的长度
- lrem + count + list 移除list中指定个数的list值 如 lrem 2 one 移除2个one(如果count>0,从表头开始删除,如果count=0,删除所有,如果count<0,从尾部开始删)
- ltrim + list+ sIndex + eIndex 获取list中 下表为 sindex到eindex中的元素(删除不在区间的其他值)
- rpoplpush + 原来数组 + 新数组 将原来数组的最后一个元素移到新数组去,返回的值是原来数组的最后一个元素
- lset + list +index+ value 将list中指定下标的值替换成value(如果list不存在会报错)
- linsert + list +before/after + 指定值 + value 在list指定值的前/后插入value,如果指定值有多个,则会添加在第一个指定值的前或者后
应用场景
- 最新消息排行等功能(比如朋友圈的时间线)
- 消息队列
set
- sadd + set + v1 v2 v3 向set中添加值(可一次性添加多个)
- smembers + set 获取set中的所有值(无序不可重复)
- sismember +set + value 判断set中是否包含指定的value值
- scard + set 获取set中的元素个数
- srem + set + key 删除set中的指定key,可一次性删除多个
- srandmember +set (+ count) 在set中随机挑选一个(count 个)元素
- spop +set (+count) 从set中随机弹出(删除)一个(count个)元素
- smove +原来set + 新set + key 将指定的元素剪贴到新的set里
- sdiff + set1 + set2 两个集合的差集(相比set1来说,如set1 有 abc,set2有bcd,返回结果为ab)
- sinter +set1+set2 两个集合的交集
- sunion + set1 +set2 两个集合的并集
应用场景
- 共同好友
- 利用唯一性,统计访问网站的所有独立ip
- 好友推荐时,根据tag求交集,大于某个阈值就可以推荐
hash
与string类似,hash更适合对象的存储
- hset + hash + field + value 在一个hash中存入一个key-value
- hget + hast + field 获取hash中某个field的值
- hset + hash+ field + value 在hash中一次性插入多个field 和值 如 hset hash1 f1 v1 f2 v2(如果field存在,则会覆盖之前的值)
- mgetall + hash 获取hash中的所有键值对
- mdel + hash + field1 + field2.. 从hash中删除一个或多个key-value
- hlen + hash 查看hash中有个几个键值对(长度)
- hexists + hash + field 判断hash中的filed是否存在
- hkeys + hash 获取hash中所有的key
- hvals + hash 获取hash中所有的value
zset
- zadd + set + number + key + number + key 往set中添加值(需要加序号,可以一次添加多个)
- zrangebyscore + set + 范围(如 -inf +inf,负无穷到正无穷) 给一个set按照序号从小到大排序(只有key没有值)
- zrangebyscore set -inf +inf withscores 从小到大排序(显示key和value)
- zrevrangebyscore + set + +inf + -inf 从大到小给set排序(ps:范围需要从大到小,与正序相反 如:正无穷~负无穷)
- zrevrang + set + 0 + -1 给set反向排序
- zrem + set + key 移除set中key值
- zcard + set 显示set的长度(有几个值)
- zcount + set + num1 + num2 获取指定区间值的个数
应用场景
- 排行榜
- 带权重的消息队列
三大特殊类型
geospatial
地理位置, 底层是zset,所以可以用zset操作geo
- geoadd + key +经度 + 维度 + 城市名 (可一次性添加多个) egg: geoadd china:city 116.40 39.90 beijing
- geopos + key + city 获取城市经纬度 egg: geopos china:city beijing
- geodist + key + city1 + city2 + 单位 两地之间的直线距离
- georadius + key + 经度 + 维度 + 数值 + 单位 以经纬度为中心数值半径内的城市 egg:georadius china:city 110 30 100 km
- georadius + key + 经度 + 维度 + 数值 + 单位 withcoord withdist withhash count 2 withcoord:带距离 withcoord:带经纬度 count:限制返回值的个数 egg:georadius china:city 110 30 10000 km withcoord withdist withhash count 2
- georadiusbymember +key + 城市 + 数值 + 单位 查询以城市(对象)为中心,数值半径内的对象 egg:georadiusbymember china:city beijing 10000 km
- geohash + key + 城市 返回 11位字符的GeoHash字符串,将二维的经纬度转换为字符串 egg:geohash china:city beijing
- zrange china:city 0 -1 查询所有城市(zset的用法)
- zrem china:city beijing 删除北京(zset的用法)
Hyperloglog
统计基数
- pfadd + set + 元素1 + 元素2 + .. 往数据组添加元素,可一次性添加多个(不重复)
- pfcount + set 返回数据组里的元素
- pfmerge + newSet + set1 + set2 将set1和set2数据组合并成newSet
BitMaps
- setbit + key + 位 +0/1
- getbit + key + 位 返回key中该位的值 0/1
- bitcount + key +(start + end)key中的值为1的个数
事务
Redis事务的本质:一组命令的集合。一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行
==Redies事务没有隔离级别的概念==
==Redies单挑命令保证原子性,但是Redis事务不保证原子性==
Redies事务
- 开启事务(multi)
- 命令入队(…)
- 终止事务(discard,事务终止后,入队的命令也不会被执行)
- 执行事务(exec)
事务执行
1 | 127.0.0.1:6379> multi #开启事务 |
问题类型
编译型错误(代码/命令有问题),事务中所有命令都不会执行
1 | 127.0.0.1:6379> multi #开启事务 |
运行时异常(如 1/0),其他正常命令可以执行,错误的命令抛出异常
1 | 127.0.0.1:6379> set k1 "v1" |
Redies监视器测试(可充当乐观锁)
1 | 127.0.0.1:6379> set money 100 #初始 |
Jedis
1.导入对应的依赖
1 | <dependencies> |
2.编码测试
连接数据库
前提
1 | 连接外网首先有在redis.conf改几个设置 |
连接
1 | public static void main(String[] args) { |
操作命令
java操作事务
1 | public static void main(String[] args) { |
断开连接
1 | jedis.close(); |
SpringBoot整合
下载jar包
配置application文件
1 | spring: |
连接测试
实际应用中,一般用 ObjectMapper 将对象序列化然后存入

如果 pojo(entity)实体类没有 序列化(implements Serializable),直接将对象存入,则会报错

自定义redisTemplate
1 |
|
在使用自定义的redisTemplate时,需指定
1 | @Autowired |
自定义 redis util工具类
类似 jdbc的 Helper,方便操作,不用在使用 redisTemplate.ops 方法操作
1 | public class RedisUtils { |
redis.conf详解
单位
配置文件units单位对大小写不敏感

包含
类似于 import,include

网络
1 | bind 127.0.0.1 #绑定的ip |
GENERAL 通用
1 | daemonize yes #以守护线程的方式执行,默认是 no,需要手动更改为yes |
SNAPSHOTTING 快照
持久化,在规定的时间内,达到规定的操作次数,则会持久化到文件 .rdb, .aof
redis 是内存数据库,如果没有持久化,断电即失
1 | # 900s内,至少有 1 个key进行了修改,就进行持久化操作 |
SECURITY 安全
1 | config get requirepass #获取数据库的密码 |
CLIENTS 客户端
1 | maxclients 10000 #最大客户端连接数 |
APPEND ONLY MODE aof配置
1 | appendonly no #默认不开启(rdb持久化方式可以适应的大部分场景,一般用不到 aof) |
redis持久化
Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,一但服务器进程退出,服务器中的数据库状态也会丢失,Redis提供了持久化功能
RDB(Redis DataBase)
在指定的时间间隔内内存中的数据集快照写入磁盘(Snapshot),它恢复时是将快照文件直接读到内存里
Redis会单独创建( fork ) 一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中, 主进程是不进行任何I0操作的。确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一 次持久化后的数据可能丢失。默认的就是RDB,一般情况下不需要修改这个配置(最后一次持久化如果宕机了,数据可能丢失)
==rdb保存的文件是 dump.rdb== 在配置文件中 SNAPSHOTTING 快照 中设置

dump.rdb 触发机制
- save 条件满足的情况下,会自动触发 rdb规则
- 执行flushall 命令 也会触发 rdb规则
- 退出也会产生rdb文件(kill 结束进程的话,redis会来不及保存)
恢复rdb文件
- 只需要将rdb文件放在redis启动目录即可,redis启动时会自动检测dump.rdb,恢复其中的数据
- 查看需要存在的位置
1 | 127.0.0.1:6379> config get dir |
优点
- 适合大规模的数据恢复
缺点
- 需要一定的时间间隔进行操作(可以自定义为每次修改都同步,但是会消耗资源),如果redis意外宕机,可能会丢失数据
- fork进程时,会占用一定的内存空间
AOF(Append Only File)
以日志的形式记录每一个操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,reids再启动时,会读取该文件重新构建数据
==Aof 保存的是 appendonly.aop 文件==
优点
每次修改都同步,保证了数据的完整性
缺点
相对于数据文件来说,aof远远大于rdb,修复速度慢与rdb
运行效率比rdb慢
Rewrite
是什么
Aof采用的是文件追加方式,文件会越来越大,为避免这种情况,新增了重写机制,当AOF文件大小超过规定的阈值时,Redis会启动AOF文件的内容压缩
重写原理
AOF文件持续增长过大时,会fork出新进程将文件重写(也就是先写零时文件最后再rename),遍历新进程的内存中数据,每条记录有一条set语句。重写aof文件的操作,没有读取旧的aof文件
触发机制
Redis会记录上一次重写时的AOF大小,默认配置是aof大小是上次rewrite后得大小且文件大于64M
redis发布订阅
Redis发布/订阅(pub/sub)是一种==消息通信模式==:发送者(pub)发送信息,订阅者(sub)接收消息。(微信,微博,哔哩哔哩有关注系统的)
Redis客户端可订阅任意数量的频道
发布订阅命令

订阅频道
subscribe + 频道名,订阅一个或多个频道,如果频道不存在也可订阅
1 | 127.0.0.1:6379> subscribe kuanshen |
(频道操作者)向频道发送内容
publish + 频道名 + 内容
1 | 127.0.0.1:6379> publish kuanshen aaaaa |
发送内容后,已订阅该频道的人都会接收到消息
1 | 127.0.0.1:6379> subscribe kuanshen |
原理
Redis是使用C实现的,通过分析Redis源码里的pubsub.c文件,了解发布和订阅机制的底层实现,籍此加深对Redis的理解。Redis通过PUBLISH、SUBSCRIBE 和PSUBSCRIBE等命令实现发布和订阅功能。
通过SUBSCRIBE命令订阅某频道后,redis-server 里维护了一个字典,字典的键就是一个个 channel , 而字典的值则是一个链表,链表中保存了所有订阅这个channel的客户端。SUBSCRIBE 命令的关键,就是将客户端添加到给定channel的订阅链表中。通过PUBLISH命令向订阅者发送消息,redis-server 会使用给定的频道作为键,在它所维护的channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
Pub/Sub从字面上理解就是发布( Publish )与订阅( Subscribe ),在Redis中 ,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。
redis主从复制
概念
主从复制,是指将一台Redis服务器的数据 ,复制到其他的Redis服务器。前者称为主节点master/leader),后者称为从节点(slavefollower) ;==数据的复制是单向的,只能由主节点到从节点==。Master以写为主 ,Slave 以读为主。(一主多仆,只有允许有一个主机)
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点) ,但一个从节点只能有一 个主节点主从复制的作用主要包括:
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
3、负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点) , 分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4、高可用基石:除了上述作用外,主从复制还是哨兵和集群能够实施的基础,因此主从复制是Redis高可用的基础
==主从复制,读写分离,主机用来写,从机只能读==
环境配置
只配置从库,主库不做配置(每连接一个reids,会默认为是一个主机Master)
1 | 127.0.0.1:6379> info replication #查看当前库的状况 |
复制三个配置文件,修改对应的信息(将信息改为自己的),避免会造成文件覆盖
- 端口
- pid名字
- log文件名字
- dump.rdb名字
修改完毕之后,启动三个redis服务器

配置为子节点
命令配置
暂时的,断开连接,再重连就会自动变回主机(默认),==与主机共享的数据也会获取不到==
1 | 127.0.0.1:6380> slaveof 127.0.0.1 6370 #将当前redis配置为 子节点 salveof + ip + 端口号 |
配置文件配置
删除注释,replicaof + 主机ip + 主机端口,重连之后,==与主机共享的数据不会丢失,依旧可以获取==

重新登录后,依然是从机
1 | [root@xiaoxi bin]# redis-server fconfig/redis-1.conf |
主机写,从机读
主机可以写
1 | 127.0.0.1:6379> set k1 v1 |
从机只能读,不能写
1 | 127.0.0.1:6380> get k1 |
主要redis被配置为从机,则会实时共享主机的所有数据(成为该主机的从机前,主机存的数据也可以获取到)
复制原理
Slave启动成功连接到master后会发送一个synct同步命令
Master接到命令, 启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,==master将传送整个数据文件到slave,并完成一次完全同步==。
==全量复制==:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
==增量复制==: Master继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
谋朝篡位(没有配置哨兵模式)
如果主机失去连接,从机可以使用slaveof no one把自己作为主机,其他节点可以连接到该最新的主节点(手动),如果之前主节点修复了,也只能重新配置
哨兵模式
自动选老大(主节点)的模式
概述
主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,就需要人工干预,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,优先考虑哨兵模式。
谋朝算位的自动版,能够后台监控主机是否故障,如果故障 了根据投票数自动将从库转换为主库。
哨兵模式是一种特殊的模式 ,首先Redis提供了哨兵的命令, 哨兵是一个独立的进程 ,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
配置
建一个 sentinel.conf 的哨兵配置文件
在配置文件写入 核心配置语句
命令格式 sentinel monitor + 名字(可自定义) + 需要监视的redis的 ip + 端口号 + n(数字指的是主机挂了之后,从机需要得到哨兵 ‘n’ 张票之后,才可以自动设为主机)
sentinel monitor myredis 127.0.0.1 6379 1
启动哨兵
redis-sentinel fconfig/sentinel.conf
启动成功:
1 | #监视的主机 |
主机宕机后
哨兵的日志
1 | 21689:X 28 Feb 2021 00:34:09.347 # +sdown master myredis 127.0.0.1 6379 |
6381的replication信息
1 | 127.0.0.1:6381> info replication |
当之前的主机(6379)重连后,会自动变为当前主机(6381)的从机
1 | 127.0.0.1:6379> info replication |
优缺点
优点
- 哨兵集群,基于主从复制模式,包含所有主从配置的优点
- 主从一切换,故障可以转移,系统的可用性会更好
- 哨兵模式是主从复制的升级,手动到自动,更加健壮
缺点
- 集群容量一旦达到上限,Redis扩容会很困难
- 实现哨兵模式的配置麻烦,里面有很多选择
哨兵模式全部配置
1 | # 哨兵sentinel实例运行的端口 默认是26379,如果有哨兵集群,我们还需要配置每个哨兵端口 |
缓存穿透和雪崩
缓存穿透
概述
用户想要查询某个数据,但是redis内存数据库没有(也就是缓存没有命中),于是向持久层数据库进行查询,发现也没有,查询失败。当用户很多,缓存都没有命中(如:秒杀),都去请求了持久层数据库, 给持久层数据库带来很带压力,这时候相当于出现了缓存穿透。
解决方案
布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式储存,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力(人话:在用户和缓存之间加入布隆过滤器,布隆过滤器会对用户查询的参数进行判断,判断缓存是否有这个参数)
缓存空对象
当存储层未命中后,及时返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据会从缓存中获取,保护了后端数据源(人话:用户查询数据(如:user1),缓存中没有这个数据,然后会查询数据库(如:mysql),数据库也没有这个数据,这时候就在缓存中加入该数据(user1),但是该数据的值是空)
缓存空对象存在的问题
- 如果空值被缓存起来,意味着需要更多的空间缓存更多的键(因为这当中有很多的空值键)
- 即使对空值设置了过期时间,还是会存在 缓存层和数据层会有一段时间窗口的不一致,对于需要保持一致性的业务有影响
缓存击穿
概述
缓存击穿,指的是一个key非常热点,大并发集中对这一个点访问,当该key过期失效的瞬间,持续的大并发就穿破缓存,直接请求数据库
解决方案
设置热点数据永不过期
从缓存层面上讲,没有设置过期时间,就不会出现key失效后产生的问题
加互斥锁
分布式锁:使用分布式锁,保证每个key同一时间只有一个线程去查询后端服务,其他线程没有获取分布式锁的的权限(人话:在缓存和持久层数据库之间加一把锁,只有一个线程可以获取锁,保证同一时间只有一个线程可以访问数据库)
缓存雪崩
概述
缓存雪崩是指在某一个时间段内,缓存集中过期(如:redis宕机/设置过期时间过于集中,如:双11),或缓存服务器某个节点宕机或断网
缓存集中过期是自然形成的雪崩,数据库一般可以顶住压力。缓存服务器某个节点宕机或断网形成的雪崩可能会瞬间把数据库冲垮
解决方案
redis高可用性(搭建集群)
redis可能挂掉,那可以多增加几台服务器,搭建集群
限流降级
缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。
数据预热
在正式部署前,把可能的数据先访问一次,部分可能大量访问的数据就加载到缓存中,手动触发加载缓存不同的key,让缓存失效的时间尽量均匀
缓存和持久层数据库数据一致性
- 本文作者:
腾飞
- 本文链接:
https://www.tengfei.eu.org/article/8eda3648.html
- 版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!