StackExchange.Redis-中文使用文档

本文介绍了StackExchange Redis的基本用法、配置、管道和多路复用器、键,值以及通道、事务、事件、发布/订阅 消息顺序、KEYS,SCAN,FLUSHDB 等命令的位置、性能分析以及脚本
展开查看详情

1. 目 录 致谢 StackExchange.Redis 中文使用文档 基础 配置 事件 同步执行 键查找 键与值 管道与重用链接 分析 发布订阅顺序 脚本 超时 事务 本文档使用 书栈(BookStack.CN) 构建 - 1 -

2.致谢 致谢 当前文档 《StackExchange.Redis 中文使用文档》 由 进击的皇虫 使用 书栈 (BookStack.CN) 进行构建,生成于 2018-05-10。 书栈(BookStack.CN) 仅提供文档编写、整理、归类等功能,以及对文档内容的生成和导出工 具。 文档内容由网友们编写和整理,书栈(BookStack.CN) 难以确认文档内容知识点是否错漏。如 果您在阅读文档获取知识的时候,发现文档内容有不恰当的地方,请向我们反馈,让我们共同携手, 将知识准确、高效且有效地传递给每一个人。 同时,如果您在日常生活、工作和学习中遇到有价值有营养的知识文档,欢迎分享到 书栈 (BookStack.CN) ,为知识的传承献上您的一份力量! 如果当前文档生成时间太久,请到 书栈(BookStack.CN) 获取最新的文档,以跟上知识更新换 代的步伐。 文档地址:http://www.bookstack.cn/books/StackExchange.Redis-docs-cn 书栈官网:http://www.bookstack.cn 书栈开源:https://github.com/TruthHun 分享,让知识传承更久远! 感谢知识的创造者,感谢知识的分享者,也感谢每一位阅读到此处的 读者,因为我们都将成为知识的传承者。 本文档使用 书栈(BookStack.CN) 构建 - 2 -

3.StackExchange.Redis 中文使用文档 StackExchange.Redis 中文使用文档 StackExchange.Redis 中文使用文档 Intro Redis 简介 StackExchange.Redis 简介 StackExchange.Redis中文使用文档 目录 More Contact me: weihanli@outlook.com StackExchange.Redis 中文使用文档 Intro 翻译 StackExchange.Redis 的文档 原文文档在线地址: https://stackexchange.github.io/StackExchange.Redis/ Redis 简介 Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。 从2015年6月开始,Redis的开发由Redis Labs赞助,而2013年5月至2015年6月期间,其开发由 Pivotal赞助。在2013年5月之前,其开发由VMware赞助。 根据月度排行网站DB-Engines.com的数据显示,Redis是最流行的键值对存储数据库。 更多介绍可参考 https://zh.wikipedia.org/wiki/Redis Redis官网 https://redis.io/ StackExchange.Redis 简介 StackExchange.Redis 是 Stackoverflow 开发的 Redis C# 客户端,是目前.net应用使用 的最多的 redis 客户端,性能优越。 StackExchange.Redis中文使用文档 Github:https://weihanli.github.io/StackExchange.Redis-docs-cn/ 本文档使用 书栈(BookStack.CN) 构建 - 3 -

4.StackExchange.Redis 中文使用文档 Gitbook : https://www.gitbook.com/book/weihanli/stackexchange-redis- docs-cn/details 点击阅读 下载 PDF 目录 基础 配置 事件 同步执行 键查找 键与值 管道与重用链接 分析 发布订阅顺序 脚本 超时 事务 More 作者水平有限,若有疏漏或错误还望提醒,十分感谢。 您可以 提出问题 或者给我 发邮件。 Contact me:weihanli@outlook.com 本文档使用 书栈(BookStack.CN) 构建 - 4 -

5.基础 基础 基本使用 使用 redis 数据库 使用 redis 发布/订阅 访问单独的服务器 同步 vs 异步 vs 执行后不理 查看原文 基本使用 StackExchange.Redis 中核心对象是在 StackExchange.Redis 命名空间中的 ConnectionMultiplexer 类,这个对象隐藏了多个服务器的详细信息。 因为 ConnectionMultiplexer 要做很多事,它被设计为在调用者之间可以共享和重用。 你不应该在执行每一个操作的时候就创建一个 ConnectionMultiplexer . 它完全是线程安全的,并准 备好这种用法(多线程)。 在后续所有的例子中,我们假设你有一个 ConnectionMultiplexer 类的实例保存以重用。 但现在,让我们来先创建一个。 这是使用 ConnectionMultiplexer.Connect 或 ConnectionMultiplexer.ConnectAsync 完成的,传递配置字符串或 ConfigurationOptions 对象。 配置字符串可以采用逗号分隔的一系列节点的形式,所以让我们在默认端口(6379)上连接到本地机 器上的一个实例: 1. using StackExchange.Redis; 2. ... 3. ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost"); 4. // ^^^ store and re-use this!!! 请注意, ConnectionMultiplexer 实现了 IDisposable 接口而且可以在不再需要的时候处理释放 掉。 这是故意不展示使用 using 语句用法,因为你想要只是简单地使用一 个 ConnectionMultiplexer 的情况是极少见的 ,因为想法是重用这个对象。 更复杂的情况可能涉及主/从设置; 对于此用法,只需简单的指定组成逻辑redis层的所有所需节点 (它将自动标识主节点) 1. ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("server1:6379,server2:6379"); 如果它发现两个节点都是主节点,则可以可选地指定可以用于解决问题的仲裁密钥,然而幸运地是这 样的条件是非常罕见的。 本文档使用 书栈(BookStack.CN) 构建 - 5 -

