Redis基础知识

持久化机制

redis速度快很大部分的原因是它将数据放在了内存里面,但是如果断电或者宕机就会导致数据丢失,为保证重启后数据不丢失没提供三种持久化机制RDB(Read dataBase) AOF(Append Only File)混合持久化

RDB

* 概述
	* redis默认持久化方案,触发机制是只要满足一定的条件就会将内存中的数据生成快照(dump.rdb),重启时就会读取文件并恢复数据
	* RDB默认是二进制存储,这样存的原因是恢复数据的时候更快
	
* 内存数据如何写入的硬盘
	* 首先获取数据,然后清空dump.rdb文件,然后将整个内存的数据写入到硬盘
	* 缺点:如果刚好数据清空却断电了,那么内存中的数据就丢失了,找不回来
	
* 满足什么条件生成快照(触发条件)
	* 自动生成
		* <seconds> <changes>在多少秒之内有过多少的key修改就会产生快照
			* save 900 1 save 300 10 save 60 10000
			
* 这是在配置文件中的配置,前面那个是秒钟数,后面那个是增删改查的数量

* 使用shutdown关机的时候生成
	* flushall
	
* 手动生成
	* 手动的触发生成快照(比如重启服务或者迁移数据)
		* 使用save命令就会生成一个rdb文件,不过这个命令执行的时候会阻塞redis服务其,
		这个时候并不能执行其他的任务,所以在生产环境不会使用这个命令
		* 为了解决上面的问题,引入了一个 bgsave,在后台对数据进行持久化,这是一个异步的过程,
		一边生成数据一边响应客户端请求。但它不是单线程吗,那是因为它fork了一个子进程来对数据进行
	    持久化,它也会阻塞,不过这个过程是在fork时到子进程出来的这个阶段,相对更好
		
* 既然这种模式有问题,为什么还要设计这种持久化方式?
	* 1.记录的是二进制数据,方便数据恢复
	* 2.它设计的初衷就是为了数据备份,而且是记录某一时刻,内存的数据的状况

AOF

* 概述
	* 当aof和rdb共同存在的时候 会以AOF模式优先
	* 高版本,当打开AOF的时候,也会默认同时打开混合持久化
	
* 配置文件配置
	* appendonly yes 这个就是打开aof的开关
	* appendfilename "appendonly.aof" 数据库文件的名字
	* aof-use-rdb-preamble no :关闭混合持久化
	
* 持久化到硬盘
	* 申明什么的时候会持久化
		* appendfsync always :只要有key发生改变 立马将内存的数据持久化到硬盘 这种模式 保证的数据不会发生丢失 但是有个致命的缺陷是 效率太低了
		* appendfsync everysec:每秒每秒钟 内存和硬盘的数据同步一次:开发中一般就选择他 如果是数据丢失也只会丢失一秒钟的数据
		* appendfsync no:内存的数据和硬盘永远都不同步,这种效率是最高的 风险也是最高的 丢数据就全部丢了
		
	* 如何写入硬盘
		* 这里不会像RDB模式一样 还要清空文件 这里使用的是追加 每一次内存和硬盘同步的时候 只是写改变了的内容 追加 所以效率来说相对也是比较高的
		
	* 存入硬盘上到底写的啥
		* *3 $3 set $8 xiaobobo $3 123
		* 以上使用的是RESP协议
		* 上面这一段存储的是 操作请求Redis服务器 这一次操作传输的协议体 

	* 优化
		* 在配置文件配置bgrewriteaof 就可以优化了这有什么用呢
		* 其实在AOF的内部也提供了 优化策略
		* 触发点
			* auto-aof-rewrite-percentage 100 ------- 表示的是AOF文件满足了下面的64M之后 并且产生了 100%增量的时候才进行触发
			* auto-aof-rewrite-min-size 64mb ------ AOF文件满了 64M的触发触发重写?
			* 64M+64M=128M

混合持久化

* 概述
	* 当每一次进行bgrewrite的时候 它就会将原来记录的协议提内容,转换成RDB的二进制模式存储到aof文件
	的顶部。当你继续进行Redis命令操作的时候,就会在RDB的下面继续记录协议体
	* 注意:在高版本中 默认情况下 混合持久化是打开的 只要你打开了AOF模式混合持久化也就打开了

