Redis
2021-03-01 18:12:47 1 举报
AI智能生成
登录查看完整内容
Redis基础使用,各种数据类型应用场景,配置文件,主从复制,哨兵,缓存,缓存淘汰策略,面试中的问题
作者其他创作
大纲/内容
redis
概念
产生背景
1.数据量变大,建立索引、优化数据结构无法根本解决
2.大量的数据一个库是放不下的
3.读写分离,读多写少,读写混合数据库承受不了
什么是nosql
not only sql(不仅仅是sql)
非关系型数据库
特点
易扩展:数据间没有关系、方便扩展,不需要事先设计数据库
大数据量高性能:大数据高性能,每秒读取11万次,写8万次,是一种细粒度的缓存,性能比较高
灵活的数据结构:数据类型是多样型的
远程字典服务(Remote Dictionary Server)开源免费的
中文网:http://www.redis.cn/
英文网:https://redis.io/
特性
支持存储多样数据类型
集群
事务
持久化
应用场景(能干什么)
持久化数据(aof、rdb)
缓存,效率高
发布订阅消息队列
任务队列
应用排行榜
网站访问统计浏览量
数据过期处理
分布式集群架构的session分离
安装
windows
需要在GitHub上下载:https://github.com/redis/redis/releases
linux
查看是否安装了redis
whereis redis-server
whereis redis-cli
查看是否启动
ps -ef |grep redis
下载安装包
redis-benchmark官方自带的性能测试
查看redis版本
redis-server -v
root@fbf42af7ab09:/data# redis-server -vRedis server v=6.0.10 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=66898bb7acd47e81
info
root@fbf42af7ab09:/data# redis-cli127.0.0.1:6379> info# Serverredis_version:6.0.10redis_git_sha1:00000000
基础知识
面试题:redis是单线程的
配置文件中默认有16个数据库
默认使用的第0个数据库
可以通过select [1] 来切换数据库
dbsize
查看数据库大小
keys *
查看所有的key
flushdb
#清空当前数据库
flushall
#清空所有数据库
设置密码
命令设置
127.0.0.1:6379> config get requirepass1) \"requirepass\"2) \"\"127.0.0.1:6379> config set requirepass 123456OK127.0.0.1:6379> config get requirepass(error) NOAUTH Authentication required.127.0.0.1:6379> auth 123456OK127.0.0.1:6379> config get requirepass1) \"requirepass\"2) \"123456\"127.0.0.1:6379> config set requirepass ''OK127.0.0.1:6379> config get requirepass1) \"requirepass\"2) \"\"
配置文件设置
redis-key
exists name
判断key值是否存在
move name 1
从当前库移除key值
expire name 10
设置过期时间
ttl name
查看name剩余时间
redis 命令不区分大小写,key是区分大小写的
help @类型名词
help @set
五种基本数据类型
string
String使用场景
计数器
统计多单位的数量
粉丝数
对象缓存存储
商品号订单号采用incr命令生成
是否喜欢的文章
命令
set key1 vl
get key1
append key1 hello #追加字符串,没有的话新增
strlen key1 #字符串的长度
exists key1 #key是否存在
set views 0 #初始化为0
incr views #自增步长为1
decr views #自减步长为1
incrby views 10 #自增步长自定义
decrby views 10 #自减步长自定义
getrange name 0 -1 #获得全部的字符串
setrange key 1 xx #替换字符串 从下标1开始
setex key3 30 gdj33 #设置过期时间 (在分布式锁中常常使用)
ttl key3 #查看key剩余的过期时间
setnx mykey gdj #设置key key不存在则成功返回是1
setnx mykey zy #设置key,key存在则失败返回是0
mset k1 v1 k2 v2 k3 v3 #批量设置值
mget k1 k2 k3 #批量get值
msetnx #是一个原子性操作,要么一起成功,要么一起失败
msetnx k1 v1 k5 v5 #批量不存在设置 当key中有一个已经存在的就设置失败
getset name gdj #先get再set 如果不存在则返回nil
getset name zy #如果存在返回存在的值,在设置为新的值
list:基本数据类型列表;可以被玩成栈,队列,阻塞队列
小结
实际上是链表,before node after node,left、right都可以操作插入值
如果key不存在就是创建新的链表
如果key存在就是新增内容
如果移除所有值,就表示空的链表,也代表不存在
两边插入改动值,效率高!改动中间的值,相对效率低一些
lpush list one #将一个值或者多个值插入到列表的头部(从左边放入)
lrange list 0 0 #获取某个值,通过区间获取具体的值,通过下标获取值从0开始的
lrange list 0 -1 #获取全部
rpush list right #将一个值从右边放入
lpop list #左边第一个值弹出
lindex list 0 #获取下标为0的值
llen list #获取list的长度llen
lrem list 2 three #移除几个指定的值,可以指定数量
ltrim mylist 1 1 #截取指定区间的list数据这个list集合已经被改变为截取后剩下的值
rpoplpush mylist mylist1 #移动集合mylist中右边第一个元素到新的集合mylist1中
lset list 0 gdj #更改集合list下标为0的值,没有集合list所以更改失败
lset list 1 love #更改集合下标为1的值,因为这个集合这个下标是没有的,所以失败
linsert mylist before gdj love #往集合mylist指定值gdj前面插入值love
linsert mylist after gdj forever #往集合mylist指定值gdj后面插入值forever
应用场景
微信文章订阅公众号
set中的值是无序不能重复的
sadd myset gdj #sadd添加
smembers myset #smember查看myset集合中的值
sismember myset love #查看某个值是否是集合myset中的成员
scard myset #查看集合myset中的存放的元素个数
srem myset love #移除myset中的指定元素
srandmember myset #从集合中随机抽取出一个元素
srandmember myset 2 #从集合中随机抽取出指定个数的元素
spop myset #随机移除元素
smove myset myset2 love #移动集合1中指定元素到集合2中
sdiff k1 k2 #差集
sinter k1 k2 #交集
sunion k1 k2 #并集
微信抽奖类小程序
srandmember myset #从集合中随机抽取出一个元素 spop myset #随机移除元素
微信朋友圈点赞
微信好友关注的社交关系
共同关注的人
我关注的人也关注了他(大家的共同爱好)
QQ内推可能认识的人
hash更加适合存储我们经常变动的数据,更加适合存储对象,例如我们的用户数据
String更加适合字符串的存储
hset myhash k1 v1 #存入hash集合myhash
hget myhash k1 #获取某个hash集合中某个key的value值
hmset myhash k1 xx k3 v3 #批量存入,当存入key值一样时更新
hmget myhash k1 k2 k3 #根据hash值中的key批量获取
hgetall myhash #获取全部的值,包含hash值的key和value
hdel myhash k1 #删除某个指定的值
hlen myhash #查看hash的指定长度
hexists myhash k2 #判断hash中的key是否存在
hkeys myhash #只获取key
hvals myhash #只获取value
hincrby myhash k4 1 #对hash中的指定key的value自增自定义步长
hincrby myhash k4 2
hsetnx myhash k5 v5 #对hash中的添加某个key,没有就新增,有则失败
使用场景
购物车早期当前中小厂可以用
zset (有序集合)数据结构 key score value
总结
班级成绩表,工资表排序
排行榜,取topN
带权重的消息,普通消息,重要消息
zadd myset 1 one #存入值
zadd myset 3 two 2 three #批量存入值
zrange myset 0 -1 #查看全部
zrangebyscore salary -inf +inf #排序 -inf表示负无穷 +inf 表示正无穷
zrangebyscore salary -inf 300 withscores #带出scores成绩的排序
zrevrange salary 0 -1 #排序降序
zrem salary gww #移除元素
zcard salary #查看元素的个数
zcount myzset 1 3 #获取指定区间的成员数量
根据商品销售对商品进行排序显示
抖音热搜
三种特殊数据类型
geospatial-特殊数据类型
geo底层实现原理其实是zset,我们可以使用zset命令操作geo
geospatial地址位置朋友的定位,附近的人,打车距离计算?
geoadd
规则:两级无法添加,城市一般通过程序一次性导入数据结构:key value(纬度、经度、名称)有效的经度从-180度到180度。有效的纬度从-85.05112878度到85.05112878度
geoadd china:city 116.23 40.22 bejing #添加城市的地理位置
geopos
#获取指定城市的经度和纬度
geopos china:city bejing shanghai changzhi taiyuan
geodist
返回两个给定位置之间的距离。指定单位的参数 unit 必须是以下单位的其中一个:m 表示单位为米。km 表示单位为千米。mi 表示单位为英里。ft 表示单位为英尺。
geodist china:city changzhi taiyuan #两个城市间的直线距离默认单位米
geodist china:city changzhi taiyuan km #变更单位
georadius
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。范围可以使用以下其中一个单位:m 表示单位为米。km 表示单位为千米。mi 表示单位为英里。ft 表示单位为英尺
georadius china:city 110 30 1000 km #以110、30为中心附近1000km的城市
georadius china:city 110 30 1000 km withcoord withdist count 2
#以110、30为中心附近1000km的城市显示经度纬度,直线距离,获取数量
georadiusbymember
指定成员的位置被用作查询的中心。
georadiusbymember china:city changzhi 1000 km #找出指定元素周围的城市
geohash #返回一个指定位置的hash值
geohash china:city changzhi
zrange china:city 0 -1 #查看地图中的全部元素
zrem china:city changzhi #移除指定元素
Hyperloglog
简介
redis2.8.9的版本更新了hyperloglog的数据结构redis hyperloglog 基数统计的算法优点:占用的内存是固定的,2^64不同的元素技术,只需要耗费12KB的内存,如果从内存角度来比较的话hyperloglog首选
什么是基数?
应用场景:
网页的UV(一个人访问一个网站多次,但是算作一个人的)传统的方式,set保存用户的id,然后统计set中的元素作为判断标准这个方式需要耗费大量的内存,比较麻烦,我们最终目的是为了计数,不是为了保存用户Hyperloglog,可以很好的解决这个问题,但是他有0.81%错误率,统计UV任务可以忽略不计!
pfadd mykey a b c d e f g #添加元素
pfadd mykey2 a b c d h i g j k l #批量添加元素
pfcount mykey mykey2 #合并mykey和mykey2,求它们的基数
pfmerge mykey3 mykey mykey2 #合并mykey和mykey2到mykey3
pfcount mykey3 #求mykey3的基数
Bitmap
例子
存储一周7天的打卡记录:0~6表示星期一到星期日;0和1表示0未打卡,1是打卡
操作案例
存储一周7天的打卡记录:0~6表示星期一到星期日;0和1表示0未打卡,1是打卡127.0.0.1:6379> setbit week 0 0(integer) 0127.0.0.1:6379> setbit week 1 0(integer) 0127.0.0.1:6379> setbit week 2 1(integer) 0127.0.0.1:6379> setbit week 3 1(integer) 0127.0.0.1:6379> setbit week 4 0 (integer) 0127.0.0.1:6379> setbit week 5 1(integer) 0127.0.0.1:6379> setbit week 6 0(integer) 0127.0.0.1:6379>
查看某一天是否打卡127.0.0.1:6379> getbit week 1 (integer) 0127.0.0.1:6379> getbit week 3(integer) 1127.0.0.1:6379>
统计操作127.0.0.1:6379> bitcount week #统计这周的打卡记录(integer) 3127.0.0.1:6379>
注意
redis的单条命令是保证原子性的,但是redis的事务是不保证原子性的!
redis的事务是没有隔离级别的概念!
redis事务本质
一组命令的集合!一个事务中的所有命令都会被序列化,在事务的执行过程中,会按照顺序执行!
redis事务的特性
一次性,顺序性,排他性!执行一系列的命令!
所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会被执行!exec
redis的事务:
开启事务(multi)
命令入队(……)
执行事务(exec)
案例
正常的事务执行案例
127.0.0.1:6379> multi #开启事务OK#下面命令入队列127.0.0.1:6379> set k1 v1 QUEUED127.0.0.1:6379> set k2 v2 QUEUED127.0.0.1:6379> set k2 xxQUEUED127.0.0.1:6379> get k2 QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> exec #执行事务,队列中的命令一次性执行1) OK2) OK3) OK4) \"xx\"5) OK127.0.0.1:6379>
放弃事务
127.0.0.1:6379> multi #开启事务OK127.0.0.1:6379> set k1 v1 QUEUED127.0.0.1:6379> set k4 v4QUEUED127.0.0.1:6379> discard #放弃事务OK127.0.0.1:6379> get k4 #事务队列中的命令都不会被执行(nil)127.0.0.1:6379>
编译型异常(代码有问题!命令有错),事务中的所有命令都不会被执行
127.0.0.1:6379> multiOK127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2 QUEUED127.0.0.1:6379> getset k3 #命令有错,事务中的所有命令都不会被执行(error) ERR wrong number of arguments for 'getset' command127.0.0.1:6379> set k4 v4QUEUED127.0.0.1:6379> exec #执行事务报错(error) EXECABORT Transaction discarded because of previous errors.127.0.0.1:6379> get k1 #所有命令都不会被执行(nil)127.0.0.1:6379>
运行时异常,如果事务队列中存在语法型错误,那么执行命令的时候,其他命令可以正常执行,错误命令抛出异常
127.0.0.1:6379> set k1 v1OK127.0.0.1:6379> 127.0.0.1:6379> multi #开启事务OK127.0.0.1:6379> 127.0.0.1:6379> incr k1 #命令正确,但是字符串没法加一,语法错误QUEUED127.0.0.1:6379> set k2 v2 QUEUED127.0.0.1:6379> get k2QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> exec #执行事务,错误的语法报错,其他命令正常执行了1) (error) ERR value is not an integer or out of range2) OK3) \"v2\"4) OK127.0.0.1:6379> get k1\"v1\"127.0.0.1:6379> get k2\"v2\"127.0.0.1:6379>
redis-乐观锁-面试
悲观锁
很悲观,认为什么时候都会出问题,无论做什么都会加锁
乐观锁
很乐观,认为什么时候都不会出问题,所以不会上锁,更新数据的时候会去判断一下,在此期间是否有人修改过这个数据
监控
redis监测测试案例
正常执行的情况下
127.0.0.1:6379> set money 100OK127.0.0.1:6379> set out 0OK127.0.0.1:6379> watch money #监视money对象OK127.0.0.1:6379> multi #事务正常结束money没又发生变动,这个时候就正常执行成功OK127.0.0.1:6379> decrby money 20QUEUED127.0.0.1:6379> incrby out 20QUEUED127.0.0.1:6379> exec1) (integer) 802) (integer) 20127.0.0.1:6379> get money\"80\"127.0.0.1:6379> get out\"20\"127.0.0.1:6379>
模拟多线程的情况下
线程1
127.0.0.1:6379> set money 100 #初始化OK127.0.0.1:6379> set out 0 #初始化OK127.0.0.1:6379> get money\"100\"127.0.0.1:6379> get out\"0\"127.0.0.1:6379> watch money #监测OK127.0.0.1:6379> multi #开启事务OK127.0.0.1:6379> decrby money 20 #消费QUEUED127.0.0.1:6379> incrby out 20 #账单QUEUED127.0.0.1:6379> exec #在执行exec前,线程2修改了money的值,执行失败(nil)127.0.0.1:6379> get money\"1000\"127.0.0.1:6379>
线程2
127.0.0.1:6379> set money 1000 #模拟在线程1未执行事务前,修改监测的money值OK127.0.0.1:6379>
解决方案
127.0.0.1:6379> unwatch #如果事务执行失败,放弃监测;重新监测要修改的值,执行逻辑OK127.0.0.1:6379>
jedis
什么是jedis?
jedis是redis官方推荐的java连接开发工具,使用java操作redis的中间件,如果你要使用java操作redis,那么一定要十分熟系jedis!
测试
导入maven依赖
编码测试
常用api
springboot整合redis
为什么SpringBoot2.x之后,原来使用的jedis替换为了lettuce?
jedis:采用的直连,多线程操作的话,是不安全的,如果想要避免不安全,使用jedis pool连接池,这个更像Bio模式
lettuce:采用的netty,实例可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
整合步骤
导入依赖
配置链接
自定义redistemplate
redistemplate固定模板
redis.conf详解
单位
配置文件中unit单位对大小写不敏感
包含
# 可以包含其他配置文件# include /path/to/local.conf# include /path/to/other.conf
网络
bind 127.0.0.1
# 绑定网络,远程可以访问的话,写*,或者本机自己的ip
protected-mode yes
# 是否收保护的模式
port 6379
# 端口
GENERAL 通用配置
daemonize yes
pidfile /var/run/redis_6379.pid
#如果以后台方式运行,就需要指定一个pid文件
loglevel notice
# 日志:生产环境 notice 级别就行;还有debug 、verbose 、notice 、warning
logfile \"\"
#生成的日志文件名
databases 16
# 默认的数据库数量
always-show-logo yes
# 是否显示启动的logo
快照
持久化
在规定时间内执行多少次的操作,则会持久化到.rdb、.aof文件中去
持久化规则
redis是内存数据库,没有持久化断电就失去了
save 900 1
900秒内,如果至少有一个key进行了修改,就进行持久化操作
save 300 10
300秒内,如果至少有十个key进行了修改,就进行持久化操作
save 60 10000
60秒内,如果至少有10000个key进行了修改,就进行持久化操作
stop-writes-on-bgsave-error yes
# 持久化错误后是否继续工作
rdbcompression yes
# 是否压缩rdb文件,需要消耗一些CPU资源
rdbchecksum yes
# 保存rdb文件时候,进行一些错误的检查校验
dir ./
# rdb文件保存的目录
dbfilename dump.rdb
#rdb保存的文件名
配置文件
requirepass foobared
# 可以设置密码默认没有: 格式为 requirepass 123456
客户端限制
maxclients 10000
设置能链接到redis的最大客户端数量
maxmemory-policy noeviction
redis设置最大的内存内存达到上限的处理策略:1.移除过期的key 2.报错 ……
allkeys-random: 所有key通用; 随机删除一部分 key。
volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
maxmemory
redis默认内存多少可以用
如果不设置最大内存大小或者设置的最大内存大小为0,在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存
APPEND ONLY MODE模式 aof配置
appendonly no
#默认是不开启的,默认是使用rdb方式持久化的,在大部分情况下,rdb完全够用了
appendfilename \"appendonly.aof\"
#持久化的文件名字
appendfsync always
#每次的修改都要写入,消耗性能速度比较慢
appendfsync everysec
#每秒去同步一次,可能丢失这1秒的数据
appendfsync no
#不同步了,这个时候操作系统自己同步数据,一般是最快的
redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失,所以redis提供了持久化功能!
RDB
什么是RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时,是将快照文件直接读到内存里
redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能,如果需要进行大规模数据恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加高效。RDB缺点是最后一次持久化后的数据可能丢失。我们默认的就是RDB,一般情况下不需要修改这个配置!
有时候在生产环境我们会将dump.rdb文件进行备份!防止丢失
rdb保存的文件是dump.rdb都是在我们的配置文件中快照中进行配置的!
触发机制
save的规则满足的情况下,会自动触发rdb规则!
执行flushall命令,也会触发rdb规则!
退出redis,也会产生rdb文件!
如何恢复rdb文件
只需要将rdb文件放到我们redis启动目录就可以,redis启动的时候会自动检查dump.rdb恢复其中的数据!
查看需要存在的位置
127.0.0.1:6379> config get dir1) \"dir\"2) \"/\" #如果在这个目录下存在dump.rdb文件,启动就行自动恢复其中的数据127.0.0.1:6379>
优点
适合大规模的数据恢复!
对数据的完整性要求不高!
缺点
需要一定的时间间隔进程操作!如果redis意外宕机,这个最后一次修改数据就没有了
fork进程的时候,会占用一定的内存空间!
AOF(Append Only File)将我们的所有命令都记录下来,history,恢复的时候就是把这个文件全部在执行一遍!
AOF是什么?
以日志的形式来记录每一个写操作,将redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取改文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完整的数据恢复工作
配置
AOF保存在appendonly.aof文件中配置文件中appendonly no #默认是不开启的,变为yes开启,需要我们手动开启改完配置文件,重启redis就配置就可以生效
文件出错修复
如果aof文件出错,redis是启动不起来的,我们需要修复这个aof文件redis提供一个工具 redis-check-aof --fix
如果文件正常重启就可以恢复了
重写规则
aof默认就是文件的无限追加,文件会越来越大!
no-appendfsync-on-rewrite no
#开启重写
auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb
文件大于64M,会fork出一个新的进程对文件进行重写
# appendfsync always #每次的修改都要写入,消耗性能速度比较慢appendfsync everysec #每秒去同步一次,可能丢失这1秒的数据# appendfsync no #不同步了,这个时候操作系统自己同步数据,一般是最快的
1.每一次修改都是同步的,文件完整性会更加好
2.每秒同步一次可能会丢失一秒的数据
3.从不同步,由操作系统自动调度刷磁盘,性能是最好的
1.相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢
2.aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化
扩展
1.RDB持久化方式能够在指定的时间间隔内对你的数据进行快照存储
2.AOF持久化方式记录每次服务器的写操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件的末尾,redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
3.只做缓存,如果你只希望你的数据在服务器运行的时候存在,你可以不使用任何持久化
4.同时开启两种持久化方式
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整
RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢?作者建议是不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为万一的手段
5.性能建议
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
如果Enable AOF,好处是在恶略情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新的文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础默认值64M大小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
如果不Eable AOF,仅靠Master-Slave Replication实现高可用也可以,能省掉一大笔IO,也减少rewrite时带来的系统波动,代价是如果Master/Salve同时宕机,会丢失十几分钟的数据,启动脚本也要比较两个Master/Salve中的RDB文件,载入较新的哪个,微博就是这种架构
发布订阅
介绍
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接受消息。微信、微博关注系统!
Redis客户端可以订阅任意数量的频道
订阅/发布消息图
消息发送者
频道
消息订阅者
PSUBSCRIBE pattern [pattern ...]
订阅一个或多个符合给定模式的频道。
PUBSUB subcommand [argument [argument ...]]
查看订阅与发布系统状态。
PUBLISH channel message
将信息发送到指定的频道。
PUNSUBSCRIBE [pattern [pattern ...]]
退订所有给定模式的频道。
SUBSCRIBE channel [channel ...]
订阅给定的一个或多个频道的信息。
UNSUBSCRIBE [channel [channel ...]]
指退订给定的频道。
实例
第一个 redis-cli 客户端
第二个 redis-cli 客户端
原理
Redis是使用C实现的,通过分析Redis源码里的public.c文件,了解发布和订阅机制的底层实现,籍此加深对Redis的理解。
Redis通过PUBLISH、SUBSCRIRE和PSUBSCRIRE等命令实现发布和订阅功能。
通过SUBSCRIBE命令订阅某频道后,redis-server里维护了一个字典,字典的键就是一个个channel,而字典的值则是一个链表,链表中保存了所有订阅这个channel的客户端。SUBSCRIRE命令的关键,就是将客户端添加给定channel的订阅链表中。
通过PUBLISH命令向订阅者发送消息,redis-server会使用给定的频道作为键,在它所维护的channel字典中查找记录了订阅这个频道的所有客户端链表,遍历这个链表,将消息发布给所有订阅者
PUB/SUB从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。
使用场景:
1.实时消息系统!
2.实时聊天!(频道当做聊天室,将信息回显给所有人即可!)
3.订阅,关注系统都是可以的!
主从复制
主从复制,是指将一台Redis服务器的数据,复制到其他Redis服务器。前者称为主节点(master/leader),后者称为从节点(salve/follower);数据的复制是单向的,只能由主节点到从节点。master以写为主,salve以读为主。默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但是一个从节点只能有一个主节点。
环境配置
只配置从库,不用配置主库!
复制3个配置文件,然后修改对应的信息
1.端口
2.pid名字
3.开启后台运行
4.log文件名字
5.dump文件名字
检测
修改完后,启动3个Redis服务器,可以通过进程信息查看!
主从复制作用
1.数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
2.故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际是一种服务冗余。
3.负载均衡:在主从复制的基础是哪个,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
4.高可用(集权)基石:除了上述作用外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
为什么要主从复制
1.从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
2.从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量256G,也不能将所有内存作为Redis的存储内容,一般来时单台Redis最大使用内存不应该超过20G。电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点就是多读少写。
主从复制,读写分离!80%的情况下都是在进行读操作!减缓服务器的压力!架构中经常使用一主二从!
细节
复制原理
Salve启动成功连接到master后发送一个sync同步命令
Master记到命令,启动后台存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传递整个数据文件到salve,并完成一次完成同步。
全量复制:而salve服务在接收到数据库文件数据后,将其存盘并加载到内存中。增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步但是只要是重新连接master,一次完成同步(全量复制)将自动执行!我们的数据一定可以在从机中看到的
如果没有老大,这个时候能不能选择一个老大出来呢?手动!
谋朝篡位如果主机断开了连接,我们可以使用 salveof on one 让自己变为主机!其他的节点就可以手动连接到最新的这个主节点(手动)!如果这个时候老大修复了,那就重新连接!
哨兵模式:自动选举老大模式
概述
主从切换技术的方法是:当主服务宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题。
谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。哨兵模式是一种特殊模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
两个作用
通过发送命令,让Redis服务器返回监控其运行状态,包括主服务和从服务器。
当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
工作分析
假设主服务宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。
优缺点
1.哨兵集群,基于主从复制模式,所有的主从配置优点它全有
2.主从可以切换,故障可以转移,系统的可用性就会更好
3.哨兵模式就是从主从模式的升级,手动到自动,更加健壮
1.Redis不好在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦!
2.实现哨兵模式的配置其实是很麻烦的,里面有很多选择
redis缓存
缓存问题
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存另外的一些典型问题就是,缓存穿透,缓存击穿和缓存雪崩。目前业界也都有比较流行的解决方案。
缓存穿透
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败了。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候相当于出现了缓存穿透。
布隆过滤器
布隆过滤器是一种数据结构,对所有有可能查询的参数以hash形式存储,在控制层先进行校验,不符合规则丢弃,从而避免了对底层存储系统的查询压力;
缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同事会设置一个过期时间,之后在访问这个数据将会从缓存中获取,保护了后端数据源;
两个问题
1.如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
2.即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
缓存击穿
这里需要注意和缓存穿透的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开一个洞。
当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导致数据库瞬间压力过大。
设置热点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题。
加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到分布式锁,因此对分布式锁的考验很大。
缓存雪崩
缓存雪崩,是指某一段时间段,缓存集中过期失效。
产生雪崩的原因之一,比如在双十二的一波抢购,大量商品比较集中的放入缓存,假设缓存一个小时,那么到凌晨一点的时候,这批商品的缓存就过期了。而对这批商品的访问查询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会打到存储层,存储层的调用会暴增,造成存储层也会挂掉的情况。
其实集中过期,倒不是非常致命,比较致命的是缓存雪崩,是缓存服务器某个节点宕机或断网,因为自然形成的雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库周期性的压力而已。而缓存服务节点宕机,对数据库服务器造成的压力是不可预估的,很有可能瞬间就把数据库压垮。
Redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群(异地多服)
限流降级(在spingcloud讲解过)
这个解决方案的思想是,在缓存失效后,通过加锁或者队列控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
数据预热
数据加热的含义是就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效时间点尽量均匀。
redis缓存过期淘汰策略中问题
生产上你们redis的内存设置多少
如何配置修改redis的内存大小
如果内存满了你怎么办
redis清理内存方式?定期删除,惰性删除了解过吗?
redis缓存淘汰策略
redis的lru了解过吗?能否手写个lru算法?
redis内存满了怎么办?
redis默认内存大小多少?在哪里查看?如何设置修改?
查看Redis最大占用内存
一般生产上如何配置
一般推荐redis设置内存为最大物理内存的四分之三
如何修改redis内存设置
命令方式
config get maxmemory
获取内存大小
config set maxmemory 1024
设置内存大小为1024字节
info命令也能查看
什么命令查看redis内存使用情况
info memory
真要打满了会怎么样?如果redis内存使用超出了设置的最大值会怎样?
故意把内存大小谁知为1kb试试
OOM
结论
设置了maxmemory的选型,假如redis内存使用达到上限
没有加上过期时间就会导致数据写满maxmemory为了避免类似的情况,引入内存淘汰策略
往redis写的数据是怎么没的
redis过期键的删除策略
三种不同的删除策略
定时删除
总结:对cpu不友好,用处理器性能换取存储空间(拿时间换空间)
惰性删除
总结:对memory不友好,用存储空间换取处理器性能(拿空间换时间)
上面两种方案都走极端
定期删除
总结:
定期抽样key,判断是否过期
漏网之鱼
上述步骤还有漏洞吗?
内存淘汰策略登场
allkeys-lfu:对所有key使用lfu算法进行删除
volatile-lfu:对所有设置了过期时间的key使用lfu算法进行删除
2个维度
过期键中筛选
所有键中筛选
4个方面
LRU
LFU
random
ttl
平时用哪一种
allkeys-lru
如何修改
config set maxmemory-policy allkeys-lru
redis的LRU算法简介
redis的lru算法了解过吗?可以手写个lru算法吗?
什么是lru
算法来源
https://leetcode-cn.com/problems/lru-cache/
设计思想
lru算法的核心,哈希链表
编写lru算法
案例1
参考linkedhashmap
依赖jdk
案例2
不依赖jdk
0 条评论
回复 删除
下一页