Redis由浅入深学习:Redis简介,安装下载,Redis可视化工具,基本数据类型,Jedis,事务,redis持久化,redis删除策略,企业级解决方案详细介绍

Redis简介

在这里插入图片描述

  • 问题现象
    1. 海量用户
    2. 高并发
  • 罪魁祸首——关系型数据库
    1. 性能瓶颈:磁盘IO性能低下
    2. 扩张瓶颈:数据关系复杂,扩展性差,不便于大规模集群
  • 解决思路
    1. 降低磁盘IO次数,越低越好 —— 内存存储
    2. 去除数据间关系,越简单越好 —— 不存储关系,仅存储数据
    3. NoSQL解决

NOSQL

NoSQL(NoSQL = Not Only SQL),意即“不仅仅是SQL”,是一项全新的数据库理念,泛指非关系型的数据库。随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是超大规模和高并

发的SNS类型,web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据

库则由于其本身的特点得到了非 常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多

重数据种类带来的挑战,尤其是大数据应用难题。

非关系型数据库和关系型数据库对比

在这里插入图片描述

优点:

  1. 成本:nosql数据库简单易部署,基本都是开源软件,不需要像使用oracle那样花费大量成本购

买使用,相比关系型数据库价格便宜

  1. 查询速度:nosql数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询

    速度远不及nosql数据库

  2. 存储数据的格式:nosql的存储格式是key,value形式、文档形式、图片形式等等,所以可以存 储

    基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型

  3. 扩展性:关系型数据库有类似join这样的多表查询机制的限制导致扩展很艰难

缺点:

  1. 维护的工具和资料有限,因为nosql是属于新的技术,不能和关系型数据库10几年的技术同日而

  2. 不提供对sql的支持,如果不支持sql这样的工业标准(不遵循SQL标准),将产生一定用户的学习

    和使用成本

  3. 不提供关系型数据库对事务的处理

主流的NoSQL产品

  • 键值(key-value)存储数据库

    相关产品:Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB

    典型应用: 内容缓存,主要用于处理大量数据的高访问负载。数据模型: 一系列键值对

    优势:快速查询

    劣势:存储的数据缺少结构化

  • 列存储数据库

    相关产品:Cassandra, HBase, Riak

    典型应用:分布式的文件系统

    数据模型:以列簇式存储,将同一列数据存在一起

    优势:查找速度快,可扩展性强,更容易进行分布式扩展劣势:功能相对局限

  • 文档型数据库

    相关产品:CouchDB、MongoDB

    典型应用:Web应用(与Key-Value类似,Value是结构化的) 数据模型: 一系列键值对

    优势:数据结构要求不严格

    劣势: 查询性能不高,而且缺乏统一的查询语法

  • 图形(Graph)数据库

    相关数据库:Neo4J、InfoGrid、Infinite Graph

    典型应用:社交网络数据模型:图结构

    优势:利用图结构相关算法。

    优势:利用图结构相关算法。

  • 数据库排名:网站

解决方案(点商场景)

  1. 商品基本信息

    1.名称

    2.价格 Mysql

    3.厂商

  2. 商品附加信息

    1.描述

    2.详情 MongoDB

    3.评论

  3. 图文信息 分布式文件系统

  4. 搜索关键字 ES,solr

  5. 热点信息

    1.高频 Redis、memcache、tair

    2.波段性

在这里插入图片描述

什么是Redis

概率:Redis (REmote DIctionary Server) 是用 C 语言开发的一个开源的高性能键值对(key-value)数

据库

特征:

  1. 数据间没有必然的关联关系

  2. 内部采用单线程机制进行工作

  3. 高性能,官方提供测试数据,50个并发执行100000 个请求,读的速度是110000 次/s,写的速度是

81000次/s

  1. 多数据类型支持

    字符串类型 string

    列表类型 list

    散列类型 hash

    集合类型 set

    有序集合类型 sorted_set

  2. 持久化支持。可以进行数据灾难恢复

Redis下载安装

  • Linux 版 (适用于企业级开发)

    • ​ Redis 高级开发使用
      • ​ 以4.0 版本作为主版本
  • Windows 版本 (适合零基础学习)

  • 下载后解压缩即可

在这里插入图片描述

核心文件:

  • redis-server.exe 服务器启动命令
  • redis-cli.exe 命令行客户端
  • redis.windows.conf redis核心配置文件
  • redis-benchmark.exe 性能测试工具
  • redis-check-aof.exe AOF文件修复工具
  • redis-check-dump.exe RDB文件检查工具(快照持久化文件)

启动Redis

启动redis服务器

  1. 双击redis-server.exe(未改动配置文件可以双击运行,改动配置文件后必须用命令)
  2. 打开解压后的redis目录,打开cmd终端,输入命令:redis-server.exe redis.windows.conf

在这里插入图片描述

看到此信息,服务器启动成功,双击redis-cli.exe启动命令行客户端

在这里插入图片描述

Redis可视化工具安装

在这里插入图片描述

连接可视化页面需要开启redis服务

安装完新建连接,名字随便起,Host填localhost,端口号6379

可以点击测试,测试测功就可以点ok

Redis基本操作

  • 功能性命令

  • 清屏指令:clear或快捷键:ctrl+L

  • 帮助信息查阅

    ​ 获取命令帮助文档,获取组中所有命令信息名称

    ​ help 命令名称

    help @组名

  • 退出客户端命令行模式:quit exit

Redis数据类型

最新redis已经支持8中数据类型,最常用以下五种

string String

hash HashMap

list LinkedList

set HashSet

sorted_set TreeSet

redis数据存储格式:

  • redis 自身是一个 Map,其中所有的数据都是采用 key : value 的形式存储

  • 数据类型指的是存储的数据的类型,也就是 value 部分的类型,key 部分永远都是字符串

    key的语法:

    • 在一个项目中,key最好使用统一的命名模式
    • key区分大小写
    • key不要太长,尽量不要超过1024字节。不仅消耗内存,也会降低查找的效率
    • key不要太短,太短可读性会降低

String

  • 存储的数据:单个数据,最简单的数据存储类型,也是最常用的数据存储类型
  • 存储数据的格式:一个存储空间保存一个数据
  • 存储内容:通常使用字符串,如果字符串以整数的形式展示,可以作为数字操作使用
