使用Redis进行开发

一、常见应用场景

1.会话存储

在现代的架构中,通常多个服务器位于一个或多个负载均衡器之后。会话(Session)通常需要存储在外部存储系统中。如果有一个服务器宕机,其他的服务器可以从外部存储中获取会话并继续服务。因为与关系数据库相比Redis的访问延迟非常低,所以使用Redis来保存会话数据堪称是一种完美的会话存储机制。并且Redis中对键过期的支持可以天然地用于会话的超时管理。

2.分析

还可以用于统计分析的场景。例如,如果想要计算某个功能被使用了多少次,或者想要计算某个API被调用的次数,那么简单地使用INCR命令增加某功能的被点击次数或某API的被调用次数的计数器即可;甚至还可以结合Lua脚本限制公开 API 的请求次数(将 API 的最大请求数限制在每个 IP 地址每秒钟十个之内)。因为所有的Redis命令都是原子的,所以无需担心竞态。基于诸如哈希、有序集合和HyperLoglog等数据类型,还可以构建其他更高级的计数器或统计数据捕获系统。

3.排行榜

利用有序集合(Sorted Set),就可以轻松地实现一个排行榜。例如:可以为投票目标创建一个有序集合并将用户的投票数作为权重。因此,使用ZREVRANGE命令就能够按照投票目标的受欢迎程度返回投票目标的列表。同样的功能也可以在关系数据库中实现,但是SQL查询要比Redis查询慢得多。

4.队列

利用列表(List)的PUSH/POP命令(阻塞类型)就能实现任务队列。例如:先使用BLPOP命令从任务列表左端移除并获取第一个任务(没有的时候则阻塞了),相当于获取任务;然后使用RPUSH命令从任务列表右端插入一个新任务,相当于分配任务;此时刚才排队获取任务的客户端就能第一时间拿到新任务。

5.最新的N个记录

假设我们想要获取最近新签的10个合同,通常采用一个SQL查询:select top 10 * from contract order by signtime desc

而采用Redis解决则可以先创建一个列表,新签的合同采用LPUSH命令插入,然后再执行LTRIM命令只保留10个元素。

6.缓存

例如:在查询关系数据库之前,我们首先在Redis中查找记录。如果在Redis中找不到记录,则查询关系数据库并将记录放到Redis中。在向关系数据库写入时,我们也同时将记录写入Redis。为了限制缓存的大小,可以对缓存中的记录设置过期时间或应用诸如最近最少使用(LeastRecentlyUsed,LRU)的收回策略。

二、使用正确的数据类型

时间-空间的平衡

例如:10000个用户信息(id,name,sex,age,registertime)

1.首先存储为字符串类型的键值对,内存消耗4.04M;

2.改为哈希类型,内存消耗2.29M左右,此时节省了约43%((4.04-2.29)/4.04)的内存空间;

3.改为将一个用户信息存储为一个json字符串,内存消耗2.29M,与哈希类型所消耗内存几乎相同(键的数量并没有减少);

4.使用Lua脚本,利用msgpack库(https://msgpack.org/)序列化原始json字符串,内存消耗2.06M,此时节省了约49%((4.04-2.06)/4.04)的内存空间;

5.

又例如:计数时可以使用集合、位图或HyperLogLog

官方一些内存优化的案例:https://redis.io/topics/memory-optimization

三、使用正确的Redis API

保证性能

1.尽量将数据操作组合在一起来降低往返延时(RTT),采用管道,采用批量处理的API;

2.因为Redis是单线程的数据存储服务,所以应该多考虑API的时间复杂度,避免阻塞服务器。

四、使用C#连接到Redis

通过VS(VisualStudio 2017)创建一个控制台应用程序

通过NuGet获取StackExchange.Redis

简单示例

使用管道(事务类似)

运行Lua脚本

local id = @key
local data = @arg
local dataSource = cjson.decode(data)

local retJson = redis.call('get', id)
if retJson == false then
    retJson = {}
else
    retJson = cjson.decode(retJson)
end

for k,v in pairs(dataSource) do
    retJson[k] = v
end
redis.call('set', id, cjson.encode(retJson))
return redis.call('get', id)


五、使用Python连接到Redis

Python(https://www.python.org/),我此时使用的是Python3.7.1

通过PyPI安装redis-py:执行命令pip install redis

通过VS(VisualStudio 2017)创建一个Python应用程序

简单示例

示例程序输出结果如下:

使用管道

输出结果将显示对应响应的列表

运行Lua脚本

local id = KEYS[1]
local data = ARGV[1]
local dataSource = cjson.decode(data)

local retJson = redis.call('get', id)
if retJson == false then
    retJson = {}
else
    retJson = cjson.decode(retJson)
end

for k,v in pairs(dataSource) do
    retJson[k] = v
end
redis.call('set', id, cjson.encode(retJson))
return redis.call('get', id)

执行结果输出的是更新后的json

六、使用Redis编写MapReduce作业

七、使用Redis编写Spark作业