内容: Redis 基本使用及百亿数据量中的使用技巧分享
记录人: 依乐祝
热场准备
熟悉的开场白, 大家晚上好啊, 今天给大家分享的是 Redis 在大数据中的使用, 可能真正讲的是一些 Redis 的使用技巧, Redis 基本的一些东西.
首先给大家个地址, 源码以及实例都在里面, 当然今天的分享也是按照里面的实例来进行的, 大家可以先进行下载.
http://git.newlifex.com/NewLife/NewLife.Redis
当然这里也附上 Redis 的下载地址:
- Windows: https://github.com/MicrosoftArchive/redis/releases
- http://x.newlifex.com/Redis-x64-3.2.100.msi
- Linux: https://redis.io/download
开始
Redis 封装架构讲解
实际上 NewLife.Redis 是一个完整的 Redis 协议的功能的实现, 但是 Redis 的核心功能并没有在这里面, Redis 的核心功能的实现是在 NewLife.Core 里面. 这里可以打开看一下, NewLife.Core 里面有一个 NewLife.Caching 的命名空间, 里面有一个 Redis 类里面实现了 Redis 的基本功能, 另一个类是 RedisClient 是 Redis 的客户端. Redis 的核心功能就是有这两个类实现. RedisClient 代表着 Redis 客户端对服务器的一个连接. Redis 真正使用的时候有一个 Redis 连接池, 里面存放着很多个 RedisClient 对象.
所以我们 Redis 的封装有两层, 一层是 NewLife.Core 里面的 Redis 以及 RedisClient. 另一层就是 NewLife.Redis. 这里面的 FullRedis 是对 Redis 的实现了 Redis 的所有的高级功能. 这里你也可以认为 NewLife.Redis 是 Redis 的一个扩展.
Test 实例讲解 Redis 的基本使用
实例
打开 Program.cs 看下代码
这里 XTrace.UseConsole(); 是向控制台输出日志, 方便调试使用查看结果.
接下来看第一个例子 Test1. 具体的我都在代码中进行了注释, 大家可以看下
- static void Test1()
- {
- var ic = Redis.Create("127.0.0.1:6379", 3);// 创建 Redis 实例, 得到 FullRedis 对象
- //var ic = new FullRedis();// 另一种实例化的方式
- //ic.Server = "127.0.0.1:6379";
- //ic.Db = 3;//Redis 中数据库
- ic.Log = XTrace.Log;// 显示日志, 进行 Redis 操作把日志输出, 生产环境不用输出日志
- // 简单操作
- Console.WriteLine("共有缓存对象 {0} 个", ic.Count);// 缓存对象数量
- ic.Set("name", "大石头");//Set K-V 结构, Set 第二个参数可以是任何类型
- Console.WriteLine(ic.Get<String>("name"));//Get 泛型, 指定获取的类型
- ic.Set("time", DateTime.Now, 1);// 过期时间秒
- Console.WriteLine(ic.Get<DateTime>("time").ToFullString());
- Thread.Sleep(1100);
- Console.WriteLine(ic.Get<DateTime>("time").ToFullString());
- // 列表
- var list = ic.GetList<DateTime>("list");
- list.Add(DateTime.Now);
- list.Add(DateTime.Now.Date);
- list.RemoveAt(1);
- Console.WriteLine(list[list.Count - 1].ToFullString());
- // 字典
- var dic = ic.GetDictionary<DateTime>("dic");
- dic.Add("xxx", DateTime.Now);
- Console.WriteLine(dic["xxx"].ToFullString());
- // 队列
- var mq = ic.GetQueue<String>("queue");
- mq.Add(new[] { "abc", "g", "e", "m" });
- var arr = mq.Take(3);
- Console.WriteLine(arr.Join(","));
- // 集合
- var set = ic.GetSet<String>("181110_1234");
- set.Add("xx1");
- set.Add("xx2");
- set.Add("xx3");
- Console.WriteLine(set.Count);
- Console.WriteLine(set.Contains("xx2"));
- Console.WriteLine("共有缓存对象 {0} 个", ic.Count);
- }
Set 的时候如果是字符串或者字符数据的话 Redis 会直接保存起来(字符串内部机制也是保存二进制), 如果是其他类型会默认进行 JSON 序列化然后再保存起来
Get 的时候如果是字符串或者字符数据会直接获取, 如果是其他类型会进行 JSON 反序列化
Set 第三个参数过期时间单位是秒.
vs 调试小技巧, 按 F5 或者直接工具栏 "启动" 会编译整个解决方案会很慢 (VS 默认), 可以选中项目然后右键菜单选择调试 -> 启动新实例. 会只编译将会用到的项目, 这样对调试来说会快很多.
大家运行调试后可以看到控制台输出的内容: 向右的箭头 =》是 ic.Log=XTrace.Log 输出的日志
字典的使用: 对象的话需要把 JSON 全部取出来然后转换成对象, 而字典的话就可以直接取某个字段.
队列是 List 结构实现的, 使用场景可以上游数据太多, 下游处理不过来的时候, 那么就可以使用这个队列. 上游的数据发到队列, 然后下游漫漫的消费. 另一个应用, 夸语言的协同工作, 比方说其他语言实现的程序往队列里面塞数据, 然后另一种语言来进行消费处理. 哈, 这种方式类似 mq 的概念, 虽然有点 low, 但是也很好用.
集合, 用的比较多的是用在一个需要精确判断的去重功能. 像我们每天有三千万订单, 这三千万订单可以有重复, 这时候我想统计下一共有订单, 这时候直接数据库 group by 是不大可能的, 因为数据库中分了十几张表, 这里分享个实战经验: 比方说揽收, 商家发货了, 网点要把件收回来, 但是收回来之前网点不知道自己有多少货啊, 这时候我们做了一个功能, 也就是订单会发送到我们公司来, 我们会建一个 time_site 的 key 的集合, 而且集合本身有去重的功能, 而且我们可以很方便的通过 set.Count 功能来统计数量, 当件被揽收以后, 我们后台把这个件从集合中 Remove 掉. 然后这个 Set 中存在的就是网点还没有揽收的件, 这时候通过 Count 就会知道这个网点今天还有多少件没有揽收. 实际使用中这个数量比较大, 因为有几万个网点.
Redis 中布隆过滤器, 去重的, 面试的时候问的比较多
小经验分享:
数据库中不合法的时间处理: 判断时间中的年份, 是否大于 2000 年. 如果小于 2000 就认为不合法. 习惯大于小于号不习惯用等于号, 这样可以处理很多意外的数据
Set 的时候最好指定过期时间防止有些需要删除的数据, 我们忘记删了
Redis 异步尽量不用, 因为 Redis 延迟本身很小, 大概在 100us-200us, 再一个就是 Redis 本身是单线程的, 异步任务切换的耗时比网络耗时还要大.
List 用法: 物联网中数据上传, 量比较大时, 我们可以把这些数据先放在 Redis 的 List 中, 比如说一秒钟 1 万条, 然后再批量取出来然后批量插入数据库中. 这时候要设置好 key, 可以前缀 + 时间, 对于已经处理的 List 可以进行 remove 移除.
压力测试
接下来看第四个例子, 我们直接做压力测试, 代码如下:
- static void Main(String[] args)
- {
- XTrace.UseConsole();
- // 激活 FullRedis, 否则 Redis.Create 会得到默认的 Redis 对象
- FullRedis.Register();
- Test4();
- Console.ReadKey();
- }
- static void Test4()
- {
- var ic = Redis.Create("127.0.0.1:6379", 5);
- //var ic = new MemoryCache();
- ic.Bench();
- }
运行的结果如下图所示:
测试就是进行 get,set remove, 累加等的操作. 大家可以看到在我本机上轻轻松松的到了六十万, 多线程到时候甚至到了一百多万. 为什么会达到这么高的 ops 呢, 下面给大家说一下.
Bench 会分根据线程数分多组进行添删改压力测试.
rand 参数, 是否随机产生 key/value.
batch 批大小, 分批执行读写操作, 借助 GetAll/SetAll 进行优化.
Redis 中 NB 的函数来提升性能
上面的操作如果大家都掌握的基本算 Redis 入门了, 接下来进行进阶. 会了基本比别人更胜一筹了.
GetAll()与 SetAll()
GetAll: 比方说我要取是个 key, 这个时候可以用 getall. 这时候 Redis 就执行了一次命令. 比方说我要取 10 个 key 那么用 get 的话要取 10 次, 如果用 getall 的话要用 1 次. 一次 getall 时间大概是 get 的一点几倍, 但是 10 次 get 的话就是 10 倍的时间, 这个账你应该会算吧. 强烈推荐大家用 getall.
setall 跟 getall 相似. 批量设置 K-V.
setall 与 getall 性能很恐怖, 官方公布的 ops 也就 10 万左右, 为什么我们的测试轻轻松松到五十万甚至上百万, 因为我们就用了 setall,getall.
如果 get,set 两次以上, 建议用 getall,setall
Redis 管道 Pipeline
比如执行 10 次命令会打包成一个包集体发过去执行, 这里实现的方式是 StartPipeline()开始, StopPipeline()结束中间的代码就会以管道的形式执行. 这里推荐使用我们的更强的武器, AutoPipeline 自动管道属性. 管道操作到一定数量时, 自动提交, 默认 0. 使用了不需要使用 AutoPipeline, 就不需要 StartPipeline,StopPipeline 指定开始结束了!
Add 与 Replace
Add:Redis 中没有这个 Key 就添加, 有了就不要添加, 返回 false
Replace: 有则替换, 还会返回原来的值, 没有则不进行操作
Add 跟 Replace 就是实现 Redis 分布式锁的关键
Redis 使用技巧, 经验分享
在项目的 Readme 中, 这里摘录下:
特性
在 ZTO 大数据实时计算广泛应用, 200 多个 Redis 实例稳定工作一年多, 每天处理近 1 亿包裹数据, 日均调用量 80 亿次
低延迟, Get/Set 操作平均耗时 200~600us(含往返网络通信)
大吞吐, 自带连接池, 最大支持 1000 并发
高性能, 支持二进制序列化(默认用的 JSON,JSON 很低效, 转成二进制性能会提升很多)
Redis 经验分享
在 Linux 上多实例部署, 实例个数等于处理器个数, 各实例最大内存直接为本机物理内存, 避免单个实例内存撑爆(比方说 8 核心处理器, 那么就部署 8 个实例)
把海量数据 (10 亿 +) 根据 key 哈希 (Crc16/Crc32) 存放在多个实例上, 读写性能成倍增长
采用二进制序列化, 而非常见的 JSON 序列化
合理设计每一对 Key 的 Value 大小, 包括但不限于使用批量获取, 原则是让每次网络包控制在 1.4k 字节附近, 减少通信次数(实际经验几十 k, 几百 k 也是没问题的)
Redis 客户端的 Get/Set 操作平均耗时 200~600us(含往返网络通信), 以此为参考评估网络环境和 Redis 客户端组件(达不到就看一下网络, 序列化方式等等)
使用管道 Pipeline 合并一批命令
Redis 的主要性能瓶颈是序列化, 网络带宽和内存大小, 滥用时处理器也会达到瓶颈
其它可查优化技巧
以上经验, 源自于 300 多个实例 4T 以上空间一年多稳定工作的经验, 并按照重要程度排了先后顺序, 可根据场景需要酌情采用!
缓存 Redis 的兄弟姐妹
Redis 实现 ICache 接口, 它的孪生兄弟 MemoryCache, 内存缓存, 千万级吞吐率.
各应用强烈建议使用 ICache 接口编码设计, 小数据时使用 MemoryCache 实现;
数据增大 (10 万) 以后, 改用 Redis 实现, 不需要修改业务代码.
提问环节聊聊大数据中 Redis 使用的经验, 问题
一条数据多个 key 怎么设置比较合理?
如果对性能要求不是很高直接用 JSON 序列化实体就好, 没必要使用字典进行存储.
队列跟 List 有什么区别? 左进右出的话用 List 还是用队列比较好?
队列其实就是用 List 实现的, 也是基于 List 封装的. 左进右出的话直接队列就好. Redis 的 List 结构比较有意思, 既可以左进右出, 也能右进左出. 所以它既可以实现列表结构, 也能队列, 也能实现栈
存放多个字段的类性能一样吗?
大部分场景都不会有偏差, 可能对于大公司数据量比较大的场景会有些偏差
可否介绍一下使用 Redis 进行数据计算, 统计的场景?
略. 自己看视频吧! o(∩_∩)o 哈哈!(因为我没听清!)
大数据写入到数据库之后 比如数据到亿以上的时候 统计分析这块 查询这块 能不能分享些经验.
分表分库, 拆分到一千万以内.
CPU 为何暴涨?
程序员终极理念: CPU 达到百分百, 然后性能达到最优, 尽量不要浪费. 最痛恨的是: 如果 CPU 不到百分百, 性能没法提升了, 说明代码有问题!
来源: https://www.cnblogs.com/yilezhu/p/9941208.html