Redis
概述
本文章仅仅包含一些简单的基础知识,其他请查看 官网。
Redis 简介
Redis 是一款NoSQL型开源的数据库。Redis支持数据持久化硬盘和数据备份和多种数据结构,Redis具有很高的读写速度,非常适合作为应用缓存使用。
下载与安装
下载与安装请点击查看
使用源码安装的使用:apt-get purge --auto-remove redis-server
进行卸载
基本使用
基本知识
Redis 有一些常用的基本命令和功能。以下列举一些:
一些命令:
模拟性能测试:redis-benchmark
通信测试:ping
查看服务器统计及信息:info
切换当前操作的数据库实例: select 编号
查看当前数据库中Key的数量:dbsize
查看当前数据库中所有的Key: Keys *
清空当前数据库:flushdb
清空所有数据库:flushall
查看Redis所有的配置信息: config get *
一些常识:
Redis 默认提供16个数据库,以编号命名,默认使用的是第 0 号数据库实例。对配置文件进行修改可以创建或减少数据库实例个数。
数据结构
Redis提供了五中数据结构,分别适用于不同特点的数据。这些类型分别是:String
,List
,set
,zset
,hash
。以下是这五种数据结构的存储特点:
数据结构 | 特点 |
---|---|
String | 单Key,单Value |
List | 单Key,多Value,存储有序数据,有序可重复 |
set | 单Key,多Value,存储无序数据,不可重复 |
zset | 单Key,多 Value,存储无序数据,可以自动排序,不可重复 |
hash | 单Key,Value为对象形式 |
字符串类型 String
string类型的数据是Redis中最基本的数据结构,他可以存储任何类型的数据,包括二进制数据,图片,JSON对象,序列化的数据,最大支持 512M 数据。
列如:
列表类型 List
在Redis中List是存储简单字符串的列表,按照集合的插入顺序排序,底层使用链表,可以从头部或尾部插入数据。
例如:
集合类型 Set
set存储无序不可重复的字符串,数据因为有顺序所以也有下标,可以从左至右使用正下标,从右至左使用负下标。
列如:
哈希类型 hash
hash 类型适合存储对象,它是字符串类型的 field 和value 的映射表。
列如:
有序集合 zset (srted set)
set存储字符串类型,无需且不允许重复,但 zset 的每个元素都会关联一个分数(分数可以重复), Redis 通过分数来为集合中的成员进行从小到大排序。
关于key的指令
Redis 中对Key的操作是最关键的,在对数据进行操作时都是通过Key来操作Value。以下列出对数据进行操作的命令。(未包含全部命令)
1. 查询符合条件的 Key
语法: keys pattern
作用: 查找所有符合模式 pattern的 key. pattern可以使用通配符
通配符:
*
表示0或多个字符。 例如 : keys * , 表示查询所有的key, Keys h*o表示查询所有以h开头,以o结尾的任意长度的字符串。
?
表示单个字符。 例如: wo?d,表示查询以wo开头的,中间为一个任意字符,以d结尾的字符串。
[]
表示其中任意一个字符。表示选择 [ ] 内的任意一个字符。例如 wo[or]d,匹配word,wood,不匹配wold。
2. 查询某个或某些Key是否存在
语法: exists 所查的key
作用 : 判断在当前库中是否存在某个Key。如果存在返回 1 ,否则返回 0 。
语法:exists 某个k,某个k,某个k
作用 : 判断是否存在这些 Key ,返回存在Key的数量
3.将Key移动到其他库中
语法: move 某个K 库编号
作用: 将指定的Key移动到指定的库中。
4.查看生存时间
Redis中数据存储在内存中,而内存容量宝贵且有限,所以redis对数据的存活时间有限制,超过存活时间的数据将被清除,清除长期处于无用状态的数据恢复空间,以存放新数据。
语法: ttl 某个k
作用 : 查看某个名称的Key剩余生存时间。返回值:-1 表示未设置最大生存时间,-2 表示当前名称的key不存在,其他为剩余时间。
5.设置Key的 生存时间
语法: expire 某个k 时间
作用 : 为一个Key设置生存时间,时间单位为 seconds (秒)
6.查看数据的数据结构
语法: type 某个k
作用:根据key的名称查看数据的结果,返回 数据的结构类型。
7.更改Key的名称
语法:rename 某个k 新名称
作用: 将一个key的名称更改为某个名称
8.删除某个或某些Key的数据
语法:del 某个k
作用:删除指定名称的Key,不存在的Key将被忽略,返回实际删除的的数量。
语法:del 某个k 某个K 某个K ...
作用:删除指定名称的Key,不存在的Key将被忽略,返回实际删除的的数量。
操作数据
以下为各个数据结构的操作命令(未含所有)
String
增:
语法: set k的名称 值
说明: 请遵守标识符命名规范。如果在增加时Key已存在则会覆盖其值。
删:
请看上一章的第8节。
改:
语法:append 某个K 追加内容
说明 :对一个 字符串 进行追加,返回追加后字符串的长度。若Key不存在则使用指令中的数据创建一个新的 String 数据结构的数据。
查:
语法:get KEY
说明:查看此Key的数据。
其他:
指令 | 概述 | 返回值 | 若不存在 |
---|---|---|---|
strlen 某个K | 获取字符串的长度 | 字符长度 | / |
incr 某个K | 对数字类型的字符串进行自+1 | 返回自+1后的结果 | 创建一个新的其值为0的String数据结构进行自+1 |
decr 某个k | 对数字类型的字符串进行自-1 | 返回自-1后的结果 | 创建一个新的其值为0的String数据结构进行自-1 |
incrby 某个k 值 | 将此key的值与某个数字进行相加 | 相加后的结果 | 创建一个新的其值为0的String数据结构与此值相加 |
decrby 某个 k 值 | 将此key的值与某个数字进行相减 | 相减后的结果 | 创建一个新的其值为0的String数据结构与此值相减 |
getrange 某个k 从index开始 到index结束 | 截取从?开始到?结束 | 返回截取的字符串 | |
setrange 某个k 从index开始 覆盖内容 | 覆盖字符从?开始到?结束 | ||
set k 时间 V | 在创建数据的同时设置生存时间 | ||
setnx K V | 创建一个String数据类型 如果该数据存在则放弃创建(防止覆盖) | ||
mset K_ 1 V_ 1 K_2 V_2 ...... | 一次创建多个数据 | ||
mget K_ 1 V_ 1 K_2 V_2 ...... | 一次获取多个数据 | ||
msetnx K_ 1 V_ 1 K_2 V_2 ...... | 一次创建多个数据,如果该数据存在则放弃创建(防止覆盖) | 若其中有一个已经存在 则全部放弃 |
List
List列表是单Key 多Value的数据类型,且有序可重复,我们可以从左至右时下标为正,从右至左为负下标。我们可以从任意方向进行元素的插入。
增:
语法:lpush K V V V .....
说明:创建一个List,以正顺序插入数据。
语法:rpush K V V V .....
说明:创建一个List,以逆顺序插入数据。
删:
语法:lpop K 下标
说明:以正顺序删除指定下标的元素
语法:rpop K 下标
说明:以逆顺序删除指定下标的元素
语法:lpop K 开始下标 结束下标
说明:以正顺序删除指定下标区间内的元素
语法:rpop K 开始下标 结束下标
说明:以逆顺序删除指定下标区间内的元素
语法:lrem k 个数 v
说明: 删除指定列表中指定个数的值与V相同的数据。当个数>0时为从左侧开始数,< 0时则从右侧开始数,若个数=0则删除所有与此值相同的元素。
改:
语法:?
说明:?
查:
语法:lrange K 开始下标 结束下标
说明:获取指定列表中指定下标区间的数据
语法:lrange K 下标
说明:获取指定列表中指定下标的数据
其他
|指令|概述 |
|–|–|–|–|
| llen K
| 获取指定表内元素的个数 |
Set
对Set集合的数据进行操作时,只需要指定KEY使用Value来操作数据。
增:
如果添加已经存在的数据则会忽略该指令
语法:sadd K V
说明:创建一个集合向集合中添加一个元素
返回: 添加成功的记录条数
语法:sadd K V V V ...
说明:创建一个集合向集合中添加多个元素
删:
语法:srem K V V V ...
| srem K V
说明: 删除此集合中的某些元素 | 删除一个元素
返回: 成功删除的个数。
语法:spop K V
| spop K V V V ...
spop K 个数
说明:随机删除一个元素 | 随机移除多个元素 | 随机移除指定个数的元素
改:
语法:?
说明:?
查:
语法:smembers K
说明:获取集合中的所有元素
在其他中最后三条和查询相关
其他
指令 | 概述 | 返回值 |
---|---|---|
sismembers K | 判断该元素是否存在 | 存在返回1,否则返回0 |
scard K | 查看指定集合中元素的个数 | 返回具体个数 |
srandmember K | 随机获取集合中的一个元素 | 返回随机得到的元素 |
srandmember K 个数 | 随机获取集合中的多个元素,当个数 >0时获取到的随机数不会重复,而<0时得到的随机数可能会重复 | 返回随机得到的元素们 |
smove 从K 移动到K2 具体元素 | 将指定集合中的指定元素移动到指定集合中 | 成功移动的记录条数 |
sdiff K 其他集合K ... | 获取指定集合中存在但其他集合中不存在的元素 | 将结果作为一个新的集合 |
sinter k ... | 获取这些集合中相同的元素 | 将结果作为一个新的集合 |
sunion K K K ... | 将这些集合中的所有数据复制并生成一个新的集合存放其中 | 将结果作为一个新的集合 |
ZSet
增:
语法:zadd K 分数 值 分数 值 ...
说明:创建一个元素(如果元素存在则覆盖分数)
删:
语法:zrem K 值 ...
说明: 移除一些值。
改:
语法:?
说明: ?
查:
语法:zrange K 下标
| zrange K 从?下标开始 到?下标结束
说明:获取指定下标的元素 | 获取制定下标区间的元素。
语法:zrangbyscore K 分数 分数
说明:获取此key的分数区间的值
其他
指令 | 概述 | 返回值 |
---|---|---|
zcard K | 获取K中的元素个数 | 个数 |
zrank K 值 | 获取指定集合中指定值的排名位置 | |
zcount k 分数 分数 | 获取指定集合中指定分数区间的元素 | |
zscore K 值 | 获取指定值的分数 |
Hash
增:
语法:hset k 属性 值 ...
说明:创建一个拥有一个或多个属性的hash
语法:hsetnx K 属性 值
说明:创建一个拥有指定属性和值的Key,如果此属性存在则忽略。
删:
语法:hdel K 属性 ...
说明:删除指定Key中指定的属性。
改:
语法:?
说明:?
查:
语法:hget K 属性
说明:获取指定Key的指定属性的值(一次获取一个),
语法:hmget K 属性 属性.....
说明:获取指定K的指定属性们的值。
语法:hgetall K
说明:获取指定K的所有属性和值
其他
指令 | 概述 | 返回值 |
---|---|---|
hlen K | 获取指定K的属性的个数 | 个数 |
hexists K 属性 | 判断此key中是否存在该属性 | 存在返回1,否则返回0 |
hkeys K | 获取此Key中所有的属性 | 返回查询到的结果 |
hvals K | 获取此key的所有值 | 返回查询得到值 |
配置文件
在Redis安装目录下为我们提供了一个 redis.conf 配置文件,这样的配置文件可以被 修改,复制, ,在启动时使用此配置文件启动则使Redis应用配置文件中的配置,则可以改变Redis的“行为”。若不使用指定的配置文件启动,则Redis使用默认配置。
我们可以直接修改 redis.conf 或使用 config set 命令来修改配置
语法:config set 某个设置 新值
指定使用某个配置文件启动 命令:
redis-server redis.conf &
配置文件详解:
请参阅各中配置文章
持久化策略
Redis 是内存数据库,它把数据存储在内存中,这样在加快读取速度的同时也对数据安全性产生了新的问题,即当 Redis 所在服务器发生宕机后, Redis i数据库里的所有数据将会全部丢失。为了解决这个问题, Redis 提供了持久化功能一RDB和AOF( Append Only File)。因此我们需要使用一些策略将数据持久化到硬盘中。
这两种持久化策略可以同时使用或单独使用或禁用。
RDB策略
RDB( Redis Database)是 Redis默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个 dump. rdb文件。Reds重启会通过加载 dump. rdb文件来恢复数据。
策略原理:
Redis会复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程,来进行持久化。整个过程中,主进程是不进行任何io操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效,
策略缺点:
如果在策略目标未达到时发生了宕机则会丢失最后一次持久化之后到下一次持久化之前的所有数据。
设置策略:
在 redis.conf 文件中修改配置 save 时间 改变次数
列如: save 900 1 在900秒内改变了1条数据就进行一次持久化。
持久化位置:
dbfilename:持久化后的数据默认存储在dump.rdb文件下,此文件名为默认文件名。可以进行自定义配置
dir:持久化数据保存的目录,默认为 . / 即redis的启动目录。可以进行自定义配置
AOF 策略
在redis每次进行写操作时都会将此次写操作的指令写入到文件中,在发生宕机时,则使用此文件进行数据恢复。
设置策略:
在配置文件中 appendonly: 配置是否开启AOF, yes 表示开启, no表示关闭。默认为no
持久化位置:
appendfilename : AOF保存文件名 。
策略缺点:
在恢复数据时效率较低,
事务
redis中的事务和Mysql的事务大同小异,redis是部分原子性的。不同的是redis的事务是以队列的形式执行(将指令压入事务,从队列中依次执行事务),使用 redis 的事务首先需要开启事务,再将指令压入队列,然后再执行队列中的指令。如果在压入指令时发生了错误则全部指令都不会执行。如果压入队列时并未发生错误,而在执行此队列时发生了错误,则只会导致发生错误的那条指令无法执行,而其他指令依旧会执行,这就是保证部分原子性。
单独的隔离操作:
事务中的所有命令都会序列化、顺序地执行。事务在执行过程中,不会被其它客户端发来的命令请求所打断,除非使用 watch命令监控某些键。
开启事务:
multi
压入指令:
set K v
....
开始事务
exec
取消事务
如果我们在开启事务后,在压入指令时,如果因为一些原因要放弃队列中的事务时,使用以下指令则可以取消事务。
指令:discard
监控一个值
Redis 提供了一个命令,此命令可以监控一个值,使用此值可以作为参考依据,在此值发生改变时做一些事,或者放弃一些事。
列如:使用 watch
监控一个值,如果此值发生改变则放弃事务。
watch 一个值
multi
set K v
....
放弃监控
Redis 提供了一个命令,此命令可以放弃所有监控,此命令可以依据某些条件放弃监控。
列如:使用 unwatch
放弃所有监控,
watch 一个值
unwatch
multi
set K v
....
订阅与发布
订阅与发布使客户端之间得以通信。redis 中使用 “频道” 作为地址,客户端可以对任意频道进行消息发布和订阅,发布消息的客户端在对此频道消息发布完成后,订阅客户端将自动收到由 redis 推送的此频道的新的消息。
客户端_1向某个频道发布消息后,由 redis 将新的消息发布给订阅了此频道的其他客户端。redis只会将此条消息通知给订阅了此频道的客户端。
订阅指令:subscribe 频道 ...
| psubscribe 频道_* ...
(使用通配符)
发布指令:publish 频道 消息
主从复制
虽然 redis 的处理速度很快,但是当请求大过 redis 客户端的处理极限时,redis 仍会宕机。因此,我们在中情况下应使用 redis 集群。
在一个从机从属与主机后首先进行全量复制,完成之后持续进行增量复制。
最简单的一主二从:
主从复制,使用三个Redis 实现做基本的主从复制,通常写的操作要远远少于读,可以使用少数Redis服务器作为主机,而部署众多从机,通常主机只负责写数据(读写),而读数据的重任则交给从机们(不能写),众多的从机使用负载均衡,使从机间的负载达到大概相同的水平。
当主机发生写操作时会自动全量赋值到每个从机,从而使从机内可以读到新的数据。
相关指令:
名称 | 指令 | 概述 | 注意事项 |
---|---|---|---|
查看角色 | info replication | 查看当前Redis的角色。 | / |
设置关系 | slaveof ip和端口 | 设置当前 Redis 服务器从属与某台 Redis 服务器 | 只用设置从机而不设置主机 |
切断关系 | slaveof no one | 断开与主机的主从关系 | |
设置redis后台启动:
在配置文件中将 daemonize no
改为 yes
访问其他redis:
在使用 redis-cli 脚本时 添加参数例如: ./redis-cli -h ip地址 -p 端口号
关闭redis :
进入redis-cli 客户端后输入: shutdown save
或者在redis-cli 所在的目录直接使用 redis-cli shutdown save
查看redis进程和端口号:ps -ef | grep redis
设置当前redis为从机(主机不需要操作):
在redis.conf文件中将replicaof masterip地址 master端口号
在vim中使用/关键字
来查找关键字
高可用
在高流量的环境下要求集群拥有高可用特性。redis 提供了一些机制,使得无论是主机还是从机在发生宕机后服务依然可用。以下列举主机和从机分别宕机后如何保证服务依然可用。
主机宕机
在此种集群模式下,主机宕机后,若未有设置则服务不可写,从机原地待命,在主机恢复后集群恢复正常。在使用了哨兵模式后主机恢复后会成为新的从机,从属由哨兵从从机中选出的新的主机。
从机宕机
在从机宕机后会使集群缺少一个从机,对于主机来说并未有什么影响,但是对于其他从机来说本应由多台从机负责的服务,由于缺少一个从机这会使其他从机的处理压力上升。在拥有稳定访问量的集群中,集群中单个从机的负载至少在任意一台从机宕机后不会导致其他从机的处理压力大于99%,否则整个集群可能因此崩溃。从机个数 = 具体访问量/单个 redis 的最大承受能力+ 1 redis(如果不考虑从机上位)。
当从机恢复后会处于为从属主机的状态 此时从机并未从属任何主机,此时需要再次设置从属关系。
从机上位
若短时间内主机无法恢复,此时应由某一个从机上位作为新的主机,需要手动的 重新的 指定新的主从关系。
在主机恢复后,作为一个没有从属关系的 redis 服务器,可以将他设为新的主机或者从机
哨兵模式
为了在 redis 宕机后 快速的发现与恢复,节省服务恢复的时间的金钱成本。redis 提供了哨兵模式。
哨兵模式作为一个程序可以全时段不间断的对主机进行状态监控,若发生主机宕机后,此程序将会根据配置 票选出某台从机 使其成为主机。恢复整体服务的可写性。
使用一个 redis 服务单独部署哨兵。
部署哨兵
哨兵模式的三的任务:监控,提醒,故障迁移,前提是请已经准备好主从节点。
使用 redis 中的 sentinel.conf 配置文件或在 redis 安装目录自定义一个 .conf 配置文件来配置哨兵 。
指令:sentinel monitor 哨兵名称 ip 端口 票数
概述:当某个最快得到哨兵数量总和过半票数 的 redis 服务将成为新的主机。(每个哨兵会对其他哨兵投票)
启动哨兵
在 某一个不参与读写的 redis 服务中,使用配置文件启动 redis 来启动哨兵模式
指令:./redis-sentinel 配置文件名.conf
概述:启动哨兵。
集群
搭建集群
搭建集群至少需要3台主服务器,且需要安装依赖 ruby (apt install ruby
)
- 下载解压
//在/usr/local/下创建一个redis文件夹(mkdir redis)
//使用此命令下载指定版本都redis文件夹中
wget http://download.redis.io/releases/redis-6.2.6.tar.gz
//解压
tar xzf redis-3.2.0.tar.gz
//切换到解压好的目录中
cd redis-6.2.6
//编译
make
//安装到指定文件夹下
make install PREFIX=/usr/local/redis-cluster
- 修改配置
//切换到安装目录下
//从解压的文件夹中拷贝 redis.conf 到安装文件夹中
//拷贝redis解压文件夹下的 sentinel.conf
cp /usr/local/redis/redis-6.2.6/redos.conf redis.conf
//修改reids.conf配置文件 修改其中以下配置作为主从配置文件的原始配置
daemonize yes #后台启动
cluster-enabled yes #开启cluster,去掉注释
cluster-config-file nodes.conf # 节点配置文件
cluster-node-timeout 15000
appendonly yes #开启aof
protected-mode no #允许外部访问
# 127.0.0.1 # 注释掉这个否则外部链接报错
//将安装目录 bin 复制多分并更换配置文件名称为master-1 slave-1 sentinel-1....
//将所有文件夹中的配置文件使用相同的名称,以便使用xhell批量启动
//修改每个配置文件端口号
- 启动服务
//在redis-cluster下启动服务
cd master-3
./redis-server master-3.conf
cd slave-1
./redis-srver slave-1.conf
//或者使用脚本(适合节点为后台启动的)
//创建脚本
touch start-all.sh
cd master-1
./redis-server master-1.conf
cd ..
cd slave-1
./redis-srver slave-1.conf
cd..
cd master-2
./redis-server master-2.conf
cd ..
cd slave-2
./redis-srver slave-.conf
cd ..
.....省略
//设置脚本权限和启动脚本
chmod 777 start-all.sh
./start-all.sh
- 使用 redis-trib.rb 创建集群
//在redis解压文件夹下的src中使用 redis-trib.rb 构建集群
/* 命令格式为:redis-cli --cluster create 为固定语法,--cluster-replicas 1 为 每一个 主节点下有多少个从节点,最后ip部分为: 主节点 + 两个从节点的ip地址,格式即为:主节点 从节点1... 主节点 从节点1... 。最终.输入 yes 确认配置 */
redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6369 127.0.0.1:6378 127.0.0.1:6368 127.0.0.1:6377 127.0.0.1:6367 --cluster-replicas 1
集群构架完成
- 启动哨兵
//使用相同的方式拷贝安装目录下的bin文件夹 拷贝三个作为哨兵启动 并修改文件名 sentinel-1...
//修改他们中的 sentinel.conf中的端口号
//启动
./redis-sentinel sentinel.conf
`
存储数据
在我们使用任意节点的客户端连接任意reids节点时后,客户端会获取所有节点中数据槽,通过hash算法计算存储或者读取的数据key,再对此计算结果使用算法进行取模,得到slote槽位的编号,直接去此槽位写入数据。每个槽位时存储空间的编号其中存储的是众多数据而不是一个数据!
宕机时或加入节点时
在新加某个节点后,现有的存活的所有节点会将自身的slote 等量的取出一部分,将这些槽位编号分发给新的节点,并通知客户端。当此节点宕机后槽位将原路返回,交回到原先节点,如若是启动时的原始节点中的节点宕机,宕机节点的槽位将会被存活的节点吸收,保证总体槽位数量不变,槽位总数大概存在16384个。
数据寻找
当获取数据时可能使用的是随机从摸个节点读取数据(不确定数否如此),若在此节点找到计算出的数据所在的槽位,则获取并返回数据。由于节点间进行了互联,彼此知道对方拥有槽位的情况,则由被访问的节点返回给客户端此数据应存储的槽位地址,重新定向到指定的节点并获取数据,这可以从我们使用客户端方位数据时,被提示被重定向证实。
Jedis
Jedis 是官方推荐的在java应用中操作Reds的程序。 Jedis几乎涵盖了 Redis 的所有命令。操作Reds的命令在Jedis中以方法的形式体现。可以将其理解为数据库驱动。
极简的示例:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
public class Jedis_test {
public static void main(String[] args) {
//获取Jedis对象
Jedis jedis = new Jedis("192.0.0.1",6379);
//测试是否连通
jedis.ping();
//其他一些操作....
//事务
//获取事务对象
Transaction multi = jedis.multi();
multi.set("k1","v1");
//执行事务
multi.exec();
}
}
客户端连接工具
redis 也有一些可视化的客户端用来操作Redis 。比如Redis Desktop Manager, 使用文档。具体请参阅:动力节点的一篇文章
锁
redis 数据锁,使用锁机制可以保证数据的安全性。
分布式锁
设置一个K:V为一个锁,如果在设置时,此锁存在则不进行任何操作,此锁为约定性锁,完成相同功能时需遵守创建同一个锁的原则,(在锁存在时其他客户端可以进行数据操作,可以删除此锁。),此锁像是标志性锁。
添加锁:
指令:setnx 锁名 值
概述:设置一个锁,当此锁存在时他人无法创建该锁。
返回值: 设置成功返回1,失败返回0。
删除锁:
指令: del 锁名
概述:删除指定的锁,
返回值:删除成功返回1 。
死锁
如果加分布式锁的客户端发生了宕机,锁会一直处于未释放状态,其他客户端则不能进行锁设置,此时被称为死锁。因此我们需要在创建锁之后为此锁设置一个生存时间锁在设置后超过这个时间会自动解除锁。
由于操作通常都是微秒或毫秒级,因此该锁定时间不宜设置过大。具体时间需要业务测试后确
- 例如:持有锁的操作最长执行时间127ms,最短执行时间7ms
- 测试百万次最长执行时间对应命令的最大耗时,测试百万次网络延迟平均耗时
- 锁时间设定推荐:最大耗时120%+平均网络延迟110%
- 如果业务最大耗时<<网络平均延迟,通常为2个数量级,取其中单个耗时较长即可
为分布式锁设置时效:
语法: expire 锁名 秒
删除策略
在 redis 中数据存储在内存中,而数据由cpu进行处理,在cpu处理少量数据时,数据可能会在指令到达后立即删除或及时删除过期数据,而当 redis 接收到大量请求时处于高负载情况下且内存容量宽裕时,redis 并不会立刻对过期数据进行删除,而是有限执行重要的set和get操作。删除策略更多的是为了内存与CPU 之间负载的考虑,可以避免内存于CPU 任意一方发生负载过高发生redis宕机。
数据删除的三种策略:
- 定时删除
- 惰性删除
- 定期删除
过期数据底层数据结构
在我们创建一个数据后,其Value部分除了保存了值还保存了内存地址,在redis 对过期数据进行删除时,会直接根据时间对内存地址中的数据进行删除,而不是使用其他的标识进行。
定时删除
当定时器发现生存时间准时到达时,会将expies中内存地址对应的KV移除,再将自身存储内存地址与生存时间的kv移除。
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
优点 : 节约内存,到时就删除,快速释放掉不必要的内存占用
缺点 : CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响 redish服务器响应时间和指令吞吐量
总结 : 用处理器性能换取存储空间
惰性删除
数据到达过期时间,不做处理。等下次访问该该数据时如果未过期,返回数据,发现已过期或删除,返回不存在
在 对任何数据执行get 操作时底层都会执行一个expirelfneeded()
方法进行判读,
优点 : 节约CPU性能,发现必须删除的时候才删除
缺点 : 内存压力很大,出现长期占用内存的数据
总结 : 用存储空间换取处理器性能
定期删除
文字难以描述请看:视频
逐出算法
文字难以描述请看:视频
缓存问题及解决方案:
缓存预热
在 Redis 服务器没有内容时,比如:服务器初始化时,Redis 中没有数据会导致数据库承受大量的访问有可能导致 数据库 宕机。
解决方案:
前置准备工作:
1.日常例行统计数据访问记录,统计访问频度较高的热点数据
2.利用RU数据删除策略,构建数据留存队列例如: storm与kaka配合
准备工作:
3.将统计结果中的数据分类,根据级别, redis优先加载级别较高的热点数据
4.利用分布式多服’器同时进行数据读取,提速数据加载过程
实施:
1.使用脚本程序固定触发数据预热过程
2.如果条件允许,使用了CDN(内容分发网络),效果会更好。
缓存雪崩
在一个较短的时间内,缓存中较多的key集中过期,此周期内请求访问过期的数据, redis末命中, redis向数据库获取数据,数据库同时接收到大量的请求无法及时处理,Redis大量请求被积压,开始出现超时现象,数据库流量激增,数据库崩溃重启后仍然面对缓存中无数据可用,Redis服务器资源被严重占用, Redis服务器崩溃Redis集群呈现崩塌,集群瓦解应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服’器崩溃。
解决思路:
- 更多的页面静态化处理
- 构建多级缓存架构Nginx缓存+ redis缓存+ ehcache缓存
- 检测Mysq严重耗时业务进行优化: 对数据库的瓶颈排查:例如超时查询、耗时较高事务等
- 灾难预警机制监控
redis服务器性能指标
CPU占用、CPU使用率
内存容量查询平均响应时间
线程数 - 限流、降级
短时间范围内牺牲一些客户体验,限制—部分请求访问,降低应用服务器压力,待业务低柬运转后再逐步放开访问
解决方案:
1.LRU与LFU切换
2.数据有效期策略调整
根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟,过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量
3.超热数据使用永久key
4.定期维护(自动+人工)对即将过期数据做访问量分析,确认是否延时,合访问量统计,做热点数据的延时
5.加锁(慎用!)
缓存击穿
现象:
在系统平稳运行过程中,数据库连接量瞬间激增,Redis服务器无大量key过期,Redis内存平稳无波动,Redis服务器CPU正常,但是数据库崩溃。
问题所在:
Redist中某个key过期,该key访问量巨大多个数据请求从服务器直接压到 Redis后,均未命中Redis在短时间内发起了大量对数据库中同一数据的访问。
解决方案:
-
预先设定
以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物注意:购物节不仅仅指当天,以及后续若千天,访冋峰值呈现逐渐降 -
现场调整
监控访问量,对自然流量激増增的数据延长过期时间或设置为永久性ke -
后台刷新
数据启动定时任务,高峰期来临之前,新数据有效期,确保不丟失 -
二级缓存
设置不同的失效时间,保障不会被同时淘汰就行 -
加锁
分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!
预防为主
缓存穿透
现象:
- 系统平稳运行过程中
- 应用服务器流呈随时间増呈较大
- Redis服务器命中率随时间逐步降低
- Redis内存平稳,内存无压力
- Redis服务器CPU占用激增
- 数据库服务器压力激增
- 数据库崩溃
问题所在:
- Redis中大面积出现未命中
- 出现非正常URL访问
解决方案:
- 缓存null
对查询结果为nu的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高5分钟 - 白名单策略
- 提前预热, 各种分类数据d对应的 bitmaps,id作为 bitmaps的 offset,相当于设置了数据白名单。当加载正常数据时,放行,加载异常数据时直接拦截(效率偏低)
- 使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)
- 实施监控
- 实时监控 Redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比
- 非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象
- 活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象
- 根据倍数不同,启动不同的排査流程。然后使用黑名单进行防控(运营)
- key加密
问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验例如每天随机分配60个加密串,挑选2到3个,混淆到面数据d中,发现访问key不满足规则,驳回数据访问
总结:
缓存击穿访问了不存在的数据,跳过了合法数据的 redis数据缓存阶段,每次访问数据库,导致对数据库服努器造成压力。通常此类数据的出现量是—个较低的值,当岀现此类情况以毒攻毒,并及时报警。应对策略应该在临时预寀防范方面多做文章无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除。
监控性能指标