Redis
Redis基础知识
- Redis可用作数据库、缓存、和消息中间件
- Redis默认有16个数据库
- Redis启动路径在
/usr/bin/redis-cli
- 操作后返回值为1为成功,0则失败
- Redis数据类型:String,Set,Hash,Zset以及三种特殊数据类型:geospatial,hyperloglog,bitmaps;
- Redis是单线程的,是基于内存操作,CPU不是redis性能瓶颈,redis性能瓶颈是根据机器的内存和网络带宽,既然可以用单线程来实现,所以就用单线程了!
- Redis核心:redis是将所有的数据全部放到内存中,所以说单线程去操作效率就是最高的,多线程(CPU上下文会切换,耗时的操作),对内存来说,没有上下文的切换就是最高的。
Redis的基本使用
开启、关闭、退出:
- 开启redis服务:service redis start
- 关闭redis服务:shutdown(结束redis进程)
- 退出redis:exit 或者 quit
基本操作:
- 查看所有Key:**Keys ***
- 选择数据库:select 数字编号(默认16个)
- 查看数据库大小:dbsize
- 清空全部库:flushall
- 清空当前库:flushdb
- 查看Key是否存在:Exists Key名
- 过期时间:Expire key seconds(时间)
- 查看过期时间:ttl key
- 查看数据类型:type key
- 移动Key:move key db
- 删除key:del key
String:
- 追加:append key 追加内容(如果key不存在,就相当于set key)
- 获取字符串长度:strlen
- 增量:incr key(对value加1)
- 减量:decr key(对value减1)
- 步长:decrby key 步长(自定义增量)
- 截取:getRange key start end(end为-1时,查看全部字符串;[start end]闭区间的方式)
- 替换:setRange key offset value
- 批量set:mset key1 value1 key2 value2
- 批量get:mget key1 key2
- 对象:例 set user:1 {name:ikart,age:20}设置一个user:1对象,值为json字符串来保存一个对象;
- 设置过期时间:setex(set with expire)
- 不存在再设置:setnx(set if exist)(如果key不存在则设置,key存在则失败,分布式锁中常常使用)
- 批量不存在再设置:msetnx (原子性操作)
- getSet:先get然后set,如果不存在则返回nil后set,存在则获取原来的值后再set
List列表:
所有list命令都是以L开头的
>>插入
- lpush:将一个或多个元素,插入到列表的头部
- rpush:将一个或多个元素,插入到列表的尾部
- insert:将指定的一个元素,插入到列表另一个元素的前面或后面(before,after )
>>移除
- lpop:移除列表头部的一个元素
- rpop:移除列表尾部的一个元素
- lrem:移除指定个数的元素,精确匹配
>>截取、替换
-
trim:
-
rpoplpush:移除列表最后一个元素到指定列表(集合)中;
-
lset:将列表中指定下标元素替换为另一个元素,更新操作
-
lindex:获取某一个list列表中索引元素
-
llen:查看list长度
Set集合:
无序不重复集合;set的命令以s开头
- sAdd:向set集合中添加元素
- sMembers:查看指定set集合中所有成员;(member:成员)
- sIsMember:判断某一个元素是否在指定的set集合中;
- scard:获取set集合中元素的个数;
- srem:移除指定元素
- sRandMember:随机抽取集合中指定个数的元素
- sPop:随机删除一个元素
- sMove:将一个指定集合中的元素移动到另一个set集合中
>>交、并、差集
- sDiff:差集
- sInter:交集(应用:共同好友)
- sUnion:并集
Hash集合:
Map集合类型 key-value;hash命令以h开头;适用于对象的存储,信息经常变动的场景(对象用hash!!!);
- hSet:set一个具体的key-value;指定增量
- hmSet:set多个key-value
- hGet:获取一个字段值
- hmGet:获取多个字段值
- hGetAll:获取指定hash集合中全部数据
- hLen:获取hash表长度
- **hExists:**判断hash中指定字段是否存在
- hkeys:获取hash表中所有的key
- hvals:获取hash表中所有的value
- hincrby:增量、自定义步长;
- hsetnx:如果不存在则可以设置,存在则不可设置
- hDel:删除hash集合中一组键值对
Zset(有序集合):
有序的集合,在set基础上,增加了一个属性score。
- zadd:添加一个或多个元素
- zRangeByScore:排序,从小到大
- zRevRange:排序,从大到小
- zRem:移除指定元素
- zCrad:计算有序元素个数
***** -inf、+inf表示无穷小和无穷**
三种特殊数据类型
geospatial(地理位置):
有效的经度范围 -180度~180度;
有效的纬度范围 -85.05度~85.05度;
Geo底层实现是基于Zset,所以可以使用Zest来操作Geo;
- GeoAdd:添加地理位置 格式:geoadd key 经度 纬度 城市名
- **GeoDist:**获取两点间的距离 geodist key 城市1 城市2 单位 (M:米 Km:千米 FT: 英尺 MI:英米)
- **GeoPos:**获取指定指定城市的经纬度 格式:geopos key 城市名
- **GeoRadius:**已给定的经纬度为中心,找出某一半径内的元素
- **GeoRadiusByMember:**以指定城市为中心,找出某一半径内的元素
hyperloglog(数据结构):
基数:不可有重复的数;
0.81%的误差率
PFadd
:创建一组元素
PFcount
:统计一组元素中的数量
PFmerge
:以并集的方式将两组合并为一个新的组
bitmap(位存储):
统计用户信息,活跃还是不活跃,只有0和1两个状态
setBit
:设置
getBit
:获取
BitCount
: 统计
事务
1、Redis的原子性:单条命令式保证原子性,但是事务不保证原子性;
2、Redis事务本质:一组命令的集合;一个事物中所有命令都会被序列化,在事务的执行过程中,会按照顺序执行,一致性,顺序性,排他性,执行一些列的命令。
3、Redis事务没有隔离级别的概念
Redis的事务
正常执行事务:
- 开启事务:
multi
- 命令入队
- 执行事务:
exec
放弃执行事务:
DisCard
:放弃执行事务
异常:
- 编译型异常:事务中所有命令都不会被执行。
- 运行时异常:存在语法性错误时,其他命令正常执行,错误命令抛出异常。
监控(Watch)
悲观锁:
- 很悲观,认为程序随时都会出问题,无论做什么都会加上锁
乐观锁:
Watch是乐观锁
- 认为程序不会出问题,所以没有上锁,更新数据的时候去判断一下是否有他人修改过数据
- 获取version
- 更新的时候比较version
>>当乐观锁的数据发生了变动的处理:
- 如果发现事务执行失败,就先解锁(unWatch)
- 获取最新的值,再次监视(Watch)
- 对比监视的值是否发生了变化,如果没有变化,那么可以执行成功,如果变量就执行失败。
Jedis
Jedis是Redis官方推荐的Java连接开发工具,使用Java来操作Redis中间件。
第一步:导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.5.2</version>
</dependency>
第二步:编码测试
- 连接数据库
- 操作命令
- 断开连接
SpringBoot整合:
原来使用的Jedis被替换了Lettuce
Jedis:采用直连的方式,多个线程操作的话是不安全的,如果想要避免不安全,使用Jedis pool连接池,更像BIO(阻塞)。
Lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数据,更像NIO(非阻塞)模式。
Redis.conf解析:
配置文件unit单位大小写不敏感
网络:
bind 127.0.0.1 #绑定的ip
port 6379 #redis的端口
通用(GENERAL):
loglevel notice #日志类型,有**debug,verbose,**notice ,warning
logfile "" #日志文件位置名
databases 16 #默认数据库的数量
always-show-logo yes #是否显示Redis启动图形
logfile "/www/server/redis/redis.log" #日志信息地质
快照:
持久化,在规定时间内执行了多少次操作,则会持久化到文件(.rdb .aof)中
#如果900秒内,如果至少有1个Key进行了修改,则进行持久化操作
save 900 1
#如果300秒内,如果至少有10个Key进行了修改,则进行持久化操作
save 300 10
#如果60秒内,如果至少有10000个Key进行了修改,则进行持久化操作
save 60 10000
stop-writes-on-bgsave-error yes #如果持久化出错,是否还要继续工作。
rdbcompression yes #是否压缩rdb文件,需要消耗一些cpu资源
rdbchecksum yes #保存rdb文件的时候,进行错误的校验
dir /www/server/redis/ #rdb文件保存的目录
dbfilename dump.rdb #rdb文件名
主从复制(REPLICATION):
安全(SECURITY):
Redis默认没有密码
requirepass foobared #设置redis的密码
#获取密码,默认为空
127.0.0.1:6379> CONFIG GET requirepass
1) "requirepass"
2) ""
#通过连接设置密码
127.0.0.1:6379> CONFIG set requirepass "password"
OK
#登录 auth [用户名] 密码
127.0.0.1:6379> auth password
OK
Redis持久化
1、将内存中的数据写入磁盘,Redis默认采用RDB的方式。
2、RDB:指定间隔时间内将内存中的数据写入磁盘。
3、AOF是将每一步命令记录下来(只记录写的操作),恢复的时候就把这个文 件全部在执行一遍。
RDB(Redis DataBase):
RDB:指定间隔时间内将内存中的数据写入磁盘。
rdb保存的文件时dump.rdb
触发机制:
- sava的规则满足的情况下,会自动触发rdb规则
- 执行fulshAll命令,也会触发rdb规则
- 退出redis,也会产生rdb文件
如何恢复rdb中的数据?
Redis自动配置好了,了解即可!
- 将rdb文件放在Redis启动目录就可以,Redis启动的时候会在自动检查dump.rdb恢复其中的数据
- 查看需要存在的位置
#Redis的启动目录
127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/www/server/redis"
RDB的优点:
- 适合大规模的数据恢复。
- 适合对数据完整性要求不高时使用。
RDB的缺点:
- 需要一定时间间隔进程操作,如果Redis宕机了,则可能造成一些数据的丢失。
- fork进程的时候,会占用一定的内存空间。
AOF(Append Only File):
AOF是将每一步命令记录下来(只记录写的操作),恢复的时候就把这个文件全部在执行一遍。
AOF保存的是appendonly.aof文件。
默认是不开启的;开启在配置文件中将appendonly 设置为yes即可!
AOP默认为无限追加,文件会越来越大,所以超过64mb后会fork一个新的线程将文件进行重写(可以指定文件大小后重写)
如果两种配置都开启了,则优先AOF
注意项:
- 如果aof文件有错位,此时Redis是不能启动的,使用
redis-check-aof
来修复(在Redis的src目录下)
#修复:./redis-check-aof --fix 要修复的文件路径
[root@iZf0railv34ewtZ src]# ./redis-check-aof --fix ../appendonly.aof
0x a7: Expected \r\n, got: 6673
AOF analyzed: size=183, ok_up_to=142, diff=41
This will shrink the AOF from 183 bytes, with 41 bytes, to 142 bytes
Continue? [y/N]: y
Successfully truncated AOF
#修复完成后重启redis服务
[root@iZf0railv34ewtZ src]# service redis start
Starting redis server...
Starting redis success!
#再次连接即可!
[root@iZf0railv34ewtZ src]# redis-cli
127.0.0.1:6379>
AOF的配置:
#追加的配置,默认每秒钟同步一次
# appendfsync always
appendfsync everysec
# appendfsync no
#AOP默认为无限追加,文件会越来越大,所以超过64mb后会fork一个新的线程将文件进行重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
AOF的优点:
- 每一次同步修改,文件的完整性会更好
- 每秒钟同步一次,可能会丢失一秒钟的数据
AOF的缺点:
- 相对于数据文件来说,AOF远远大RDB,修复速度也比RDB
- AOF运行效率也比RDB慢
发布订阅
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接受信息。Redis客户端可以订阅任意数量的频道。
命令
PSubscribe
:订阅一个或多个符合给定模式的频道Pubsub
:查看订阅与发布系统状态Publish
:将信息发送到指定的频道 **PunSubscribe
:退订所有给定模式的频道Subscribe
:订阅给定的一个或多个频道信息UnSubscribe
:退订指定的频道
Redis主从复制
主从复制:是指将一台服务器的数据,复制到其他的Redis服务器,前者称为主节点(master/leader),后者称为从节点(slave/follower);数据复制时单向的,只能右主到从节点。master以写为主,slave以读为主。
主从复制,读写分离;主机负责写,从机负责读。
主从复制的作用:
-
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式;
-
故障恢复:当节点出问题时,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务冗余;
-
负载均衡:当从机复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载,尤其是在写少读多的场景下,通过多个从节点分担负载,可以大大提高Redis服务器的并发数量。
-
高可用(集群)基石:除了上述作用外,主从复制还是哨兵和集群能够实施,因此说主从复制是Redis高可用的基础;
复制原理:
全量复制:slave服务在接收到数据库文件后,将master所有数据存盘并加载到内存中。
增量复制:Master继续将所收集到的修改命令依次传给slave,完成同步。
- Slave 启动成功后连接到master后会发送一个sync命令
- master接到命令后,启动后台的存盘进程,同时收集所有接受到的用于修改数据命令,在后台执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。
主从配置:手动模式
如果只有一台主机时只能模拟,复制多份redis.config(不同名),修改里面的端口号 ,日志文件名,rdb文件名及pid文件名即可 。
启动指定的redis:./redis-server ../temp/redis6381.conf
只需要配置从库,主库不用配置。因为redis默认是主机
方式一:在Redis-cli里面配置(暂时的)
shutdown后重启自动变回独立的主机,不能够读取到shutdown之后的数据;再次变为从机数据就能立马回来
恢复主机身份 :slaveof no one
即与主机断开连接
#从机配置
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 #添加主机信息
OK
#主机信息
127.0.0.1:6379> info replication #查看redis配置信息
# Replication
role:master #redis的角色
connected_slaves:2 #从机数量
slave0:ip=127.0.0.1,port=6380,state=online,offset=210,lag=0 #第一台从机信息
slave1:ip=127.0.0.1,port=6381,state=online,offset=210,lag=1 #第二台从机信息
master_failover_state:no-failover
master_replid:332f9d1a52c489d0a31a1d239544cf8ddc2ce805
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:210
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:210
方式二:修改配置文件(永久的,服务启动即生效)
主从配置:哨兵模式(自动选举主节点):
第一步:编写配置文件sentinel.conf
sentinel monitor 主机名(随意) ip地址 port 1
第二步:运行redis-sentinel
./redis-sentinel ../temp/sentinel.conf
Redis缓存穿透和雪崩:
缓存穿透:
缓存穿透:简单来说就是用户查询一个数据,发现Redis里面没有,也就是缓存没有命中。于是向持久层数据库查询,发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求持久层数据库,给持久层数据库造成很大的压力,这时候就相当出现了缓存穿透。
缓存穿透的解决方案
方式一:布隆过滤器:
*布隆过滤器:是一种数据结构,对所有查询的参数以hash形式存储,在控制层进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力*
方式二:缓存空对象
当存储层不命中后,即使返回空对象也将其缓存起来,同时会设置一个过期时间,之后在访问这个数据会从缓存中获取,保护了后端数据源。
缓存击穿:
缓存击穿:即某一个key非常热点,在不停的抗着大的并发,大并发集中对这一点进行一个访问,当这个key在失效的瞬间(缓存过期,访问数据库来查询最新数据的时间间隙),持续的大并发就穿破缓存,直接请求数据库,就像在墙上凿开了一个洞。
解决方案
- 设置热点数据永不过期:从缓存层面来看,没有设置过期时间,所以就不会出现key过期后产生的问题。
- 加锁斥锁;分布式锁:保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转到了分布式锁,所以对分布式锁的考验很大。
缓存雪崩
缓存雪崩:在某一段时间内,缓存集中过期生效,Redis宕机。更致命的是Redis服务器因为不可抗拒的因素造成缓存雪崩,如断电、断网
- Redis高可用:集群,设置多台Redis;
- 限流降级:在缓存失效后,通过加锁或者队列来控制读数据库缓存的数量。例如对某个key只允许一个线程查询数据和缓存,其他在线程等待;
- 数据预热:数据预热就是在正式部署之前,把数据先预先访问一遍,这样部分可能大量访问的数据会加载到缓存中,在即将发生并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
参考文献
Q.E.D.