1、Redis介绍
1.1、Redis是什么
- Redis(Remote Dictionary Server),远程字典服务
- C语言编写、支持网络、基于内存、支持持久化、key-value的数据库,提供多种语言API,且支持事,支持集群
1.2、基础知识
1.2.1、基本命令
- redis默认有16个数据库,可以使用 select [index] 进行切换
- 使用 DBSIZE 可以查看当前数据库大小
- 使用 keys * 可以查看当前数据库所有key
- 使用 flushdb 可以清除当前数据库
- 使用 flushdball 可以清除全部数据
- set [key] [value] 设置key
- EXISTS [key] 查看key是否存在
- move [key] [index] 移动key到index数据库,index是数据库下标
- del [key] 删除key
- get [key] 得到key的值
- EXPIRE [key] [second] 设置key的过期时间,second单位是秒
- ttl [key] 查看ttl的剩余时间(过期)
- type [key] 查看key中数据类型
1.2.2、Redis:单线程+多路I/O复用
- Redis基于内存实现,所以其瓶颈不是CPU,而是内存和网络
- Redis将数据存在内存中,使用单线程去操作效率高,不需要进行多线程中的线程切换。对于内存来说,没有线程上下文切换是快速的。
- 多路复用,非阻塞
2、五大数据类型
2.1、String类型
- append [key] [string] 在key对应value后追加字符string,如果key不存在,那么新建key
- strlen [key] 获取key对应value的长度
- incr [key] key对应值value自增1
- decr [key] key对应值value自减1
- incrby [key] [num] key对应值value自增num
- getrange [key] [start] [end] 查看key对应value中[start, end](包含)下标的值,end=-1即全部包含
- setrange [key] [index] [string] 将key对应的value中下标为index的值改为string
- setex [key] [second] [value] 设置key-value,有效期是second秒
- setnx [key] [value] 设置key-value,如果key不存在,那么设置key;如果key已经存在,那么不设置(不操作)
- mset [key1] [value1] [key2] [value2] [key3] [value3] ...同时设置多个key-value
- mget [key1] [key2] ... 得到多个value
- setnx [key1] [value1] [key2] [value2] [key3] [value3] ...同时设置多个key-value,如果一个失败了,全部失败(原子操作)
- set [key] [{filed}] 设置对象,key可以设置为string:string;filed中为string,中间用,分开,不能有空格。set user:1 {name:qdc,age:18}
- getset [key] [value] 如果如果原来已经存在key值,那么返回原来的值并设置新值
- string可以是字符串、数字,可以用作计数器,统计网站访问量、粉丝数等
2.2、List列表
- 单键-多值
- 底层是一个双向链表
- lpush/rpush [key] [value1] [value2] ... 往list左/右增加数据
- lpop/rpop [key] 弹出list中左/右第一个元素
- rpoplpush [key1] [key2] 从key1的右边弹出第一个,放入key2左边
- lrange [key] [start] [stop] 按照索引下标范围获取,由左开始,[0] [-1]获取所有,-1表示最后一个元素
- lindex [key] [index] 按照索引下标获取
- llen [key] 获取长度
- linsert [key] before [value] [newvalue] 在元素value之前增加newvalue
- linsert [key] after [value] [newvalue] 在元素value之后增加newvalue
- lrem [key] [n] [value] 删除n个value
2.3、Set集合
- 无序集合,无重复,底层是value为null的hash表,相当于map<key, value>中的key
- sadd <key> <value1> <value2> ... 将一个/多个元素加到key代表的集合中
- smembers <key> 取出集合中所有元素
- sismember <key> <value> 集合中是否含有value,有则返回1,无则返回0
- scard <key> 返回集合中元个数
- srem <key> <value1> <value2> ... 删除集合中元素
- spop <key> 随机弹出集合中一个值
- srandmember <key> <n> 随机弹出n个值,但是不会删掉
- sinter <key1> <key2> 返回两个集合的交集元素
- sunion <key1> <key2>返回两个集合的并集元素
- sdiff <key1><key2> 返回两个集合的差集,key1中有,key2中没有
2.4、Hash
- 键值对集合,{key, hash},hash={{field1, value1, {field2, value2}, {field3, value3}...},field是hash中的key,field格式可以是string:string:string
- 可用于存储对象
- hset <key> <filed>:<value> 赋值
- hget <key> <filed> 取值
- hmset <key> <filed1>:<value1> <filed2>:<value2> .. 多个同时赋值
- hexists <key> <filed> 给定key中filed是否存在
- hkeys <key> 得到key中所有field(不含value)
- hvals <key> 得到key中所有value(不含key)
- hgetall <key> 得到所有field和value
- hinceby <key> <field> <increment> 给key中field对应的value加上increment
- hsetnx <key> <field> <value> 有了这个key-field就不操作,否则新设值为value
2.5、Zset
- 有序集合,没有重复,每个元素(成员)都关联一个评分(score),评分用于进行排序,集合中元素不可重复,但是评分可以
- zadd <key> <score1> <value1> <score2> <value2> <score3> <value3>...,赋值,score是分数,用于排序,分数越小,排名越靠前;如果添加的值已经存在,那么更改分数
- zrange <key> <start> <stop> (withscores),返回key代表的集合中下标在[start, stop]之间的元素,如果带上withcores,连同分数一起返回(分数顺序)
- zrerange <key> <start> <stop>,反着返回(分数顺序)
- zrangebyscore <key> <min> <max>,按照分数范围返回元素
- zrangebyscore <key> <min> <max>,反着返回
- zincrby <key> <increment> <value> 为元素value增加分数increment
- zrem <key> <value> 删除元素
- zcount <key> <min> <max> 返回返回分数范围内元素个数
- zrank <key> <value> 返回值在集合中的排名,从0开始,也可以说是下标
3、事务
3.1、三大特性
- 单独的隔离操作,事务中的命令序列化、串行化,不会被其它事务中的命令干扰(事务中间不会插入其它命令)
- 没有隔离级别的概念,事务中的命令在提交前不会操作
- 不保证原子性,一个事务中,一条命令失败,其它命令正常执行
3.2、执行过程
- 开启事务
- 命令入队(未提交)
- 提交事务(执行)
3.3、命令
- discare,取消事务,放弃事务内所有命令
- exec,执行事务中的命令
- multi,开始事务
- watch <key1> <key2>... 监视key,如果事务执行前key-value改变了,那么该事务打断(取消)
- unwatch,取消监视所有key
4、持久化
4.1、RDB持久化方式(Redis DataBase)
- 在指定时间内将内存中的数据存入硬盘(也就是快照)
4.1.1、持久化过程
- 单独创建(fork)一个子进程来进行持久化,该子进程与主进程共享内存
- 此时父进程在内存页面副本中处理新的写请求
- 子进程将数据写到一个临时文件
- 数据写入完成后,用这个临时文件替换上一次持久化好的文件
4.1.2、RDB优缺点
- 持久化过程中,主进程不进行IO操作,所以性能高
- 最后一次持久化后的数据可能丢失,会丢失宕机时的数据,仅保存着上次持久化的rdb数据
- 恢复速度快,适用于大规模且数据完整性不敏感的情况,默认使用RDB
4.1.3、触发持久化与恢复
- save 时间 次数,在规定的时间内redis操作次数达到规定后,执行持久化操作,生成 文件名.rdb文件
- save,手动保存,阻塞所有请求(一般不用)
- 执行flushall,触发,生成 文件名.rdb文件
- 退出redis,触发,生成 文件名.rdb文件
- 将rdb文件放入redis启动目录,启动时自动获取dump.rdb
4.2、AOF持久化方式(Append Only File)
4.2.1、AOF概念
- 将所有命令(写操作)记录下来,恢复的时候将命令执行一遍
- 以日志形式记录写操作,只允许追加文件,不允许修改文件
- redis启动之初,会去读取该日志文件重新构建上次关闭前的数据
4.2.3、重写AOF
- 文件大于阈值,那么Redis会对AOF文件进行压缩,只保留能恢复数据的最小指令集(比如多个set a,那么仅保留最后一个set a)
- 系统载入或者上次重写完毕时,Redis会记录此时AOF的大小为base_size,如果新的AOF大小>=base_size+base_size*100%且大于64mb,那么会重写
4.2.4、优缺点
- 备份更好,丢失数据概率小
- 备份数据是要执行命令,速度慢
- 每次写都要备份,性能有影响
5、发布订阅
5.1、模型图
5.2、命令
- psubscribe pattern <pattern1> <pattern2> ...订阅符合模式的频道
- psubscribe subcommand <argument<argument...>...> 查看订阅与发布系统状态
- publish <channel> <message> 发送信息到指定频道
- punsubscribe pattern <pattern1> <pattern2> ...退订符合模式的频道
- subscribe channel <channel>... 订阅给定频道
- unsubscribe <channel>...退订给定频道
5.3、原理(原理就是订阅者模式)
- redis-server中维护一个字典(key-value),字典的键就是一个个的频道,每个频道中的值是一个列表,存储着订阅该频道的订阅者
- subscribe 订阅命令就是将订阅者添加到频道的列表中
- publish发送消息,redis-server使用频道作为key,获得key对应的列表,遍历这个列表,将消息发送给所有订阅者
6、主从复制
6.1、概念
- 主从复制,将一台Redis服务器的数据,复制到其他Redis服务器,前者为主节点(Master),后者为从节点(Slaver)
- 主节点用于写,从节点用于读
- 默认情况下,所有节点都是都是主节点
6.2、作用
- 数据冗余,实现数据热备份,冗余存储
- 故障恢复,主节点故障,由从节点提供服务
- 负载均衡,读写均衡,主节点写,从节点读
- 高可用(集群)
6.3、配置命令
- info replication 查看当前库信息
- slaveof <ip> <port> 配置从节点(不配置的话,默认都是主节点)
6.4、原理
6.4.1、全量复制
- 从节点执行slaveof命令后,主节点收到,将当前所有数据传送给从节点,一次完成同步
6.4.2、增量复制
- 主从连接后,主节点每进行一次操作,将当前操作命令发送给从节点,从节点执行
6.4.3、主机宕机
- 主机宕机后,可以在从节点执行slaverof no one,此时该从节点变为主节点
- 其它从节点可以手动连接到当前节点
- 如果宕机的主节点恢复了,那么需要重新连接
7、哨兵模式
7.1、概念
- 哨兵是一个独立进程,哨兵通过发送命令,等待Redis服务器(主从)响应来监控Redis实例
- 如果检测到主机宕机,那么通过投票,将从机转为主机,并通过发布订阅模式通知其它从节点
- 为了避免哨兵出现问题,一般是用多个哨兵进行监控,哨兵之间也可以相互监视
7.2、哨兵检测
- 一个哨兵检测到主机不可用,那么此时属于主观下线
- 当足够数量的额哨兵检测到主机不可用,由一个哨兵发起投票,选举新主机。通知从节点切换新主机后,哨兵便开始监视新主机,此过程是客观下线
- 如果主机宕机后回来了,此时只能当从机
7.3、优缺点
- 提升系统可用性,主从自动切换,自动实现主从复制
- 不便于在线扩容,当集群容量达到上限,在线扩容麻烦
- 实现哨兵模式的配置麻烦
8、缓存穿透和雪崩
8.1、缓存穿透(缓存和数据库都没有,整个缓存没用)
- 指缓存和数据库中都没有数据,但是用户不断发送请求,导致数据库压力过大
- 可在接口层校验(布隆过滤器),校验id等是否有效
- 可在redis中设置key-value,但是value为空,防止一个用户反复使用同一id攻击
8.2、缓存击穿(缓存没有某个,数据库有,像缓存打了个洞)
- 高并发下,多线程同时查询同一个资源(key),但是缓存中没有这个资源,于是这些线程到数据库中寻找,那么对数据库造成极大压力
- 可以主动更新缓存数据,比如缓存数据有效期是30分钟,可以20分钟更新一次。但是这样增加系统难度,不同key时间不一样,适用于key固定且粒度较大的场景
- 将key的过期时间存到缓存中,get时发现快要过期了,重新设置过期时间。其实也有问题,比如快要过期的时候,就是没有get,那么也不会更新
- (分级缓存,L1和L2,L1缓存时间短,L2长,优先从L1取。如果L1未命中,那么加锁,此时只有操作的线程获取这个锁,这个线程在数据库中读取数据更新到L1和L2,在更新过程中L2未加锁,可以访问。造成冗余、读取脏数据)
8.3、缓存雪崩
- 在某一个时间段内,缓存集中过期失效,Redis宕机
- 搭建集群,一台机子宕机其它可用
- 限制流量,当缓存失效后,通过加锁或队列来控制 读数据库、写缓存的线程数量,比如对某个key仅允许一个线程查询和谢缓存
- 数据预热,服务上线前,将数据存到缓存(Redis)中,并设置不同过期时间