==========================字符串类型 string==========================
添加/修改数据
set key value

获取数据
get key

删除数据
del key

添加/修改多个数据
mset key1 value1 key2 value2 …(m代表Multiple)

获取多个数据
mget key1 key2 …

获取数据字符个数(字符串长度)
strlen key

追加信息到原始信息后部(如果原始信息存在就追加,否则新建)
append key value

按照范围获取
GETRANGE key 开始下标 结束下标

替换
SETRANGE key 位置 value

先get然后在set
getset key value

业务场景一:==========================================================
大型企业级应用中,随着数据量的递增,分表操作是基本操作,使用多张表存储同类型数据,但是对应的主键id 必须保证统一性,不能重复。Oracle 数据库具有 sequence 设定,可以解决该问题,但是 MySQL数据库并不具有类似的机制,那么如何解决?

设置数值数据增加指定范围的值
incr key
incrby key increment
incrbyfloat key increment

设置数值数据减少指定范围的值
decr key
decrby key increment

业务场景二:==========================================================
“中国有嘻哈”启动海选投票,只能通过微信投票,每个微信号每 4 小时只能投1票。
电商商家开启热门商品推荐热门商品不能一直处于热门,每种商品热门期维持3天,3天后自动取消热门。
新闻网站会出现热点新闻,热点新闻最大的特征是时效性,如何自动控制热点新闻的时效性。

设置数据具有指定的生命周期
setex key seconds value
psetex key milliseconds value

设置key的过期时间
EXPIRE key seconds

查看key的剩余时间
ttl key

Tips 2:
redis 控制数据的生命周期,通过数据是否失效控制业务行为,适用于所有具有时效性限定控制的操作

业务场景三:==========================================================
主页高频访问信息显示控制,例如新浪微博大V主页显示粉丝数与微博数量
关注数focuss 粉丝数fans 微博数blogs
解决方案
在redis中为大V用户设定用户信息,以用户主键和属性值作为key,后台设定定时刷新策略即可
eg: user:id:3506728370:fans → 12210947
eg: user:id:3506728370:blogs → 6164
eg: user:id:3506728370:focuss → 83

在redis中以json格式存储大V用户信息,定时刷新(也可以使用hash类型)
eg: user:id:3506728370 →
{"id":3506728370,"name":"春晚","fans":12210862,"blogs":6164, "focus":83}

Tips 3:redis应用于各种结构型和非结构型高热度数据访问加速

key 的设置约定===数据库中的热点数据key命名惯例
表名:主键名:主键值:字段名
eg: order:id:9527:name : 王瑞豪

注意事项:==========================================================
数据操作不成功的反馈与数据正常操作之间的差异
	① 表示运行结果是否成功
	(integer) 0false 失败
	(integer) 1true 成功
	② 表示运行结果值
	(integer) 33 3(integer) 11 1个
数据未获取到
	(nil)等同于null
数据最大存储量
	512MB
数值计算最大范围(java中的long的最大值)
	9223372036854775807

hash

存储的困惑

对象类数据的存储如果具有较频繁的更新需求操作会显得笨重

hash的特点

  • 新的存储需求:对一系列存储的数据进行编组,方便管理,典型应用存储对象信息
  • 需要的存储结构:一个存储空间保存多个键值对数据
  • hash类型:底层使用哈希表结构实现数据存储

hash存储结构优化

  • 如果field数量较少,存储结构优化为类数组结构
  • 如果field数量较多,存储结构使用HashMap结构
==========================hash哈希类型==========================
添加/修改数据
hset key field value

添加,但是有则不添加,无则添加
hsetnx key field value

获取数据
hget key field
hgetall key

删除数据
hdel key field1 [field2]

添加/修改多个数据
hmset key field1 value1 field2 value2 …

获取多个数据
hmget key field1 field2 …

获取哈希表中字段的数量
hlen key

获取哈希表中是否存在指定的字段
hexists key field

获取哈希表中所有的字段名或字段值
hkeys key
hvals key

设置指定字段的数值数据增加指定范围的值
hincrby key field increment
hincrbyfloat key field increment

hash 类型数据操作的注意事项
1.hash类型下的value只能存储字符串,不允许存储其他数据类型,不存在嵌套现象。如果数据未获取到,对应的值为(nil)
2.每个 hash 可以存储 2^32^-1个键值对
3.hash类型十分贴近对象的数据存储形式,并且可以灵活添加删除对象属性。但hash设计初衷不是为了
存储大量对象而设计的,切记不可滥用,更不可以将hash作为对象列表使用
4.hgetall 操作可以获取全部属性,如果内部field过多,遍历整体数据效率就很会低,有可能成为数据访问瓶颈

业务场景一:==========================================================
string存储对象(json)与hash存储对象
Stirng存对象强调整体性一般用于读
hash存对象强调灵活性一般用于写

业务场景二:==========================================================
电商网站购物车设计与实现
购物车的redis存储模型
添加、浏览、更改数量、删除、清空

在这里插入图片描述

解决方案:

  • 以客户id作为key,每位客户创建一个hash存储结构存储对应的购物车信息
  • 将商品编号作为field,购买数量作为value进行存储
  • 添加商品:追加全新的field与value
  • 浏览:遍历hash
  • 更改数量:自增/自减,设置value值
  • 删除商品:删除field
  • 清空:删除key

List

  • 数据存储需求:存储多个数据,并对数据进入存储空间的顺序进行区分
  • 需要的存储结构:一个存储空间保存多个数据,且通过数据可以体现进入顺序
  • list类型:保存多个数据,底层使用双向链表存储结构实现
==========================list==========================
添加/修改数据
lpush key value1 [value2] ……
rpush key value1 [value2] ……

获取数据
lrange key start stop
lindex key index
llen key

获取并移除数据
lpop key
rpop key

通过下标截取指定的长度,这个list已经被改变了,只剩下截取的元素
ltrim key start end

lset将列表中指定下标的值替换为另外一个值,更新操作
lset key 下标 新数据 # 如果不存在列表我们去更新就会报错

将某个具体的value插入到列中的某个元素的前面或者后面!
LINSERT mylist before "world" "other"
LINSERT mylist after "world" "other"

