一、前言

本篇主要使用StackExchangeRedis在.Net Core中使用Redis,使用基础见:点击此处。

二、五种基础数据结构

1.字符串类型String

字符串类型是Redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。你可以用其存储用户的邮箱、JSON化的对象甚至是一张图片。一个字符串类型键允许存储地得数据的最大容量是512MB。

字符串类型是其他4种数据类型的基础,其他数据类型和字符串类型的差别从某种角度来说只是组织字符串的形式不同。例如,列表类型是以列表的形式组织字符串,二集合类型是以集合的形式组织字符串。

以下为StackExchangeRedis中字符串常用方法及其命令:

(1)赋值与取值

//方法
redisConnection.GetDatabase().StringSetAsync(key, value);
redisConnection.GetDatabase().StringGetAsync(key);
//命令
127.0.0.1:> set stringKey stringValue
OK
127.0.0.1:> get stringKey
"stringValue"

(2)返回 key 中字符串值的子字符

//方法
redisConnection.GetDatabase().StringGetRangeAsync(key, start, end);
//命令
127.0.0.1:> getrange stringKey
"Value"

(3)将给定 key 的值设为 value ,并返回 key 的旧值(old value)

//方法
var oldvalue = await redisConnection.GetDatabase().StringGetSetAsync(key, oldkey);
//命令
127.0.0.1:> getset stringKey newValue
"stringValue"

(4)如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾

//方法
redisConnection.GetDatabase().StringAppendAsync(key, appendValue);
//命令
127.0.0.1:> append stringKey append
(integer)

2.散列类型Hash

Redis是采用字典结构以键值对的形式存储数据的,而Hash的键值也是一种字典结构,其存储了字段(field)和字段值的映射,但字段值只能是字符串,不支持其他数据类型,不能嵌套其他数据类型(其他数据类型同理)。

散列类型适合存储对象:使用对象类别和ID构成键名,使用字段表示对象的属性,而字段值则存储属性值。例如要存储ID为2的汽车对象,可以分别使用名为color、name和price的三个字段来存储该辆汽车的颜色、名称和价格。

以下为StackExchangeRedis中散列类型常用方法及其命令:

(1)赋值

//方法
HashEntry[] hashEntry = new HashEntry[] {
  new HashEntry("id",),
  new HashEntry("color","red"),
  new HashEntry("price",),
};
redisConnection.GetDatabase().HashSetAsync("youCar", hashEntry);
//命令
127.0.0.1:> hset myCar price
(integer)
127.0.0.1:> hset mCar color blue
(integer)
127.0.0.1:> hset mCar name tractor
(integer)

(2)取值

//方法
redisConnection.GetDatabase().HashGetAsync("youCar", "color");
redisConnection.GetDatabase().HashGetAllAsync("youCar");
//命令
127.0.0.1:> hget youCar color
"red"
127.0.0.1:> hgetall youCar
) "id"
) ""
) "color"
) "red"
) "price"
) ""

(3)获取所有散列类型中的字段

//方法
var keys = redisConnection.GetDatabase().HashKeys("youCar");
//命令
127.0.0.1:> hkeys youCar
) "id"
) "color"
) "price"

3.列表List

列表类型(List)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的一个片段。

列表类型内部是使用双向链表(double linked list)实现的,所以向列表两段添加元素的时间复杂都为O(1),获取越接近两端的元素速度就越快。这意味着即使是一个由几千万个元素的列表,获取头部或者尾部的10条记录也是极快的。

不过使用链表的代价是通过索引访问元素比较慢。于是,列表类型很适合于如社交网站的新鲜事、记录日志、消息队列等,类似获取最前几条的数据、从列表尾部插入等场景。

以下为StackExchangeRedis中列表类型常用方法及其命令:

(1)向列表两端增加元素

//方法
redisConnection.GetDatabase().ListLeftPushAsync("myList","head1");
redisConnection.GetDatabase().ListRightPush("myList","bottom1");
//命令
127.0.0.1:> lpush myList head1
(integer) 127.0.0.1:> rpush myList bottom1
(integer)

