1 Redis快速实战
1.1 缓存原理与设计
1.1.1 缓存基本思想
1.1.2 缓存的优势、代价
1.1.3 缓存的读写模式
缓存有三种读写模式
1.1.3.1 Cache Aside Pattern(常用)
Cache Aside Pattern(旁路缓存),是最经典的缓存+数据库读写模式。
读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
1.1.3.2 Read/Write Through Pattern
应用程序只操作缓存,缓存操作数据库。
Read-Through(穿透读模式/直读模式):应用程序读缓存,缓存没有,由缓存回源到数据库,并写入
缓存。(guavacache) Write-Through(穿透写模式/直写模式):应用程序写缓存,缓存写数据库。
该种模式需要提供数据库的handler,开发较为复杂
1.1.3.3 Write Behind Caching Pattern
应用程序只更新缓存。
缓存通过异步的方式将数据批量或合并后更新到DB中
不能时时同步,甚至会丢数据
1.1.4 缓存架构的设计思路
缓存的整体设计思路包括:
2、数据类型
简单数据类型
Value是字符串或整数或二进制
Value的值比较大(大于100K)
只进行setter和getter
可采用Memcached
Memcached纯内存缓存,多线程 K-V
复杂数据类型
Value是hash、set、list、zset
需要存储关系,聚合,计算
可采用Redis
3、要做集群
分布式缓存集群方案(Redis)
codis
哨兵+主从
RedisCluster
4、缓存的数据结构设计
1、与数据库表一致
数据库表和缓存是一一对应的
缓存的字段会比数据库表少一些
缓存的数据是经常访问的
用户表,商品表
2、与数据库表不一致
需要存储关系,聚合,计算等
比如某个用户的帖子、用户的评论。
1.2 Redis数据类型选择和应用场景
Redis是一个Key-Value的存储系统,使用ANSI C语言编写。
key的类型是字符串。
value的数据类型有:
常用的:string字符串类型、list列表类型、set集合类型、sortedset(zset)有序集合类型、hash类型。
不常见的:bitmap位图类型、geo地理位置类型。
Redis5.0新增一种:stream类型
注意:Redis中命令是忽略大小写,(set SET),key是不忽略大小写的 (NAME name)
1.2.1 string
1.2.2 list
应用场景:
1、作为栈或队列使用
列表有序可以作为栈和队列使用
2、可用于各种列表,比如用户列表、商品列表、评论列表等。
1.2.3 set集合类型
应用场景:
适用于不能重复的且不需要顺序的数据结构
比如:关注的用户,还可以通过spop进行随机抽奖
1.2.4 sortedset有序集合类型
SortedSet(ZSet) 有序集合: 元素本身是无序不重复的
每个元素关联一个分数(score)
可按分数排序,分数可重复
常见操作命令如下表:
应用场景:
由于可以按照分值排序,所以适用于各种排行榜。比如:点击排行榜、销量排行榜、关注排行榜等。
1.2.5 hash类型(散列表)
应用场景:
对象的存储 ,表数据的映射
1.2.6 bitmap位图类型
bitmap是进行位操作的
通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。
bitmap本身会极大的节省储存空间。
常见操作命令如下表:
1.2.7 geo地理位置类型
geo是Redis用来处理位置信息的。在Redis3.2中正式使用。主要是利用了Z阶曲线、Base32编码和
geohash算法
geohash算法
应用场景:
1、记录地理位置
2、计算距离
3、查找"附近的人"
2 Redis扩展功能
2.1 发布与订阅
2.2 事务
所谓事务(Transaction) ,是指作为单个逻辑工作单元执行的一系列操作
2.3 Lua脚本
lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用
程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua应用场景:游戏开发、独立应用脚本、Web应用脚本、扩展和数据库插件。
nginx上使用lua 实现高并发
OpenRestry:一个可伸缩的基于Nginx的Web平台,是在nginx之上集成了lua模块的第三方服务器
OpenRestry是一个通过Lua扩展Nginx实现的可伸缩的Web平台,内部集成了大量精良的Lua库、第三
方模块以及大多数的依赖项。
用于方便地搭建能够处理超高并发(日活千万级别)、扩展性极高的动态Web应用、Web服务和动态网
关。
功能和nginx类似,就是由于支持lua动态脚本,所以更加灵活。
OpenRestry通过Lua脚本扩展nginx功能,可提供负载均衡、请求路由、安全认证、服务鉴权、流量控
制与日志监控等服务。
类似的还有Kong(Api Gateway)、tengine(阿里)
2.4 慢查询日志
我们都知道MySQL有慢查询日志
Redis也有慢查询日志,可用于监视和优化查询
config set的方式可以临时设置,redis重启后就无效
config set slowlog-log-slower-than 微秒
config set slowlog-max-len 条数
2.5 监视器
Redis客户端通过执行MONITOR命令可以将自己变为一个监视器,实时地接受并打印出服务器当前处理
的命令请求的相关信息。
此时,当其他客户端向服务器发送一条命令请求时,服务器除了会处理这条命令请求之外,还会将这条
命令请求的信息发送给所有监视器。
2.5.1 Redis监控平台
3 Redis核心原理
3.1 缓存过期和淘汰策略
expire数据结构
在Redis中可以使用expire命令设置一个键的存活时间(ttl: time to live),过了这段时间,该键就会自动
被删除。
删除策略
Redis的数据删除有定时删除、惰性删除和主动删除三种方式。
Redis目前采用惰性删除+主动删除的方式。
定时删除
在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除
操作。
需要创建定时器,而且消耗CPU,一般不推荐使用。
惰性删除
在key被访问时如果发现它已经失效,那么就删除它。
调用expireIfNeeded函数,该函数的意义是:读取数据之前先检查一下它有没有失效,如果失效了就删
除它。
数据淘汰机制
3.2 通讯协议及事件处理机制
3.2.1 通信协议
telnet和redis-cli 发出的命令 都属于该种模式
特点:
有问有答
耗时在网络传输命令
性能较低
双工的请求响应模式(pipeline)
批量请求,批量响应
请求响应交叉进行,不会混淆(TCP双工)
3.2.2 事件处理机制
Redis服务器是典型的事件驱动系统。
MVC : java 上层调下层
事件驱动: js
Redis将事件分为两大类:文件事件和时间事件。
文件事件即Socket的读写事件,也就是IO事件。 file descriptor (文件描述符)
客户端的连接、命令请求、数据回复、连接断开
socket
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据。
Reactor
Redis事件处理机制采用单线程的Reactor模式,属于I/O多路复用的一种常见模式。
IO多路复用( I/O multiplexing )指的通过单个线程管理多个Socket。
4种IO多路复用模型与选择
select,poll,epoll、kqueue都是IO多路复用的机制。
I/O多路复用就是通过一种机制,一个进程可以监视多个描述符(socket),一旦某个描述符就绪(一
般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
时间事件
时间事件分为定时事件与周期事件:
一个时间事件主要由以下三个属性组成:
id(全局唯一id)
when (毫秒时间戳,记录了时间事件的到达时间)
timeProc(时间事件处理器,当时间到达时,Redis就会调用相应的处理器来处理事件)
4 Redis企业实战
4.1 架构设计
4.1.1 组件选择/多级
缓存的设计要分多个层次,在不同的层次上选择不同的缓存,包括JVM缓存、文件缓存和Redis缓存
文件缓存
这里的文件缓存是基于http协议的文件缓存,一般放在nginx中。
因为静态文件(比如css,js, 图片)中,很多都是不经常更新的。nginx使用proxy_cache将用户的请
求缓存到本地一个目录。下一个相同请求可以直接调取缓存文件,就不用去请求服务器了。
Redis缓存
分布式缓存,采用主从+哨兵或RedisCluster的方式缓存数据库的数据。
在实际开发中
作为数据库使用,数据要完整
作为缓存使用
作为Mybatis的二级缓存使用
4.2 缓存问题
4.2.1 缓存穿透
4.2.2 缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如
DB)带来很大压力。
突然间大量的key失效了或redis重启,大量访问数据库,数据库崩溃
解决方案:
1、 key的失效期分散开 不同的key设置不同的有效期
2、设置二级缓存(数据不一定一致)
3、高可用(脏读)
4.2.3 缓存击穿
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热 点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决方案:
1、用分布式锁控制访问的线程
使用redis的setnx互斥锁先进行判断,这样其他线程就处于等待状态,保证不会有大并发操作去操作数据库。
2、不设超时时间,volatile-lru 但会造成写一致问题
当数据库数据发生更新时,缓存中的数据不会及时更新,这样会造成数据库中的数据与缓存中的数据的
不一致,应用会从缓存中读取到脏数据。可采用延时双删策略处理
4.2.4 数据不一致
4.2.5 数据并发竞争
这里的并发指的是多个redis的client同时set 同一个key引起的并发问题。
多客户端(Jedis)同时并发写一个key,一个key的值是1,本来按顺序修改为2,3,4,最后是4,但是顺
序变成了4,3,2,最后变成了2。
第二种方案:利用消息队列
在并发量过大的情况下,可以通过消息中间件进行处理,把并行读写进行串行化。
把Redis的set操作放在队列中使其串行化,必须的一个一个执行。
4.2.6 Hot Key
4.2.7 Big Key
4.2.8 缓存与数据库一致性
4.2.9 分布式锁
4.2.9.1 分布式锁特性
分布式锁的实际应用
4.2.10 分布式集群架构中的session分离
传统的session是由tomcat自己进行维护和管理,但是对于集群或分布式环境,不同的tomcat管理各自的session,很难进行session共享,通过传统的模式进行session共享,会造成session对象在各个tomcat之间,通过网络和Io进行复制,极大的影响了系统的性能。
可以将登录成功后的Session信息,存放在Redis中,这样多个服务器(Tomcat)可以共享Session信息。
利用spring-session-data-redis(SpringSession),可以实现基于redis来实现的session分离。这个知识点在讲Spring的时候可以讲过了,这里就不再赘述了
5 Redis高可用方案
“高可用性”(High Availability)通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性。
CAP的AP模型,单机的Redis是无法保证高可用性的,当Redis服务器宕机后,即使在有持久化的机制下也无法保证不丢失数据。所以我们采用Redis多机和集群的方式来保证Redis的高可用性。
单进程+单线程 + 多机 (集群)
5.1 主从复制
Redis支持主从复制功能,可以通过执行slaveof(Redis5以后改成replicaof)或者在配置文件中设置
slaveof(Redis5以后改成replicaof)来开启复制功能。
5.2 哨兵模式
哨兵(sentinel)是Redis的高可用性(High Availability)的解决方案:
由一个或多个sentinel实例组成sentinel集群可以监视一个或多个主服务器和多个从服务器。
当主服务器进入下线状态时,sentinel可以将该主服务器下的某一从服务器升级为主服务器继续提供服
务,从而保证redis的高可用性。
5.3 集群与分区
分区是将数据分布在多个Redis实例(Redis主机)上,以至于每个实例只包含一部分数据。