规定时间内获取并移除数据 b代表block阻塞
blpop key1 [key2] timeout
brpop key1 [key2] timeout
eg:lpush list1 a b
lpop list1 ===a
lpop list1 ===b
lpop list1 ===nil 立刻响应为空,但如果是blpop会阻塞,也就是会等,现在没数据不意味着未来没有
blpop list1 30 获取数据,有则返回,没有则等待30秒
可以打开2个客户端演示

业务场景一:==========================================================
朋友圈点赞

在这里插入图片描述

List类型数据操作注意事项

  1. list中保存的数据都是string类型的,数据总容量是有限的,最多232-1个元素 (4294967295)
  2. list具有索引的概念,但是操作数据时通常以队列的形式进行入队出队操作,或以栈的形式进行入栈出栈操作
  3. 获取全部数据操作结束索引设置为-1
  4. list可以对数据进行分页操作,通常第一页的信息来自于list,第2页及更多的信息通过数据库的形式加载

set

  • 新的存储需求:存储大量的数据,在查询方面提供更高的效率
  • 需要的存储结构:能够保存大量的数据,高效的内部存储机制,便于查询
  • set类型:与hash存储结构完全相同,仅存储键,不存储值(nil),并且值是不允许重复的
添加数据
sadd key member1 [member2]

获取全部数据
smembers key

删除数据
srem key member1 [member2]

获取集合数据总量
scard key

判断集合中是否包含指定数据
sismember key member

业务场景一:==========================================================
每位用户首次使用今日头条时会设置3项爱好的内容,但是后期为了增加用户的活跃度、兴趣点,必须让用户对其他信息类别逐渐产生兴趣,增加客户留存度,如何实现?
业务分析
1.系统分析出各个分类的最新或最热点信息条目并组织成set集合
2.随机挑选其中部分信息
3.配合用户关注信息分类中的热点信息组织成展示的全信息集合
解决方案
随机获取集合中指定数量的数据
srandmember key [count]

随机获取集合中的某个数据并将该数据移出集合
spop key [count]

Tips 1:
redis 应用于随机推荐类信息检索,例如热点歌单推荐,热点新闻推荐,热卖旅游线路,应用APP推荐,大V推荐等

业务场景二:==========================================================
1.脉脉为了促进用户间的交流,保障业务成单率的提升,需要让每位用户拥有大量的好友,事实上职场新人不
具有更多的职场好友,如何快速为用户积累更多的好友?
2.新浪微博为了增加用户热度,提高用户留存性,需要微博用户在关注更多的人,以此获得更多的信息或热门
话题,如何提高用户关注他人的总量?
3.QQ新用户入网年龄越来越低,这些用户的朋友圈交际圈非常小,往往集中在一所学校甚至一个班级中,如何
帮助用户快速积累好友用户带来更多的活跃度?
4.美团外卖为了提升成单量,必须帮助用户挖掘美食需求,如何推荐给用户最适合自己的美食?
解决方案

求两个集合的交、并、差集
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

set类型数据操作的注意事项

  1. set 类型不允许数据重复,如果添加的数据在 set 中已经存在,将只保留一份
  2. set 虽然与hash的存储结构相同,但是无法启用hash中存储值的空间

sorted_set

  • 新的存储需求:数据排序有利于数据的有效展示,需要提供一种可以根据自身特征进行排序的方式
  • 需要的存储结构:新的存储模型,可以保存可排序的数据
  • sorted_set类型:在set的存储结构基础上添加可排序字段
==========================sorted_set==========================
添加数据
zadd key score1 member1 [score2 member2]

获取全部数据
zrange key start(开始索引) stop(结束索引) [WITHSCORES] #[WITHSCORES]可以除了数据还

显示分值
zrevrange key start stop [WITHSCORES] 反转

删除数据
zrem key member [member ...]

获取集合数据总量
zcard key

Jedis

我们要使用 Java 来操作 Redis,什么是Jedis 是 Redis 官方推荐的 java连接开发工具! 使用Java 操作Redis 中间件!(jedis相当于jdbc)

实现步骤:

  1. 导入pom文件坐标

    <!-- jedis坐标-->
    <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>3.2.0</version>
    </dependency>
    
  2. 编写测试代码

    import redis.clients.jedis.Jedis;
    public class TestPing {
        public static void main(String[] args) {
            // 1、 new Jedis 对象即可
            Jedis jedis = new Jedis("127.0.0.1",6379);
         // jedis 所有的命令就是我们之前学习的所有指令!所以之前的指令学习很重要!
            System.out.println(jedis.ping());
        }
    }
    

Redis事务

概念

Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。Redis事务的主要作用就是串联多个命令防止别的命令插队

特点

  1. Redis事务没有没有隔离级别的概念
  2. 所有的命令在事务中,并没有直接被执行,只有发起执行命令的时候才会执行!exec
  3. Redis单条命令式保存原子性的,但是事务不保证原子性

常用命令

命令描述
multi标记一个事务的开始
exec执行所有事务块内的命令
discard取消事务,放弃执行事务块内的所有命令
watch key [key]监视一个或多个key,如果在事务执行之前key被其他命令改动,那么事务将被打断。类似乐观锁
unwatch取消watch命令对所有 key 的监视
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec # 执行事务
1) OK
2) OK
3) "v2"
4) OK
=========================放弃事务
127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD # 取消事务
OK
127.0.0.1:6379> get k4 # 事务队列中命令都不会被执行!
(nil)

=========================编译型异常(代码有问题! 命令有错!)事务中所有的命令都不会被执
行!
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3
# 错误的命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec # 执行事务报错!
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 # 所有的命令都不会被执行!
(nil)

=========================运行时异常,如果事务队列中存在语法性,那么执行命令的时候其他命令是
可以正常执行的,错误命令抛出异常!
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 # 会执行的时候失败!
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range
# 虽然第一条命令报错了,但是依旧正常执行成功了!
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"

为什么添加事务

想想一个场景:有很多人有你的账户,同时去参加双十一抢购

银行存款10000元,三个人同时使用一个账户购买商品,发现存款变负数

一个请求想给金额减8000

一个请求想给金额减5000

一个请求想给金额减1000

解决方案:

悲观锁

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁

乐观锁

乐观锁(Optimistic Lock) 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的

在这里插入图片描述

  1. 客户端1监视key:user1
  2. 开启事务
  3. 客户端2,运行set user1,对user1修改
  4. 客户端1,exec
  5. 事务失败