(2)从列表两端移除并获取一个元素

//方法
var value = redisConnection.GetDatabase().ListLeftPopAsync("myList");
var value = redisConnection.GetDatabase().ListRightPopAsync("myList");
//命令
127.0.0.1:> lpop myList
"head1"
127.0.0.1:> rpop myList
"bottom1"
127.0.0.1:> rpop myList
(nil)

(3)获取列表中的元素片段(不删除只获取)

//方法
var value = redisConnection.GetDatabase().ListRangeAsync("myList",,);
//命令
127.0.0.1:> lrange myList
) ""
) ""
) ""

4.集合Set

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。常用的操作是向集合加入或者删除元素、判断某个元素是否存在等,由于Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

最方便的是多个集合类型键之间还可以进行并集、交集和差集运算。

可以运用在共同好友、喜好和好友推荐(交集超过阈值)等可以运用交差并集操作的场景。

以下为StackExchangeRedis中集合类型常用方法及其命令:

(1)增加/删除元素(可多个,相同元素自动忽略)

//方法
redisConnection.GetDatabase().SetAddAsync("mySet", new RedisValue[] { "a", "b" });
redisConnection.GetDatabase().SetRemoveAsync("mySet", "a");
//命令
127.0.0.1:> sadd mySet a
(integer)
127.0.0.1:> sadd mySet a b c
(integer)
127.0.0.1:> srem mySet c
(integer)
127.0.0.1:> srem mySet b a
(integer)

(2)获取交集

//方法
var value = redisConnection.GetDatabase().SetCombine(SetOperation.Intersect, "mySet1", "mySet2");
//命令
127.0.0.1:> sadd mySet1 a b c
(integer)
127.0.0.1:> sadd mySet2 b c d
(integer)
127.0.0.1:> sinter mySet1 mySet2
) "c"
) "b"

5.有序集合Sort Set

从有序集合的名字就可以看出它和集合的区别就是”有序“二字。

在集合类型的基础尚有序集合类型为集合中的每个元素都关联了一个分数,按分数大小进行从小到大的排序,这使得我们不仅可以完成插入、删除和判断元素是否在等集合类型支持的操作,还能够获得分数最高(或最低)的前N个元素、获得指定分数范围内的元素等与分数有关的操作。虽然集合中每个元素都是不同的,但是它们的分数却可以相同。

有序集合类型在某方面和列表类型有些相似

  • 二者都是有序。

  • 二者都可以获取某一范围的元素

但是二者由这很大的区别,这使得他们的应用场景有所不同。

  • 列表是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间元素的速度会较慢,所以它更加适合实现如“新鲜事”或“日志”这样很少访问中间元素的应用。

  • 有序集合是使用散列表和跳表(skip list)实现的,所以即使读取文娱中间部分的数据速度也快(时间复杂度是O(log(N)))。

  • 列表中不能简单地调整元素的位置,但有序集合可以(通过更改元素的分数),所以可以用于排行榜、权重应用(有权重的消息队列)。

  • 有序集合要比类别更耗内存。

以下为StackExchangeRedis中有序集合类型常用方法及其命令:

(1)增加元素

//方法
SortedSetEntry[] sortedSetEntry = new SortedSetEntry[] {
  new SortedSetEntry("tom",),
  new SortedSetEntry("peter",),
  new SortedSetEntry("david",),
};
redisConnection.GetDatabase().SortedSetAddAsync("mySs", sortedSetEntry);
//命令
127.0.0.1:> zadd mySs tom peter david
(integer)

(2)获取排名在某个分数范围的元素列表

//方法
var value = redisConnection.GetDatabase().SortedSetRemoveRangeByScoreAsync("mySs", , );
//命令
127.0.0.1:> zrangebyscore mySs
) "tom"
) "peter"

(3)增加某个元素的分数

//方法
var sorce = redisConnection.GetDatabase().SortedSetIncrementAsync("mySs", "top", );
//命令
127.0.0.1:> zincrby mySs tom
""

