中小型研发团队架构实践五:Redis快速入门及应用
Redis的使用难吗?不难,Redis用好容易吗?不容易。Redis的使用虽然不难,但与业务结合的应用场景特别多、特别紧,用好并不容易。我们希望通过一篇文章及Demo,即可轻松、快速入门并学会应用。
一、Redis 简介
Redis是一个开源的Key-Value存储,但又不仅仅是Key-Value存储,用官网上的话来说,Redis是一个数据结构存储,可用作数据库、缓存和消息中间件。相对于传统的Key-Value存储Memcached来说,Redis具有如下特点:
- 速度快
- 丰富的数据结构,除String之外,还有List、Hash、Set、Sorted Set
- 单线程,避免了线程切换和锁的性能消耗
- 原子操作
- 可持久化(RDB与AOF)
- 发布/订阅
- 支持Lua脚本
- 分布式锁
- 事务
- 主从复制与高可用(Redis Sentinel)
- 集群(3.0版本以上)
( 备注:
Redis中数据存储模式有2种:cache-only,persistence:
cache-only:即只做为“缓存”服务,不持久数据,数据在服务终止后将消失,此模式下也将不存在“数据恢复”的手段,是一种安全性低/效率高/容易扩展的方式;
persistence:即为内存中的数据持久备份到磁盘文件,在服务重启后可以恢复,此模式下数据相对安全;
对于persistence持久化存储,Redis提供了两种持久化方法:
Redis DataBase(简称RDB)
Append-only file(简称AOF)
RDB:是在某个时间点将数据写入一个临时文件,持久化结束后,用这个临时文件替换上次持久化的文件,达到数据恢复。
1)优点:使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了redis的高性能
2)缺点:RDB是间隔一段时间进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨的时候
AOF:将“操作 + 数据”以格式化指令的方式追加到操作日志文件的尾部,在append操作返回后(已经写入到文件或者即将写入),才进行实际的数据变更,“日志文件”保存了历史所有的操作过程;当server需要数据恢复时,可以直接replay此日志文件,即可还原所有的操作过程。AOF相对可靠,它和mysql中bin.log、apache.log、zookeeper中txn-log简直异曲同工。AOF文件内容是字符串,非常容易阅读和解析。
1)优点:可以保持更高的数据完整性,如果设置追加file的时间是1s,如果redis发生故障,最多会丢失1s的数据;且如果日志写入不完整支持redis-check-aof来进行日志修复;AOF文件没被rewrite之前(文件过大时会对命令进行合并重写),可以删除其中的某些命令(比如误操作的flushall)。
2)缺点:AOF文件比RDB文件大,且恢复速度慢。
)
二、Redis 数据结构
1、String
这是最简单的Redis类型。如果只使用这种类型,Redis就像一个可持久化的Memcached服务器。
2、List
Redis的List是基于双向链表实现的,可以支持反向查找和遍历。
常用案例:聊天系统、社交网络中获取用户最新发表的帖子、简单的消息队列、新闻的分页列表、博客的评论系统。
3、Hash
Hash是一个String类型的field和value之间的映射表,请见下图,类似于.NET中的Hashtable和Dictionary。主要用来存储对象,可以避免序列化的开销和并发修改控制的问题。
4、Set
Set也是一个列表,不过它的特殊之处在于它是可以自动排重的:当需要存储一个列表数据,而又不希望出现重复的时候,Set是一个很好的选择(比如ID的集合)。并且Set提供了判断某个成员是否在一个Set集合内的接口,这也是List所没有的。
5、Sorted Set
Sorted Set和Set的使用场景类似,区别是Sorted Set会根据提供的score参数来进行自动排序。当你需要一个有序的并且不重复的集合列表,那么就可以选择Sorted Set数据结构。常用案例:游戏中的排行榜。
三、 Redis 重要特性
以下特性请重点看管道和事务。
1、管道
Redis管道是指客户端可以将多个命令一次性发送到服务器,然后由服务器一次性返回所有结果。管道技术在批量执行命令的时候可以大大减少网络传输的开销,提高性能。
2、事务
Redis事务是一组命令的集合。一个事务中的命令要么都执行,要么都不执行。如果命令在运行期间出现错误,不会自动回滚。
管道与事务的区别:管道主要是网络上的优化,客户端缓冲一组命令,一次性发送到服务器端执行,但是并不能保证命令是在同一个事务里面执行;而事务是原子性的,可以确保命令执行的时候不会有来自其他客户端的命令插入到命令序列中。
3、分布式锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作,如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。
4、地理信息
从Redis 3.2版本开始,新增了地理信息相关的命令,可以将用户给定的地理位置信息(经纬度)存储起来,并对这些信息进行操作。
四、 使用方法
步骤1、在需要使用Redis的项目中引用FxCommon.dll和Redis.dll。
步骤2、在App.config或Web.config文件中添加如下配置:
1 <add key="RedisServerIP" value="redis:uuid845tylabc123@139.198.13.12:4125"/>
2 <!-- 提供的 Redis 环境是单机版配置。如果 Redis 是主从配置,则还需设置 RedisSlaveServerIP-->
3 <!--<add key="RedisSlaveServerIP" value="redis:uuid845tylabc123@139.198.13.13:4125"/>-->
4
5 <!--Redis 数据库。如果不需要指定 Redis 数据库,就配置默认值 0-->
6 <add key="RedisDefaultDb" value="0"/>
步骤 3、使用 PooledRedisClientManager 类创建 Redis 连接池:
1 // 读取 Redis 主机 IP 配置信息
2 string[] redisMasterHosts = ConfigurationManager.ConnectionStrings["RedisServerIP"].ConnectionString.Split(',');
3
4 // 如果 Redis 服务器是主从配置,那么还需要读取 Redis Slave 机的 IP 配置信息
5 string[] redisSlaveHosts = null;
6 var slaveConnection = ConfigurationManager.ConnectionStrings["RedisSlaveServerIP"];
7 if (slaveConnection != null && !string.IsNullOrWhiteSpace(slaveConnection.ConnectionString))
8 {
9 string redisSlaveHostConfig = slaveConnection.ConnectionString;
10 redisSlaveHosts = redisSlaveHostConfig.Split(',');
11 }
12
13 // 读取 RedisDefaultDb 配置
14 int defaultDb = 0;
15 string defaultDbSetting = ConfigurationManager.AppSettings["RedisDefaultDb"];
16 if (!string.IsNullOrWhiteSpace(defaultDbSetting))
17 {
18 int.TryParse(defaultDbSetting, out defaultDb);
19 }
20
21 var redisClientManagerConfig = new RedisClientManagerConfig
22 {
23 MaxReadPoolSize = 50,
24 MaxWritePoolSize = 50,
25 DefaultDb = defaultDb
26 };
27
28 // 创建 Redis 连接池
29 Manager = new PooledRedisClientManager(redisMasterHosts, redisSlaveHosts, redisClientManagerConfig)
30 {
31 PoolTimeout = 2000,
32 ConnectTimeout = 500
33 };
步骤4、通过PooledRedisClientManager的实例获取Redis客户端,然后就可以开始通过Redis客户端的API进行操作。
五、其它
5.1、 Redis Key命名规范
Redis Key命名规范:AppID:KeyName。
可能有很多人习惯用英文状态的点号来作为AppID和KeyName的分隔符,而笔者建议使用冒号作为AppID和KeyName的分隔符,其原因是:这么写会使Redis Key会以AppID作为分类显示在Redis Desktop Manager中,方便你能够快速查到要查阅的Redis Key对应的Redis Value值,请见下图:
但如果使用英文状态的点号来作为分隔符的话,那么在Redis Desktop Manager中,Redis Key就不会被分类了,请见下图:
5.2、常见应用问题
- 缓存穿透处理:什么是缓存穿透?当根据Redis key在缓存中查询后,不存在对应Value,就应该会在后端系统如DB中去查找,该Key的并发请求量一旦变大,那么就会对DB造成很大的压力。解决办法有:a.前端风险控制,将恶意穿透情况排除在外;b.对查询结果为空的情况依然进行缓存,但缓存时间会设置得很短,一般是几分钟。
- 缓存雪崩处理:什么是缓存雪崩?当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。解决办法有:后端连接数限制,错误阈值限制,超时处理,缓存失效时间均匀分布,前端永不失效及后端主动更新。
- 缓存时长:策略定位复杂,需要多维度的计算。
- 缓存失效:按时失效,事件失效,后端主动更新。
- 缓存Key:Hash、规则、前缀+Hash,异常情况可人工干预。
- Lua脚本:服务端批量处理及事务能力,有条件逻辑的可扩展脚本。使用它的好处有:减少网络开销、原子操作、可复用。
- Limit:可滑动时间窗口,如应用于Session,Memcached需每次传Key和Value。
六、Demo 下载及更多资料
- RedisDemo下载地址:https://github.com/das2017/RedisDemo
- RedisDesktopManage下载地址:https://redisdesktop.com/
- Redis官网:https://redis.io/
- ServiceStack.Redis客户端:https://github.com/ServiceStack/ServiceStack.Redis
- Redis命令大全:http://www.redis.cn/commands.html