* 打开混合持久化
	* aof-use-rdb-preamble yes

内存回收

定时过期

* 每次给key设置TTL 定时器,时间到了清楚key,节省内存,不过占用cpu资源,如果很多占用资源会产生一定的影响(主动淘汰)

惰性过期

* 设置了TTL之后,设置了时间,不过不是立马删除,而是在下次访问的时候只有对她进行get、set的时候才会进行
访问判断这个key是否过期。不需要启动那么多定时器去监听,不过对内存却不太友好,占用内存(被动淘汰)

定期过期

* 添加一个值的时候发现达到上限,调用一个方法扫描内存中的键。扫描一定数量的进行采样,
达到过期时间就清除,属于以上的折中

	* 场景二
		* 可以使用mset来一次性的设置多个键值对
			* mset user:4:userName xiaobobo user:4:password 123
		* 如果你需要获取这个对象所有的值 那么可以使用mget
			* mget user:1:userName user:1:password

redis采用惰性和定期过期的策略,但如果内存还是达到了最大容量,就会触发内存淘汰策略如果不设置TTL,那怎么办,还有一个服务淘汰策略(8种)
8种算法(见后面)

数据类型

string(字符串对象)

* 常用命令
	* set key value :这个表是的是想String类型中存储一个键值对
	* mset key value key value :一次性可以设置多个键值对
	* get key:获取一个key的值
	* mget key key key:一次性获取多个值
	* getset key value:先获取这个key原来的值 然后再设置一个新的值
	* setnx key value:当这个key不存在的时候再将这个value设置给这个key存储到Redis数据库中
	* INCR key :自增
	* decr key :自减
	* incrby key step :一次性增加多少
	* decrby key step:一次性减少多少
* 应用场景

	* 场景一
		* 存储数据库数据,字段有id,名字,密码
			* 数据库字段还是用hash吧
		* set user:4:userName china set user:4:password 123
			* set 表名:id:字段名 字段值
			
	* 场景二
		* setnx的应用场景(分布式锁)
			* 因为在分布式上,两个机器虽然代码相同,但是是两个虚拟机,所以synchronized锁是锁不到的。
			但是我们的Redis是同一个,setnx只有这个值不存在才执行,也就是可以通过以下执行逻辑,
			另一个机器在其他机器还在执行逻辑时,是执行不了逻辑的
			* if(setnx){}

	* 场景三
		* incr和decr
			* set weibo:zhuanfa:文章id 0
			* set weibo:pinglun:文章id 0
			* set weibo:dianzan:文章id 0
			* incr weibo:dianzan:文章id
			* decr weibo:dianzan:文章id

hash(哈希对象)

* 常用命令
	* hdel 集合的名字 键的名字 删除集合中的一个键值对
	* hset 集合的名字 key value :这个表示的是 向Redis写入一条Hash的数据
	* hmset 集合的名字 键 值 键 值 键 值 :一次性设置 多个键值对的信息给Hash结构
	* hexists h1 k1 :判断h1这个集合中 k1这个键是否存在
	* hget 集合的名字 键的名字 :获取Hash结构中的一个值
	* hmget 集合的名字 键的名字 键的名字 ... :一次性获取多个key对应的值
	* hgetall 集合的名字 :获取这个集合中所有的键值对
	* hlen 集合的名字 :获取键值对的个数
	* hvals 集合的名字 :获取这个集合中所有的值
	* hincrby 集合的名字 键的名字 增加的值 :就是给值增加一个数

* 应用场景
	* 场景1
		* 存储数据库数据,字段有id,名字,密码
		* hset user 用户id:键 值 hset user 4:userName china hset user 4:password 123 hmset user 4:userName china 4:password 123
	* 场景2
		* 购物车的例子,以购物车做示例
		* 存储到Redis中 应该怎么存储? hset car:用户id 商品id 商品数量
		* 添加一个商品到购物车 hset car:1 100 1
		* 删除一个商品 hdel car:1 100
		* 增加商品的数量 hincrby car:1 100 1
		* 选中所有的商品 hgetall car:1
		* 计算所有的商品数量 hvals car:1 求和
		* 获取某一个商品的信息 hget car:1 200