6.小结

上面各类型所举得例子有限,基本所有的Redis命令在StackExchangeRedis中都有相应的异步和同步的方法,大家可以参考https://redis.io/commands

三、其他数据结构

1.HyperLogLog

Redis 在 2.8.9 版本添加了 HyperLogLog 结构(其本身也是一种算法)。

Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

所以主要应用于:

  • 统计访问IP数量;

  • 统计搜索关键词数量;

  • 统计用户在线数;

  • 等等大数量统计。

以下为StackExchangeRedis中常用方法及其命令:

(1)添加指定元素到 HyperLogLog 中

//方法
RedisValue[] redisValue = new RedisValue[] {
,,,
};
redisConnection.GetDatabase().HyperLogLogAddAsync("key", redisValue);
//命令
127.0.0.1:> pfadd hyperlogKy
(integer)

(2)返回给定 HyperLogLog 的基数估算值。

//方法
var count = redisConnection.GetDatabase().HyperLogLogLength("key");
//命令
127.0.0.1:> pfcount hyperlogKy
(integer)

2.geo

Redis在3.2版本之后新增了一个geo(地理位置),其数据结构为有序集合sort set。

geo可以将地理位置信息(经纬度)储存起来,并计算两个地理坐标之间的位置、返回以指定位置为圆心指定半径内的所有地理位置信息等。

像是我们平时的打车、租房地图等功能中就可以用到。

既然前面说到geo是有序集合那它的score怎么来?是我们自己输入嘛?其实是通过我们存储的经纬度通过geohash计算后的base32编码字符串。geohash原理点击此处。

以下为StackExchangeRedis中常用方法及其命令:

(1)添加地理位置

//方法
GeoEntry[] geoEntry = new GeoEntry[] {
  new GeoEntry(120.20000, 30.26667, "hangzhou"),
  new GeoEntry(116.41667, 39.91667, "beijing"),
  new GeoEntry(121.47, 31.23, "shanghai"),
};
redisConnection.GetDatabase().GeoAdd("city", geoEntry);
//命令
127.0.0.1:> geoadd city 120.20000 30.26667 hangzhou 116.41667 39.91667 beijing 121.47 31.23 shanghai
(integer)

(2)获取geohash

//方法
var geohash = redisConnection.GetDatabase().GeoHash("city", "hanghzou");
//命令
127.0.0.1:> geohash city hangzhou
) "wtmkpjyuph0"

(3)获取指定元素范围的地理信息位置集合

//方法
var geo = redisConnection.GetDatabase().GeoRadius("city", "hanghzou", , GeoUnit.Kilometers, , Order.Ascending, GeoRadiusOptions.WithGeoHash);
//命令
127.0.0.1:> georadiusbymember city hangzhou km withcoord withdist withhash asc count
) ) "hangzhou"
) "0.0000"
) (integer)
) ) "120.20000249147415"
) "30.266670658987586"
) ) "shanghai"
) "161.9183"
) (integer)
) ) "121.47000163793564"
) "31.229999039757836"

在给定以下可选项时, 命令会返回额外的信息:

  • WITHDIST : 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。

  • WITHCOORD : 将位置元素的经度和维度也一并返回。

  • WITHHASH : 以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。

  • ASC : 根据中心的位置, 按照从近到远的方式返回位置元素。DESC : 根据中心的位置, 按照从远到近的方式返回位置元素。

它还有个类似的georadius命令,区别是由给定的经纬度为圆心。

(4)计算两个位置之间的距离

//方法
var geo = redisConnection.GetDatabase().GeoDistance("city", "hanghzou","beijing");
//命令
127.0.0.1:> geodist city hangzhou beijing km
"1126.8937"

3.发布订阅pub/sub 

"发布/订阅"模式中包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或者若干个频道(channel),而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都会收到此消息。

发布者通过publish命令发送消息:

