数据结构
字符串String、字典Hash、列表List、集合Set、有序集合Sorted
5.0新特性:Stream
缓存类型
缓存是高并发场景下提高热点数据访问性能的一个有效手段,在开发项目时会经常使用到
缓存的类型分为:本地缓存、分布式缓存和多级缓存
本地缓存
本地缓存就是在进程中的内存中进行缓存,比如我们的jvm堆中,可以用 **LRUMap** 来实现,也可以使用 **Ehcache** 这样的工具来实现。
本地缓存是内存访问,没有原创交互开销,性能最好,但是受限于单机容量,一般缓存较小且无法扩展。
分布式缓存
分布式缓存一般都具有良好的水平扩展能力,对较大数据量的场景也能应付自如。缺点就是需要进行远程请求,性能不如本地缓存。
多级缓存
为了平衡这种情况,实际业务中一般采用**多级缓存**,本地缓存只保存访问频率最高的部分热点数据,其他的热点数据放在分布式缓存中。
在目前的一线大厂中,这也是最常用的缓存方案,单考单一的缓存方案往往难以撑住很多高并发的场景。
淘汰策略
当存储的数据超过缓存容量时,需要对缓存的数据进行剔除
FIFO :淘汰最早数据
LRU 剔除最近最少使用的
LFU 剔除最近使用频率最低的数据的几种策略
memcache与redis的特点
Memcache特点
1、 处理请求时使用多线程异步IO的方式,可以合理利用CPU多核的优势,性能非常优秀
2、 Kv存储
3、 内存存储,没有持久化
4、 不提供主从同步
5、 缓存失效的策略采用延迟失效,就是当再次使用数据时检查是否失效
它的限制:
Key不超过250字节
Value 不超过1M字节
最大失效时间时30天
内存结构
Memcache通过slab Allocator管理内存,主要用来解决频繁 malloc/free 会产生内存碎片的问题,Slab Allocator创建Slab时的参数有三个:Chunk大小的增长因子、Chunk大小的初始值、Page的大小。在运行时会根据要保存的对象的大小来逐渐创建Slab
A)Slab:MC把内存分为许多不同类型的 Slab,每种类型的Slab用来保存不同大小的对象。
B) Page:每个Slab由若干Page组成,不同Slab的Page,默认大小都一样是1M(这也是默认MC存储对象不能超过1M的原因)。
C) Chunk:每个Page内划分为许多Chunk,就是实际存储对象的空间,不同类型Slab中的Chunk大小不同,当保存一对象时,MC会根据对象大小选择最合适的Chunk来存储,减少空间浪费。
钙化问题
考虑这样一个场景,使用 MC 来保存用户信息,假设单个对象大约 300 字节。这时会产生大量的 384 字节大小的 Slab。运行一段时间后,用户信息增加了一个属性,单个对象的大小变成了 500 字节,这时再保存对象需要使用 768 字节的 Slab,而MC 中的容量大部分创建了384 字节的 Slab,所以 768 的 Slab 非常少。这时虽然 384 Slab 的内存大量空闲,但 768 Slab 还是会根据 LRU 算法频繁剔除缓存,导致 MC 的剔除率升高,命中率降低。这就是所谓的 MC 钙化问题。
如何解决钙化问题?
解决钙化问题可以开启 MC 的 Automove 机制,每 10s 调整 Slab。也可以分批重启 MC 缓存,不过要注意重启时要进行一定时间的预热,防止雪崩问题。
在使用 Memcached 时,最好计算一下数据的预期平均长度,调整 growth factor,以获得最恰当的设置,避免内存的大量浪费。
redis的特点
1、 单线程模式处理。
A) 采用了非阻塞的异步事件处理机制
B) 缓存数据都是内存操作IO时间不会太长,单线程可以避免上下文切换产生的代价
2、 数据可以持久化
3、 支持多种数据类型.KV 、list、set、sorted set 、hash
4、 提供主从同步机制,以及集群部署能力,能够提供高可用服务
String的实际应用场景
缓存功能
计数器
共享用户session
Hash:类似Map 将一个对象(没有嵌套其他的对象)缓存到缓存中。
List** 是有序列表
比如可以通过 **List** 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的东西。
比如可以通过 **lrange** 命令,读取某个闭区间内的元素,可以基于 **List** 实现分页查询,这个是很棒的一个功能,基于 **Redis** 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西,性能高,就一页一页走。
比如可以搞个简单的消息队列,从 **List** 头怼进去,从 **List** 屁股那里弄出来。
**List**本身就是我们在开发过程中比较常用的数据结构了,热点数据更不用说了。
- **消息队列:Redis**的链表结构,可以轻松实现阻塞队列,可以使用左进右出的命令组成来完成队列的设计。比如:数据的生产者可以通过**Lpush**命令从左边插入数据,多个数据消费者,可以使用**BRpop**命令阻塞的“抢”列表尾部的数据。
- 文章列表或者数据分页展示的应用。
比如,我们常用的博客网站的文章列表,当用户量越来越多时,而且每一个用户都有自己的文章列表,而且当文章多时,都需要分页展示,这时可以考虑使用**Redis**的列表,列表不但有序同时还支持按照范围内获取元素,可以完美解决分页查询功能。大大提高查询效率。
Set** 是无序集合,会自动去重的那种。
交集(共同好友)
Sorted set** 是排序的 **Set**,去重但可以排序,写进去的时候给一个分数,自动根据分数排序。
排行榜
用**Sorted Sets**来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。
redis持久化
**RDB** 把整个 Redis 的数据保存在单一文件中,比较适合用来做灾备,但缺点是快照保存完成之前如果宕机,这段时间的数据将会丢失,另外保存快照时可能导致服务短时间不可用。
**AOF** 对日志文件的写入操作使用的追加模式,有灵活的同步策略,支持每秒同步、每次修改同步和不同步,缺点就是相同规模的数据集,AOF 要大于 RDB,AOF 在运行效率上往往会慢于 RDB。
Key失效机制
Redis的key可以设置过期时间,过期后redis采用主动和被动结合的失效机制,一个是和MC一样在访问时触发被动删除,另一个时定期的主动删除。
定期+惰性+内存淘汰