Redis配置

用编辑器打开redis.windows.conf文件进行修改

#是否在后台运行;no:不是后台运行
daemonize yes

#是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,拒绝外部访问。
protected-mode yes

#redis的进程文件
pidfile /var/run/redis/redis-server.pid

#redis监听的端口号。
port 6379

#此参数确定了TCP连接中已完成队列(完成三次握手之后)的长度, 当然此值必须不大于Linux系统定义的/proc/sys/net/core/somaxconn值,默认是511,而Linux的默认参数值是128。当系统并发量大并且客户端速度缓慢的时候,可以将这二个参数一起参考设定。该内核参数默认值一般是128,对于负载很大的服务程序来说大大的不够。一般会将它修改为2048或者更大。在/etc/sysctl.conf中添加:net.core.somaxconn = 2048,然后在终端中执行sysctl -p。
tcp-backlog 511

#指定 redis 只接收来自于该 IP 地址的请求,如果不进行设置,那么将处理所有请求
#bind 127.0.0.1
#bind 0.0.0.0

#配置unix socket来让redis支持监听本地连接。
# unixsocket /var/run/redis/redis.sock

#配置unix socket使用文件的权限
# unixsocketperm 700

# 此参数为设置客户端空闲超过timeout,服务端会断开连接,为0则服务端不会主动断开连接,不能小于0。
timeout 0

#tcp keepalive参数。如果设置不为0,就使用配置tcp的SO_KEEPALIVE值,使用keepalive有两个好处:检测挂掉的对端。降低中间设备出问题而导致网络看似连接却已经与对端端口的问题。在Linux内核中,设置了keepalive,redis会定时给对端发送ack。检测到对端关闭需要两倍的设置值。
tcp-keepalive 0

#指定了服务端日志的级别。级别包括:debug(很多信息,方便开发、测试),verbose(许多有用的信息,但是没有debug级别信息多),notice(适当的日志级别,适合生产环境),warn(只有非常重要的信息)
loglevel notice

#指定了记录日志的文件。空字符串的话,日志会打印到标准输出设备。后台运行的redis标准输出是/dev/null。
logfile /var/log/redis/redis-server.log

#是否打开记录syslog功能
# syslog-enabled no

#syslog的标识符。
# syslog-ident redis

#日志的来源、设备
# syslog-facility local0

#数据库的数量,默认使用的数据库是DB 0。可以通过SELECT命令选择一个db
databases 16

# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000

#当RDB持久化出现错误后,是否依然进行继续进行工作,yes:不能进行工作,no:可以继续进行工作,可以通过info中的rdb_last_bgsave_status了解RDB持久化是否有错误
stop-writes-on-bgsave-error yes

#使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,但是需要一些cpu的消耗。no:不压缩,需要更多的磁盘空间
rdbcompression yes

#是否校验rdb文件。从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文件的容错性,但是在保存rdb文件的时候,会有大概10%的性能损耗,所以如果你追求高性能,可以关闭该配置。
rdbchecksum yes

#rdb文件的名称
dbfilename dump.rdb

#数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
dir /data

############### 主从复制 ###############
#复制选项,slave复制对应的master。
# slaveof <masterip> <masterport>

#如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。masterauth就是用来配置master的密码,这样可以在连上master后进行认证。
# masterauth <master-password>

#当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续响应客户端的请求。2) 如果slave-serve-stale-data设置为no,除去INFO和SLAVOF命令之外的任何请求都会返回一个错误”SYNC with master in progress”。
slave-serve-stale-data yes

#作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)。
slave-read-only yes

#是否使用socket方式复制数据。目前redis复制提供两种方式,disk和socket。如果新的slave连上来或者重连的slave无法部分同步,就会执行全量同步,master会生成rdb文件。有2种方式:disk方式是master创建一个新的进程把rdb文件保存到磁盘,再把磁盘上的rdb文件传递给slave。socket是master创建一个新的进程,直接把rdb文件以socket的方式发给slave。disk方式的时候,当一个rdb保存的过程中,多个slave都能共享这个rdb文件。socket的方式就的一个个slave顺序复制。在磁盘速度缓慢,网速快的情况下推荐用socket方式。
repl-diskless-sync no

#diskless复制的延迟时间,防止设置为0。一旦复制开始,节点不会再接收新slave的复制请求直到下一个rdb传输。所以最好等待一段时间,等更多的slave连上来。
repl-diskless-sync-delay 5

#slave根据指定的时间间隔向服务器发送ping请求。时间间隔可以通过 repl_ping_slave_period 来设置,默认10秒。
# repl-ping-slave-period 10

#复制连接超时时间。master和slave都有超时时间的设置。master检测到slave上次发送的时间超过repl-timeout,即认为slave离线,清除该slave信息。slave检测到上次和master交互的时间超过repl-timeout,则认为master离线。需要注意的是repl-timeout需要设置一个比repl-ping-slaveperiod更大的值,不然会经常检测到超时。
# repl-timeout 60

#是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。默认是no,即使用tcp nodelay。如果master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络带宽。但是这也可能带来数据的延迟。默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择yes。
repl-disable-tcp-nodelay no

#复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。这样在slave离线的时候,不需要完全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状态。缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内存。没有slave的一段时间,内存会被释放出来,默认1m。
# repl-backlog-size 5mb

#master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为秒。
# repl-backlog-ttl 3600

#当master不可用,Sentinel会根据slave的优先级选举一个master。最低的优先级的slave,当选master。而配置成0,永远不会被选举。
slave-priority 100

#redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数小于N,mater就禁止写入。master最少得有多少个健康的slave存活才能执行写命令。这个配置虽然不能保证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入来避免数据丢失。设置为0是关闭该功能。
# min-slaves-to-write 3

#延迟小于min-slaves-max-lag秒的slave才认为是健康的slave。
# min-slaves-max-lag 10

# 设置1或另一个设置为0禁用这个特性。
# Setting one or the other to 0 disables the feature.
# By default min-slaves-to-write is set to 0 (feature disabled) and
# min-slaves-max-lag is set to 10.

