Redis官网自行下载点我
先赞后看已成习惯
Rides学习之路
Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库,其具备如下特性:
- 完全基于内存运行,性能高效
- 数据结构简单,对数据操作也简单
- 支持分布式,理论上可以无限扩展
- key-value存储系统
- 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗
CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
使用多路I/O复用模型,非阻塞IO;这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程
使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制
,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求 - 开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API
相比于
其他数据库类型,Redis具备的特点是:
- C/S通讯模型
- 单进程单线程模型
- 丰富的数据类型
- 操作具有原子性
- 持久化
- 高并发读写
- 支持lua脚本
Redis的应用场景有哪些?
Redis 的应用场景包括:缓存系统(“热点”数据:高频读、低频写)、计数器、消息队列系统、排行榜、社交网络和实时系统
Redis 数据类型
-
Bitmap : 位图是支持按 bit 位来存储信息,可以用来实现 布隆过滤器(BloomFilter);
-
HyperLogLog: 供不精确的去重计数功能,比较适合用来做大规模数据的去重统计,例如统计 UV;
-
Geospatial:可以用来保存地理位置,并作位置距离计算或者根据半径计算位置等。有没有想过用Redis来实附近的人?或者计算最优地图路径?
以上三种是特殊类型,这三个其实也可以算作一种数据结构,如果只知道五种基础类型那只能拿60分,如果面试你能讲出高级用法,那就觉得你有点东西 -
字符类型:String
该类型是redis的最基础的数据结构,也是最经常使用到的类型。而且其他的四种类型多多少少都是在字符串类型的基础上构建的,所以String类型是redis的基础。String
类型的值最大能存储
512MB,这里的String类型可以是简单字符串、复杂的xml/json的字符串、二进制图像或者音频的字符串、以及可以是数字的字符串。
用法:缓存功能:String字符串是最常用的数据类型,不仅仅是Redis,各个语言都是最基本类型,因此,利用Redis作为缓存,配合其它数据库作为存储层,利用Redis支持高并发的特点,可以大大加快系统的读写速度、以及降低后端数据库的压力。
计数器:许多系统都会使用Redis作为系统的实时计数器,可以快速实现计数和查询的功能。而且最终的数据结果可以按照特定的时间落地到数据库或者其它存储介质当中进行永久保存。
共享用户Session:用户重新刷新一次界面,可能需要访问一下数据进行重新登录,或者访问页面缓存cookie,但是可以利用Redis将用户的Session集中管理,在这种模式只需要保证Redis的高可用,每次用户Session的更新和获取都可以快速完成。大大提高效率 -
哈希类型:Hash 该类型是由field和关联的value组成的map。其中,field和value都是字符串类型的。这个是类似 Map
的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是这个对象没嵌套其他的对象)给缓存在 Redis
里,然后每次读写缓存的时候,可以就操作 Hash 里的某个字段 -
列表类型:List 该类型是一个插入顺序排序的字符串元素集合, 基于双链表实现。比如可以通过 List
存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的东西。比如可以通过 lrange 命令,读取某个闭区间内的元素,可以基于
List 实现分页查询,这个是很棒的一个功能,基于 Redis
实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西,性能高,就一页一页走。比如可以搞个简单的消息队列,从 List 头怼进去,从
List 屁股那里弄出来。List本身就是我们在开发过程中比较常用的数据结构了,热点数据更不用说了。
消息队列:Redis的链表结构,可以轻松实现阻塞队列,可以使用左进右出的命令组成来完成队列的设计。比如:数据的生产者可以通过Lpush命令从左边插入数据,多个数据消费者,可以使用BRpop命令阻塞的“抢”列表尾部的数据。文章列表或者数据分页展示的应用。 -
集合类型:set Set类型是一种无顺序去重集合, 它和List类型最大的区别是:集合中的元素没有顺序, 且元素是唯一的。
Set类型主要应用于:在某些场景,如社交场景中,通过交集、并集和差集运算,通过Set类型可以非常方便地查找共同好友、共同关注和共同偏好等社交关系。 -
顺序集合类型:
ZSet是一种有序去重排序集合类型,每个元素都会关联一个double类型的分数权值,通过这个权值来为集合中的成员进行从小到大的排序。与Set类型一样,其底层也是通过哈希表实现的。有序集合的使用场景与集合类似,但是set集合不是自动有序的,而Sorted
set可以利用分数进行成员间的排序,而且是插入时就排序好。所以当你需要一个有序且不重复的集合列表时,就可以选择Sorted
set数据结构作为选择方案。
排行榜:有序集合经典使用场景。例如视频网站需要对用户上传的视频做排行榜,榜单维护可能是多方面:按照时间、按照播放量、按照获得的赞数等。
用Sorted Sets来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行
缓存穿透
描述:
- 缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求。由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。
解决方案:
- 接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
缓存击穿
描述:
- 缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
。
-
解决方案: 1、设置热点数据永远不过期。
2、接口限流与熔断,降级。重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些 服务
不可用时候,进行熔断,失败快速返回机制。3、布隆过滤器。bloomfilter就类似于一个hash
set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小,4、加互斥锁,互斥锁。
public static String getData(String key) throws InterruptedException {
//从Redis查询数据
String result = getDataByKV(key);
//参数校验
if (StringUtils.isBlank(result)) {
try {
//获得锁
if (reenLock.tryLock()) {
//去数据库查询
result = getDataByDB(key);
//校验
if (StringUtils.isNotBlank(result)) {
//插进缓存
setDataToKV(key, result);
}
} else {
//睡一会再拿
Thread.sleep(100L);
result = getData(key);
}
} finally {
//释放锁
reenLock.unlock();
}
}
return result;
}
缓存雪崩
描述:
- 缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,
缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。 - 解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
设置热点数据永远不过期。
Redis哈希槽的概念?
- Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽
Reids6种淘汰策略:
- noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存,
直接返回错误信息。大多数写命令都会导致占用更多的内存(有极少数会例外。 - allkeys-lru:所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
- volatile-lru:只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU)
的 key。 - allkeys-random:所有key通用; 随机删除一部分 key。
- volatile-random: 只限于设置了expire的部分; 随机删除一部分 key。
- volatile-ttl: 只限于设置了expire的部分; 优先删除剩余时间(time to live,TTL) 短的key。
Redis内存划分
-
数据 作为数据库,数据是最主要的部分;这部分占用的内存会统计在used_memory中。 进程本身运行需要的内存
Redis主进程本身运行肯定需要占用内存,如代码、常量池等等;这部分内存大约几兆,在大多数生产环境中与Redis数据占用的内存相比可以忽略。这部分内存不是由jemalloc分配,因此不会统计在used_memory中。 -
缓冲内存
缓冲内存包括客户端缓冲区、复制积压缓冲区、AOF缓冲区等;其中,客户端缓冲存储客户端连接的输入输出缓冲;复制积压缓冲用于部分复制功能;AOF缓冲区用于在进行AOF重写时,保存最近的写入命令。在了解相应功能之前,不需要知道这些缓冲的细节;这部分内存由jemalloc分配,因此会统计在used_memory中。 -
内存碎片
内存碎片是Redis在分配、回收物理内存过程中产生的。例如,如果对数据的更改频繁,而且数据之间的大小相差很大,可能导致redis释放的空间在物理内存中并没有释放,但redis又无法有效利用,这就形成了内存碎片。内存碎片不会统计在used_memory中
Reids三种不同删除策略
-
定时删除:****在设置键的过期时间的同时,创建一个定时任务,当键达到过期时间时,立即执行对键的删除操作
-
优点: 对内存友好,定时删除策略可以保证过期键会尽可能快地被删除,并释放国期间所占用的内存
-
缺点:
对cpu时间不友好,在过期键比较多时,删除任务会占用很大一部分cpu时间,在内存不紧张但cpu时间紧张的情况下,将cpu时间用在删除和当前任务无关的过期键上,影响服务器的响应时间和吞吐量 -
**定期删除:**每隔一点时间,程序就对数据库进行一次检查,删除里面的过期键,至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
-
优点:
定期删除策略每隔一段时间执行一次删除过期键操作,并通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响定时删除策略有效地减少了因为过期键带来的内存浪费 -
**惰性删除:**放任键过期不管,但在每次从键空间获取键时,都检查取得的键是否过期,如果过期的话,就删除该键,如果没有过期
-
优点:
对cpu时间友好,在每次从键空间获取键时进行过期键检查并是否删除,删除目标也仅限当前处理的键,这个策略不会在其他无关的删除任务上花费任何cpu时间 -
缺点:
对内存不友好,过期键过期也可能不会被删除,导致所占的内存也不会释放。甚至可能会出现内存泄露的现象,当存在很多过期键,而这些过期键又没有被访问到,这会可能导致它们会一直保存在内存中,造成内存泄露。
Redis的配置以及持久化方案有以下两种RDB方式和AOF方式
什么是RDB方式?
-
是RDB是对内存中数据库状态进行快照
RDB方式:将Redis在内存中的数据库状态保存到磁盘里面,RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态(默认下,持久化到dump.rdb文件,并且在redis重启后,自动读取其中文件,据悉,通常情况下一千万的字符串类型键,1GB的快照文件,同步到内存中的
时间是20-30秒) RDB的生成方式: 1、执行命令手动生成
有两个Redis命令可以用于生成RDB文件,一个是SAVE,另一个是BGSAVE
SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求,BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求,创建RDB文件结束之前,客户端发送的BGSAVE和SAVE命令会被服务器拒绝 -
2、通过配置自动生成
可以设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令,可以通过save选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行BGSAVE命令
例如: save 900 1 save 300 10 save 60 10000
那么只要满足以下三个条件中的任意一个,BGSAVE命令就会被执行 服务器在900秒之内,对数据库进行了至少1次修改
服务器在300秒之内,对数据库进行了至少10次修改 服务器在60秒之内,对数据库进行了至少10000次修改
什么是AOF方式?
AOF持久化方式在redis中默认是关闭的,需要修改配置文件开启该方式。 AOF:把每条命令都写入文件,类似mysql的binlog日志
AOF方式:是通过保存Redis服务器所执行的写命令来记录数据库状态的文件。 AOF文件刷新的方式,有三种:
- appendfsync always 每提交一个修改命令都调用fsync刷新到AOF文件,非常非常慢,但也非常安全
- appendfsync everysec 每秒钟都调用fsync刷新到AOF文件,很快,但可能会丢失一秒以内的数据
- appendfsync no - 依靠OS进行刷新,redis不主动刷新AOF,这样最快,但安全性就差
默认并推荐每秒刷新,这样在速度和安全上都做到了兼顾
AOF数据恢复方式 服务器在启动时,通过载入和执行AOF文件中保存的命令来还原服务器关闭之前的数据库状态,具体过程: 载入AOF文件
创建模拟客户端 从AOF文件中读取一条命令 使用模拟客户端执行命令 循环读取并执行命令,直到全部完成
如果同时启用了RDB和AOF方式,AOF优先,启动时只加载AOF文件恢复数据
Redis事务的概念
事务的基本命令
- multi 标记一个事务的开始
- exec 执行所有事务块内的命令
- discard 取消事务,放弃执行事务块内的所有命令
事务特性
事务中的命令都是串行执行的。
事务执行期间redis不会再对其它的客户端提供任何服务,从而保证事务中的命令能够原子化执行。
- 单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis
事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
redis 127.0.0.1:6379> MULTI //开启事务 OK redis 127.0.0.1:6379> SET book-name "Mastering C++ in 21 days" QUEUED redis 127.0.0.1:6379> GET book-name QUEUED redis 127.0.0.1:6379> SADD tag "C++" "Programming" "Mastering Series" QUEUED redis 127.0.0.1:6379> SMEMBERS tag QUEUED redis 127.0.0.1:6379> EXEC //提交事务 1) OK 2) "Mastering C++ in 21 days" 3) (integer) 3 4) 1) "Mastering Series" 2) "C++" 3) "Programming"
Redis集群,哨兵,主从方面可以阅读此文章redis高可用文章是笔者收集整理因而只做阅读使用,如有不对还请指教,万万手下留情。如果可以还请三连一波动动发财小手