//命令
127.0.0.1:> publish channel_1 hi
(integer)
//方法
var count = redisConnection.GetSubscriber().Publish("channel_1", "hi");

消息是发送出去了,publish命令的返回值表示接收到这条消息的订阅者数量。因为当前没有订阅这订阅这个频道,所以返回0。

注意,发送出去的消息是不会持久化的,订阅者只能收到订阅之后发布者发送的消息。

订阅者通过subscribe命令订阅一个或者多个频道:

//命令
127.0.0.1:> subscribe channel_1 channel_2
Reading messages... (press Ctrl-C to quit)
) "subscribe"
) "channel_1"
) (integer)
) "subscribe"
) "channel_2"
) (integer)
) "message"
) "channel_1"
) "hi"
//方法
while (true)
{
  redisConnection.GetSubscriber().Subscribe("channel_1", (channel, message) =>
  {
    var msg = message;//收到的消息
    var chan = channel;//频道名称
  });
}

执行subscribe命令之后进入订阅状态,可能收到三种类型的回复。每种类型的回复都包含三个值,第一个值是消息的类型,根据消息类型的不同,第二、三个值得含义也不同。消息类型可能得取值有以下三个:

  • subscribe:表示订阅成功得反馈信息。第二个值是订阅成功得频道名称,第三个值是当前客户端订阅得频道数量。

  • message:这个类型得回复是我们最关心得,它表示接收到得消息。第二个值表示禅师消息的频道名称,第三个值是消息的内容。

  • unsubscribe:表示成功取消订阅某个频道。第二个值是对应频道的名称,第三个值是当前客户端订阅的频道数量。

StackExchangeRedis中用到message和unsubscribe这两种。

除了通过频道名之外,还可以使用psubscribe命令通过规则订阅频道。如约定规则为channel_?*则可以匹配channel_为开头的频道,如channel_1、channel_2等。

//命令
127.0.0.1:> psubscribe cahnnel_?*
Reading messages... (press Ctrl-C to quit)
(integer)
) "psubscribe"
) "cahnnel_?*"
) (integer)
//方法
while (true)
{
  redisConnection.GetSubscriber().Subscribe("channel_?*", (channel, message) =>
  {
    var msg = message;//收到的消息
    var chan = channel;//频道名称
  });
}

解除订阅通过命令unsubscribe,如果不指定频道则取消全部订阅。在StackExchangeRedis中分别时方法Unsubscribe()和UnsubscribeAll()。

.Net Core使用分布式缓存Redis:数据结构的更多相关文章

  1. ASP.Net Core使用分布式缓存Redis从入门到实战演练

    一.课程介绍 人生苦短,我用.NET Core!缓存在很多情况下需要用到,合理利用缓存可以一方面可以提高程序的响应速度,同时可以减少对特定资源访问的压力.  所以经常要用到且不会频繁改变且被用户共享的 ...

  2. .Net Core使用分布式缓存Redis:基础

    一.前言 Redis的介绍网上很多不再赘述.本次环境为net core 2.2,使用的StackExchange.Redis来操作Redis. 二.引用Microsoft.Extensions.Cac ...

  3. .Net Core使用分布式缓存Redis:Lua脚本

    一.前言 运行环境window,redis版本3.2.1.此处暂不对Lua进行详细讲解,只从Redis的方面讲解. 二.Redis的Lua脚本 在Redis的2.6版本推出了脚本功能,允许开发者使用L ...

  4. 一个技术汪的开源梦 —— 公共组件缓存之分布式缓存 Redis 实现篇

    Redis 安装 & 配置 本测试环境将在 CentOS 7 x64 上安装最新版本的 Redis. 1. 运行以下命令安装 Redis $ wget http://download.redi ...

  5. 企业项目开发--分布式缓存Redis

    第九章 企业项目开发--分布式缓存Redis(1) 注意:本章代码将会建立在上一章的代码基础上,上一章链接<第八章 企业项目开发--分布式缓存memcached> 1.为什么用Redis ...

  6. .NET分布式缓存Redis从入门到实战

    一.课程介绍 今天阿笨给大家带来一堂NOSQL的课程,本期的主角是Redis.希望大家学完本次分享课程后对redis有一个基本的了解和认识,并且熟悉和掌握 Redis在.NET中的使用. 本次分享课程 ...

  7. 第十章 企业项目开发--分布式缓存Redis(2)

    注意:本章代码是在上一章的基础上进行添加修改,上一章链接<第九章 企业项目开发--分布式缓存Redis(1)> 上一章说了ShardedJedisPool的创建过程,以及redis五种数据 ...

  8. 【开源项目系列】如何基于 Spring Cache 实现多级缓存(同时整合本地缓存 Ehcache 和分布式缓存 Redis)

    一.缓存 当系统的并发量上来了,如果我们频繁地去访问数据库,那么会使数据库的压力不断增大,在高峰时甚至可以出现数据库崩溃的现象.所以一般我们会使用缓存来解决这个数据库并发访问问题,用户访问进来,会先从 ...

  9. 三点须知:当我们在开发过程中需要用到分布式缓存Redis的时候

    当我们在开发过程中需要用到分布式缓存Redis的时候,我们首先要明白缓存在系统中用来做什么? 1. 少量数据存储,高速读写访问.通过数据全部in-momery 的方式来保证高速访问,同时提供数据落地的 ...