############### 安全相关 ###############
#requirepass配置可以让用户使用AUTH命令来认证密码,才能使用其他命令。这让redis可以使用在不受信任的网络中。为了保持向后的兼容性,可以注释该命令,因为大部分用户也不需要认证。使用requirepass的时候需要注意,因为redis太快了,每秒可以认证15w次密码,简单的密码很容易被攻破,所以最好使用一个更复杂的密码。注意只有密码没有用户名。
# requirepass foobared

#把危险的命令给修改成其他名称。比如CONFIG命令可以重命名为一个很难被猜到的命令,这样用户不能使用,而内部工具还能接着使用。
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52

#设置成一个空的值,可以禁止一个命令
# rename-command CONFIG ""

############### 进程限制相关 ###############
# 设置能连上redis的最大客户端连接数量。默认是10000个客户端连接。由于redis不区分连接是客户端连接还是内部打开文件或者和slave连接等,所以maxclients最小建议设置到32。如果超过了maxclients,redis会给新的连接发送’max number of clients reached’,并关闭连接。
# maxclients 10000

#redis配置的最大内存容量。当内存满了,需要配合maxmemory-policy策略进行处理。注意slave的输出缓冲区是不计算在maxmemory内的。所以为了防止主机内存使用完,建议设置的maxmemory需要更小一些。
# maxmemory <bytes>

#内存容量超过maxmemory后的处理策略。
#volatile-lru:利用LRU算法移除设置过过期时间的key。
#volatile-random:随机移除设置过过期时间的key。
#volatile-ttl:移除即将过期的key,根据最近过期时间来删除(辅以TTL)
#allkeys-lru:利用LRU算法移除任何key。
#allkeys-random:随机移除任何key。
#noeviction:不移除任何key,只是返回一个写错误。
#上面的这些驱逐策略,如果redis没有合适的key驱逐,对于写命令,还是会返回错误。redis将不再接收写请求,只接收get请求。写命令包括:set setnx setex append incr decr rpush lpush rpushxlpushx linsert lset rpoplpush sadd sinter sinterstore sunion sunionstore sdiffsdiffstore zadd zincrby zunionstore zinterstore hset hsetnx hmset hincrby incrbydecrby getset mset msetnx exec sort。
# maxmemory-policy noeviction

#lru检测的样本数。使用lru或者ttl淘汰算法,从需要淘汰的列表中随机选择sample个key,选出闲置时间最长的key移除。
# maxmemory-samples 5

############### APPEND ONLY 持久化方式 ###############
#默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendonly no

#aof文件名
appendfilename "appendonly.aof"

#aof持久化策略的配置
#no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
#always表示每次写入都执行fsync,以保证数据同步到磁盘。
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
appendfsync everysec

# 在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。如果对延迟要求很高的应用,这个字段可以设置为yes,,设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,不会造成阻塞的问题(因为没有磁盘竞争),等rewrite完成后再写入,这个时候redis会丢失数据。Linux的默认fsync策略是30秒。可能丢失30秒数据。因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受数据丢失,则设置为no。
no-appendfsync-on-rewrite no

#aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100

#设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
auto-aof-rewrite-min-size 64mb
#aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被载入内存。重启可能发生在redis所在的主机操作系统宕机后,尤其在ext4文件系统没有加上data=ordered选项(redis宕机或者异常终止不会造成尾部不完整现象。)出现这种现象,可以选择让redis退出,或者导入尽可能多的数据。如果选择的是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。如果是no,用户必须手动redis-check-aof修复AOF文件才可以。
aof-load-truncated yes

############### LUA SCRIPTING ###############
# 如果达到最大时间限制(毫秒),redis会记个log,然后返回error。当一个脚本超过了最大时限。只有SCRIPT KILL和SHUTDOWN NOSAVE可以用。第一个可以杀没有调write命令的东西。要是已经调用了write,只能用第二个命令杀。
lua-time-limit 5000

############### 集群相关 ###############
#集群开关,默认是不开启集群模式。
# cluster-enabled yes

#集群配置文件的名称,每个节点都有一个集群相关的配置文件,持久化保存集群的信息。这个文件并不需要手动配置,这个配置文件有Redis生成并更新,每个Redis集群节点需要一个单独的配置文件,请确保与实例运行的系统中配置文件名称不冲突
# cluster-config-file nodes-6379.conf

#节点互连超时的阀值。集群节点超时毫秒数
# cluster-node-timeout 15000

#在进行故障转移的时候,全部slave都会请求申请为master,但是有些slave可能与master断开连接一段时间了,导致数据过于陈旧,这样的slave不应该被提升为master。该参数就是用来判断slave节点与master断线的时间是否过长。判断方法是:
#比较slave断开连接的时间和(node-timeout * slave-validity-factor) + repl-ping-slaveperiod

#如果节点超时时间为三十秒, 并且slave-validity-factor为10,假设默认的repl-ping-slaveperiod是10秒,即如果超过310秒slave将不会尝试进行故障转移
# cluster-slave-validity-factor 10

#master的slave数量大于该值,slave才能迁移到其他孤立master上,如这个参数若被设为2,那么只有当一个主节点拥有2 个可工作的从节点时,它的一个从节点会尝试迁移。
# cluster-migration-barrier 1

#默认情况下,集群全部的slot有节点负责,集群状态才为ok,才能提供服务。设置为no,可以在slot没有全部分配的时候提供服务。不建议打开该配置。
# cluster-require-full-coverage yes