6.基础 一旦你有一个 ConnectionMultiplexer ,你可能有3个主要想做的事: 访问一个 redis 数据库(注意,在集群的情况下,单个逻辑数据库可以分布在多个节点上) 使用 redis 的的 发布/订阅 功能 访问单独的服务器以进行维护/监视 使用 redis 数据库 访问redis数据库非常简单: 1. IDatabase db = redis.GetDatabase(); 从 GetDatabase 返回的对象是一个成本很低的通道对象,不需要存储。 注意,redis支持多个数据库(虽然“集群”不支持),这可以可选地在调用 GetDatabase 中指 定。 此外,如果您计划使用异步API,您需要 Task.AsyncState 有一个值,也可以指定: 1. int databaseNumber = ... 2. object asyncState = ... 3. IDatabase db = redis.GetDatabase(databaseNumber, asyncState); 一旦你有了 IDatabase ,它只是一个使用 redis API 的情况。 注意,所有方法都具有同步和异步实现。 根据微软的命名指导,异步方法都以 ...Async(...) 结尾,并且完全是可以等待的 await 等。 最简单的操作也许是存储和检索值: 1. string value = "abcdefg"; 2. db.StringSet("mykey", value); 3. ... 4. string value = db.StringGet("mykey"); 5. Console.WriteLine(value); // writes: "abcdefg" 需要注意的是,这里的 String... 前缀表示 redis 的 String 类型,尽管它和 .NET 的字符 串类型 都可以保存文本, 它们还是有较大差别的。 然而,redis 还允许键和值使用原始的二进制数据,用法和字符串是一样的: 1. byte[] key = ..., value = ...; 2. db.StringSet(key, value); 3. ... 4. byte[] value = db.StringGet(key); 本文档使用 书栈(BookStack.CN) 构建 - 6 -

7.基础 覆盖所有redis数据类型的所有 [redis数据库命令](http://redis.io/commands)的都是可 以使用的。 使用 redis 发布/订阅 redis的另一个常见用法是作为 发布/订阅消息分发工具; 这也很简单,并且在连接失败的情况下, ConnectionMultiplexer 将处理重新订阅所请求的信道的 所有细节。 1. ISubscriber sub = redis.GetSubscriber(); 同样,从 GetSubscriber 返回的对象是一个不需要存储的低成本的通道对象。 发布/订阅 API没有数据库的概念,但和之前一样,我们可以选择的提供一个异步状态。 注意,所有订阅都是全局的:它们不限于 ISubscriber 实例的生命周期。 redis中的 发布/订阅 功能使用命名的“channels”; channels 不需要事先在服务器上定义(这 里有一个有趣的用法是利用每个用户的通知渠道类驱动部分的实时更新) 如在.NET中常见的,订阅采用回调委托的形式,它接受通道名称和消息: 1. sub.Subscribe("messages", (channel, message) => { 2. Console.WriteLine((string)message); 3. }); 另外(通常在一个单独的机器上的一个单独的进程),你可以发布到该通道: 1. sub.Publish("messages", "hello"); 这将(实际上瞬间)将“hello”写到订阅进程的控制台。 和之前一样,通道名和消息都可以是二进 制的。 有关顺序和并发消息处理的使用文档说明,请参见 发布/订阅消息顺序 。 访问单独的服务器 出于维护目的,有时需要发出服务器特定的命令: 1. IServer server = redis.GetServer("localhost", 6379); GetServer 方法会接受一个 EndPoint .aspx) 终结点或者是一个可以唯一标识一个服务器的 键/值对。 本文档使用 书栈(BookStack.CN) 构建 - 7 -

8.基础 像之前介绍的那样, GetServer 方法返回的是一个不需要被存储的轻量级的通道对象,并且可以 可选的指定 async-state(异步状态)。 需要注意的是,多个可用的节点也是可以的: 1. EndPoint[] endpoints = redis.GetEndPoints(); 一个 IServer 实例是可以使用服务器命令的 Server commands,例如: 1. DateTime lastSave = server.LastSave(); 2. ClientInfo[] clients = server.ClientList(); 同步 vs 异步 vs 执行后不理 StackExchange.Redis有3种主要使用机制: 同步 - 适用于操作在方法返回到调用者之前完成(注意,尽管这可能阻止调用者,但它绝对不 会阻止其他线程:StackExchange.Redis的关键思想是它积极地与并发调用者共享连接) Asynchronous - where the operation completes some time in the future, and a Task or Task<T> is returned immediately, which can later: 异步 - 操作在将来完成一些时间,并且立即返回一个 Task 或 ‘Task‘ 对象,也可以稍后 再返回: 是可以等待的(阻塞当前线程,直到响应可用) .Wait() 可以增加一个后续的回调 (TPL 中的 ContinueWith .aspx)) awaited 可等待的(这是简化后者的语言级特性,同时如果答复已经知道也立即继续) 执行后不理 - 适用于你真的对这个回复不感兴趣,并且乐意继续不管回应 同步的用法已经在上面的示例中展示了。这是最简单的用法,并且不涉及 TPL。 对于异步使用,关键的区别是方法名称上的 Async 后缀,以及(通常)使用“await”语言特性。 例如: 1. string value = "abcdefg"; 2. await db.StringSetAsync("mykey", value); 3. ... 4. string value = await db.StringGetAsync("mykey"); 5. Console.WriteLine(value); // writes: "abcdefg" 执行后不理 的用法可以通过所有方法上的可选参数 CommandFlags flags (默认值为 null )来 访问使用。 本文档使用 书栈(BookStack.CN) 构建 - 8 -