随机推荐

  1. LyX Error convert to loadable format - error handling

    This question used to spend my half a day, and this time again, half a day. Here I write it down in ...

  2. Java基础:数值类型转换、强制类型转换

    数值类型之间的转换 数值类型之间的转换,在小数值往大数值转换时,不会发生精度的损失.在小数值往大数值转换时有可能发生精度的损失. 比如byte最大值也只有127,如果一个大于127的int类型数据往b ...

  3. 力扣(LeetCode)两数相加 个人题解

    给出两个 非空 的链表用来表示两个非负的整数.其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字. 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和 ...

  4. gin索引优化实例1

    GIN(Generalized Inverted Index, 通用倒排索引) 是一个存储对(key, posting list)集合的索引结构,其中key是一个键值,而posting list 是一 ...

  5. 小白学习React官方文档看不懂怎么办?2.JSX语法

      接下来我们就要讲到JSX语法了,在我们讲它之前,我们先引入一个概念叫语法糖.     听到这个名字首先我们可能会想到一个词叫”糖衣炮弹“,那么什么叫糖衣炮弹呢,就是给你说各种好听的话,来迷惑你,但 ...

  6. win10中java环境变量配置

    首先,应该安装jdk,jdk的安装一般是jdk8,一般情况下去官网下载,此处有jdk8的网盘链接: -- 在安装jdk时候,可以看下这篇jdk和jre区别的博客--,有助于理解两者的区别和联系. 接触 ...

  7. Excel的常用函数

    1.查找重复内容=IF(COUNTIF(A:A,A2)>1,"重复","") 2.重复内容首次出现时不提示=IF(COUNTIF(A$2:A2,A2)&g ...

  8. Springboot 系列(十六)你真的了解 Swagger 文档吗?

    前言 目前来说,在 Java 领域使用 Springboot 构建微服务是比较流行的,在构建微服务时,我们大多数会选择暴漏一个 REST API 以供调用.又或者公司采用前后端分离的开发模式,让前端和 ...

  9. Spring Cloud Alibaba(四)实现Dubbo服务消费

    本项目演示如何使用 Spring Cloud Alibaba 完成 Dubbo 的RPC调用. Spring Cloud与Dubbo Spring Cloud是一套完整的微服务架构方案 Dubbo是国 ...

  10. UCloud 云服务内容审核 Java 版本实现

    前言 最近不少小伙伴反映上传小黄图偶尔性的异常,并且不能上传动态图片,很是苦恼!无她,鉴黄API还没有这么智能,毕竟是自己训练的,不是那么专业!为了更好的服务广大网友,撸主决定接入更加智能快速的鉴黄服 ...