############### SLOW LOG 慢查询日志 ###############
###slog log是用来记录redis运行中执行比较慢的命令耗时。当命令的执行超过了指定时间,就记录在slow log中,slog log保存在内存中,所以没有IO操作。
#执行时间比slowlog-log-slower-than大的请求记录到slowlog里面,单位是微秒,所以1000000就是1秒。注意,负数时间会禁用慢查询日志,而0则会强制记录所有命令。
slowlog-log-slower-than 10000
#慢查询日志长度。当一个新的命令被写进日志的时候,最老的那个记录会被删掉。这个长度没有限制。只要有足够的内存就行。你可以通过 SLOWLOG RESET 来释放内存。
slowlog-max-len 128
############### 延迟监控 ###############
#延迟监控功能是用来监控redis中执行比较缓慢的一些操作,用LATENCY打印redis实例在跑命令时的耗时图表。只记录大于等于下边设置的值的操作。0的话,就是关闭监视。默认延迟监控功能是关闭的,如果你需要打开,也可以通过CONFIG SET命令动态设置。
latency-monitor-threshold 0
############### EVENT NOTIFICATION 订阅通知 ###############
#键空间通知使得客户端可以通过订阅频道或模式,来接收那些以某种方式改动了 Redis 数据集的事件。因为开启键空间通知功能需要消耗一些 CPU ,所以在默认配置下,该功能处于关闭状态。
#notify-keyspace-events 的参数可以是以下字符的任意组合,它指定了服务器该发送哪些类型的通知:
##K 键空间通知,所有通知以 __keyspace@__ 为前缀
##E 键事件通知,所有通知以 __keyevent@__ 为前缀
##g DEL 、 EXPIRE 、 RENAME 等类型无关的通用命令的通知
##$ 字符串命令的通知
##l 列表命令的通知
##s 集合命令的通知
##h 哈希命令的通知
##z 有序集合命令的通知
##x 过期事件:每当有过期键被删除时发送
##e 驱逐(evict)事件:每当有键因为 maxmemory 政策而被删除时发送
##A 参数 g$lshzxe 的别名
#输入的参数中至少要有一个 K 或者 E,否则的话,不管其余的参数是什么,都不会有任何 通知被分发。详细使用可以参考http://redis.io/topics/notifications
notify-keyspace-events ""
############### ADVANCED CONFIG 高级配置 ###############
#数据量小于等于hash-max-ziplist-entries的用ziplist,大于hash-max-ziplist-entries用hash
hash-max-ziplist-entries 512
#value大小小于等于hash-max-ziplist-value的用ziplist,大于hash-max-ziplist-value用hash。
hash-max-ziplist-value 64
#数据量小于等于list-max-ziplist-entries用ziplist,大于list-max-ziplist-entries用list。
list-max-ziplist-entries 512
#value大小小于等于list-max-ziplist-value的用ziplist,大于list-max-ziplist-value用list。
list-max-ziplist-value 64
#数据量小于等于set-max-intset-entries用iniset,大于set-max-intset-entries用set。
set-max-intset-entries 512
#数据量小于等于zset-max-ziplist-entries用ziplist,大于zset-max-ziplist-entries用zset。
zset-max-ziplist-entries 128
#value大小小于等于zset-max-ziplist-value用ziplist,大于zset-max-ziplist-value用zset。
zset-max-ziplist-value 64
#value大小小于等于hll-sparse-max-bytes使用稀疏数据结构(sparse),大于hll-sparse-maxbytes使用稠密的数据结构(dense)。一个比16000大的value是几乎没用的,建议的value大概为3000。如果对CPU要求不高,对空间要求较高的,建议设置到10000左右。
hll-sparse-max-bytes 3000

#Redis将在每100毫秒时使用1毫秒的CPU时间来对redis的hash表进行重新hash,可以降低内存的使用。当你的使用场景中,有非常严格的实时性需要,不能够接受Redis时不时的对请求有2毫秒的延迟的话,把这项配置为no。如果没有这么严格的实时性要求,可以设置为yes,以便能够尽可能快的释放内存。
activerehashing yes
##对客户端输出缓冲进行限制可以强迫那些不从服务器读取数据的客户端断开连接,用来强制关闭传输缓慢的客户端。
#对于normal client,第一个0表示取消hard limit,第二个0和第三个0表示取消soft limit,normal client默认取消限制,因为如果没有寻问,他们是不会接收数据的。
client-output-buffer-limit normal 0 0 0

#对于slave client和MONITER client,如果client-output-buffer一旦超过256mb,又或者超过64mb持续60秒,那么服务器就会立即断开客户端连接。
client-output-buffer-limit slave 256mb 64mb 60

#对于pubsub client,如果client-output-buffer一旦超过32mb,又或者超过8mb持续60秒,那么服务器就会立即断开客户端连接。
client-output-buffer-limit pubsub 32mb 8mb 60
#redis执行任务的频率为1s除以hz。
hz 10
#在aof重写的时候,如果打开了aof-rewrite-incremental-fsync开关,系统会每32MB执行一次fsync。这对于把文件写入磁盘是有帮助的,可以避免过大的延迟峰值。
aof-rewrite-incremental-fsync yes

redis持久化

Redis 是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以 Redis 提供了持久化功能

持久化过程保存什么

  1. 将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据 (RDB)
  2. 将数据的操作过程进行保存,日志形式,存储操作过程,关注点在数据的操作过程(AOF)

RDB方式

概念:

​ 在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里

RDB手动

save指令

命令:save

作用:手动执行一次保存操作

save指令相关配置

dbfilename dump.rdb

说明:设置本地数据库文件名,默认值为 dump.rdb

经验:通常设置为 dump-端口号.rdb

dir

说明:设置存储.rdb文件的路径

经验:通常设置成存储空间较大的目录中,目录名称data

rdbcompression yes

说明:设置存储至本地数据库时是否压缩数据,默认为 yes,采用 LZF算法 压缩

经验:通常默认为开启状态,如果设置为no,可以节省 CPU 运行时间,但会使存储的文件变大(巨大)

rdbchecksum yes

说明:设置是否进行CRC64算法RDB文件格式校验, 该校验过程在写文件和读文件过程均进行

经验:通常默认为开启状态,如果设置为no,可以节约读写性过程约10%时间消耗,但是存储一定的数据损坏风险

save指令工作原理(单线程任务执行序列)

客户端1 127.0.0.1:6379>set key1 value1

客户端2 127.0.0.1:6379>set key2 value2

客户端3 127.0.0.1:6379>save

客户端4 127.0.0.1:6379>get key

对redis数据库执行4此指令顺序===> set set save get

**注意:**save指令的执行会阻塞当前Redis服务器,直到当前RDB过程完成为止,有可能会造成长时间阻塞,线上环境不建议使用

bgsave指令

命令 :bgsave

作用 :手动启动后台保存操作,但不是立即执行

bgsave指令工作原理

在这里插入图片描述

注意: bgsave命令是针对save阻塞问题做的优化。Redis内部所有涉及到RDB操作都采用bgsave的方式,save命令可以放弃使用

Fork

Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程

RDB手动

配置 :save second changes

作用 : 满足限定时间范围内key的变化数量达到指定数量即进行持久化

参数 :

