Redis是一个支持数据结构更多的键值对数据库。它的值不仅可以是字符串等基本数据类型,也可以是类对象,更可以是Set、List、计数器等高级的数据结构。

  Memcached也可以保存类似于Set、List这样的结构,但是如果说要向List中增加元素,Memcached则需要把List全部元素取出来,然后再把元素增加进去,然后再保存回去,不仅效率低,而且有并发访问问题。Redis内置的Set、List等可以直接支持增加、删除元素的操作,效率很高,操作是原子的。

Memcached数据存在内存中,memcached重启后数据就消失;

Redis会把数据持久化到硬盘中,Redis重启后数据还存在。

1 安装

redis for windows >=2.8的版本支持直接安装为windows服务(Redis-x64-3.2.100.msi才可以,zip不行)

https://github.com/MicrosoftArchive/redis

如果下载msi自动装完服务,如果下载zip需要按照下面的方法安装为服务:

http://www.runoob.com/redis/redis-install.html

2 redis与Memcached 区别

2.1 redis优缺点

2.1.1 redis的优点:

  1. 支持string、list、set、geo等复杂的数据结构。
  2. 高命中的数据运行时是在内存中,数据最终还是可以保存到磁盘中,这样服务器重启之后数据还在。
  3. 服务器是单线程的,来自所有客户端的所有命令都是串行执行的,因此不用担心并发修改(串行操作当然还是有并发问题)的问题,编程模型简单;
  4. 支持消息订阅/通知机制,可以用作消息队列;
  5. Key、Value最大长度允许512M;

2.1.2 redis的缺点:

  1. Redis是单线程的,因此单个Redis实例只能使用一个CPU核,不能充分发挥服务器的性能。可以在一台服务器上运行多个Redis实例,不同实例监听不同端口,再互相组成集群。
  2. 做缓存性能不如Memcached;

2.2 Memcached的优缺点

2.2.1 Memcached的优点:

  1. 多线程,可以充分利用CPU多核的性能;
  2. 做缓存性能最高;

2.2.2 Memcached的缺点:

  1. 只能保存键值对数据,键值对只能是字符串,如果有对象数据只能自己序列化成json

    字符串;
  2. 数据保存在内存中,重启后会丢失;
  3. Key最大长度255个字符,Value最长1M。

2.3 总结

Memcached只能当缓存服务器用,也是最合适的;Redis不仅可以做缓存服务器(性能没有Memcached好),还可以存储业务数据。

3 redis命令行管理客户端

3.1 直接启动redis安装目录下的redis-cli即可。

执行set myKey abc,就是设置键值对myKey=abc

执行get myKey就是查找名字是myKey的值;

keys *是查找所有的key

key *n*是查找所有名字中含有n的key

3.2 数据没有隔离性

和Redis一样,Redis也是不同系统放到Redis中的数据都是不隔离的,因此设定Key的时候也要选择好Key。

3.3 尽量选用默认的数据库

Redis服务器默认建了16个数据库,Redis的想法是让大家把不同系统的数据放到不同的数据库中。但是建议大家不要这样用,因为Redis是单线程的,不同业务都放到同一个Redis实例的话效率就不高,建议放到不同的实例中。因此尽量只用默认的db0数据库。

命令行下可以用select 0、select 1这样的指令切换数据库,最高为15。试试在不同数据库下新建、查询数据。

了解的常用的几个命令就可以。所有对数据的操作都可以通过命令行进行,后面讲的.net操作Redis的驱动其实就是对这些命令的封装。

4 GUI管理客户端

RedisDesktopManager (0.9.3以后需要订阅)

https://github.com/uglide/RedisDesktopManager/releases

5 .NET连接redis

推荐组件:StackExchange.Redis

https://stackexchange.github.io/StackExchange.Redis/

其他作品:

NewLife.Redis基础教程

https://www.cnblogs.com/nnhy/p/icache.html