list(列表对象)

* 常用命令
	* lpush 集合的名字 值
	* lpop 集合的名字
	* rpush 集合的名字 值
	* rpop 集合的名字
	* brpop 集合的名字
	* llen 集合的名字 :获取集合中元素的个数
	* lrange 集合的名字 开始的位置 结束的位置 :获取指定位置的元素的值
	* lrem 集合的名字 删除的元素的个数 删除的元素 :这个表示删除几个什么元素
	* lset 集合的名字 位置 值 :给集合中某一个位置设置一个值

* 应用场景
	* lpush+rpop rpush+lpop
		* 队列的结构
	* lpush+lpop rpush+rpop
		* 栈的结构
	* lpush+brpop
		* 阻塞队列
	* 微博关注这个平台,会给你发消息,不过肯定有先后顺序,类似于观察者设计模式
		* lpush weibo:msg:用户id 消息id

set(集合对象)

* 常用命令
	* sadd 集合的名字 值 :向Set集合中添加一个数据
	* srandmember 集合的名字 个数:返回这个集合中一个随机的值
	* spop 集合的名字 个数:返回几个随机值
	* scard 集合的名字 查看集合中所有的元素的个数
	* sismember 集合的名字 :查看某一个元素是否属于这个集合
	* sinter 集合1 集合2 :求交集
	* sinterstore 目标集合 集合1 集合2 :将集合1和集合2的交集放到一个新的集合中
	* sdiff 集合1 集合2 :求差集
	* sunion:求并集
	* srem 集合 元素 :移除某一个元素

* 应用场景
	* 抽奖
		* 我们将用户所有的id 都添加到 一个抽奖的集合中去
		* 有放回抽
			* srandmember choujiang 2
		* 无放回抽
			* spop choujiang 2
	* 求用户的共同好友
	* ...关注的人,也关注了他...
	* ...可能认识的人

Sorted Set(有序集合对象)

* 常用命令
	* zadd 集合的名字 得分 键的名字 :向ZSet中 添加一个元素(score 是排序的一个标记)
	* zrange 集合的名字 得分的开始区间 得分的结束区间 :查看所有开始区间和结束区间的key
	* zscore 集合的名字 键的名字 :查看这个key的得分
	* zcount 集合的名字 分数的开始区间 分数的结束区间 :获取指定分数内的成员数
	* zincrby 集合的名字 增加的分数 键的名字 :这个表示这个这个集合中指定的key增加分数
	* zrangebyscore 集合的名字 得分的开始区间 得分的结束区间
	* zrank 集合的名字 键的名字:返回集合中指定的key的下标
	* zrem 集合的名字 键的名字 :移除某一个key
	* ZREVRANGE zSet1 0 10000 :获取指定分数区间的成员 牛逼是从高到低
	
* 应用场景
	* 场景一
		* 最热商品
			* 最热商品指的是 商品的点击数 或者 购买数 最高的 一定是 最热商品 现在我们以 购买数最多的为最热商品
			* zadd hotGoods 0 商品id
			* zincrby hotGoods 100 商品id
			* ZREVRANGE zSet1 0 1000000
			
	* 场景二
		* 接口的防刷和限流(滑动窗口)
			* 可以这样根据ip地址来进行设计我们要求
			* zadd limit:ip地址 访问这个接口的当前的时间戳 随机数

	* 场景三
		* 黑名单
			* black:ip地址 随机值

	* 场景四
		* 脏数据
			* 用户发了一段话 要判断这段话是否含有脏数据
			* dirtword:词语 随机值

其他命令的应用

* 基于key的命令的使用
	* keys * :这个表示的是查看 当前数据库中所有的key *是正则表达式
	* select 数据库的下标 :相当于mysql中的 use 数据库 。使用某一个数据库。Redis默认16个数据库,下标0-15
	* exists key:判断这个key是否存在
	* ttl key:查看这个key的过期时间 -1 :表示的是不过期 永久有效
	* expire key:这个是给这个key设置过期时间 如果是默认设置一个key---value 那么默认这个key是一个持久化的key
	* persist key:可以将一个拥有过期时间的key设置成一个拥有有效的key
	* randomkey :返回一个随机的key 这里要注意(这个意义上拉说可以用来抽奖 但是 不好控制)
	* rename 原来的key的名字 新的名字 :改名字
	