9.基础 这种用法中,方法会立即方法一个默认值(所以通常返回一个 String 的方法总是返回 null , 而一个通常返回一个 Int64 的方法总是返回 0 )。 操作会在后台继续执行。这种情况的典型用例可能是增加页面浏览数量: 1. db.StringIncrement(pageKey, flags: CommandFlags.FireAndForget); 查看原文 本文档使用 书栈(BookStack.CN) 构建 - 9 -

10.配置 配置 配置 配置选项 自动和手动配置 重命名命令 Twemproxy Tiebreakers 和配置更改公告 重新连接重试策略 查看原文 配置 因为有很多不同配置 redis 的方式,StackExchange.Redis 提供了一个丰富的配置模型,当调 用 Connect (或 ConnectAsync )时调用它。 1. var conn = ConnectionMultiplexer.Connect(configuration); 这里的 configuration 可以是下面的任意一个: 一个 ConfigurationOptions 实例 一个代表配置的 string 后者是 基本上 是前者的标记化形式。 基本配置字符串 - 最简单的 配置示例只需要一个主机名: 1. var conn = ConnectionMultiplexer.Connect("localhost"); 这将使用默认的redis端口(6379)连接到本地计算机上的单个服务器。 附加选项只是简单地附加(逗号分隔)。 端口通常用冒号( : )表示。 配置选项在名称后面包含 一个 = 。 例如: 1. var conn = ConnectionMultiplexer.Connect("redis0:6380,redis1:6380,allowAdmin=true"); 下面显示了 string 和 ConfigurationOptions 表示之间的映射概述,但您可以轻松地在它们之 间切换: 本文档使用 书栈(BookStack.CN) 构建 - 10 -

11.配置 1. ConfigurationOptions options = ConfigurationOptions.Parse(configString); 或者: 1. string configString = options.ToString(); 常见的用法是将 基础配置 细节存储在一个字符串中,然后在运行时应用特定的详细信息: 1. string configString = GetRedisConfiguration(); 2. var options = ConfigurationOptions.Parse(configString); 3. options.ClientName = GetAppName(); // only known at runtime 4. options.AllowAdmin = true; 5. conn = ConnectionMultiplexer.Connect(options); 带密码的 Microsoft Azure Redis 示例 1. var conn = ConnectionMultiplexer.Connect("contoso5.redis.cache.windows.net,ssl=true,passwor d=..."); 配置选项 ConfigurationOptions 对象具有许多的属性,所有这些都在智能提示中都有。 一些更常用的选项包括: 配置字符串 ConfigurationOptions 默认值 如果为 true (Azure 上默认值为 true, abortConnect={bool} AbortOnConnectFail false ) 没有服务器可用时 不会创建连接 启用被认为具有风 allowAdmin={bool} AllowAdmin false 的一系列命令 所有发布/订阅操 channelPrefix={string} ChannelPrefix null 的可选频道前缀 在初始 connectRetry={int} ConnectRetry 3 期间重复连接尝试 次数 连接操作的超时时 connectTimeout={int} ConnectTimeout 5000 (ms) 用于传达配置更改 configChannel={string} ConfigurationChannel __Booksleeve_MasterChanged 广播通道名称 本文档使用 书栈(BookStack.CN) 构建 - 11 -

