1、redis简述
redis 是一门C语音开发的,redis开发者,一开始的本意是作用消息队列,后面随着IT圈的迅速发展,redis不满足诉求;最后开发成k/v形式的内存存储的工具
特性:速度快、单进程单线程、支持集群、持久化
为啥快?
a)、操作内存
b)、单进程单线程,不存在资源竞争,上下文切换
c)、多路复用,多路-->网络连接 复用--> 复用的是一个线程
2、redis支持类型
前言
所有key的类型是redis内部定义sds string类型,长度最大是512M,所有value的类型都是redisObject
但是redisObject里面又细分,这里后面不同类型阐述。
1、string
操作指令:set key value get key
外部类型 --> string
内部类型 --> 如下
可通过object encoding key或者debug object key,查看value信息
a)、内容是数字: < 44 --> int ; >44 -->raw
b)、内容非数字:< 44length --> empstr ; > 44 --> raw
c)、为什么是44length?
内存分配器(2/4/8/16/32/64 字节)
sds --> 3字节
redisObject --> 16字节
也就是19个字节,至少要分配内存32字节下来, 这里就不在深入进去,在深入我不懂ing,尬了尬了
2、hash
操作指令: hset hget hmgetAll不罗列
外部类型 --> 哈希表 key :: field :: value
内部类型 --> 如下
a)、hashtable哈表
dict外层 --> 字典数据结构,两个dictht表ht[0],ht[1],一个使用 一个为空,方便扩容
dictht -- > 指向dictentry数组的指针
dictentry -> 数组+链表形式存放
b)、ziplist
使用条件 --> 保存field数量小于512; 所有的field和value的字符串长度都小于64字节
经过特殊编码,连续的内存块组成双向链表, 不存指针的地址,存储的上节点长度和当前节点长度
c)、扩容 --> 元素的个数 / 数组的长度 > 5 触发扩容
触发扩容 --> 在空的dictht表里创建原本大小的2的幂次方, 重新rehash分配到新表 , 释放ht[0]的空间,
将ht[1]这张表改为ht[0],创建新的ht[1],为下次做准备
3、set
操作指令: sadd sget
存储类型: intset hashtable
4 、list
特性:存储有序的字符串(从左往右), 元素可重复
数据类型: quicklist -- > a) head 记录头节点
b) tail 记录尾节点
c) count 所有ziplist的元素个数
d) len 双向链表的长度
e) queicklistNode 双向链表 --> pre 上节点 tail 下节点 ziplist 压缩列表
5、zset
特性: 存储有序元素,不允许重复元素,按照score从小到大排序,score相同按照 ASCII码排序
数据模型 : ziplist -- > 元素数量小于128,所有长度小于64字节
skiplist + dict -- > skiplist跳表 -- >
6、BitMaps
a)、存储类型: 字符串存储
b)、数据模型 : 一个字节八位二进制位组成
7、Hyperloglog
未了解
8、Geospatial
存放经纬度,计算经纬度距离
3、redis内存淘汰、过期策略
a)、过期策略:redis采用定时清除和惰性清除,
定时清除 -> 定时清除ttl过期的key,扫描部分,造成可能大量已经ttl过期的key未删除
惰性清除 --> 客户端get的时候,查看是否ttl过期,过期就删除
大量ttl过期key未删除,会导致内存不够用,要保证redis是可用状态,就出了内存淘汰,过期
b)、内存淘汰 --> 如上过期数据还是可能出现内存不过用,所以淘汰一些数据来保证可用性
淘汰策略 --> lru 最少使用
--> lfu 最少使用(评率)
--> random 随机
--> volatile 设置了过期时间
--> allKey 全部key ,不看是否有设置ttl
--> noeviction 不做任务淘汰策略,直接报错
组合8种使用 --> volatile-lru --> 设置了ttl,最少使用, 例:A很少用,redis准备删除A了,客户端突然使用下A,
一下就热点数据了,不公平吧。
volatile-lfu --> 在volatile-lru基础下,增加次数,具体实现,出门左转百度下,这里不做阐述实现
volatile-ttl --> 快要过期的
volatile-random --> 随机删除设置时间的
不管你有没有设置时间
allKey-lru --> 挑选最近最少使用的数据淘汰
allKey-lfu --> 挑选最近最少次数使用的数据淘汰
allKey-random --> 随机挑选使用的数据淘汰
no-noeviction 不做淘汰,oom出去。不能插入,但是可以get操作
c)、建议使用: volatile-lru 保证正常服务的情况下,优先删除最近最少使用的key
4、redis持久化
1、aof
执行一次指令就保存
持久策略: no --> 不执行fsync,由操作系统保证数据同步到磁盘, 速度快,但是不安全
always --> set a 123, 先更新到redis缓存,在刷新到aof文件中
everysec(推荐) --> 先把执行命令写入到buffer中,在以每秒执行一次fsync, 效率可以保证,但可能会丢失一秒的数据
优点:提供丰富的同步策略
缺点:aof文件更大,不合适做数据备份
2、rdb
生成快照:在conf配置的x秒内达到ykey就会生成快照
正常关机也会生成快照
指令flushall会生成快照
指令save会生成快照,但是这个是阻塞式的,执行这个指令,其他不能执行
执行bgsave会生成快照,fork一个子进程,共享父类内存,不阻塞
优点: 生成的文件会比aof文件小,适合做数据备份,文件小,更合适恢复数据
缺点: 会丢失数据
aof,rdb的混合
Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行, 在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。
在同时开启rdb和aof情况下
set a 123
set b 123
set c 123
aof每秒设置到aof文件中
同时也快快照到rdb文件中,在二次快照后,会清空aof文件。
3、方案比较
1、如果能忍受一小段时间的数据丢失,选择rdb,如果不能,就选择aof
2、(推荐)不要单独设置一种持久化机制,两种一起用,AOF比RDB的数据要完整
4、redis重启加载文件顺序
1、如果开启了aof,优先查看是否有aof文件,如果有,则加载aof文件,启动redis。
2、如果开启了aof,无aof文件,则查看是否有rdb文件,如果有,则加载rdb文件,启动redis。
3、如果开启了aof,无aof文件,则查看是否有rdb文件,如果无,启动redis。
4、如果无开启aof,则查看是否有rdb文件,如果有,则加载rdb文件,启动redis。
5、如果无开启aof,则查看是否有rdb文件,如果无,则加载rdb文件,启动redis。
3、redis集群
后面在写一篇文档关于集群
4、redis应用场景
1、string类型适合场景 --> 缓存热点时间
--> 分布式session登录
--> 分布式锁
--> 计数器incr/decr
--> 限流?
2、hash类型适合场景 --> 跟string差不多,但是field无法设置时间
3、list类型适合场景 --> 热点数据列表 (文章)
--> 消息队列
4、set类型适合场景 --> 抽奖(不能重复)
--> 点赞/打卡/签到
--> 关系模型
--> 交集/并集/差集操作
5、zset类型适合场景 --> 热搜/主题
6、bitmap类型适合场景 --> 用户访问统计
--> 在线用户统计
--> 用户签到
--> 与、或、异或、非运算
7、Hyperloglog类型适合场景 --> 不太精确的基数统计
--> 统计网站UV
--> 日活、月活
8、Geospatial类型适合场景 --> 经纬度操作,距离比较
5、redis高级特性
1、事物
特点: 把一组指令一起(进入队列顺序)执行
不会受到其他命令干扰,一组命令set 11 22 set 22 33 在开启事务后,如果有另外的命令在操作11,22 key的命令。那么事务会取消了。 事物是不能嵌套的
用法: multi开启命令
exec 执行事务 --> 只能捕捉错误语法的指令,事物不会执行成功
--> 没有语法错误,但是执行过程中,指令错误,已执行的指令执行成功,错误的且未执行的不成功
discard 取消事务
watch 监视 --> 防止事务过程中某个key的值被其他客户端修改,并且取消事务
--> CAS乐观锁行为
2、发布订阅
3、lua
特点:批量执行指令
原子性 --> 排斥其他客户端指令 (排他)
脚本写入文件,集合复用
应用场景: 限流
秒杀
好处:
①高效,在服务器上执行,节省了网络传输开销等,特别lua脚本,执行多个脚本,好处就体现出 来了。
②原子性;一个业务需要多个指令完成,lua脚本比较好的选择
缺点:那就是如果你lua过于复杂,假如在里面取bigkey操作了,获取取值过大等等,就会阻塞;
执行时间过长;
如果配置主从,有两种同步方式:
1、脚本复制:意思很明确,就是在从节点也执行一边,在5.0后,就移除了,默认就是效果复制。
2、效果复制:就是把结果同步。
指令:
1、加载lua脚本到redis服务中
./redis-cli -a 密码 -p 端口 script load '$(cat 1.lua)';
返回一个sha1的值
通过evelsha sha1的值