* 基于key命令的应用场景
	* 场景一
		* 在进行用户身份判定的时候要断定这个token是否存在 存在 那么身份合法 否则身份不合法
		* exists token

	* 场景二
		* 在找缓存的时候要判断这个缓存是否 有这个缓存
		* exists 缓存的key

	* 场景三
		* expire : 给token设置过期时间
		* 秒杀的时候给这个商品设置 过期时间

Redis为什么快

1.纯内存操作
2.单线程,没有锁的竞争
3.多路复用IO模型,非阻塞IO

常见问题及解决方案

缓存穿透

当我们在使用Redis做缓存的时候 我们的逻辑是先查询 Redis中是否有数据 如果有数据 那么直接返回 如果没有数据 那么查询数据库 如果查询数据库 查询到了内容 那么放到Redis中 同时将数据进行返回 如果是MySQL中也没有查询到数据 这个时候就存在问题了。
大量的请求都会跑到 MySQL上去 MySQL本身是基于文件系统的 也就会产生大量的IO操作 一旦达到了MySQL能够支持的最大限度 MySQL就会奔溃 这种现象 就称为缓存穿透 或者 缓存击穿。
缓存穿透的解决方案:如果查询MySQL没有查询到数据的话 那么也要返回一个空的字符串给Redis 这样下次的请求到来 就不会直接去访问MySQL了

雪崩问题

* 大量的key同时过期?会不会产生什么问题? 就会造成大量的请求请求到MySQL上去 这样也会造成MySQL奔溃
* 在设置过期时间的时候在固定的时间上 加上一个范围内的随机数 作为 最终的key的过期时间
* Redis死了 就会造成大量的请求访问数据库 去获取数据 这样也会造成MySQL奔溃
* Redis尽量做成集群 保证高可用 提前做压力测试

脑裂问题

向主服务器写入了一条数据 成功的 但是 写完数据之后 主服务器 出现了网络抖动 现在哨兵查看主服务器的状态发现一只没有回应 哨兵启动了选举机制 从剩余的从服务器中 选举了 一台作为主服务器 当原来的主服务器 网络好了之后发现 自己的位置 被别人占了 于是自己内部原来的数据 就无法进行同步 这个时候要保证整个集群有用 自己只能作为从服务器 连接到 新的主服务器上 这个时候 自己就只有被同步数据的权利 没有 同步数据的权利了 原来的数据就丢失了 这种现象就称为脑裂。

主从模式

主从复制指的是 将写入到主服务器的数据自动的同步给从服务器 主从复制最终是为了解决 读写分离 读写分离 指的是将读放到一个服务器上 写放到另外的服务器上 主从复制的最终目的是为了减压。
主从复制的目的是解决读写分离,读写分离的目的是为了减压。

哨兵模式

主服务器一旦死了,那么整个部署架构就死了,无法对外继续提供服务。
哨兵模式就是主服务器死了,立马进行投票选举,最终在剩余的从服务器中选举一个从服务器作为新的主服务器,继续对外提供服务。

集群模式

无论主从还是哨兵,都只有一个写的服务器,当用户量增大,写的服务器也有可能不能承载,那么如何扩大写的能力?那就是运用集群模式;
纵向上是一个主从架构,主从架构的所有数据都是一样的 所以在纵向上添加机器的话是为了缓解读的压力,纵向减压;
横向上,所有机器的数据都是不一样的,主服务器之前的数据各不相同,他们的所有数据加起来才是全部的数据。横向的扩展机器是为了扩容;
一个集群最少六个,三个主服务器,三个从服务器;
他们是通过配置文件进行配置来连接的。

数据淘汰策略

Redis本身是基于内存的 那么如果是 内存满了 怎么办? 内存中的数据要实现淘汰 那么就设计到一个问题了。Redis中内存数据的淘汰策略有哪些呢?
在这里插入图片描述
LRU算法:最长时间内最少使用
LFU算法:一定时间内最少访问

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值