​ second:监控时间范围

​ changes:监控key的变化量

位置 : 在conf文件中进行配置

注意:

save配置要根据实际业务情况进行设置,频度过高或过低都会出现性能问题,结果可能是灾难性的save配置中对于second与changes设置通常具有互补对应关系,尽量不要设置成包含性关系save配置启动后执行的是bgsave操作

RDB优点

RDB是一个紧凑压缩的二进制文件,存储效率较高

RDB内部存储的是redis在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景

RDB恢复数据的速度要比AOF快很多

RDB节省磁盘空间

RDB缺点

Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑

虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能

RDB方式无论是执行指令还是利用配置,无法做到实时持久化,具有较大的可能性丢失数据

Redis的众多版本中未进行RDB文件格式的版本统一,有可能出现各版本服务之间数据格式无法兼容现象

AOF方式

概念:

AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的;与RDB相比可以简单描述为改记录数据为记录数据产生的过程AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式

AOF执行过程

客户端的请求写命令会被append追加到AOF缓冲区内;

AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;

AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;

Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的;

AOF写数据三种策略(appendfsync)

always(每次)

每次写入操作均同步到AOF文件中,数据零误差,性能较低

everysec(每秒)

每秒将缓冲区中的指令同步到AOF文件中,数据准确性较高,性能较高

在系统突然宕机的情况下丢失1秒内的数据

no(系统控制)

由操作系统控制每次同步到AOF文件的周期,整体过程不可控

AOF相关配置

配置 :appendonly yes|no

作用 :是否开启AOF持久化功能,默认为不开启状态

配置 :appendfsync always|everysec|no

作用 :AOF写数据策略

配置:appendfilename filename

作用:AOF持久化文件名,默认文件名未appendonly.aof,建议配置为appendonly-端口号.aof

配置:dir

作用 :AOF持久化文件保存路径,与RDB持久化文件保持一致即可

AOF写数据遇到的问题

在这里插入图片描述

AOF重写

随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制压缩文件体积。AOF文件重写是将Redis进程内的数据转化为写命令同步到新AOF文件的过程。简单说就是将对同一个数据的若干个条命令执行结果转化成最终结果数据对应的指令进行记录

AOF重写的作用

降低磁盘占用量,提高磁盘利用率

提高持久化效率,降低持久化写时间,提高IO性能

降低数据恢复用时,提高数据恢复效率

AOF重写规则

进程内已超时的数据不再写入文件

忽略无效指令,重写时使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令

如del key1、 hdel key2、srem key3、set key4 111、set key4 222等

对同一数据的多条写命令合并为一条命令如lpush list1 a、lpush list1 b、 lpush list1 c 可以转化为:lpush list1 a b c。

为防止数据量过大造成客户端缓冲区溢出,对list、set、hash、zset等类型,每条指令最多写入64个元素

AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)

AOF重写方式

手动重写bgrewriteaof

自动重写

​ 触发机制,何时重写

​ Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发;重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写=

auto-aof-rewrite-min-size 设置重写的基准值,最小文件64MB。达到这个值开始重写。

auto-aof-rewrite-percentage 设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)

持久化方式RDBAOF
占用内存空间小(数据级:压缩)大(指令级:重写)
存储速度
恢复速度
数据安全性会丢失数据一句策略决定
资源消耗高/重量级低/轻量级
启动优先级

总结:官方推荐两个都启用,如果对数据不敏感,可以选单独用RDB,不建议单独使用AOF,因为可能出现Bug;如果说是做纯内存缓存,可以都不用

Redis删除策略

过期数据

Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态

  • xx:具有时效性的数据
  • -1:永久有效的数据
  • -2:已经过期的数据或被删除的数据或未定义的数据

过期的数据不是真的被删除

数据的删除策略

数据删除策略的目标

在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体Redis性能的下降,甚至引发服务器宕机或内存泄露哦

  1. 定时删除
  2. 惰性删除
  3. 定期删除

定时删除

  • 创建一个定时器,当key设置有过期时间,且过期时间到达,由定时器任务立即执行对键的删除操作
  • 优点:节约内存,到时就删除,快速释放掉不必要的内存空间
  • 缺点:CPU压力很大,无论CPU此时负载量多高,均占用CPU,会影响redis服务器响应时间和指令吞吐量
  • 总结:用处理器性能换取存储空间(拿时间换空间)

惰性删除

  • 数据达到过期时间,不做处理,等下次访问该数据时
    • 如果未过期,返回数据
    • 如果已过期,删除,返回不存在
  • 优点:节约CPU性能,发现必须删除的时候才删除
  • 缺点:内存压力很大,出现长期内存占用内存的数据
  • 总结:用存储空间换取处理器性能(拿空间换时间)

定期删除

定时删除和惰性删除都走极端,定期删除是折中方案

  • Redis启动服务器初始化时,读取配置server.hz的值,默认为10
  • 每秒钟执行server.hz次serverCron()中的方法—databasesCron()—activeExirpCycle()
  • activeExirpCycle()对每个expires[*]逐一进行检测,每次执行250ms/server.hz
  • 对某个expires[*]检测时,随机挑选W个key检测
    1. 如果key超时,删除key
    2. 如果一轮中删除的key的数量>W * 25% ,循环该过程
    3. 如果一轮中删除的key的数量<= W * 25% ,检查下一个expires[*],0-15循环
    4. W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值
  • 参数current_db用于记录activeExpireCycle()进入哪个expires[*]执行
  • 如果activeExpireCycle()执行时间到期,下次从current_db继续向下访问

在这里插入图片描述

定期删除:周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度

优点:

  1. CPU性能占用设置有峰值,检测频度可自定义设置
  2. 内存压力不是很大,长期占用内存的冷数据会被持续清理

总结:

周期性抽查存储空间(随机抽查,重点抽查)

删除策略对比

删除策略内存CPU总结
定时删除节约内存,无占用不分时段占用CPU资源,频度高空间换时间
惰性删除内存占用严重延时执行,CPU利用率高时间换空间
定期删除内存定期随即处理每秒花费固定的CPU资源维护内存随机抽查,重点抽查

逐出算法