12.配置 检查配置的时间 configCheckSeconds= (秒)。如果支持 ConfigCheckSeconds 60 话,这会以交互式 {int} 接字的方式保持活 动。 默认数据库索引, 从 defaultDatabase={int} DefaultDatabase null 到 Databases.Cou -1) 发送消息以帮助保 套接字活动的时间 keepAlive={int} KeepAlive -1 (秒)(默认时间 60s) 标识 redis 中的 name={string} ClientName null 连接 redis 服务器的 password={string} Password null 码 正在使用的代理类 proxy={proxy type} Proxy Proxy.None (如果有); 例 如“twemproxy” 指定DNS解析应该 resolveDns={bool} ResolveDns false 显式和热切,而不 隐式 目前尚未实施(预 serviceName={string} ServiceName null 与sentinel一起 用) ssl={bool} Ssl false 指定应使用SSL加 在服务器证书上强 sslHost={string} SslHost null 执行特定的SSL主 标识 允许同步操作的时 syncTimeout={int} SyncTimeout 1000 (ms) 用于在不明确的主 tiebreaker={string} TieBreaker __Booksleeve_TieBreak 景中选择服务器的 Redis版本级别( version={string} DefaultVersion (3.0 in Azure, else 2.0) 服务器要使用的版 默认不可用时使用 writeBuffer={int} WriteBuffer 4096 输出缓冲区的大小 ReconnectRetryPolicy= ReconnectRetryPolicy 重新连接重试策略 {IReconnectRetryPolicy} 补充的只有代码才支持的选项: ReconnectRetryPolicy (IReconnectRetryPolicy) - Default: ReconnectRetryPolicy = LinearRetry(ConnectTimeout); 本文档使用 书栈(BookStack.CN) 构建 - 12 -

13.配置 重连重试策略( IReconnectRetryPolicy )。默认设置: ReconnectRetryPolicy = LinearRetry(ConnectTimeout); 配置字符串中的令牌是逗号分隔的;任何没有 = 符号的都假定为redis服务器端点。 没有显式端口的端点将在未启用ssl的情况下使用6379,如果启用了ssl则使用6380。 以 $ 开头的令牌被用来表示命令映射,例如: $ config = cfg 。 自动和手动配置 在许多常见的情况下,StackExchange.Redis将自动配置很多设置,包括服务器类型和版本,连接 超时和主/从关系。 有时,在redis服务器上禁用了这些命令。 在这种情况下,可以提供更多的信息: 1. ConfigurationOptions config = new ConfigurationOptions 2. { 3. EndPoints = 4. { 5. { "redis0", 6379 }, 6. { "redis1", 6380 } 7. }, 8. CommandMap = CommandMap.Create(new HashSet<string> 9. { // EXCLUDE a few commands 10. "INFO", "CONFIG", "CLUSTER", 11. "PING", "ECHO", "CLIENT" 12. }, available: false), 13. KeepAlive = 180, 14. DefaultVersion = new Version(2, 8, 8), 15. Password = "changeme" 16. }; 它相当于命令字符串: 1. redis0:6379,redis1:6380,keepAlive=180,version=2.8.8,$CLIENT=,$CLUSTER=,$CONFIG=, $ECHO=,$INFO=,$PING= 重命名命令 redis的一个很不寻常的功能是可以禁用或重命名或禁用并重命名单个命令。 根据前面的例子,这是通过 CommandMap 来实现的,但不是传递一个 HashSet <string> 到 Create() (表示可用或不可用的命令),而是传递一个 Dictionary < string> 。 本文档使用 书栈(BookStack.CN) 构建 - 13 -

14.配置 字典中未提及的所有命令都默认已启用且未重命名。 “null”或空白值记录命令被禁用。 例如: 1. var commands = new Dictionary<string,string> { 2. { "info", null }, // disabled 3. { "select", "use" }, // renamed to SQL equivalent for some reason 4. }; 5. var options = new ConfigurationOptions { 6. // ... 7. CommandMap = CommandMap.Create(commands), 8. // ... 9. } 以上代码等同于(在连接字符串中): 1. $INFO=,$SELECT=use Twemproxy Twemproxy 是允许使用多个 redis 实例就像它是一个单个服务器,具有内置分片和容错(很像 redis 集群,但单独实现)的工具。 Twemproxy可用的功能集减少。 为了避免手动配置,可以使用 Proxy 选项: 1. var options = new ConfigurationOptions 2. { 3. EndPoints = { "my-server" }, 4. Proxy = Proxy.Twemproxy 5. }; Tiebreakers 和配置更改公告 通常 StackExchange.Redis 都会自动解析 主/从节点。然而,如果你不使用诸如 redis- sentinel 或 redis 集群之类的管理工具,有时你会有多个主节点(例如,在重置节点以进行维护 时,它可能会作为主节点重新显示在网络上)。 为了帮助解决这个问题,StackExchange.Redis 可以使用 tie-breaker 的概念 - 这仅在检测 到多个主节点时使用(不包括需要多个主节点的redis集群)。 为了与 BookSleeve 兼容,tiebreaker 使用默认的key为 "__Booksleeve_TieBreak" (总是在 数据库0中)。 这用作粗略的投票机制,以帮助确定首选 主机,以便能够按正确的路由工作。 本文档使用 书栈(BookStack.CN) 构建 - 14 -

15.配置 同样,当配置改变时(特别是主/从配置),连接的实例使得他们意识到新的情况(在可用的地方通过 INFO , CONFIG 等进行通知 )是很重要的。 StackExchange.Redis 通过自动订阅可以在其上发送这样的通知的发布/订阅通道来实现。 由于类似的原因,这默认为 "__Booksleeve_MasterChanged" 。 这两个选项都可以通过 .ConfigurationChannel 和 .TieBreaker 配置属性来定制或禁用(设置 为 "" )。 These settings are also used by the IServer.MakeMaster() method, which can set the tie-breaker in the database and broadcast the configuration change message. The configuration message can also be used separately to master/slave changes simply to request all nodes to refresh their configurations, via the ConnectionMultiplexer.PublishReconfigure method. 这些设置也由 IServer.MakeMaster() 方法使用,它可以设置数据库中的 tie-breaker 并广播配 置更改消息。 配置消息也可以单独用于主/从变化,只需通过调用 ConnectionMultiplexer.PublishReconfigure 方法 请求所有节点刷新其配置。 重新连接重试策略 当连接由于任何原因丢失时,StackExchange.Redis会自动尝试在后台重新连接。 它将继续重试,直到连接恢复。 它将使用 ReconnectRetryPolicy 来决定在重试之间应该等待多 长时间。 ReconnectRetryPolicy可以是线性的(默认),指数的或者是一个自定义的重试策略。 举个例子: 1. config.ReconnectRetryPolicy = new ExponentialRetry(5000); // defaults maxDeltaBackoff to 10000 ms 2. //retry# retry to re-connect after time in milliseconds 3. //1 a random value between 5000 and 5500 4. //2 a random value between 5000 and 6050 5. //3 a random value between 5000 and 6655 6. //4 a random value between 5000 and 8053 7. //5 a random value between 5000 and 10000, since maxDeltaBackoff was 10000 ms 8. //6 a random value between 5000 and 10000 9. 10. config.ReconnectRetryPolicy = new LinearRetry(5000); 11. //retry# retry to re-connect after time in milliseconds 12. //1 5000 13. //2 5000 本文档使用 书栈(BookStack.CN) 构建 - 15 -

16.配置 14. //3 5000 15. //4 5000 16. //5 5000 17. //6 5000 查看原文 本文档使用 书栈(BookStack.CN) 构建 - 16 -

17.事件 事件 事件 查看原文 事件 ConnectionMultiplexer 类型提供了许多事件可以用来理解被封装的底层是怎么工作的。这在记录日 志时会特别有用。 ConfigurationChanged - 当连接的配置从 ConnectionMultiplexer 内部发生修改时触发 ConfigurationChangedBroadcast - 当经由发布/订阅接收到重新配置消息时引发; 这通常是由于 IServer.MakeMaster 用于更改节点的复制配置,可以选择将这样的请求广播到所有客户端 ConnectionFailed - 当连接由于无论任何原因失败时触发; 请注意,在连接重新建立之前是不 会再收到该连接的 ConnectionFailed 通知 ConnectionRestored - 当重新建立到先前失败的节点的连接时触发 ErrorMessage - 当redis服务器响应任何用户发起的具有错误消息的请求时触发; 这种情况不 包含将被报告给直接调用者的常规异常/故障的情况 HashSlotMoved - 当“redis集群”指出 散列槽(hash-slot) 已在节点之间迁移时触发; 请 注意,请求通常会自动重新路由,因此用户不需要在这里做任何特殊操作 InternalError - 当库在一些意想不到的方式失败时触发; 这主要是为了调试目的,并且大多数 用户应该不需要这个事件 需要注意,StackExchange.Redis 中的 发布/订阅 工作方式与事件非常相似,接收到消息时会调 用接受一个 Action<RedisChannel, RedisValue> 类型回调方法的 Subscribe / SubscribeAsync 方法。 查看原文 本文档使用 书栈(BookStack.CN) 构建 - 17 -

18.同步执行 同步执行 连续同步执行的危险 查看原文 连续同步执行的危险 一旦遇到这样的问题,这里还有更多内容,然后发现了 一个适当恶劣的解决方法。 这篇文章没有列在索引中,但是为满足你的好奇心而保留了下来。 查看原文 本文档使用 书栈(BookStack.CN) 构建 - 18 -

19.键查找 键查找 KEYS , SCAN , FLUSHDB 这些在哪里? 那么如何使用它们呢? 所以我需要记住我连接到哪个服务器? 这真糟糕! 查看原文 KEYS , SCAN , FLUSHDB 这些在哪里? 这里是一些非常常见的常见问题: 似乎没有一个 Keys(...) 或者 Scan(...) 方法? 如何查询数据库中存在哪些键? 或者 似乎没有一个 Flush(...) 方法?如何删除数据库中的所有的键? 很奇怪的是,这里的最后一个关键词是数据库。 因为StackExchange.Redis的目标是针对集群等场景,知道哪些命令针对 数据库 (可以是分布在 多个节点上的逻辑数据库)以及哪些命令针对 服务器 是很重要的。 以下命令都针对单个服务器: KEYS / SCAN FLUSHDB / FLUSHALL RANDOMKEY CLIENT CLUSTER CONFIG / INFO / TIME SLAVEOF SAVE / BGSAVE / LASTSAVE SCRIPT (不要混淆 EVAL / EVALSHA ) SHUTDOWN SLOWLOG PUBSUB (不要混淆 PUBLISH / SUBSCRIBE / 等) 一些 DEBUG 操作 (我可能错过了至少一个)大多数这些将显得很明显,但前3行不那么明显: KEYS / SCAN 只列出当前服务器上的键; 而不是更广泛的逻辑数据库 FLUSHDB / FLUSHALL 只删除当前服务器上的密钥;而不是更广泛的逻辑数据库 RANDOMKEY 仅选择当前服务器上的密钥; 而不是更广泛的逻辑数据库 实际上,StackExchange.Redis 通过简单地随机选择目标服务器来欺骗 IDatabase API上的 本文档使用 书栈(BookStack.CN) 构建 - 19 -

20.键查找 RANDOMKEY ,但这对其他服务器是不可能的。 那么如何使用它们呢? 最简单的:从服务器开始,而不是数据库。 1. // get the target server 2. var server = conn.GetServer(someServer); 3. 4. // show all keys in database 0 that include "foo" in their name 5. foreach(var key in server.Keys(pattern: "*foo*")) { 6. Console.WriteLine(key); 7. } 8. 9. // completely wipe ALL keys from database 0 10. server.FlushDatabase(); 注意,与 IDatabase API(在 GetDatabase() 调用中已经选择了的目标数据库)不同,这些方 法对数据库使用可选参数,或者默认为 0 。 Keys(...) 方法值得特别一提:它并不常见,因为它没有一个 *Async 对应。 这样做的原因 是,在后台,系统将确定使用最合适的方法(基于服务器版本的 KEYS VS SCAN ),如果可能 的话,将使用 SCAN 方法 一个 IEnumerable<RedisKey> 在内部执行所有的分页 - 所以你永远 不需要看到游标操作的实现细节。 如果 SCAN 不可用,它将使用 KEYS ,这可能导致服务器上的阻塞。 无论哪种方式, SCAN 和 KEYS 都需要扫描整个键空间,所以在生产服务器上应该避免 - 或者至少是针对从节点服务 器。 所以我需要记住我连接到哪个服务器? 这真糟 糕! 不,不完全是。 你可以使用 conn.GetEndPoints() 来列出节点(所有已知的节点,或者在原始配置 中指定的节点,这些不一定是相同的东西),并且使用 GetServer() 迭代找到想要的服务器(例 如,选择一个从节点)。 查看原文 本文档使用 书栈(BookStack.CN) 构建 - 20 -

21.键查找 本文档使用 书栈(BookStack.CN) 构建 - 21 -

22.键与值 键与值 键,值和通道 键 值 哈希 通道 脚本 结论 查看原文 键,值和通道 在处理redis时,是键不是键之间有很重要的区别。 键是数据库中数据片段(可以是String,List,Hash或任何其他redis数据类型)的独一无二的名 称。 键永远不会被解释为…好吧,任何东西:它们只是惰性名称。 此外,当处理集群或分片系统时,它是定义包含此数据的节点(或者如果有从节点的节点)的关键 - 因此键对于路由命令是至关重要的。 这与 值 形成对比; 值是单独(对于字符串数据)或分组对键的内容存储 。 值不影响命令路由 (注意:使用 SORT 命令时除非指定 BY 或者 GET,否则是很难解释的) 同样,为了操作的目的,值通常被redis翻译为: incr (和各种类似的命令)将String值转换为数值数据 排序可以使用数字或unicode规则解释值 和许多其他操作 关键是使用API需要理解什么是键,什么是值。 这反映在StackExchange.Redis API中,但是好消息是,大部分时间你根本不需要知道这一点。 当使用 发布/订阅 时,我们处理 channels ; channel 不会影响路由(因此它们不是密钥),但 与常规值非常不同,因此要单考虑。 键 StackExchange.Redis 通过 RedisKey 类型表示键。 好消息是,可以从 string 和 byte[] 的隐式转换,允许使用文本和二进制密钥,没有任何复 杂性。 本文档使用 书栈(BookStack.CN) 构建 - 22 -

23.键与值 例如, StringIncrement 方法使用一个 RedisKey 作为第一个参数,但是你不需要知道 ; 举个例子: 1. string key = ... 2. db.StringIncrement(key); or 1. byte[] key = ... 2. db.StringIncrement(key); 同样,有一些操作返回 键为 RedisKey - 再次,它依然可以自动隐式转换: 1. string someKey = db.KeyRandom(); 值 StackExchange.Redis 用 RedisValue 类型表示值。 与 RedisKey 一样,存在隐式转换, 这意味着大多数时候你从来没有看到这种类型,例如: 1. db.StringSet("mykey", "myvalue"); 然而,除了文本和二进制内容,值还可能需要表示类型化的原始数据 - 最常见的(在.NET术语 中) Int32 , Int64 , Double 或 Boolean 。 因此, RedisValue 提供了比 RedisKey 更 多的转换支持: 1. db.StringSet("mykey", 123); // this is still a RedisKey and RedisValue 2. ... 3. int i = (int)db.StringGet("mykey"); 请注意,虽然从基元类型到 RedisValue 的转换是隐式的,但是从 RedisValue 到基元类型的许 多转换是显式的:这是因为如果数据没有合适的值,这些转换很可能会失败。 另外注意,当做数字 处理时,redis将不存在的键视为零; 为了与此一致,将空响应视为零: 1. db.KeyDelete("abc"); 2. int i = (int)db.StringGet("abc"); // this is ZERO 如果您需要检测空状态,那么你就可以这样检查: 本文档使用 书栈(BookStack.CN) 构建 - 23 -

24.键与值 1. db.KeyDelete("abc"); 2. var value = db.StringGet("abc"); 3. bool isNil = value.IsNull; // this is true 或者更简单地,只是使用提供的 Nullable <T> 支持: 1. db.KeyDelete("abc"); 2. var value = (int?)db.StringGet("abc"); // behaves as you would expect 哈希 由于哈希中的字段名称不影响命令路由,它们不是键,但可以接受文本和二进制名称, 因此它们被视 为用于API目的的值。 通道 发布/订阅 的通道名称由 RedisChannel 类型表示; 这与 RedisKey 大体相同,但是是独立处 理的,因为虽然通道名是正当的第一类元素,但它们不影响命令路由。 脚本 redis中的脚本 有两项显著的特性: 输入必须保持键和值分离(在脚本内部分别成为 KEYS 和 ARGV ) 返回格式未预先定义:这将特定于您的脚本 正因为如此, ScriptEvaluate 方法接受两个独立的输入数组:一个用于键的 RedisKey [] ,一个 用于值的 RedisValue [] (两者都是可选的,如果省略则假定为空)。 这可能是你实际需要在代 码中键入 RedisKey 或 RedisValue 的少数几次之一,这只是因为数组变动规则: 1. var result = db.ScriptEvaluate(TransferScript, 2. new RedisKey[] { from, to }, new RedisValue[] { quantity }); (其中 TransferScript 是一些包含Lua的 string ,在这个例子中没有显示) 响应使用 RedisResult 类型(这是脚本专用的;通常API尝试尽可能直接清晰地表示响应)。 和前 面一样, RedisResult 提供了一系列转换操作 - 实际上比 RedisValue 更多,因为除了可以转 换为文本,二进制,一些基元类型和可空元素,响应也可以转换为 数组 ,例如: 1. string[] items = db.ScriptEvaluate(...); 本文档使用 书栈(BookStack.CN) 构建 - 24 -

25.键与值 结论 API中使用的类型是非常故意选择的,以区分redis keys 和 values。 然而,在几乎所有情况 下,您不需要直接去参考所涉及的底层类型,因为提供了转换操作。 查看原文 本文档使用 书栈(BookStack.CN) 构建 - 25 -

26.管道与重用链接 管道与重用链接 管道和链接复用 管道线 执行后不理 复用链接 并发 查看原文 管道和链接复用 延迟严重。现代计算机可以以惊人的速度搅动数据,并且高速网络(通常具有在重要服务器之间的多 个并行链路)提供巨大的带宽,但是… 该延迟意味着计算机花费大量的时间等待数据 和 这是基于连 续的编程越来越受欢迎的几个原因之一。 让我们考虑一些常规的程序代码: 1. string a = db.StringGet("a"); 2. string b = db.StringGet("b"); 在涉及的步骤方面,这看起来大致是这样: 1. [req1] # client: the client library constructs request 1 2. [c=>s] # network: request one is sent to the server 3. [server] # server: the server processes request 1 4. [s=>c] # network: response one is sent back to the client 5. [resp1] # client: the client library parses response 1 6. [req2] 7. [c=>s] 8. [server] 9. [s=>c] 10. [resp2] 现在让我们突出客户端正在做的一些事的时间点: 1. [req1] 2. [====waiting=====] 3. [resp1] 4. [req2] 5. [====waiting=====] 6. [resp2] 本文档使用 书栈(BookStack.CN) 构建 - 26 -

27.管道与重用链接 请记住,这是不成比例的 - 如果这是按时间缩放,它将是完全由 waiting 控制的。 管道线 因此,许多redis客户端允许你使用 pipelining, 这是在管道上发送多个消息而不等待来自每个 的答复的过程 - 并且(通常)稍后当它们进入时处理答复。 在 .NET 中,可以启动但尚未完成,并且可能以后完成或故障的由TPL封装的操作可以由 TPL 通过 Task / Task <T> API 来实现。 本质上, Task<T> 表示 “ T 类型未来可能的值”(非泛型的 Task 本质尚是 Task<void> )。你可以使用任意一种用法: 在稍后的代码块等待直到操作完成( .Wait() ) 当操作完成时,调度一个后续操作( .ContinueWith(...) 或 await ) 例如,要使用过程化(阻塞)代码来借助管道传递这两个 get 操作,我们可以使用: 1. var aPending = db.StringGetAsync("a"); 2. var bPending = db.StringGetAsync("b"); 3. var a = db.Wait(aPending); 4. var b = db.Wait(bPending); 注意,我在这里使用 db.Wait ,因为它会自动应用配置的同步超时,但如果你喜欢你也可以使用 aPending.Wait() 或 Task.WaitAll(aPending,bPending); 。 使用管道技术,我们可以立即将这两个请求发送到网络,从而消除大部分延迟。 此外,它还有助于减少数据包碎片:单独发送(等待每个响应)的20个请求将需要至少20个数据包, 但是在管道中发送的20个请求可以通过少得多的数据包(也许只有一个)。 执行后不理 管道的一个特例是当我们明确地不关心来自特定操作的响应时,这允许我们的代码在排队操作在后台 继续时立即继续。 通常,这意味着我们可以在单个调用者的连接上并发工作。 这是使用 flags 参数来实现的: 1. // sliding expiration 2. db.KeyExpire(key, TimeSpan.FromMinutes(5), flags: CommandFlags.FireAndForget); 3. var value = (string)db.StringGet(key); FireAndForget 标志使客户端库正常地排队工作,但立即返回一个默认值(因为 KeyExpire 返回 一个 bool ,这将返回 false ,因为 default(bool) 是 false - 但是返回值是无意义 的,应该忽略)。 这也适用于 *Async 方法:一个已经完成的 Task <T> 返回默认值(或者为 void 方法返回 本文档使用 书栈(BookStack.CN) 构建 - 27 -

28.管道与重用链接 一个已经完成的 Task )。 复用链接 管道是很好的,但是通常任何单个代码块只需要一个值(或者可能想要执行几个操作,但是依赖于彼 此)。 这意味着我们仍然有一个问题,我们花大部分时间等待数据在客户端和服务器之间传输。 现在考虑一个繁忙的应用程序,也许是一个Web服务器。 这样的应用程序通常是并发的,所以如果你有20个并行应用程序请求都需要数据,你可能会想到旋转 20个连接,或者你可以同步访问单个连接(这意味着最后一个调用者需要等待 延迟的所有其他19之 前,甚至开始)。 或者折中一下,也许一个5个连接的租赁池 - 无论你怎么做,都会有很多的等 待。 StackExchange.Redis不做这个; 相反,它做 很多 的工作,使你有效地利用所有这个空闲时间复 用 一个连接。 当不同的调用者同时使用它时,它自动把这些单独的请求加入管道,所以不管请求使用阻塞还是异步 访问,工作都是按进入管道的顺序处理的。 因此,我们可能有10或20个的“get a 和 b”包括此前的(从不同的应用程序请求)情景中,并且他 们都将尽快到达连接。 基本上,它用完成其他调用者的工作的时间来填充 waiting 时间。 因此,StackExchange.Redis不提供的唯一redis特性(不会提供)是“阻塞弹 出”(BLPOP,BRPOP 和 BRPOPLPUSH),因为这将允许单个调用者停止整个多路复用器,阻止所有 其他调用者 。 StackExchange.Redis 需要保持工作的唯一其他时间是在验证事务的前提条件时,这就是为什么 StackExchange.Redis 将这些条件封装到内部管理的 condition 实例中。 在这里阅读更多关于事务。 如果你觉得你想“阻止出栈”,那么我强烈建议你考虑 发布 / 订阅 代替: 1. sub.Subscribe(channel, delegate { 2. string work = db.ListRightPop(key); 3. if (work != null) Process(work); 4. }); 5. //... 6. db.ListLeftPush(key, newWork, flags: CommandFlags.FireAndForget); 7. sub.Publish(channel, ""); 这实现了相同的目的,而不需要阻塞操作。 注意: 数据 不通过 发布 / 订阅 发送; 发布 / 订阅 API只用于通知处理器检查更多的工作 如果没有处理器,则新项目保持缓存在列表中; 工作不会丢失 只有一个处理器可以弹出单个值; 当消费者比生产者多时,一些消费者会被通知,然后发现没有 本文档使用 书栈(BookStack.CN) 构建 - 28 -

29.管道与重用链接 什么可做的 当你重新启动一个处理器,你应该假设 有工作,以便你处理任何积压的任务 但除此之外,语义与阻止出栈相同 StackExchange.Redis 的多路复用特性使得在使用常规的不复杂代码的同时在单个连接上达到极 高的吞吐量成为可能。 并发 应当注意,管道/链接复用器/未来值 方法对于基于连续的异步代码也很好地起作用;例如你可以 写: 1. string value = await db.StringGetAsync(key); 2. if (value == null) { 3. value = await ComputeValueFromDatabase(...); 4. db.StringSet(key, value, flags: CommandFlags.FireAndForget); 5. } 6. return value; 查看原文 本文档使用 书栈(BookStack.CN) 构建 - 29 -