什么是Redis

Redis是一个基于BSD开源的项目,用于把结构化的数据放入到内存中,的存储系统,可以把它作为数据库,缓存和中间件使用。
支持复制,Lua脚本,LRU,事物等等功能。
通过redis 哨兵可以实现高可用,自动分片,事物,发布订阅,自动化故障转移。

来个需求

有一个热点新闻列表的api,http://api.xxx.com/hot-news 根据这个新闻列表将会返回一个数据,每次都需要大约2秒左右才能返回结果。
如何提升API消费者感知的性能呢?这里有个方案,服务端添加上cache-control:max-age=600 消费者可以缓存这个响应十分钟,
弊端:
1. 缓存生效的时间,得到旧数据。
2. 无视缓存直接访问,仍然需要两秒。

基于本机的缓存

API调用需要耗时2秒,主要原因: SQL获取热点耗时2秒,这里采用了一个简单的方法,把SQL查询结果直接缓存进入api服务器内存中,设置缓存有效时间为一分钟,后续一分钟直接读缓存,不需要花费时间执行SQL。
问题: 内存爆满。

服务端的Redis

继续演化,采用服务端的Redis实现。
把缓存直接放入到一个专门的服务器上,内存配置相当的大,使用Redis。使用一台单独的服务器作为Redis的服务器。API服务器的内存压力得以解决。
继续进行演化,这里演化进入下一阶段,进行持久化,和哨兵以及复制

持久化

单台的Redis会出现罢工的情况,导致缓存丢失,出现缓存雪崩,API服务器和数据库的压力一下子上来,Redis持久化这个时候就需要了,可以缓解缓存雪崩带来的影响,可以直接写入硬盘中,在redis重新启动的时候加载这些数据。

哨兵和复制

Redis服务器罢工肿么办,继续演化,这里进行备份一台,当一台挂的时候,另外一台直接出厂,哨兵可以管理多个Redis服务器,提供监控,提醒,以及自动故障转移的功能、

集群

单台服务器资源总是有上限的,CPU和IO使用主从复制,进行读写分离,一部分CPU和IO转移到从服务器。内存不能进行横向扩展,这里让每台服务器只负责其中的一部分,让这些所有的服务器构成一个整体,对外界进行消费。
Redis有两种分布式解决方案,都是基于Proxy的方式的,这里使用的都是twemproxy和codis独立处理分布式这部分逻辑。
其中tewmproxy采用的是增加中间层的方式解决

在计算机中,没有什么是不增加一个中间层就能解决掉的问题。

第二个方式是让redis服务器知道彼此的存在,通过重定向的机制引导客户端完成自己所需要的操作。比如客户端连接到一个redis服务器,说我要执行这个操作,发现没有办法执行,这个时候,直接重定向到另外一个服务器进行执行,让另外一个服务器去完成这个操作。这个时候需要的是,每台redis服务器都需要保存一套完整的redis的内容。否则无法完成寻找。

总结,对于分布式系统而言,需要一个内容来保存其能提供服务的信息,第一个是这部分信息单独处理,第二个是全部保存这部分信息。

总结

上方,根据一个接口的缓存来进行不断的演化,由最开始保存到客户端,到保存到一台服务器内存,到保存单台redis,到多台redis,各自解决了什么问题,详细的演化的过程。
最后演化到了一个高可用,去中心化,分布式的存储系统。

客户端的Redis

数据类型

Redis支持丰富的数据类型,从基础的string到复杂的常用的数据结构都支持。

string,list,set等

事物

每一个数据类型都有一个独立的命令操作,需要一次执行不止一个命令,需要同时成功or失败,所以事物诞生了。

Lua脚本

在事物基础上,如果需要执行更加复杂的操作,逻辑判断,获取缓存的时候,同时延长过期时间,这个时候需要使用Lua脚本。

管道

Redis 客户端和服务器连接基于TCP的,默认每次连接执行一个命令,管道允许一次连接来处理多条命令,从而节省TCP连接的开销。管道和事物的差异在于节省通信的开销,但是并不会保证原子性。

分布式锁

这里使用的是加锁给key,取锁,删除key。
由于需要保证一个原子性操作,这里使用LUA脚本实现。

SET resource_name my_random_value NX PX 30000

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end