当新数据进入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 )

    1. volatile-lru:挑选最近最少使用的数据淘汰
    2. volatile-lfu:挑选最近使用次数最少的数据淘汰(最近最不经常使用)
    3. volatile-ttl:挑选将要过期的数据淘汰
    4. volatile-random:任意选择数据淘汰

    检测全库数据(所有数据集server.db[i].dict)

    1. allkeys-lru:挑选最近最少使用的数据淘汰
    2. allkeys-lfu:挑选最近使用次数最少的数据淘汰
    3. allkeys-random:任意选择数据淘汰

    放弃数据驱逐

    1. no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发错误

      OOM(Out OfMemory)达到最大内存后的,对被挑选出来的数据进行删除的策略

在这里插入图片描述
上图中:
一共9秒钟,从1-9秒,name在最后一次在9秒操作,共2次;age最后在第5秒操作,操作4次;addr最后在第7秒操作,操作2次;gender最后在第8秒操作,操作1次
LRU删除策略:简单来说看时间,距离当前操作时间最远的,所以LRU删除age
LFU删除策略:简单来说看次数,gender操作1次,所以删除gender

企业级解决方案

缓存预热

问题:某服务启动后迅速宕机

问题排查:

  1. 请求数量较高
  2. 主从之间数据吞吐量较大,数据同步操作频度高,因为刚刚启动时,缓存中没有任何数据

解决方案:

准备工作:

  1. 日常例行统计数据访问记录,统计访问频度较高的热点数据
  2. 将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据实施

实施:

  1. 使用脚本程序固定触发数据预热过程
  2. 如果条件允许,使用了CDN(内容分发网络),效果会更好
CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输得更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度

总结:

缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据

缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力

解决方案:

  1. 给不同的Key的TTL添加随机值
  2. 利用Redis集群提高服务的可用性
  3. 给缓存业务添加降级限流策略
  4. 给业务添加多级缓存

在这里插入图片描述

缓存击穿

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击

常见的解决方案有两种:

  • 互斥锁
  • 逻辑过期

逻辑分析:假设线程1在查询缓存之后,本来应该去查询数据库,然后把这个数据重新加载到缓存的,此时只要线程1走完这个逻辑,其他线程就都能从缓存中加载这些数据了,但是假设在线程1没有走完的时候,后续的线程2,线程3,线程4同时过来访问当前这个方法,那么这些线程都不能从缓存中查询到数据,那么他们就会同一时刻来访问查询缓存,都没查到,接着同一时间去访问数据库,同时的去执行数据库代码,对数据库访问压力过大

在这里插入图片描述

解决方案一:使用锁解决

因为锁能实现互斥性。假设线程过来,只能一个人一个人的来访问数据库,从而避免对于数据库访问压力过大,但这也会影响查询的性能,因为此时会让查询的性能从并行变成了串行,我们可以采用tryLock方法 + double check来解决这样的问题

假设现在线程1过来访问,他查询缓存没有命中,但是此时他获得到了锁的资源,那么线程1就会一个人去执行逻辑,假设现在线程2过来,线程2在执行过程中,并没有获得到锁,那么线程2就可以进行到休眠,直到线程1把锁释放后,线程2获得到锁,然后再来执行逻辑,此时就能够从缓存中拿到数据了

在这里插入图片描述

解决方式二:逻辑过期方案

方案分析:我们之所以会出现这个缓存击穿问题,主要原因是在于我们对key设置了过期时间,假设我们不设置过期时间,其实就不会有缓存击穿的问题,但是不设置过期时间,这样数据不就一直占用我们内存了吗,我们可以采用逻辑过期方案

我们把过期时间设置在 redis的value中,注意:这个过期时间并不会直接作用于redis,而是我们后续通过逻辑去处理。假设线程1去查询缓存,然后从value中判断出来当前的数据已经过期了,此时线程1 去获得互斥锁,那么其他线程会进行阻塞,获得了锁的线程他会开启一个线程去进行以前的重构数据的逻辑,直到新开的线程完成这个逻辑后,才释放锁,而线程1直接进行返回,假设现在线程3过来访问,由于线程线程2持有着锁,所以线程3无法获得锁,线程3也直接返回数据,只有等到新开的线程2把重建数据构建完后,其他线程才能走返回正确的数据

这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。

在这里插入图片描述

进行对比

**互斥锁方案:**由于保证了互斥性,所以数据一致,且实现简单,因为仅仅只需要加一把锁而已,也没其他的事情需要操心,所以没有额外的内存消耗,缺点在于有锁就有死锁问题的发生,且只能串行执行性能肯定受到影响

**逻辑过期方案:**线程读取过程中不需要等待,性能好,有一个额外的线程持有锁去进行重构数据,但是在重构数据完成前,其他的线程只能返回之前的数据,且实现起来麻烦

在这里插入图片描述

缓存穿透

缓存穿透:缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库

常见的解决方案有两种:

  • 缓存空对象

    1. 优点:实现简单、维护方便

    2. 缺点:

      ​ 额外的内存消耗

      ​ 可能造成短期的不一致

  • 布隆过滤器

    1. 优点:内存占用比较少,没有多余key

    2. 缺点:

      ​ 实现复杂

      ​ 存在误判可能

**缓存空对象思路分析:**当我们客户端访问不存在的数据时,先请求redis,但是此时redis中没有数据,此时会访问到数据库,但是数据库中也没有数据,这个数据穿透了缓存,直击数据库,我们都知道数据库能够承载的并发不如redis这么高,如果大量的请求同时过来访问这种不存在的数据,这些请求就都会访问到数据库,简单的解决方案就是哪怕这个数据在数据库中也不存在,我们也把这个数据存入到redis 中去,这样,下次用户过来访问这个不存在的数据,那么在redis中也能找到这个数据就不会进入到缓存了

**布隆过滤:**布隆过滤器其实采用的是哈希思想来解决这个问题,通过一个庞大的二进制数组,走哈希思想去判断当前这个要查询的这个数据是否存在,如果布隆过滤器判断存在,则放行,这个请求会去访问redis,哪怕此时redis中的数据过期了,但是数据库中一定存在这个数据,在数据库中查询出来这个数据后,再将其放入到redis中,

假设布隆过滤器判断这个数据不存在,则直接返回

这种方式优点在于节约内存空间,存在误判,误判原因在于:布隆过滤器走的是哈希思想,只要哈希思想,就可能存在哈希冲突

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeMonkey-D

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值