using (ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379"))
{ IDatabase db = redis.GetDatabase();
//默认是访问db0数据库,可以通过方法参数指定数字访问不同的数据库 db.StringSet("Name", "abc");
}

支持设置过期时间:

db.StringSet("name", "rupeng.com", TimeSpan.FromSeconds(10))

获取数据:

string s = db.StringGet("Name")
//如果查不到则返回null

Redis里所有方法几乎都支持异步,比如StringGetAsync()StringSetAsync(),尽量用异步方法。

注意看到访问的参数、返回值是RedisKeyRedisValue类型,进行了运算符重载,可以和string

byte[]之间进行隐式转换。

6 命令

6.1 键(Key)

因为Redis里所有数据类型都是用KeyValue保存,因此Key操作针对所有数据类型,

  • KeyDelete(RedisKey key):根据Key删除;
  • KeyExists(RedisKey key)判断Key是否存在,尽量不要用,因为会有并发问题;
  • KeyExpire(RedisKey key, TimeSpan? expiry)KeyExpire(RedisKey key, DateTime? expiry) 设置过期时间;

6.2 字符串(String)

可以用 StringGetStringSet 来读写键值对,是基础操作

StringAppend(RedisKey key, RedisValue value):向Key的Value中附加内容,不存在则新建;

6.2.1 场景:计数器

可以用作计数器:db.StringIncrement("count", 2.5); 给 count 这个计数器增加一个值,如果不存在

则从0开始加;db.StringDecrement("count",1)计数器减值;获取还是用StringGet()获取字符串类型的

值。比如可以用这个来计算新闻点击量、点赞量,效率非常高。

public class NewsController : Controller
{
private string NEWSPREFIX = "WX_NEWS_";
// GET: News
public async Task<ActionResult> Index(int id)
{ using (ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379"))
{ IDatabase db = redis.GetDatabase();
//默认是访问db0数据库,可以通过方法参数指定数字访问不同的数据库
string clickCount = NEWSPREFIX + Request.UserHostAddress + "_ClickCount_" + id; // Task<long> StringIncrementAsync:
// 返回值:The value of key after the increment. (递增后的值)
long increment = await db.StringIncrementAsync(clickCount); //RedisValue count = await db.StringGetAsync(clickCount);
//ViewBag.count = count; ViewBag.count = increment;
} return View();
}
}

index.cshtml

<h2>点击量:@ViewBag.count</h2>

6.2 列表(List)

  • Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
  • 一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

6.2.1 常用方法

1.从左侧压栈:ListLeftPush(RedisKey key, RedisValue value)

2.从左侧弹出:RedisValue ListLeftPop(RedisKey key)

3.从右侧压栈:ListRightPush(RedisKey key, RedisValue value )

4.从右侧弹出:RedisValue ListRightPop(RedisKey key)

5.获取Key为key的List中第index个元素的值:RedisValue ListGetByIndex(RedisKey key, long index) ;

6.获取Key为key的List中元素个数:long ListLength(RedisKey key) ;

尽量不要用ListGetByIndex、ListLength因为会有并发问题。

如果是读取而不pop,则使用 ListRange:RedisValue[] ListRange(RedisKey key, long start = 0, long stop = -1)。不传 startend 表示获取所有数据。指定之后则获取某个范围。

6.2.2 应用场景

  可以把Redis的list当成消息队列使用,比如向注册用户发送欢迎邮件的工作,可以在注册的流程中把要发送邮件的邮箱放到list中,另一个程序从list中pop获取邮件来发送。 生产者、消费者模式。把生产过程和消费过程隔离。

6.3 集合(Set)

  • Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
  • Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
  • 集合中最大的成员数为 232- 1 (4294967295, 每个集合可存储40多亿个成员)。

List与Set区别:

6.3.1 常用方法

SetAdd(RedisKey key, RedisValue value) 向set中增加元素

bool SetContains(RedisKey key, RedisValue value) 判断set中是否存在某个元素;

long SetLength(RedisKey key) 获得set中元素的个数;

SetRemove(RedisKey key, RedisValue value)从set中删除元素;

RedisValue[] SetMembers(RedisKey key)获取a集合中的元素;

如果使用set保存封禁用id等,就不用做重复性判断了。

6.4 有序集合(sorted set)

  • Redis 有序集合。

    与Set不同的是,每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

  • 有序集合的成员是唯一的,但分数(score)却可以重复。

  • 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232- 1 (4294967295, 每个集合可存储40多亿个成员)。

6.4.1 常用方法

SortedSetAdd(RedisKey key, RedisValue member, double score)

在key这个sortedset中增加member,并且给这个member打分,如果member已经存在,则覆盖之前的打分;

double SortedSetIncrement(RedisKey key, RedisValue member, double value)

给key中member这一项增加value分;

double SortedSetDecrement(RedisKey key, RedisValue member, double value)

给key中member这一项减value分;

SortedSetEntry[] SortedSetRangeByRankWithScores(RedisKey key, long start = 0, long stop = -1, Order order = Order.Ascending)

根据排序返回 sortedset中的元素以及元素的打分,start、stop用来分页查询、order用来指定排序规则。

RedisValue[] SortedSetRangeByRank(RedisKey key, long start = 0, long stop = -1, Order order = Order.Ascending)

根据打分排序返回值,可以根据序号查询其中一部分;

RedisValue[] SortedSetRangeByScore(RedisKey key, double start = double.NegativeInfinity, double stop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1)

根据打分排序返回值,可以只返回start- stop 这个范围的打分;

6.4.2 sortedset应用场景

  1. 用户每搜一次一个关键词,就给这个关键词加一分;展示热搜的时候就把前N个获取出来就行了;
  2. 高积分用户排行榜;
  3. 热门商品;
  4. 给宝宝投票;

6.5 哈希(Hash)

  • Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
  • Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。

6.5.1 应用场景

存储文章数据

文章对象序列化后使用一个字符串类型键存储,可是这种方法无法提供对单个字段的原子读写操作,从而产生竞态条件。如两个客户端同事修改不同属性存储,后者覆盖前者。

使用多个字符串类型键存储一个对象,好处是只要修改一处属性,十分方便。



使用一个散列类型键存储一个对象更适合。散列更适合这个场景。

6.6 GEO 地理位置

  • Geo是Redis 3.2版本后新增的数据类型,用来保存兴趣点(POI,point of interest)的坐标信息。
  • 可以实现计算两POI之间的距离、获取一个点周边指定距离的POI。

6.6.1 常用方法

1.下面添加兴趣点数据,”1”、”2”是点的主键,点的名称、地址、电话等存到其他表中。

db.GeoAdd("ShopsGeo", new GeoEntry(116.34039, 39.94218,"1"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.340934, 39.942221, "2"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.341082, 39.941025, "3"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.340848, 39.937758, "4"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.342982, 39.937325, "5"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.340866, 39.936827, "6"));

2.删除一个点

GeoRemove(RedisKey key, RedisValue member)

3.根据点的主键获取坐标:

GeoPosition? pos = db.GeoPosition("ShopsGeo", "1")

4.查询两个POI之间的距离:

double? dist = db.GeoDistance("ShopsGeo", "1", "5", GeoUnit.Meters);//最后一个参数为距离单位

5.获取一个POI周边的POI:

GeoRadiusResult[] results = db.GeoRadius("ShopsGeo", "2", 200, GeoUnit.Meters);//获取”2”这个周边200米范围内的POI
foreach(GeoRadiusResult result in results)
{
Console.WriteLine("Id="+result.Member+",位置"+result.Position+",距离"+result.Distance);
}

6.获取一个坐标(这个坐标不一定是POI)周边的POI:

GeoRadiusResult[] results = db.GeoRadius("ShopsGeo", 116.34092, 39.94223, 200, GeoUnit.Meters);// 获取(116.34092, 39.94223)这个周边200米范围内的POI
foreach(GeoRadiusResult result in results)
{
Console.WriteLine("Id="+result.Member+",位置"+result.Position+",距离"+result.Distance);
}

Geo Hash原理:http://www.cnblogs.com/LBSer/p/3310455.html

7 批量操作

如果一次性操作很多,会很慢,那么可以使用批量操作,两种方式:

  1. 几乎所有的操作都支持数组类型,这样就可以一次性操作多条数据:比如
GeoAdd(RedisKey key, GeoEntry[] values)
SortedSetAdd(RedisKey key, SortedSetEntry[] values)
  1. 如果一次性的操作不是简单的同类型操作,那么就要使用批量模式:
IBatch batch = db.CreateBatch();
db.GeoAdd("ShopsGeo1", new GeoEntry(116.34039, 39.94218, "1"));
db.StringSet("abc", "123");
batch.Execute();

会把当前连接的CreateBatch()、Execute() 之间的操作一次性提交给服务器。

8 分布式锁

多线程中的lock等的作用范围是当前的程序范围内的,如果想跨多台服务器的锁(尽量避免这样搞),就要使用分布式锁

using (ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379"))
{
IDatabase db = redis.GetDatabase(); RedisValue token = Environment.MachineName;
//实际项目秒杀此处可换成商品ID
//第三个参数为锁超时时间,锁占用最多10秒钟,超过10秒钟如果还没有LockRelease,则也自动释放锁,避免了死锁
if (db.LockTake("mylock", token, TimeSpan.FromSeconds(10)))
{
try
{
Console.WriteLine("操作开始~");
Thread.Sleep(30000);
Console.WriteLine("操作完成~");
}
finally
{
db.LockRelease("mylock", token);
}
}
else
{
Console.WriteLine("获得锁失败");
}
Console.ReadKey();
}

9 抢红包案例

  • 把这个红包数组以List的形式存到Redis中;
  • 用户抢红包就是从List中Pop取红包。

发出一个固定金额的红包,由若干个人来抢,需要满足哪些规则?

1.所有人抢到金额之和等于红包金额,不能超过,也不能少于。

2.每个人至少抢到一分钱。

3.要保证所有人抢到金额的几率相等。

9.1 二倍均值法

参考:程序员小灰——漫画:如何实现抢红包算法?

剩余红包金额为M,剩余人数为N,那么有如下公式:

每次抢到的金额 = 随机区间 (0, M / N X 2)

这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。

举个栗子:

假设有10个人,红包总额100元。100/10X2 = 20, 所以第一个人的随机范围是(0,20 ),平均可以抢到10元。

假设第一个人随机到10元,那么剩余金额是100-10 = 90 元。90/9X2 = 20, 所以第二个人的随机范围同样是(0,20 ),平均可以抢到10元。

假设第二个人随机到10元,那么剩余金额是90-10 = 80 元。80/8X2 = 20, 所以第三个人的随机范围同样是(0,20 ),平均可以抢到10元。

以此类推,每一次随机范围的均值是相等的。

        static void Main(string[] args)
{
//例子:50元分配10个人
List<int> amountList = divideRedPackage(5000, 10);
foreach (double amount in amountList)
{
double item = (amount / 100);
Console.WriteLine($"抢到金额:{item}");
}
} //发红包算法,金额参数以分为单位
public static List<int> divideRedPackage(int totalAmount, int totalPeopleNum)
{
List<int> amountList = new List<int>();
int restAmount = totalAmount;
int restPeopleNum = totalPeopleNum;
Random random = new Random();
for (int i = 0; i < totalPeopleNum - 1; i++)
{
//随机范围:[1,剩余人均金额的两倍),左闭右开
int amount = random.Next(restAmount / restPeopleNum * 2 - 1) + 1;
restAmount -= amount;
restPeopleNum--;
amountList.Add(amount);
}
amountList.Add(restAmount);
return amountList;
}

Redis .NET操作的更多相关文章

  1. spring data redis RedisTemplate操作redis相关用法

    http://blog.mkfree.com/posts/515835d1975a30cc561dc35d spring-data-redis API:http://docs.spring.io/sp ...

  2. Spring Framework 中启动 Redis 事务操作

    背景: 项目中遇到有一系列对Redis的操作,并需要保持事务处理. 环境: Spring version 4.1.8.RELEASE Redis Server 2.6.12 (64位) spring- ...

  3. php的redis 操作类,适用于单台或多台、多组redis服务器操作

    redis 操作类,包括单台或多台.多组redis服务器操作,适用于业务复杂.高性能要求的 php web 应用. redis.php: <?php /* redis 操作类,适用于单台或多台. ...

  4. 【springboot】【redis】springboot结合redis,操作List集合实现时间轴功能

    springboot结合redis,操作List集合实现时间轴功能

  5. php redis队列操作

    php redis队列操作 rpush/rpushx 有序列表操作,从队列后插入元素:lpush/lpushx 和 rpush/rpushx 的区别是插入到队列的头部,同上,'x'含义是只对已存在的 ...

  6. redis下操作hash对象

    redis下操作hash对象 hash用于存储对象,对象的格式为键值对 命令 设置 设置单个属性 HSET key field value 设置多个属性 HMSET key field value [ ...

  7. Redis字符串操作

      字符串命令 (基本用法) GET : 获取给定键的值 SET : 设置给定键的值 DEL : 删除给定键的值(这个命令可以用于任何类型) (自增命令和自减命令) INCR : INCR key-n ...

  8. 简单的redis缓存操作(get、put)

    简单的redis缓存操作(get.put) 本文介绍简单的redis缓存操作,包括引入jedisjar包.配置redis.RedisDao需要的一些工具.向redis中放数据(put).从redis中 ...

  9. 封装php redis缓存操作类

    封装php redis缓存操作类,集成了连接redis并判断连接是否成功,redis数据库选择,检测redis键是否存在,获取值,写入值,设置生存时间和删除清空操作. php redis类代码: &l ...

  10. Spring boot配置多个Redis数据源操作实例

    原文:https://www.jianshu.com/p/c79b65b253fa Spring boot配置多个Redis数据源操作实例 在SpringBoot是项目中整合了两个Redis的操作实例 ...

随机推荐

  1. Python中的 一些常用技巧函数[.join()]

    1.str.join(item)字符串操作函数,参数item可以是字符串.元组.字典,示例 ','.join('abc') [','.join('abc')] 输出: 'a,b,c'['a', 'b' ...

  2. LODOP、C-Lodop简短排查语句

    https使用,故障:1.是https网站吗,https扩展版C-Lodop如何使用 参考http://www.c-lodop.com/faq/pp32.html2.双击桌面上的c-lodop快捷方式 ...

  3. Vue学习笔记5

    列表渲染 用 v-for 把一个数组对应为一组元素 <div id="app"> <li v-for = "item in array"> ...

  4. git 学习(3) ----- 代码共享和多人协作

    当我们开发项目的时候,项目会越来越大,就有可能需要其它同事进行参与,甚至进行开源,这时就需要找一个地方把代码存放起来,好供其它人下载并开发.这个地方,最好放到服务器上,因为只要能上网,就可以获取到, ...

  5. 启动jar的方式

    1.windows server 2008 start "srvRegistry" java -jar srvRegistry-1.0-SNAPSHOT.jar --spring. ...

  6. 洛谷P2634 聪明可可

    还是点分治 树上问题真有趣ovo,这道题统计模3为0的距离,可以把重心的子树分开统计,也可以一次性统计,然后容斥原理减掉重复的.. 其他的过程就是点分治的板子啦. #include <bits/ ...

  7. Yii2.0 安装使用报错:yii\web\Request::cookieValidationKey must be configured with a secret key.

    下载了Yii2.0的basic版,配置好apache之后,浏览器访问,出现如下错误: Invalid Configuration – yii\base\InvalidConfigException y ...

  8. 二、java基本语法

    一.标识符 java对各种变量.方法和类等要素命名时使用的字符序列成为标识符:通俗点,凡是自己可以起名字的地方都叫标识符,都遵守标识符的规则 1.标识符命名规则: 1)标识符由字符.下划线.美元符或数 ...

  9. PowerDesigner生成pdm(适用Mysql)

    废话不多说,直接开始: 1.首先安装所需要的驱动以及应用程序 ①和② 是 Mysql数据库连接驱动 ,根据PowerDesigner的位数来选择下载 下载地址:https://dev.mysql.co ...

  10. DirectX11 With Windows SDK--13 动手实现一个简易Effects框架、阴影效果绘制

    前言 到现在为止,所有的教程项目都没有使用Effects11框架类来管理资源.因为在D3DCompile API (#47)版本中,如果你尝试编译fx_5_0的效果文件,会收到这样的警告: X4717 ...