redis有了解吗
redis(remote dictionary server)是一款基于内存存储的分布式数据库,支持持久化操作和多种数据类型,因为基于内存存储所以运行速度非常快,redis还支持事务,事务中的命令会被序列化按照顺序执行,不会被其他客户端发送过来的命令所打断;
redis相比memcached有哪些优势?(重点)
- memcached的所有value是简单的string类型,redis作为其替代者支持了丰富的数据类型
- redis 运行速度比memcached快很多,并且它支持持久化操作
- redis支持master-salve复制机制
- redis的value最大可以是512m,memcached最大只能是1m
面试官:“redis都支持哪些数据类型?应用场景有哪些?”(重点)
- string:字符串的value最大可以达到512m,可以用来做一些计数功能的缓存
- list:是有序可以重复的集合,可以实现简单的消息队列功能
- set:是无序不可以重复的集合,可以用来进行全局去重
- sorted set:有序的不可以重复的集合,它给每一个元素分配一个固定的分数来保持顺序。可以用来做排行榜应用,如:微博热搜等
- hash:键值对集合,key和value都是string类型,可以用来存放一些特定结构的信息
- geospatial:指定地理位置的经纬度,可以用来做位置共享或附近的人等应用。
- bitmaps:进行位存储,只有0和1两种状态,可以用来做只有两种状态的应用,如:用户是否登录、是否在线、是否打卡等
- hyperloglog:统计不重复元素的个数,可以用统计注册人数、登录人数、访问人数、在线人数等
redis是单线程的吗?为什么执行速度这么快?(重点掌握)
redis是单线程的。执行速度快原因:
- redis基于内存存储
- redis是单线程的,没有多线程的上下文切换
- redis使用了多路IO复用的线程模型,一个线程监控多个IO流
- redis是轻量级的内存数据库,对外部依赖较少
使用redis可能出现的问题(重点)
缓存雪崩:是指在某个时间段,缓存集体失效,新来的请求直接打在数据库上,导致数据库异常。
产生雪崩的原因之一:设置缓存时间已到,例如:双11零点开始抢购,设置的一批商品是1点失效,到1点的时候,请求直接打在数据库上导致数据库异常
解决方案:
- redis高可用:多设置几台redis
- 给缓存设置不同的过期时间,使过期时间均匀分布
- 使用互斥锁:控制线程数量
缓存击穿:缓存中存放某一个热点数据,持久被高并发请求访问,某一时刻该热点数据过期,导致高并发请求直接打在数据库上,导致数据库异常
解决办法:
- 设置热点数据永不过期
- 设置
互斥锁
使得只有一个线程对该热点数据进行访问
缓存穿透:故意去请求缓存中不存在的数据,导致请求直接打在数据库上,造成数据库异常
解决办法:
- 布隆过滤器
- 使用互斥锁:控制线程数量
数据库和缓存的双写一致性问题(重点)
在高并发请求下很容易导致数据库和缓存数据不一致问题,如果你的业务需要保持高强度的一致性,建议删除缓存,在数据库和缓存数据的删除和写入过程中,如果有失败情况,很容易导致数据的不一致。
解决办法:
- 双删延时:先删除缓存,再更新数据库,在隔固定时间删除缓存。
- 更新数据库产生的binlog订阅(使用canal):将有变化的key记录下来,然后尝试去不断删除缓存
redis的持久化方式有哪些?(重点掌握)
RDB(redis database)
在指定的时间间隔内,将内存中的数据集快照写入磁盘中,也就是行话snapshot(快照)。恢复时将数据集快照直接读入内存中。
持久化过程
redis会单独创建(fork)一个子进程来进行持久化,先将数据写入到一个临时文件中,待持久化过程都结束了,再用临时文件代替上次持久化好的文件,整个过程主进程不进行任何IO操作,确保了极高的性能。
rdb默认保存的文件是啥?
dump.rdb
rdb 优点:
- 适合大规模的数据恢复,运行速度快,效率高;
rdb缺点:
- rdb对数据丢失不敏感,在最后一次持久化的时候可能会丢失数据。
- fork的时候,内存中的数据被克隆了一份,会占用更多的内存
aof(append only file)
相当于一个日志记录文件,将redis的所有操作指令记录下来,在进行数据恢复的时候,从头到尾将aof文件执行一遍,只允许在末尾追加文件,不允许修改文件。当aof文件出错时,可以使用命令redis-check-aof --fix
对它进行修复。
aof默认保存的文件是啥?
appendonly.aof
aof优点:
- aof可以做到秒级持久化,相比于rdb恢复的数据更加正确
aof缺点:
- 同样大小的数据,aof文件要大于rdb文件,恢复速度较慢,执行效率较低
持久化策略选择(重点)
- aof可以做到秒级持久化,因此需要更多IO操作,aof相比较于rdb数据恢复更加完整和安全,同样大小的数据,aof文件比rdb更大,恢复速度也慢些
- rdb数据恢复存在一定的丢失性,相较于aof也没有那么安全,但是它是master-slave数据备份和同步的最佳选择,文件尺寸相较于aof也小些恢复速度快。
redis数据的过期回收策略与内存淘汰机制(重点)
redis中的数据过期回收策略使用了定期删除和惰性删除相结合的方式。
- 定期删除:每隔固定的时间的抽查一些key判断是否过期,过期直接删除。
- 惰性删除:获取key的时候再检测是否过期,过期直接删除
内存淘汰机制
如果redis的内存不足的时候,使用如下的策略进行淘汰:
- volatile-lru:在设置了过期时间的key中,移除最近最少使用的key
- allkeys-lru:移除最近最少时候的key
- volatile-random:在设置了过期时间的key中,随机移除key
- allkeys-random:随机移除key
- volatile-ttl:在设置了过期时间的key中,移除即将过期的key
- noeviction :不移除任何key,只是返回一个写错误
redis的主从复制机制(重点)
将一台redis服务器的数据,复制到其他redis服务器上,前者叫做主节点(master),后者叫做从节点(slave),数据的复制是单向的,master以写为主,salve以读为主,实现了读写分离。
主从复制的作用?
- 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现数据快速恢复。
- 负载均衡:在主从复制的基础上,配合读写分离,master提供写服务,slave读服务,分担服务器压力。
- 实现redis服务器高可用
主从复制环境配置
只配置从机,不用配置主机
查看当前主机信息
模拟一主二从环境搭建步骤(使用命令):
- 复制3个redis.conf配置文件
- 修改每个配置文件如下信息:
- port,进程信息,日志文件,dump.rdb
- 启动每个redis
- 从机认主
slaveof 主机ip 端口号
slaveof 127.0.0.1 6379
从机信息:
主机信息:
模拟一主二从环境搭建步骤(使用配置文件):
实际开发中主从配置应该在redis.conf配置文件中进行配置,这样的话是永久的,我们这里使用的是命令,是暂时的。
细节: - 主机写,从机读,主机中的所有信息和数据,都会自动保存到从机中,如果是主机瘫痪掉,从机中依然保存了内容
- 主机断开连接从机依旧连接到主机;主机回来从机依旧从主机获取数据
- 使用命令进行主从配置,从机断开连接后会变成主机(命令是暂时的);使用配置文件进行主从配置,从机断开连接后依旧是从机(配置文件是永久的)
主从复制原理
全量复制:一般发生在从机初始化阶段,从机会将主机上的所有数据复制一份。
增量复制:从机初始化结束后,开始正常工作时,主机每执行一个写命令就会发送到从机,从机执行相同的写命令。
宕机后手动配置主机
如果6379宕机了,这个时候能不能选出一个主机出来了?
手动选择
我们选择6381作为主机,使用命令slaveof no one
,6380作为从机。
什么是CAP理论?(重点)
当网络分区发生时,一致性和可用性无法同时保证
- consistency:一致性
- availability:可用性
- partition tolerance:分区容忍度
- 网络分区中:在分布式系统中节点是分布在不同的机器上进行网络隔离的,当网络断开时,就意味着网络分区发生了
- 最终一致性:从节点会努力追上主节点,最终和主节点的状态保持一致。
redis对事务支持(重点)
redis会对事务进行序列化,事务中的命令按顺序执行,redis运行过程中不会被其他客户端发送过来的命令打断。
redis操作事务的相关命令如下所示:
- multi:开启事务
- exec:执行事务
- discard:取消事务(redis不支持回滚)
- watch key [key…]:监视一个或多个key,如果事务在执行之前key被修改了事务就会中断。
- unwatch:取消对key的监视
哨兵模式(重点)
(自动选取主机模式)
哨兵模式原理
主机出现了宕机,哨兵1检测到这个结果,系统不会立马进行故障转移(failover),因为仅仅是哨兵1发现主机宕机,这个叫做主管下线;当哨兵的数量达到设置的quorum设置的数量时,哨兵之间就会进行选举投票,从发现宕机的哨兵中选择一个进行哨兵进行故障转移工作(failover),其他哨兵依然在岗,这个叫做客观下线。
注意:必须要有半数哨兵在岗,救援工作才能被允许。
哨兵监控测试
步骤
- 创建哨兵配置文件sentinel.conf,进行相关配置
sentinel monitor myredis 127.0.0.1 6379 1
- 启动哨兵
redis-sentinel /usr/local/redis-4.0.6/etc/sentinel.conf
- 如果master宕机后,它会自动从slave中选择一个服务器做为master(这里有个投票算法!)
细节:
如果这个时候之前的master重新启动并连接,它也只能归并到当前主机下。
哨兵模式的优缺点
优点:
- 哨兵集群,基于主从复制模式,所有的主从配置优点它都有
- 主从可以切换,故障可以转移,系统可用性更好
- 哨兵模式就是主从复制模式的升级,手动到自动,更加健壮
缺点:
- redis不好在线扩容,集群容量一旦到达上限,在线扩容十分麻烦
- 实现哨兵模式的配置很复杂,里面有很多选择
哨兵模式的全部配置
redis安装和启动
参考链接:
Redis安装和启动
redis性能测试
步骤:
- 启动redis
redis-server /usr/local/redis-4.0.6/etc/redis.conf(这里是指定自己的配置文件路径) - 连接redis 6379端口
redis-cli -p 6379 - 测试redis性能
redis性能测试工具可选参数:
示例: 100个连接,100000个请求
redis-benchmark -h localhost -p 6379 -c 1000 -n 100000
运行结果:
redis基本操作命令
redis中有16个数据库,编号为0-15
切换数据库
select 索引号
运行结果:
设置key获取value
set 键 值
运行结果:
查看数据库所有键
keys *
运行结果:
清空当前数据库
flushdb
运行结果:
清空全部数据库
flushall
运行结果:
判断key是否存在
exists key #0表示不存在 1表示存在
运行结果:
设置键的过期时间
expire 键 时间(以s为单位)
ttl 键 #查看键剩余过期时间:
运行结果:
设置name 10s过期
查看当前key的类型
type 键
运行结果:
redis5大基本数据类型
String
追加字符串
append name zhangsan
运行结果:
字符串的长度
strlen name
运行结果:
加1操作
incr count
运行结果:
减1操作
decr count
运行结果:
增量操作
incrby count 9
运行结果:
减量操作
decrby count 9
运行结果:
获取指定范围内的字符串
getrange name 0 3 #获取0-3这一段
运行结果:
getrange name 0 -1 #获取整个字符串
替换字符串
setrange name 1 xx
运行结果:
设置键的过期时间并赋值
setex name 30 dongjie
运行结果:
创建键并赋值
setnx name dongjie
运行结果:
创建多个k-v对
mset k1 v1 k2 v2 k3 v3 #创建多个键值对
mget k1 k2 k3 #获取多个键
运行结果:
封装对象/获取对象
mset user:1:name zhangsan user:1:age 55 user:1:sex nan
mget user:1:name user:1:age user:1:sex
运行结果:
应用场景
可以用来做一些计数功能的缓存
List
往左边压入元素
lpush list one #往左边压入元素one
原理图:
运行结果:
获取左边区间元素值
lrange list 0 -1 #获取左边全部元素
lrange list 0 1 #获取左边部分元素
运行结果:
往右边压入元素
rpush list a
原理图:
弹出列表元素
lpop list #弹出左边第一个元素
rpop list #弹出右边第一个元素
原理图:
运行结果:
获取左边元素值
lindex list 0 #获取左边索引为0的元素值
运行结果:
获取左边列表长度
llen list
运行结果:
移除列表元素
lrem list 1 one
lrem list 2 three #移除两个元素three
运行结果:
截取指定长度的元素
ltrim list 1 2 #截取长度为2的元素
原理图:
运行结果:
弹出列表最外一个元素并将它压入其他列表
rpoplpush mylist mylist
原理图:
运行结果:
更新指定索引的元素
lset list 0 item
运行结果:
插入元素
linsert list before world other #在world之前插入元素other
linsert list after world new #在world之后插入元素new
原理图:
运行结果:
应用场景
可以实现一个简单消息队列功能,做基于redis的分页功能等
Set(无序不能重复)
添加元素
sadd myset hello
运行结果:
获取所有元素
smembers myset
运行结果:
判断元素是否在集合中
sismember myset hello
运行结果:
获取当前元素个数
scard myset
运行结果:
移除元素
srem myset hello
运行结果:
随机获取元素
srandmember myset
srandmember myset 2
运行结果:
随机弹出元素
spop myset
运行结果:
将一个集合中的元素移到另外一个集合中
smove myset myset2 kuangsheng
运行结果:
两个集合的差集和交集
sdiff key1 key2
sinter key1 key2
运行结果:
两个集合的并集
sunion key1 key2
运行结果:
应用场景
可以用来进行全局去重等
Zset(sorted set有序不重复集合)
添加值
zadd myset 1 one
zadd myset 2 two 3 three
运行结果:
获取所有值
zrange myset 0 -1
运行结果:
升序排列
zrangebyscore salary -inf +inf #-inf代表负无穷大,+inf代表正无穷大
运行结果:
升序排列并打印出key
zrangebyscore salary -inf +inf withsocres
运行结果:
降序排列并打印出key
zrevrange salary 0 -1 withscores
运行结果:
移除指定元素
zrem salary zhangsan
运行结果:
集合长度
zcard salary
运行结果:
获取指定区间的元素个数
zcount salary -inf +inf #-inf代表负无穷大
运行结果:
应用场景
可以用来做排行榜应用或者进行范围查找等
Hash
设置键值对
hset myhash field1 kuangsheng
运行结果:
根据键获取值
hget myhash field1
运行结果:
设置多个键值对
hmset myhash field1 hello field2 world
运行结果:
根据多个键获取值
hmget myhash field1 field2
运行结果:
获取所有的键值对
hgetall myhash
运行结果:
删除hash指定的key
hdel myhash field1
运行结果:
获取hash的长度
hlen myhash
运行结果:
判断hash中某个key是否存在
hexists myhash field1
运行结果:
获取hash中所有key/value
hkeys myhash
hvals myhash
运行结果:
hash更适合用来存储对象,string更适合用来存储字符串
3大特殊数据类型
geospatial(地理位置)
添加地理位置
经度有效范围:-180~180
纬度有效范围: -85.05112878~ 85.05112878
geoadd china:city 116.4 23.2 beijing
geoadd china:city 112.1 12.1 shanghai 144.2 21.3 shenzhen
运行结果:
获取地理位置
geopos china:city beijing
geopos china:city beijing shenzhen
运行结果:
两地理位置之间距离
geoadd china:city beijing shanghai km
运行结果:
查询指定经纬度附近位置
georadius china:city 110 20 1000 km
georadius china:city 110 20 1000 km withcoord
georadius china:city 110 20 1000 km withcoord withdist
georadius china:city 110 20 1000 km count 2
运行结果:
查询指定位置附近的位置
georadiusbymember china:city shenzhen 1000 km
运行结果:
获取全部位置
zrange china:city 0 -1
运行结果:
删除指定位置
zrem china:city beijing
运行结果:
hyperloglog(统计元素个数)
简介:hyperloglog统计不重复的元素个数,具有容错率,如果允许有容错率,可以使用hyperloglog来统计元素个数。
优点:占用的内存是固定的,只需要占用12kb内存
添加元素
pfadd mykey 1 2 3 4 5 6 7 8 9 10
运行结果:
统计元素个数
pfcount mykey
合并元素
pfmerge mykey3 mykey1 mykey2
运行结果:
bitmaps
简介:位存储,操作二进制来进行存储,只有0和1两种状态
使用场景:统计用户信息,是否活跃,是否登录,是否打卡,2种状态,都可以使用bitmaps
添加数据
setbit day 0 1
setbit day 1 0
setbit day 2 0
运行结果:
获取数据(查看某一天是否打卡)
getbit day 0
getbit day 1
getbit day 2
运行结果:
统计打卡人数
bitcount day
运行结果:
redis事务本质:一组命令的集合,一个事务中所有命令都会被序列化,在事务执行过程中,会按照顺序执行。一次性的、顺序性的、排他性的执行一些命令。
redis开启事务:
- 开启事务–multi
- 命令入列–…
- 执行事务–exec
运行结果:
redis事务没有隔离级别的概念,所有的命令在事务中,并没有直接被执行,而是发起执行命令exec才会被执行。
redis放弃事务:
- 开启事务—multi
- 命令入列—…
- 放弃事务—discard
运行结果:
编译型异常:
命令写错,事务中所有命令不会被执行运行结果:
运行时异常:
类似于1/0这种语法错误,错误命令抛出异常,其他命令可以正常执行,运行结果:
- 悲观锁:每次更新数据的时候都会加锁,以防别人对数据操作
- 乐观锁:每次更新数据的时候都不会加锁,在对数据更新的时候会将该数据与数据库中的数据进行比较以此判断是否更新数据。可以使用version版本号机制或者CAS实现。
测试多线程修改值,使用watch监视功能(上锁)操作乐观锁
线程1:
线程1监视money(加锁),money此时是100,在开启事务后,对money就行了操作,但是并没有执行事务。
线程2:
在线程1执行之前,线程2拿到money,并对money进行了修改
运行结果:
在线程2对money进行修改后,线程1执行事务,出现错误
乐观锁操作失败解决办法
- 如果事务执行失败,先解锁----unwatch
- 再次获取最新的值,再上锁—watch
- 开启事务—multi
- 命令入列—…
- 执行事务—exec
运行结果:
通过jedis操作redis
Jedis是Redis官方推荐的Java连接开发工具
java连接远程服务器redis
测试代码:
public class TestPing {
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.43.120",6379); #192.168.43.120是远程服务器的ip地址
System.out.println(jedis.ping());
}
}
运行结果:
redis.conf详解
bind 127.0.0.1 #绑定的ip
protected-mode no #保护模式关闭
port 6379 #端口号
daemonize yes #以守护进程的方式,后台运行,默认是no 开启为yes
loglevel notice #日志的4种级别
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
logfile "" #日志的文件位置名
databases 16 #数据库的个数
always-show-logo yes #是否显示redis开启logo
#redis是内存数据库,如果没有持久化,断电即失
save 900 1 #如果900s内,有1个key进行了修改,就进行持久化操作
save 300 10 #如果300s内,有10个key进行了修改,就进行持久化操作
save 60 10000 #如果60s内,有10000个key进行了修改,就进行持久化操作
stop-writes-on-bgsave-error yes #持久化出错,是否还需要继续工作
rdbcompression yes #是否压缩rdb文件,需要消耗资源
rdbchecksum yes #保存rdb文件的时候,进行错误的检查校验
dir ./ #rdb文件保存目录
requirepass foobared #设置密码
requirepass 123456 #设置密码
27.0.0.1:6379> auth 123456 #登录密码
OK
config get requirepass #获取密码
1) "requirepass"
2) "123456"
config set requirepass 123456 #设置密码
maxclients 10000 #设置客户端最大连接数
maxmemory <bytes> #设置内存最大容量
maxmemory-policy noeviction #内存到达上限后的处理策略
# volatile-lru 只对设置了过期时间的key进行lru
# allkeys-lru 删除lru算法的key
# volatile-random 随机删除即将过期的key
# allkeys-random 随机删除
# volatile-ttl 删除即将过期的
# noeviction 永不过期,返回错误
appendonly no #默认是不开启aof模式的,默认是使用rdb模式进行持久化的
appendfilename "appendonly.aof" #持久化文件的名称
# appendfsync always 每次都会同步,消耗性能
appendfsync everysec 每秒执行一次同步,可能会丢失这1s的数据
# appendfsync no 不执行同步,这时操作系统自己同步数据,速度最快
redis是基于内存存储的,如果不将其保存在硬盘中,数据是断电即失的
redis默认是使用rdb持久化,rdb持久化也够用了
rdb持久化演示操作
演示图:
- 首先设置save 60 5,即在60s内有5个key发生修改,就进行持久化操作
- 设置k1~k5
- 当在60s内有5个key发生修改,redis自动生成新的dump.rdb文件
- 此时有5个key
- 我们将dump.rdb文件复制给dump_bk.rdb文件
- 此时清空数据库并且断电,redis会产生新的dump.rdb文件。但是之前的dump.rdb文件已经备份为dump_bk.rdb文件
- 重新启动redis并且连接,默认它会加载dump.rdb文件(因为在redis.conf中有这样的设置),所以目前的操作都是null
- 将之前备份的dump_bk.rdb复制为dump.rdb文件即可,因为重新启动redis并连接,它会默认加载dump.rdb文件
- 操作恢复正常
图一:
图二:
图三:
aof持久化操作演示
- 演示appendonly.aof文件记录redis命令
1、当我们设置redis.conf配置文件中appendonly为 yes状态,会产生一个appendonly.aof文件
2、重启redis并连接redis,他会自动加载appendonly.aof文件
3、连接上redis后,输入redis指令,会被appendonly.aof文件记录下来 - 演示redis-check-aof文件对appendonly.aof文件出错时的恢复
1、当appendonly.aof文件发生错误时
2、此时连接redis无法连接上
3、需要使用redis-check-aof工具来恢复appendonly.aof文件
redis-check-aof --fix appendonly.aof
- 发布订阅是什么?
进程间的一种通信模式,发送者(pub)发送消息,订阅者(sub)接收消息 - 发布订阅常用命令?
publish channel message #将信息发送到指定频道
subscribe channel[channel...] #订阅一个或多个频道
unsubscribe [channel[channel...]] #退订指定的频道
psubscribe pattern[pattern...] #订阅一个或多个模式的频道
pubsub subcommand[argument[argument...]]
模拟订阅者与发布者
订阅端:
127.0.0.1:6379> subscribe chan1 #订阅频道chan1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "chan1"
3) (integer) 1
#订阅者接收到发布者发布的消息zhangsan
1) "message"
2) "chan1"
3) "zhangsan"
#订阅者接收到发布者发布的消息lisi
1) "message"
2) "chan1"
3) "lisi"
发布端:
127.0.0.1:6379> publish chan1 zhangsan #在频道chan1里发布消息zhangsan
(integer) 1
127.0.0.1:6379> publish chan1 lisi #在频道chan1里发布消息lisi
(integer) 1
- redis服务器集群示例图?