赞
踩
目录
关于各类数据存储的解决方案
特征
内部采用单线程机制进行工作
高性能
支持多数据类型
持久化支持. 放断电,可以进行数据灾难恢复.
应用
为热点数据加速查询
任务队列,比如秒杀,抢购,购票排队等
即时信息查询,如排行榜,各类网站统计,在线人数信息.....
时效性信息控制,比如验证码2分钟内有效....
分布式数据共享....
下载地址
功能性命令
添加信息 : set key value
获取信息 : get key
清除屏幕信息 : clear
帮助信息查询 : help 命令 : eg: help get
退出指令
quitexit
<esc>
Redis数据格式的由来.
作为缓存使用
原始业务功能设计
秒杀
618活动
双11活动
排队购票
运营平台监控到的突发高频访问数据
突发时政要闻,被强势关注围观
高频,复杂的统计数据
在线人数
投票排行榜
附加功能
系统功能优化或升级
单服务器升级集群
Session管理
Token管理
常用数据类型
string
hash
list
set
sorted_set
数据类型指的是存储的数据的类型,也就是value部分的类型,key部分永远都是字符串.
String类型
存储的数据 : 单个数据,最简单的数据存储类型,也是最常用的数据存储类型
存储数据的格式 : 一个存储空间保存一个数据.
存储内容 : 通常使用字符串,如果字符串以整数的形式展示,可以作为数字操作使用.
添加修改数据
set key value
获取数据
get key
删除数据
del key
添加/修改多个数据
mset key1 value1 key2 value2
在某些集群方案中,涉及多个key的操作会被限制在同一个slot中,如Redis Cluster的mget/mset操作
解决方案 : HashTag
HashTag即是用{}包裹key的一个子串,如{user:}1,{user:}2
在设置了HashTag的情况下,集群会根据HashTag决定key分配到的slot,两个key拥有相同的HashTag:{user:},他们会被分配到同一个slot,允许我们使用MGET命令.
通常情况下,HashTag不支持嵌套,即将和第一个{和第一个}中间的内容作为HashTag,若花括号不包含任何内容则会对整个key进行散列,如{}user.
HashTag可能会是过多的key分配到同一个slotz中,造成数据倾泻影响系统的吞吐量,务必谨慎使用
mset {user}name1 jiangyuandao1 {user}name2 jiangyuandao2
获取多个数据
mget key1 key2
在Redis cluster下
mget {user}name1 {user}name2
获取数据字符个数(字符串长度)
strlen key
在Redis cluster下
strlen {user}name1
追加信息到原始信息后部(如果原始信息存在就追加,否则新建)
append key value
append {HashTag}key value
以上 : 3条数据依次set 和 3条数据mset 一次的时间消耗对比.当数据量达到非常大的时候,比如说要set一亿条数据,就需要进行分割,比如执行mset一次set100万条,执行100次.
业务场景
大型企业级应用中,分表操作是基本操作,使用多张表存储同类型数据(分大表),但是对应的主键id必须保持同一,不能重复.Oracle数据库具有sequence设定,可以解决该问题,但是MySQL数据库并不具有类似的机制,那么该如何解决呢?
解决方案
设置数值数据增加指定范围的值,increment的值可以为负数.
incr key : (key ++)
incrby key increment (key = key + increment )
incrbyfloat key increment (key = key + increment.x )
设置数值数据减少指定范围的值,同样increment的值可以为负数.
decr key : (key -- )
decr key increment (key = key - increment )
redis所有的操作都是原子性的,采用单线程处理所有业务,因此无需考虑并发带来的数据影响.
redis用于控制数据库表的主键id,为数据库表主键提供生成策略,保障数据库表的主键唯一性.
此方案适用于所有数据库,且支持数据库集群.
业务场景
"最强女生"启动海选投票,只能通过微信投票,每个微信号每4小时只能投1票
电商商家开启热门商品推荐,热门商品不能一直处于热门期,每种商品热门期维持3天,3天后自动取消热门
新闻网站会出现热点新闻,热点新闻最大的特征是时效性,如何自动控制热点新闻的时效性
设置数据具有指定的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作.
setex key seconds value //秒
psetex key millisenconds value //毫秒
业务场景
主页高频率访问信息显示控制,例如新浪微博大V主页显示粉丝数与微博数量
key的命名规范举例X:中间加冒号
user:id:3506728370:fans=> 12210947
user:id:3506728370:vbo=> 6164
user:id:3506728370:guanzhu=> 83
key的命名规范举例:JSON
user:id:3506728370 {id:00789,blogs:789,fans:12210947}
数据库中的热点数据key命名惯例
表名 | 主键名 | 主键值 | 字段名 | |
---|---|---|---|---|
eg1 | order | id | 1 | name |
eg2 | equip | id | 1 | fans |
eg3 | news | id | 1 | title |
对应java中的hashMap
存储的困惑
对象类数据的存储如果具有较频繁的更新需求操作会显得笨重
新的存储需求 : 对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
需要的存储结构 : 一个存储空间保存多个键值对数据
hash类型 : 底层使用哈希表结构实现数据存储
hash存储结构优化
如果field数量较少,存储结构优化为类数组结构
如果field数量较多,存储结构使用HashMap结构
添加/修改数据
hset key field value
获取数据
hget key field
hgetall key //获取key下的所有对象信息
删除数据
hdel key field [field2]
添加/修改多个数据
hmset key field1 value1 field2 value2...
获取多个数据
hmget key field1 field2 ...
获取哈希表中字段的数量
hlen key
获取哈希表中是否存在指定的字段
hexists key field
获取哈希表中所有的字段名或者字段值
hkeys key //获取所有的field
hvals key //获取所有的value
设定指定字段的数值数据增加指定范围的值
hincrby key field increment //增加(减少) 整数
hincrbyfloat key field increment //增加(减少)浮点数
hash类型下的value只能存储字符串,不允许存储其他数据类型,不存在嵌套现象.如果数据未获取到,对应的值为(nil)
每个hash可以存储2^32 - 1个键值对
hash类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性但hash设计初衷不是为了存储大量对象而设计的,切记不可滥用,更不可以将hash作为对象列表使用
hgetall操作可以获取全部属性,如果内部field过多,遍历整体数据效率会变得很低,有可能成为数据访问瓶颈
电商网站购物车设计和实现
解决方案
以客户id作为key,每位客户创建一个hash存储结构存储对应的购物车信息
将商品编号作为field,购买属性作为value进行存储
添加商品 : 追加全新的field与value
浏览 : 遍历hash
更改数量 : 自增/自减 ,设置value值
删除商品 : 删除field
清空 : 删除key
当前设计是否加速了购物车的呈现,当前仅仅是将数据存储到了redis中,并没有起到加速的作用,商品信息还需要二次查询数据库.
每条购物车中的商品记录保存成两条field
field专用于保存购买数量
商品格式 : 商品 id : nums
保存数据 : 数值
field2专用于保存购物车中显示的信息,包含文字描述,图片地址,所属商家信息等
命名格式 : 商品id : info
保存数据 : json
hsetnx key field value //如果当前的field有值,就什么都不做,如果没值就加进去.
业务场景
双11活动日,销售手机充值卡的商家对移动,联通,电信的30元,50元,100元商品推出抢购活动,没种商品抢购上限为1000张.
redis应用于抢购,限购类,限量发放优惠券,激活码等业务的数据存储设计.
数据存储需求 : 存储多个数据,并对数据进入存储空间的顺序进行区分
需要的存储结构 : 一个存储空间保存多个数据,且通过数据可以体现进入顺序
list类型 : 保存多个数据,底层使用双向链表存储结构实现
添加/修改数据
lpush key value1 [value2] ... //从左添加 (倒序添加)
rpush key value1 [value2] ... //从右添加 (正序添加)
获取数据
lrange key start stop // 获取第start个 到 第stop个
lindex key index // 获取第index个元素
len key //获取list长度
获取并移除数据
lpop key
rpop key
规定时间内获取并移除数据,阻塞获取,list中现在没有元素不代表之后没有,调用以下两个命令,可以阻塞等待timeout秒,等待新的数据添加.
blpop key1 [key2] timeout // b => block
brpop key1 [key2] timeout //
业务场景
微信朋友圈点赞,要求按照点赞顺序显示点赞好友信息
移除指定数据
lrem key count value
key : list名
count : 移除多少个 list元素是可以重复的.
value : 移除哪个元素?
redis可以用于具有先后顺序的数据控制
list中保存的数据类型都是string类型的,数据总容量是有限的,最多2^32-1个元素
list具有索引的概念,但是操作数据时,通常以队列的形式进行入队出队操作,或以栈的形式进行入栈出栈操作
获取全部数据操作结束索引设置为-1
list可以对数据进行分页操作,通常第一页的信息来源于list,第2页一级更多的内容通过数据库的形式加载.
twitter,sina微博,腾讯微博中个人用户的关注列表需要按照用户的关注顺序进行展示,粉丝列表需要将最近关注的粉丝列在前面.
新闻,资讯类网站如何将最新的新闻或者资讯按照发生的时间顺序显示.
企业运营过程中,系统将产出大量的运营数据,如何保障多台服务器操作日志的统一顺序输出?
解决方案
依赖list的数据具有顺序的特征对信息进行管理.
使用队列模型解决多路信息汇总合并的问题.
使用栈模型解决最新消息的问题.
新的存储需求 : 存储大量的数据,在查询方面提供更高的效率
需要的存储结构 : 能够保存大量的数据,高效的内部存储机制,便于查询
set类型 : 与hash存储结构完全相同,仅存储键,不存储值,并且值是不允许重复的.
添加数据
sadd key member1 [ member2 ]
获取全部数据
smembers key
删除数据
srem key member1 [member2]
获取集合数据总量
scard key
判断集合中是否包含指定数据
sismember key member
业务场景
每位用户首次使用今日头条时会设置三项爱好的内容,但是后期为了增加用户的活跃度,兴趣点,必须让用户对其他信息类别逐渐产生兴趣,增加客户留存度,如何实现?
业务分析
系统分析出各个分类的最新或最热点信息条目并组织成set集合
随机挑选其中部分信息
配合用户关注信息分类的热点信息组织成展示的全信息集合.
解决方案
随机获取集合中指定数量的数据
srandmember key [count]
随机获取set集合中的某个数据并将该数据移除集合
spop key [count]
Redis应用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热点旅游线路,应用APP推荐,大V推荐等.....
关注相同的公众号的好友数量
求两个集合的交集,并集,差集
sinter key1 [key2]
sunion key1 [key2]
sdiff key1 [key2]
求两个集合的交集,并集,差集并存储带指定的集合中
sinterstore destination key1 [key2]
sunionstore destination key1 [key2]
sdiffstore destination key1 [key2]
将指定数据从原始集合中移动到目标集合中
smove source destination member
redis应用于同类信息的关联查询,二度关联查询,深度关联查询
显示共同关注(一度)
显示共同好友(一度)
由用户A出发,获取到好友用户B的好友信息列表(一度)
由用户A出发,获取到好友用户B的购物清单列表(二度)
由用户A出发,获取到好友用户B的游戏充值列表(二度)
set类型不允许数据重复,如果添加的数据在set中已经存在,将只保留第一份
set虽然与hash的存储结构系统,但是无法启用hash中存储值的空间
应用场景X
集团公司共有12000名员工,内部OA系统具有700多个角色,3000多个业务操作,23000多种数据,每位员工具有一个或者多个角色,如何款速进行业务操作的权限校验.
应用场景Ω
公司旗下新的网站做推广,统计网站的PV(访问量),UV(独立访客),IP(独立IP)
PV : 网站被访问的次数,可通过刷新页面提高访问量
UV : 网站被不同用户访问的次数,可通过cookie统计访问量,相同用户切换IP地址,UV不变
IP : 网站被不同IP地址访问的总次数,可通过IP地址统计访问量 ,相同IP不同用户访问,IP不变.(同一个网吧)
解决方案
利用set集合的数据去重特征,记录各种访问数据
建立string类型数据,利用incr统计日访问量
建立set模型,记录不同cookie数量
建立set模型,记录不同IP数量
redis应用于同类型数据的快速去重
应用场景Z 黑白名单
黑名单
资讯类信息类网站追求高访问量,但是由于其信息的价值,往往容易被不法分子利用,通过爬虫技术,快速获取信息,个别特种行业网站信息通过爬虫获取分析之后,可以转换成商业机密进行出售.例如第三方火车票,机票,酒店刷票代购软件,电商刷评论,刷好评.
同时爬虫带来的伪流量也会给经营者带来错觉,产生错误的决策,有效避免网站被爬虫反复爬取成为每个网站都要考虑的问题.在基本技术层面区分出爬虫用户之后,需要将此类用户有效的屏蔽,这就是黑名单
白名单
对于安全性更高的应用访问,仅仅靠黑名单是不能解决问题的,此时需要设定可访问的用户群体,依赖白名单做更加苛刻的访问验证.
基于经营战略设定问题用户发现,鉴别规则
周期性更新满足规则的用户黑名单.加入set集合
用户行为信息达到后与黑名单进行比对,确认行为去向
黑名单过滤IP地址:应用于开放游客访问权限的信息源头
黑名单过滤设备信息 : 应用于限定访问设备的信息源
黑名单过滤用户 : 应用于基于访问权限的信息
redis应用于基于黑名单与白名单设定的服务控制
新的存储需求 : 数据排序有利于数据的有效展示 , 需要提供一种可以根据自身特征进行排序的方式
需要的存储结构 : 新的存储模型,可以保存可排序的数据
sorted_set类型 : set的存储结构基础上添加可排序字段
添加数据
zadd key score1 member1 [score2 member2]
获取全部数据
zrange key start stop [WITHSCORE]
zrevrange key start stop [WITHSCORE]
删除数据
zrem key member [member...]
按条件获取数据
zrangebyscore key min max [WITHSCORE] [LIMIT index count] eg : zrangebyscore scores 9 21 limt 0,3 withscores
zrevrangebyscore key max min [WITHSCORE] 查询score从min到max的数据
条件删除数据
zremrangebyrank key start stop 删除从索引start到stop的数据
zremrangebyscore key min max 删除score从min到max的数据
获取集合数据总量
zcard key
zcount key min max
集合交,并操作
zinterstore destination numkeys key [key ... ] numkeys : 操作集合的数量,会对相同的值的scoe进行求和操作.
zunionstore destination numkeys key [key ...] numkeys : 操作集合的数量,会对相同的值的scoe进行求和操作.
业务场景
票选广东十大杰出青年,各类综艺选修海选投票
各类资源网站TOP10(电影,歌曲,文档,电商,游戏等)
聊天室活跃度统计
游戏好友亲密度
解决方案
获取数据对应的索引
zrank key member
zrevrank key member
score值获取与修改
zscore key member
zincrby key increment member
score保存的数据存储空间时64位的,如果是整数(意味着score的值可以为小数),则范围是 -9007199254740992~-9007199254740992
score保存的数据也可以是一个双精度的double值,基于双精度浮点数的特征,肯能会丢失精度,使用时要慎重.
sorted_set底层存储还是基于set结构,因此数据不能重复,如果重复添加相同的数据,score值将被反复覆盖(这一点区分于set),保留最后一次修改结果.
业务场景
基础服务 + 增值服务类网站(百度碗盆)会设定各位会员的试用,让用户充分体验会员优势.当会员到期之后,如何有效管理此类信息,即便对于正式会员用户也存在对应的管理方式.
网站会定期开启投票,讨论,限时进行,逾期作废.如何有效管理此类过期信息.
解决方案
对于基于时间线限定的任务处理(任务队列),将处理事件记录为score值,利用排序功能区分处理的先后顺序
记录下一个要处理的时间,当到期后处理对应任务,移除redis中的记录,并记录下一个要处理的时间
当新任务加入时,判断并更新当前下一个处理的任务时间
为提升sorted_set的性能,通常将任务根据特征存储成若干个sorted_set.例如1小时内,1天内,1周内....etc.操作时逐渐提升,将即将操作的若干任务纳入到1小时内处理的队列中.
time from 1970秒数
业务场景
任务/消息权重设定应用
当任务或者消息待处理,形成了任务队列或者消息队列时,对于高优先级的任务要保证对其优先进行处理,如何实现任务权重的管理?
解决方案
对于带有权重的任务,优先处理权限高的任务,采用score记录权重即可
多条件任务权重设定
如果权重条件过多时,需要对排序score值进行处理,保障score值能够兼容2条件或者多条件,例如外贸订单优先于国内订单,总裁订单优先于员工订单,经理订单优先于员工订单.
因score长度受限,需要对数据进行截断处理,尤其是时间设置为小时或者分钟集合(折算后)
先设定订单类别,后设定订单发起角色类别,整体score长度必须是统一的,不足位补0.第一排序规则首位不能是0
例如 外贸101,国内102,经理004,员工008
员工下的外贸单score值为101008(优先)
经理下的国内单score值为102004
同样的位置要求宽度是一样的.
人工智能领域的语义识别与自动对话将是未来服务员机器人呼叫体系中重要技术,百度自研用户评价语义识别服务,免费开放给企业试用,同时训练百度自己的模型.现对试用用户的使用行为进行限制,限制每个用户每分钟最多发起10次调试.
解决方案
设计计数器,记录调用次数,用于控制业务执行次数.以用户id作为key,使用次数作为value
在调用前获取次数,判断是否超过限定次数
不超过次数的情况下,每次调用计数 + 1
业务调用失败 ,计数 - 1
为计数器设置声明周期为指定周期,例如1秒或者分钟,自动清空周期内使用次数.
改良方案(利用数据上限的最大值)
取消最大值的判定,利用incr操作超过最大值抛出异常的形式替代每次判断是否大于最大值
判断是否为nil
如果是,设置为Max - 次数(业务上允许的次数)
如果不是 , 计数 + 1
业务调用失败 . 计数 -1
遇到异常即 + 操作超过上限,视为使用达到次数上限.
使用微信的过程,当微信接收消息之后,会默认将最近接收的消息置顶,当多个好友及关注的订阅号同时发送消息时,该排序会不停的进行交替,同时还可以将重要的会话设置为置顶.一旦用户离线之后,再次打开微信时,消息该按照什么样的顺序显示?
解决方案
依赖list的数据具有顺序的特征对消息进行管理 , 将list结构作为栈使用(单端)
对置顶与普通会话分别创建独立的list分别管理
当某个list中接收到用户消息后,将消息发送方的id从list一侧加入list(此处设定为左侧)
多个相同id发出的消息反复入栈会出现问题,在入栈之前无论是否具有当前id对应的消息,先删除对应id
推送消息时先推送置顶会话list,再推送普通会话list,推送完成的list清除所有数据
消息的数量,也就是微信用户对话数量采用计数器的思想另行记录,伴随list操作同步更新
删除指定key
del key
获取key是否存在
exists key
获取key的类型
type key
为指定key设置有效期
expire key seconds //为一个key设置有效期秒
pexpire key milliseconds //为一个key设置有效期秒毫秒
expireat key timestamp
pexpireat key milliseconds-timestamp
获取key的有效时间
ttl key
pttl key //时间戳
切换key从时效性转为永久性
persist key
查询key
keys pattern //pattern =>key名正则表达式
※ 匹配任意数量的任意符号 ? 匹配一个任意符号 [] 匹配一个指定符号
为key改名
rename key newkey
renamenx key newkey //若更新的key名字存在,则更新失败
对所有key排序
sort collection //对集合进行排序,不对元数据进行更改
其他key通用操作
help @generic
key的重复问题
key是由程序员定义的
redis在使用的过程中,伴随着操作数据量的增加,会出现大量的数据以及对应的key
数据不分种类,类别混杂在一起,极易出现重复或者冲突
解决方案
redis为每个服务器提供16个数据库,编号从0到15
每个数据库之间的数据相互独立
切换数据库
select index
其他操作
quit //退出客户端 ctrl + c
ping //测试服务器连通
echo message
数据移动
move key db //剪切数据到其他数据库
数据清除
dbsize //当前数据库下key的数量
flushdb //删除当前数据库下的所有数据
flushall //删除所有数据
※ Jedis中 API 与 Redis客户端中的一模一样.
Jedis其他的API,请自行查阅练习
Hello World
1.连接Redis
2.操作Redis
关闭Redis连接
- class JedisdemoApplicationTests {
- @Test
- public void test() {
- //1.连接Redis
- Jedis jedis = new Jedis("127.0.0.1", 6379);
- //2.操作Redis
- jedis.set("name", "itheima");
- System.out.println(jedis.get("name"));
- //3.关闭连接
- jedis.close();
- }
- }
服务调用次数控制
人工智能领域的语义识别与自动对话将是未来服务业机器人应答呼叫体系中的重要技术,百度自研用户评价语义服务失败服务,免费开放给企业试用,同时训练百度自己的模型.现对试用用户的使用行为进行限速,限制每个用户每分钟最多发起10次调用
案例要求
设定A,B,C三个用户
A用户限制10次/分调用,B用户限制30次/分调用,C用户不受限制.
具体代码 我就不写了,但是要注意一点
- public static void main(String[] args) throws Exception {
-
- Jedis jedis = new Jedis("127.0.0.1", 6379);
- jedis.setex("keyB", 1, "0");
-
- TimeUnit.SECONDS.sleep(2);
- jedis.incr("keyB");
- System.out.println(jedis.get("keyB"));
-
- jedis.close();
-
- }
这里虽然设置了 keyB 的存活时间为1秒,但是jedis.incr操作会重新创建一个key,将value初始值设置为0,所以打印语句打印才能成功.
所以需要做判断
- public static void main(String[] args) throws Exception {
-
- Jedis jedis = new Jedis("127.0.0.1", 6379);
- jedis.setex("keyB", 1, "0");
-
- TimeUnit.SECONDS.sleep(2);
- if(jedis.get("keyB") != null){
- jedis.incr("keyB");
- }
- System.out.println(jedis.get("keyB"));
-
- jedis.close();
- }
基于连接池获取连接
jedisPool : Jedis提供的连接池技术
poolConfig : 连接池配置对象
host : redis 服务地址
post : redis服务端口号
- public class JedisUtil {
-
- private static JedisPool jedisPool = null;
-
- static {
- //JedisPool核心配置
- JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
- //最大连接数
- jedisPoolConfig.setMaxTotal(30);
- //核心连接数
- jedisPoolConfig.setMaxIdle(10);
- //连接池
- jedisPool = new JedisPool(jedisPoolConfig,"127.0.0.1",6379);
- }
-
- public static Jedis getJedis(){
- return jedisPool.getResource();
- }
- }
常量部分请做成配置文件.
获取redis资源
解压
tar xzvf redis-4.0.8.tar.gz
进入到目录下
cd redis-4.0.8
安装
make install
进入src目录
cd src
启动
redis-server
启动了前台进程,关闭失效
启动前台进程
打开新的连接
进入目录
cd redis-4.0.8/
进入目录
cd src
打开客户端
redis-cli
Redis启动指定端口号服务
redis-server --port 6380
Redis启动对应端口号客户端
redis-cil -p 6380
在redis-4.0.8/下的配置文件
redis.conf
去掉注释和空白查看文件
cat redis.conf | grep -v "#" | grep -v "^s s->美元符号,这里打不出来
尽量不要动原配置文件,复制
cat redis.conf | grep -v "#" | grep -v "^s > redis-6379.conf
修改配置文件 redis-6379.conf
bind 127.0.0.1 //绑定使用哪个ip启动
port 6379
daemonize yes //以守护进程的方式启动 (后台启动)
log "" //后台启动,日志文件保存的文件名
dir "" //生成文件的位置
使用配置文件启动
redis-server redis-6379.conf
将配置文件放在统一目录下,统一管理
mkdir conf
意外的断电
利用永久性存储性介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持久化
持久化
方式1 : 快照 (每隔一定时间,保存备份到磁盘上 ) RDB
将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在于数据
方式2 : 日志 (记录的是操作过程 ctrl + z ctrl + z ctrl + y ctrl + y ) AOF
将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在于数据的操作过程
RDB启动方式 ----- save指令
save
作用
手动执行一次保存操作
会在设置的日志目录下生产 dump.rdb二进制文件,将数据持久化
设置本地数据库文件名,默认为dump.rdb
dbfilename dump.rdb //经验 : 通常设置为dump-端口号.rdb
设置存储.rdb文件的路径
dir //通常设置成存储空间较大的目录中,目录名称data(自定义)
设置存储至本地数据库时是否压缩数据,默认为yes,采用LZF压缩
rbdcompression yes //经验 : 通常默认为开启状态,如果设置为no,可以节省CPU运行时间,但会使存储的文件变的很大
设置是否进行RDB文件格式校验,该校验过程在写文件和读文件过程均进行
rdbchecksum yes //通常设置为开启状态,如果设置为no,可以节约读写性过程约10%时间消耗,但是存在一定的数据损坏风险
后台存储过程中如果出现错误现象,是否停止保存操作
stop-write-on-bgsave-error yes //经验 : 通常默认为开启状态
因为Redis是单线程的,所以只会一个一个执行任务.
注意 : save指令的执行会阻塞当前Redis服务器,直到当前RDB过程完成为止,有可能会造成长时间阻塞,线上环境不建议使用
数据量过大,单线程执行方式造成效率过低如何处理
解决方案 : 后台执行,手动启动后台保存操作,但不是立即执行
bgsave
bgsave 工作原理 : 创建子进程,创建rdb文件.
注意:bgsave命令是针对save阻塞问题做的优化.Redis内部所有涉及到RDB操作都采用bgsave方式,save命令可以放弃使用.
以上两种方式 save 和 bgsave 都是手动执行的,那么反复执行保存指令,忘记了怎么办?不知道数据产生了多少变化,何时保存?
Redis服务器发起指令(基于条件)
RDB 启动方式 ----save配置
满足限定时间范围内key的变化数量达到指定数量即持久化
save second changes //在second的时间范围内,检测到changes数量的key发生变化,那么久进行持久化
配置位置 : 在conf文件中进行配置
配置原理
save 10 2,即使对同一个key在10秒内操作两次,也会执行持久化操作.
注意 : save配置要根据实际业务情况进行设置,频度过高或者过低都会出现性能问题,结果可能是灾难性的,save配置中对于second与changes这支通常具有互补对应关系,尽量不要设置成包含性关系(second大,changes就小,反之亦然),save配置启动后执行的是bgsave操作.
方式 | save指令 | bgsave指令 |
---|---|---|
读写 | 同步 | 异步 |
阻塞客户端指令 | 是 | 否 |
额外内存消耗 | 否 | 是 |
启动新进程 | 否 | 是 |
全量复制
在主从复制中详细讲解(想把一个Redis服务器的数据复制到另一台上,不存储怎么复制啊?)
服务器运行过程中重启
debug reload
关闭服务器时指定保存数据
shutdown save
RDB优点
RDB是一个紧凑压缩的二进制文件,存储效率较高
RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景
RDB恢复数据的速度比AOF快很多
应用 : 服务器每X小时执行bgsave备份,并将RDB文件拷贝到远程机器中,用于灾难恢复
RDB缺点
RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据
bgsave指令每次运行要执行fork操作创建子进程,要牺牲一些性能
Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现版本服务之间数据格式无法兼容现象
AOF(append only file)持久化,以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的,与RDB相比可以简单描述为
改记录数据为记录数据产生的过程
AOF的主要作用是解决了数据持久化的实时性,目前已经成为Redis持久化的主流方式
AOF写数据三种策略(appendfsync)
always(每次)
每次写入操作均同步到AOF文件中,数据零误差,性能较差
everysec(每秒)
每秒将缓冲区中的指令同步到AOF中,数据准确性较高,性能较高,建议使用,也是默认配置在系统突然宕机的情况下丢失一秒的数据
no(系统控制)
由操作系统控制每次同步到AOF文件的周期,整体不可控制
配置文件配置
配置
appendonly yes|no //是否开启AOF持久化功能,默认为不开启状态
配置
appendfsync always|everysec|no //AOF写数据策略\
配置
appendfilename filename //AOF持久化文件名,默认文件名为appendonly.aof,建议配置为appendonly-端口号.aof
配置
dir //AOF持久化文件保存路径,与RDB持久化文件保持一致即可
如果连续执行如下指令,Redis将如何进行AOF处理
AOF重写
随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积.AOF文件重写是将Redis进程内的数据转化为写命令同步到新AOF文件的过程.简单来说就是将同一个数据的若干条命令执行结果转化成最终结果数据对应的指令进行记录.
AOF重写的作用
降低磁盘占用量,提高磁盘利用率
提高持久化效率,降低持久化写时间,提高IO性能
降低数据恢复时,提高数据恢复效率
AOF重写的规则
进程内已超时的数据不再写入文件
忽略无效指令,重写时使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令,如del key1,hdel key2,srem key3,set key4 1111 ket key4 222等.
对同一数据的写命令合并为一条命令,如lpush list1 a , lpush list b , lpusb list c 可以转化为 lpush list1 a b c,为防止数据量过大造成客户端缓冲区溢出,对list,set,hash,zset等类型,每条指令最多写入64个元素.
命令 : 手动重写
bgrewriteaof
指令
info //Redis当前运行环境下的环境值
其中有两条值
aof_current_size //AOF缓冲中已经有了多大的数据量
aof_base_size //AOF缓冲基础尺寸
配置
自动重写触发条件设置
auto-aof-rewrite-min-size size
auto-aof-rewrite-percentage percent
自动触发条件参数
aof_current_size //AOF缓冲中已经有了多大的数据量
aof_base_size //AOF缓冲基础尺寸
自动重写触发条件
持久化方式 | RDB | AOF |
---|---|---|
占用存储空间 | 小(数据量 : 压缩) | 大(指令级 : 重写) |
存储速度 | 慢 | 快 |
恢复速度 | 快 | 慢 |
数据安全性 | 会丢失数据 | 依据策略决定 |
资源消耗 | 高/重量级 | 低/轻量级 |
启动优先级 | 低 | 高 |
对数据非常敏感,建议使用AOF持久化方案
AOF持久化策略使用everysec , 每秒钟fsync一次.该策略redis仍然可以保持很好的处理性能,当出现问题时,最多丢失0 - 1秒的数据
注意 : 由于AOF文件存储体积较大,且恢复速度较慢
数据呈现阶段有效性,建议使用RDB持久化方案
数据可以良好的做到阶段内无丢失 (该阶段是开发者或者运维人员手工维护的) , 且恢复速度较为快 , 阶段点数据恢复通常采用RDB方案.
注意 : 利用RDB实现紧凑的数据持久化会使Redis的效率降得很低 (save 中的 second 和 changes 的值很小)
综合对比
RDB与AOF的选择实际上是在做一种权衡,每种都有利有弊
如不能承受数分钟内的数据丢失,对业务操作非常敏感,选用AOF
如果能承受数分钟内的数据丢失,且追究大数据集的恢复速度,选用RDB
灾难恢复选用RDB
双保险策略,同时开启RDB和AOF,重启后,Redis优先使用AOF恢复数据,降低丢失的数据量.
Q&A : 要不要持久化?
redis用于控制数据库表主键id,为数据库表主键提供生成策略,保障数据库表的主键唯一性
除了AOF的always策略可以保证数据不丢失之外,但是持久化策略又不建议使用AOF的always策略,其他的持久化策略多少有数据的丢失,对于数据库表主键这种要求绝对精准的数据的数据显然不合适,所以不适合做持久化.
redis应用于各种结构型和非结构型高热度数据访问加速
热点的新闻都是从数据库里面来的,放入缓存,再持久化没有意义,还容易丢数据,所以不建议持久化.
redis应用于购物车数据存储设计
理由同上
redis应用于抢购,限购类,限量发行优惠券,激活码等业务的数据存储设计
对于这种要求不那么精确,并且快速存储,快速消失的数据,可以用于Redis持久化,最多多发放几个激活码而已,所以可以持久化.
redis应用于具有操作先后顺序的数据控制
临时的数据可能不会再数据库持久化,并且如果数据量比较小的话建议持久化,
redis应用于最新消息展示
同上
redis应用于同类信息的关联搜索,二度关联搜索,深度关联搜索
从数据库中进行查询,同 2 , 3
redis应用于基于黑名单与白名单设定的服务控制
分为两种情况,如果黑名单是永久性记录的(记录在数据库的),就不需要Redis进行持久化,而如果黑名单是暂时性的(可能不存储在数据库,而是存储在Redis中建议持久化),对于白名单,肯定是永久性的,所以不建议持久化.
redis应用于计数器组合排序功能对应的排名
排名信息基本上不会保存在数据库中,所以建议持久化.
redis应用于即时任务/消息队列执行管理
有其他的解决方案,都没有必要存储在Redis中
redis应用于按次结算的服务控制
视情况而定吧
总结 : 就是具有时效性的或者会一直发生改变的不需要进行持久化 , 排名 , 实时统计(在线人数等等).
Redis执行指令的过程中,多条连续执行的指令被干扰,打断,插队.
redis事务就是一个命令执行的队列,将一系列预定义命令包装成一个整体(一个队列).当执行时,一次性按照添加添加顺序依次执行,中间不会被打断或者干扰.
开启事务
multi // 设定事务开始的位置,此指令执行后,后续的所有指令均加入到事务中
执行事务
exec //作用 :设定事务的结束位置,同时执行事务.与multi成对出现,成对使用.
注意 : 加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行
取消事务 : (加入队列的任务命令输入错误了) 终止当前事务的定义,发生在multi之后,exec之前.
discard
定义事务的过程中,命令格式输入错误怎么办?
语法错误
指命令书写格式有误(比如 est name 123)
处理结果
如果定义的事务中所包含的命令存在语法错误,整体事务中所有命令均不会执行.包括哪些语法正确的命令
运行错误
指命令格式正确,但是无法正确的执行.例如对list的进行incr操作
处理结果,作为程序员不应该出现这种错误.
能够正确运行的命令会执行,运行错误的命令不会被执行--->Redis这种机制时要程序员命. 已经执行完的事务...无法回滚...
手动进行事务回滚
记录操作过程中被影响的数据之前的状态
单数据 : string
多数据 : hash , list , set ,zset
设置指令恢复所有的被修改的项
单数据 : 直接set(注意周边属性,例如时效)
多数据 : 修改对应值或整体克隆赋值
业务场景
天猫双11热卖过程中,对已经售罄的货物追加补货,4个业务员都有权限进行补货.补货的操作可能是一系列的操作,牵扯到多个连续操作,如何保障不会重复操作?
业务分析
多个客户端有可能操作同一组数据,并且该数据一旦被修改后,将不适用与继续操作
在操作之前锁定要操作的数据,一旦发生变化,终止当前操作(乐观锁)
解决方案 : watch : 监控一个值是否发生改变的.
watch key1 [key2...] , 对key添加监视锁,在执行exec前,如果key发生了变化,终止事务执行
unwatch //取消对所有key的监视
被watch监控的数据发生了改变了的话,下面定义的事务就不会再执行了.
天猫双11热卖的过程中,对已经售罄的货物追加补货,且补货完成.客户购买热情高涨,3秒内将所有商品购买完毕.本次补货已经将库存全部清空,如何避免最后一件商品不被多人同时购买(超卖问题)
业务分析
使用watch监控一个key有没有改变已经不能解决问题,此处要监控的是具体数据
虽然Redis是单线程的,但是多个客户端对同一数据同时进行操作(修改)时,如何避免不被同时修改.
解决方案
使用setnx设置一个公共锁
setnx lock-KEY value
利用 setnx命令的返回值特征,有值则返回设置失败,无值则返回设置成功,多个客户端只能有一个客户端对某个KEY进行setnx lock操作,也就是说第一个客户端进行setnx的情况下,如果没有del释放掉锁,则第二个客户端进行的setnx lock操作时无效的.实现了分布式锁的效果.
对于返回设置失败的,不具有控制权,排队或等待操作完毕通过del操作释放锁.
del lock-key
业务场景
依赖分布式锁的机制,某个用户操作时对应客户端宕机 (有setnx上锁 , 没del锁),且此时已经获取到了锁.如何解决?
业务分析
由于锁操作由用户控制加锁解锁,必定会存在加锁后未解锁的风险
需要解锁操作不能仅仅依赖用户控制,系统级别要给出对应的保底处理方案.(隔多产时间上锁之后不释放锁,自动del释放锁
解决方案
使用expire为锁key添加时间限定,到时不释放的话,直接放弃锁
expire lock-key second
pexpire lock-key milliseconds
时间设置的经验
由于操作通常都是微妙或者毫秒级别的,因此锁定时间不宜设置过大.具体时间需要业务测试后确认.
例如 : 持有锁的操作最长执行时间为127ms , 最短执行时间为7ms.
测试百万次最长执行时间对应命令的最大耗时,测试百万次网络延迟平均耗时
锁时间设定推荐 : 最大耗时 * 120% + 平均网络延迟 * 110%
如果业务最大耗时 << 网络平均延迟 , 通常为2个数量级 , 取其中单个耗时较长即可.
Redis中的数据特征
Redis是一种内存级别数据库,所有数据均放在内存中,内存中的数据可以通过TTL指令获取其状态
XX : 具有时效性的数据
-1 : 永久有效数据
-2 : 已经过期的数据 或 被删除的数据 或 未定义的数据
时效性数据的存储结构
在内存占用与CPU占用之间寻找一种平衡 , 顾此失彼都会造成整体redis性能下降 , 甚至引发服务器宕机或者内存泄漏.
过期的数据真的被删除了吗? Really
执行了 del 了指令了,数据暂时没有实现真正的删除,而是等待CPU忙完其他重要指令 (set get) 之后,再指定del操作
定时删除
创建一个定时器,当key设置过期时间,且过期时间到达时 , 由于定时器任务立即执行对键的删除操作
优点 : 节约内存 , 到时就删除 , 快速释放掉不必要的内存占用
缺点 : CPU压力很大 , 无论CPU此时负载量有多高 , 均占用 CPU , 会影响redis服务器响应时间和指令吞吐量
总结 : 用处理器性能换区存储空间 (拿时间换空间)
惰性删除
数据到达过期时间 , 不做处理 . 等下次访问该数据时
如果未过期 , 返回数据
发现已经过期 , 删除 , 返回不存在
优点 : 节约CPU性能 , 发现必须删除的时候才删除
缺点 : 内存压力很大 , 出现长期占用内存的数据
总结 : 用存储空间呼喊去处理器性能 (拿时间换空间 )
定期删除 - 一种考虑占用CPU性能 , 和内存空间利用率的这种方案
Redis启动服务器初始化时 , 读取配置server.hz的值,默认为10
每秒钟执行server.hz次serverCron() ----> databaseCron() ------> activeExpireCycle
activeExpireCycle对每个expires[*]逐一进行检测 , 每次执行250ms/server.hz
对某个expires[*]检测时,随机挑选W个key检测
如果key超时 , 删除key
如果一轮中删除的key的数量>25% * W , 就循环该过程
如果一轮中删除的key的数量 <= W * 25% , 检查下一个expire[*] , 0-15(Redis database数量)循环
W取值 = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PRE_LOOP属性值,在配置文件中配置
参数current_db用于记录 activeExpireCycle()进入哪个expires[*]执行(每个数据库对应一个expire)
如果activeExpireCycle()执行时间到期 , 下次从current_db继续向下执行.
周期性轮询redis库中的时效性数据,采用随机抽取的策略 , 利用过期数据占比的方式控制删除频度
特点1 : CPU性能占用设置有峰值 , 检测频度可自定义设置
特点2 : 内存压力不是很大 , 长期占用内存的冷数据会被持续清理
总结 : 周期性抽查存储空间 (随机抽查 , 重点抽查)
当新数据进入Redis时,如果内存不足怎么办?
Redis使用内存存储数据,在执行每个命令之前,会调用freeMemoryIfNeeded(),检测内存是否充足.如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间.清理数据的策略称为逐出算法
注意 : 逐出数据的过程不是100%能够清理出足够的可使用的内存空间,如果不成功则反复执行.当对所有数据尝试完毕之后,如果不能达到内存清理的要求,将出现错误信息.
(error OOM command not allowed when used memory>'maxmemory')
影响数据逐出的相关配置
最大可使用内存
maxmemory //占用物理内存的比例,默认值为0,表示不限制.生产环境中根据需求设定,通常设置为50%以上.
每次选取待删除数据的个数
maxmemory-samples //选取删除的数据时并不会全库扫描,导致严重的性能消耗,降低读写性能,因此采用随机获取数据的方式作为待检测删除数据.
删除策略
maxmemory-policy //达到最大内存后的,被挑选出来的数据进行删除的策略.
删除策略
检测易失去数据(设有存活时间的数据,可能会过期的数据集server.db[i].expires)
volatile-lru : 挑选最近最少使用的数据淘汰,这个人已经有一周没来上班了,开除
volatile-lfu : 挑选最近使用次数最少的数据淘汰,这个人成天在公司,吃了睡,睡了吃,开除
volatile-ttl : 挑选将要过期的数据淘汰
volatile-random : 任意选择数据淘汰
检测全库数据 (所有数据集server.db[i].dict)
allkeys-lru : 挑选最近最少使用的数据淘汰
allkeys-lfu : 挑选最近使用次数最少的数据淘汰
allkeys-random : 任意选择数据淘汰
放弃数据驱逐
no-enviction // 禁止驱逐数据(redis4.0中默认策略),会引发错误OOM,大家关系都挺好的,开除谁都不容易!
配置
maxmemory-policy volatile-lru
使用INFO命令输出监控信息,查询缓存hit和miss次数,根据业务需求调优Redis配置.
设置服务器以守护进程的方式运行
deamonize yes | no
绑定主机地址
bind 127.0.0.1
设置服务器端口号
port 6379
设置数据库数量
dababase 16
日志配置
设置服务器以指定日志记录级别
loglevel debug | verbose | notice | warning
日志记录文件名
logfile 端口号.log
注意 : 日志级别开发设置为verbose即可,生产环境中配置为notice , 简化日志输出口 , 降低写日志IO频度.
客户端配置
设置同一时间最大客户端连接数 , 默认无上限 , 当客户端连接达到上限 , Redis会关闭新的连接
maxclients 0 //0: 无限制
客户端限制等待最大时长 ,达到最大值后关闭连接 , 如需关闭该功能 ,设置为0
timeout 300
多服务器快捷配置
导入并加载指定配置文件信息 , 用于快速创建redis公共配置较多的redis实例配置文件,便于维护
include /paht/server-端口号.conf
计算机中存储一个数据最少也需要一个字节(8位),但是如果我们存入的数据如果是具有标志属性的,比如有无,男女,是否可以用0,1表达的话,用字节存储显得过于浪费,因此利用比特位存储标志位类信息也许是个不错的选择.比如有8个人,可以放在一个字节之中(对应用户编号),表示是否为党员.
获取指定key对应偏移量上的bit值
getbit key offset
设置指定key对应偏移量上的bit值,value只能是1或者0
setbit key offset value
注意 : 如果您的偏移量比较大(比如直接存储按主键自增的用户Id,建议全部减去一个固定值,将offset减小)
业务场景 : redis应用于状态统计
电影网站
统计每天某一部电影是否被点播
统计每天有多少部电影被点播
统计每周/月/年有多少部电影被点播
统计年度哪部电影没有被点播
业务分析
假设一个电影网站有8部电影
我们将每天作为bitMap的key统计这部电影今天是否被播放过
比如 0101 0011 这里 offset 为1 3 6 7 代表的电影今天被播放过
0101 0011 今天有4部电影被点播过
对每周/月/年有多少部电影被点播过,对bit位求和
对全年的bitMap(每天作为key)做或操作.
Bitmaps 的扩展操作
对置顶key按位进行操作,并把结果保存在destKey当中
bitop op destKey key1 [key2]
and
or
not
xor
统计置顶key中1的数量
bitcount key [start end] //范围不给默认全范围
HyperLogLof类型及基本操作
添加数据
pfadd key element [element]
统计数据
pfcount key [key...]
合并数据
pfmerge destkey sourcekey [sourcekey...]
应用场景
redis应用于独立信息统计
用于基数统计,不是集合,不保存数据,只记录数量而不是具体数据
核心是基数估算算法,最终值存在一定误差
误差范围 : 基数估计的结果是一个带有0.81%标准错误的近似值
耗空间极小,每个hyperloglog key占用12K的内存用于标记基数
pfadd命令不是一次性分配12k内存使用,会随着基数的增加内存主键增大
pfmerge命令合并后占用的内存空间为12k,无论合并之前数据量有多少
HyperLogLog只是用于计数的,不要把它当set用!!!!(大V粉丝数量,超热度视频播放量)
生活服务类软件可以统计附近的人,比如微信等等.
GEO类型的基本操作
添加坐标点
geoadd key longitude latitude member [longtitude latitude member ... ]
获取坐标点
geopos key member [member ...]
计算坐标点距离
geodist key member1 member2 [unit]
unt : 单位
m : 米
km : 千米
根据坐标求范围内的数据
georadius key longtitude latitude radius m | km | ft | mi [withcoord] [withdist] [withhash] [start end]
根据点求范围内的数据
georadiusbymember key member radius m | km | ft | mi [withcoord] [withdist] [withhash] [start end]
获取指定点对应的坐标hash值
geohash key member [member ...]
withcoord //显示坐标
withdist //显示距离
withhash //显示hash
start end //范围
互联网的"三高"架构
高并发
高性能
高可用
单机Redis的问题
问题1 : 机器故障
现象 : 硬盘故障,系统崩溃
本质 : 数据丢失 , 很可能对业务造成灾难性打击
结论 : 基本上会放弃使用redis
问题2 : 容量瓶颈
现象 : 内存不足,从16G升级到64G,从64G升级到128G,无限升级内存
本质 : 穷,硬件条件跟不上
结论 : 放弃使用redis
结论 : 为了避免单点Redis服务器故障,准备多台服务器,互相连通.将数据复制多个副本保存在不同的服务器上,连接在一起,并保证数据是同步的.即使其中有一台服务器宕机,其他服务器依然可以继续提供服务,实现Redis的高可用,同时实现数据冗余备份.
主从复制即将master中的数据即时,有效的复制到slave中
特征 : 一个master可以拥有多个slave , 一个slave只对应一个master
职责 :
master :
写数据
执行写操作时 , 将出现变化的数据同步到slave
读数据 (可忽略)
slave
读数据
写数据 (禁止)
主从复制的作用
读写分离 : master写,slave读,提高服务器的读写负载能力
负载均衡 : 基于主从结构,配合读写分离,有slave分担master负载,并根据需求的变化,改变slave的数量,通过多个节点分担数据读取负载,大大提高Redis服务器并发量与数据吞吐量
故障恢复 : 当master出现问题时,是持久化之外的一种数据冗余方式
数据冗余 : 实现数据热备份,是持久化之外的一种数据冗余方式
高可用基石 : 基于主从复制,构建哨兵模式与集群,实现Redis的高可用方案
主从复制过程大体可以分为3个阶段
建立连接阶段 (即准备阶段)
数据同步阶段
命令传播阶段(建立连接后受到的数据,不停的发送给slave)
11.1.2.1 建立连接阶段
建立连接阶段工作流程
步骤一 : 设置master的地址和端口,保存master信息
步骤二 : 建立socket连接
步骤三 : 发送ping命令 (定时器任务)
步骤四 : 身份验证
步骤五 : 发送slave端口信息
状态 :
slave :
保存master的地址和端口
master :
保存slave的端口
总体 :
主从之间创建了连接的socket
主从连接 (slave 连接 master)
方式一 : 客户端发送命令
slaveof <masterip> <masterport>
方式二 : 启动服务器参数
redis-server -slaveof <masterip> <masterport>
方式三 : 服务器配置
slaveof <masterip> <masterport>
slave系统信息
master_link_down_since_seconds
masterhost
masterport
master系统信息
slave_listening_port(多个)
从客户端断开连接
客户端发送命令
slaveof no one
授权访问
master配置文件设置密码
requirepass <password>
master客户端发送命令设置密码
config set requirepass <password>
slave客户端发送命令设置密码
auth <password>
slave配置文件设置密码
master <password>
启动客户端设置密码
redis-cli -a <password>
11.1.2.2 数据同步阶段
11.1.2.2 数据同阶
在slave初次连接master后,复制master中所有数据到slave
将slave的数据库状态更新成master当前的数据库状态
步骤一 : 请求同步数据
步骤二 : 创建RDB同步数据
步骤三 : 恢复RDB同步数据
步骤四 : 请求部分同步数据
步骤五 : 恢复部分同步数据
至此 ,数据同步工作完成
TIP : slave连接master时,会执行bgsave时,也会创建一个数据缓冲区.此时master也会产生新的数据,而这一部分数据并不在生成的RDB文件中,将这一部分数据放入数据缓冲区(以AOF形式存在).
状态 :
slave :
具有master端全部数据,包含RDB过程接收的数据
master :
保存slave当前数据同步的位置
总体 :
之间完成了数据克隆
11.1.2.3 数据同步阶段的注意事项
11.1.2.3据同步阶段的注意事项
master
如果master数据量巨大,数据同步阶段应该避免流量高峰期,避免造成master阻塞,影响业务正常执行
复制缓冲区大小设定不合理,会导致数据溢出.如进行全量复制周期太长,进行部分复制时发现数据已经存在丢失的情况,必须进行第二次全量复制,致使slave陷入死循环状态
设置缓冲区大小 repl-backlog-size 1mb
master单机内存占用主机内存的比例不应该过大,建议使用50%-70%的内存,留下30-50%的内存用于执行bgsave命令和创建复制缓冲区.
slave
为了避免slave进行全量复制,部分复制时服务器响应阻塞或者数据不同步,建议关闭此期间的对外写服务
slave-serve-stale-data yes | no
数据同步阶段,master发送给slave信息可以理解master是slave的一个客户端,主动向slave发送命令
多个slave同时对master请求数据同步,master发送的RDB文件增多,会对宽带造成巨大冲击,如果master带宽不足,因此数据同步需要根据业务需求,适量错峰.
slave过多时,建议调整拓扑结构,有一主多从结构变为树状结构,中间的节点既是master也是slave.注意使用树状结构时,由于层级深度,导致深度越高的slave与最顶级master键数据同步延迟较大,数据一致性变差,应该谨慎选择.
11.1.2.4 命令传播阶段
11.1.2.4 命令传播阶段
当master数据库状态被修改之后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的状态,同步的动作称为命令传播.
master将接收到的数据变更命令变更为命令发送给slave,slave接收命令后执行命令.
命令传播阶段出现了断网现象
网络闪断闪连 忽略
短时间网路中断 部分复制
长时间网络中断 全量复制
部分复制的三个核心要素
服务器的运行id (run id)
概念 : 服务器运行ID是每一台服务器每次运行的身份识别码,一台服务器多次运行可以生产多个运行id
组成 : 运行id由40位字符组成,是一个随机的十六进制字符
例如 : 65982fe680b6613fd03017ef07747d5a98359fdf
作用 : 运行id被用于在服务器见进行传输,识别身份
如果想两次操作均对同一台服务器进行,必须每次携带对应的运行id,用于对方识别
实现方式 : 运行id在每台服务器启动时自动生成的,master在首次连接slave时,会将自己的运行Id发送给slave,slave保存此Id,通过info server命令,可以查看节点的runid
主服务器的复制积压缓冲区(AOF)
概念 : 赋值缓冲区,又名复制积压缓冲区,是一个先进先出(FIFO)的队列,用于存储服务器执行过的命令,每次传播命令,master都会将传播的命令记录下来,并存储在复制缓冲区.
复制缓冲区默认数据存储空间大小是1M,由于存储空间大小是固定的,当入队元素的数量大于队列长度时,最先入队的元素会被弹出,而新的元素会被放入队列
由来 : 每台服务器启动时,如果开启有AOF或被连接成为master节点,即创建复制缓冲区
作用 : 用于保存master收到的所有指令 (仅仅影响数据变更的指令,例如set,select)
数据来源 ; 当master接收到主客户端的指令时,除了将指令执行,会将该指令存储到缓冲区中去.
工作原理
组成
偏移量
字节值
工作原理
通过offerset区分不同的slave当前数据传播的差异
master记录已发送的信息对应的offset
slave记录已接收的信息对应的offset
概念 : 一个数字,描述复制缓冲区中的指令字节位置
分类 :
master复制偏移量 ; 记录发送给所有slave的指令字节对应的位置
slave复制偏移量 : 记录slave接收master发送过来的指令字节对应的位置(一个)
数据来源 :
master端 : 发送一次记录一次
slave端 : 接收一次记录一次
作用 :
同步信息 ,比对master与slave差异,当slave断线之后,记录同步位置,恢复数据使用
数据同步 + 命令传播阶段工作流程
心跳机制
进入命令传播阶段的时候,master与slave见需要进行信息交换,使用心跳机制进行维护,实现双方连接保持在线
master心跳
指令 : PING
周期 : 由repl-ping-slave-period决定,默认10秒
作用 : 判断slave是否在线
查询 : INFO replication 获取slave最后一次连接时间间隔,lag项维持在0或1视为正常
slave心跳任务
指令 REPLCONF ACK {offset}
周期 : 1秒
作用1 : 汇报slave自己的复制偏移量 , 获取最新的数据变更指令
作用2 : 判断master是否在线.
心跳阶段注意事项
当slave多数掉线,或者延迟过高时,master为保障数据稳定性,将拒绝所有信息同步操作
min-slaves-to-write 2
min-slaves-max-lag 8
slave数量少于2个,或者所有slave的延迟都大于等于10秒时,强制关闭master写功能,停止数据同步
slave数量有slave发送REPLCONF ACK命令做确认
slave延迟又slave发送REPLCONF ACK命令做确认
工作流程
频繁的全量复制X
伴随着系统的运行,master的数据量会越来越大,一旦master重启,runid将发生变化,会导致全部slave的全量复制操作
内部优化调整方案:
master内部创建master_replid变量,使用runid相同的策略生成,长度41位,并发送给所有slave
在master关闭时执行命令 shutdown save , 进行RDB持久化,将runid与offset保存在RDB文件中
repl-id repl-offset
通过redis-check-rdb命令可以查看该信息
master重启后加载RDB文件,恢复数据,重启后,将RDB文件保存的repl-id与repl-offset加载到内存中
master_repl_id = repl master_repl_offset = repl-offset
通过info命令可以查看该信息
作用 :
本机保存上次runid,重启后恢复该值,使所有slave认为还是之前的master
频繁的全量复制Ω
问题现象
网络环境不佳,出现网络中断,slave不提供服务
问题原因
复制缓冲区过小,断网后slave的offset越界,触发全量复制
最终结果
slave反复进行全量复制
解决方案
修改复制缓冲区大小
repl-backlog-size
建议设置如下 :
测算从master到slave的重连平均时长second
获取master平均每秒产生写命令数据总量write_size_per_second
最优复制缓冲区空间 = 2 * second * write_size_per_second
频繁的网络中断X
问题现象
master的CPU占用过高 或 slave频繁断开连接
问题原因
slave每一秒发送REPLCONF ACK命令到master
当slave接到了慢查询时(keys * , hgetall等),会占用大量CPU性能
master每一秒调用复制定时时函数replicationCron(),比对slave发现长时间没有进行相应
最终结果
master各种资源(输出缓冲区,带宽,连接等)被严重占用
解决方案
通过设置合理的超时时间,确认是否释放slave
repl-timeout
该定义了超时时间的阈值(默认为60秒),超过该值,则释放slave
频繁的网络中断Ω
问题现象
slave与master连接断开
问题原因
master发送ping指令频度较低
master设定超时时间较短
ping指令在网络中存在丢包
解决方案
提高ping指令发送的频度
repl-ping-slave-period
超过时间repl-time的时间至少是ping指令频度的5到10倍,否则slave很容易判定超时.
数据不一致
问题现象
多个slave获取相同数据不同步
问题原因
网络信息不同步,数据发送有延迟
解决方案
优化主从间的网络环境,通常防止在同一个机房部署,如果使用阿里云等云服务器时要注意此现象
监控主从节点延迟(通过offset)判断,如果slave延迟过大,暂时屏蔽程序对该slave的数据访问
slave-server-stale-data yes | no
开启后仅相应info,slaveof等少数命令(慎用,除非对数据一致性要求很高)
许多需要考虑的问题
将宕机的master下线
找一个slave作为master
通知所有的slave连接新的master
启动新的master与slave
(如果处理不当)会触发全量复制 * N + 部分复制 * N
谁来确认master宕机
找一个主? 怎么找法?
修改配置后,原始的主恢复了怎么办?
哨兵
哨兵(sentinel)是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master
监控
不断的检查master和slave是否正常运行
master存活检测,master与slave运行情况检测
通知(提醒)
当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知
自动故障转移
断开master与slave连接,选取一个slave作为master,将其他slave连接到新的master,并告知客户端新的服务器地址
注意 : 哨兵也是一台redis服务器,只是不提供数据服务,通常哨兵配置数量为单数
配置一拖二的主从结构
配置三个哨兵(配置相同,端口不同)
参看sentinel.conf
启动哨兵
redis-sentinel sentinel-端口号.conf
在redis的目录下有个文件 sentinel.conf
去掉注释和换行
cat sentinel.conf | grep -v "#" | grep -v "^quot
monitor mymaster 127.0.0.1 6379 2 //2 : 有多少个哨兵认定主机宕机则视为宕机
down-aftre-milliseconds mymaster 30000 //mymaster主机30秒没反应则视为宕机
parallel-syncs mymaster 1 //新的master上任之后,有1个线程数据同步
failover-timeout mymaster 180000 //180秒内如果数据未同步完成则超时(认为主机宕机)
11.2.3.1 监阶段
监控阶段
用于同步各个节点的状态信息(是否在线)
获取master的状态属性
master属性
runid
role : master
各个slave的详细信息
获取所有slave的状态(根据master中的slave信息)
slave属性
runid
role : slave
master_host , master_port
offset
11.2.3.2 通知阶段
通知阶段
11.2.3.3 故障转移阶段
故障转移阶段
过程有点多且复杂 这里我就不写文字了,up讲的非常生动
哨兵挑选剩余Redis服务器作为主机的策略
在线的
响应快的
与原master断开时间尽量短的
优先原则
优先级
offset
runid
11.2.3.4 总结
监控
同步信息
通知
保持联通
故障转移
发现问题
竞选负责人
优选新master
新master上任,其他slave切换master,原master作为slave故障回复后连接.
业务发展过程中遇到的峰值瓶颈
redis提供的服务OPS可以达到10万/秒,当前业务OPS已经达到了20万/秒
内存单机容量达到256G,当前业务需求内存容量为1T
解决方案
使用集群的方式可以快速解决上述问题
集群架构
集群就是使用网络将若干台计算机联通起来,并提供统一的管理方式,使其对外呈现单机的服务效果
分散单台服务器的访问压力,实现负载均衡
分散单台服务器的存储压力,实现可扩展性
降低单台服务器宕机带来的业务灾难
通过算法设计 , 计算出key应该保存的位置
将所有的存储空间计划切割成16384份,每台主机保存一部分,每份代表的是一个存储空间,不是一个key的保存空间
将key按照计算出的结果放在对应的存储空间
各个数据库相互通信,保存各个库中槽的编号数据
一次命中,直接返回
一次未命中,告知具体位置
cluster-enabled yes //确认使用集群模式
cluster-config-file nodes-6379.conf //集群配置文件名
cluster-node-timeout 10000 //集群未响应超时时间
cluster-migration-barrier <count> //master连接slave最小数量
设定6个Redis server 修改关于端口的配置
sed "s/6379/6380/g" redis-6379.conf > redis-6380.conf
sed "s/6379/6381/g" redis-6379.conf > redis-6381.conf
sed "s/6379/6382/g" redis-6379.conf > redis-6382.conf
sed "s/6379/6383/g" redis-6379.conf > redis-6383.conf
sed "s/6379/6384/g" redis-6379.conf > redis-6384.conf
sed "s/6379/6385/g" redis-6379.conf > redis-6385.conf
安装ruby
设置并启动cluster集群
./redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384
1主一从 (4个或6个)
1主两从 (6个或者9个)
报错
can‘t connect to node
还有我特别蠢的就是 , 服务都没开就竟然创建集群了
操作集群
redis-cli -c
查询cluster信息命令
cluster nodes
进入一个从节点redis,切换为主节点
cluster replicate <master-id>
发现一个新节点,新增主节点
cluster meet ip:port
忽略一个没有slot的节点
cluster forget <id>
手动故障转移
cluster failover
问题排查
请求数量较高
主从之间数据吞吐量较大,数据同步操作频度较高
解决方案
日常列行统计数据访问记录,统计访问频度较高的热点数据
利用LRU数据删除策略,构建数据留存队列
例如 : storm与kafka配合
准备工作:
将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据
利用分布式多服务器同时进行数据读取,提速数据加载过程
实施:
使用脚本程序固定触发数据预热过程
如果条件允许,使用CDN(内容分发网络),效果会更好
总结 :
缓存预热就是系统启动之前,提前将相关的缓存数据直接加载到缓存系统.避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
数据库服务器崩溃X
系统平稳运行过程中,忽然数据库连接量激增
应用服务器无法及时处理请求
大量408,500错误页面出现
客户反复刷新页面获取数据
数据库崩溃
应用服务器崩溃
重启应用服务器无效
Redis服务器崩溃
Redis集群崩溃
重启数据库后再次被瞬间流量放倒
问题排查
在一个较短的时间内,缓存中较多的key集中过期
此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据
数据库同时接收到大量的请求无法及时处理
Redis大量请求被挤压,开始出现超时现象
数据库流量激增,数据库崩溃
重启后仍然面对缓存中无数据可用
Redis服务器资源被严重占用,Redis服务器崩溃
Redis集群呈现崩塌,集群瓦解
应用服务器无法及时得到数据相应请求,来自客户端的请求数量越来越多,应用服务器崩溃
应用服务器,redis,数据库全部重启,效果不理想
问题分析
短时间内
大量的key集中过期
解决方案 (方法)
更多的页面静态化处理 (比如下拉链表的内容如果是长时间不动的话就写在html里)
构建更多级缓存架构
(在Redis与数据库之间加更多缓存) Nginx缓存 + redis缓存 + ehcache缓存
检测Mysql严重耗时业务进行优化
对数据库的瓶颈排查,例如超时查询,耗时较高的事务等
灾难预警机制
监控redis服务器性能指标
CPU占用 , CPU使用率
内存容量
查询平均响应时间
线程数
限流 , 降级
短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后正逐步放开访问.
解决方法 (原则)
LRU 与 LFU 切换
数据有效期策略调整
根据业务数据有效期进行分类错峰过期,A类90分钟, B类80分钟 , C类70分钟
过期时间使用固定时间 + 随机值的形式 , 稀释集中到期的key的数量
超热数据使用永久key
定期维护 (自动 + 人工)
对即将过期数据做访问量分析 , 确认是否延时 ,配合访问量统计 , 做热点数据的延时
加锁
慎用!
总结
缓存雪崩就是瞬间过期数据量太大,并且集中访问这些过期数据,导致对数据库服务器造成压力.如果能够有效避免过期时间击中,可以有效解决雪崩现象的出现(40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整.
数据库服务器崩溃
系统平稳运行过程中
数据库连接量瞬间激增
Redis服务器无大量key过期
Redis内存平稳,无波动
Redis服务器CPU正常
数据库崩溃
问题排查
Redis中某个key过期,该key访问量巨大
多个数据请求从服务器直接压到Redis后,均为命中
Redis在短时间内发起了大量对数据库中同一数据的访问.
问题分析
单个key高热数据
key过期
解决方案
预先设定
以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长
注意 : 购物节不仅仅是当天 , 以及后续若干天,访问峰值呈现逐渐降低的趋势
现场调整
监控访问量 , 对自然流量激增的数据延长过期时间或设置为永久key
后台刷新数据
启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失
二级缓存
设置不同的失效时间,保障不会被同时淘汰就行
加锁
分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!
总结
缓存击穿就是单个高热度数据过期的瞬间,数据访问量较大,未命中redis后,发起了大量对同一数据的数据库访问,导致对数据库服务器造成压力.应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过期监控难度较高,配合雪崩处理策略即可.
系统平稳运行过程中
应用服务器流量随着时间增量较大
Redis服务器命中率随时间逐步降低
Redis内存平稳,内存无压力
Redis服务器CPU占用激增
数据库服务器压力激增
数据库崩溃
问题排查
Redis中大面积出现未命中
出现非正常URL访问
问题分析
获取的数据在数据库中也不存在,数据库查询未得到对应数据
Redis获取到null数据未进行持久化,直接返回
下次此类数据达到重复上述过程
出现黑客攻击服务器
解决方案
缓存null
对查询结果为null的数据进行缓存(长期使用,定期清理) , 设定短期限,例如30-60秒,最高5分钟
白名单策略
提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单.当加载正常数据时,放行,加载异常数据时直接拦截(效率偏低)
使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)
实时监控
实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比
非活动时段波动,通常检测3-5倍,超过5倍纳入重点排查对象
活动时间波动 : 通常检测10-50倍,超过50倍纳入终点排查对象
根据倍数不同,启动不同的排查流程.然后使用黑名单进行防控(运营)
key加密
问题出现后,临时启动防灾业务key,对key进行业务层传输加密服务,设定校验程序,过来的key校验
例如每天随机分配60个加密串,挑选2-3个,混淆到页面数据id中,发现访问key不满足规则,驳回数据访问.
总结
缓存穿透访问了不存在的数据,跳过了合法数据数据的redis数据缓存阶段,每次直接访问数据库,导致对数据库服务器造成压力.通常此类数据的出现量是一个较低的值,当出现此类情况以毒攻毒,并及时报警.应对策略应该在临时预案方面多做文章.
无论黑名单还是白名单,都是对整体系统的压力,警报接触后尽快移除.
命令
benchmark
redis-benchmark [-h] [-p] [-c] [-n<request]> [-k]
范例1
redis-benchmark //50个连接 , 10000次请求对应性能
范例2
redis-benchmark -c 100 -n 5000 //100个连接 , 5000次请求对应性能
redis-cli
monitor
monitor
slowlog
slowlog [operator]
get : 获取慢日志查询
len : 获取慢查询日志条目数
reset : 重置慢查询日志
相关配置
slowlog-log-slower-than 1000 //设置慢查询的时间下限,单位 : 微秒
slowlog-max-len 100 //设置慢查询命令对应的日志显示长度 : 单位 : 命令数
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:(利用分片集群就可以舍弃)
海量数据存储问题
高并发写的问题
使用分片集群可以解决上述问题,分片集群特征:
集群中有多个master,每个master保存不同数据
每个master都可以有多个slave节点
master之间通过ping监测彼此健康状态
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:
数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:
key中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分
key中不包含“{}”,整个key都是有效部分
例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。
redis分片集群中把数据分配槽中而不是节点中,是因为即使主节点宕机之后,其上的槽会分配到其他存活的主节点中,因此主节点的插槽顺序并不是按顺序排列的(因为删删补补就乱了套了)
redis-cli --cluster提供了很多操作集群的命令,可以通过下面方式查看:
比如,添加节点的命令:
需求 : 向集群中添加一个新的master节点,并向其中存储 num = 10
启动一个新的redis实例,端口为7004
添加7004到之前的集群,并作为一个master节点
给7004节点分配插槽,使得num这个key可以存储到7004实例
redis-cli --cluster add-node 192.168.150.101:7004 192.168.150.101:7001
7004加入到集群之后没有被分配任何插槽.
默认num的key分配在7001上的2765号槽上:
思路 : 将2765号槽分配到7004节点上去
实现
redis-cli --cluster reshard 192.168.150.101:7001
即使没有哨兵也可以实现故障转移
当集群中有一个master宕机会发生什么
1.首先是该实例与其他实例失去连接
然后是疑似宕机
最后确定下线,自动提升一个slave为新的master
利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下:
老的master机器性能很差,salve来进行,手动的主从切换,在slave机器上,命令cluster failover,就可以实现手动主从切换了
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis</artifactId>
- </dependency>
- spring:
- redis:
- cluster:
- nodes:
- - 192.168.150.101:7001 #master
- - 192.168.150.101:7002 #master
- - 192.168.150.101:7003 #master
- - 192.168.150.101:8001 #slave
- - 192.168.150.101:8002 #slave
- - 192.168.150.101:8003 #slave
- @Bean
- public LettuceClientConfigurationBuilderCustomizer
- clientConfigurationBuilderCustomizer(){
- return configBuilder -> configBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
- }
- @RestController
- public class HelloController {
-
- @Autowired
- private StringRedisTemplate redisTemplate;
-
- @GetMapping("/get/{key}")
- public String hi(@PathVariable String key) {
- return redisTemplate.opsForValue().get(key);
- }
-
- @GetMapping("/set/{key}/{value}")
- public String hi(@PathVariable String key, @PathVariable String value) {
- redisTemplate.opsForValue().set(key, value);
- return "success";
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。