Redis 介绍学习

一、Redis 简介html

Redis 是一个支持数据结构更多的键值对数据库。它的值不只能够是字符串等基本数据git

类型,也能够是类对象,更能够是 Set、List、计数器等高级的数据结构。
Memcached 也能够保存相似于 Set、List 这样的结构,可是若是说要向 List 中增长元素, Memcached 则须要把 List 所有元素取出来,而后再把元素增长进去,而后再保存回去,不 仅效率低,并且有并发访问问题。github

Redis 内置的 Set、List 等能够直接支持增长、删除元素的操做,效率很高,操做是原子的。redis

Memcached 数据存在内存中,memcached 重启后数据就消失;而 Redis 会把数据持久 数据库

化到硬盘中,Redis 重启后数据还存在。
二、Redis 的安装
redis for windows >=2.8 的版本支持直接安装为 windows 服务 https://github.com/MicrosoftArchive/redis
若是下载 msi 自动装完服务,若是下载 zip 须要按照下面的方法安装为服务: https://raw.githubusercontent.com/MSOpenTech/redis/3.0/Windows%20Service%20Documenta tion.md 编程

三、redis 的优势: json

  1. 1)  支持 string、list、set、geo 等复杂的数据结构。 windows

  2. 2)  高命中的数据运行时是在内存中,数据最终仍是能够保存到磁盘中,这样服务器重启以后数据还在。数组

  3. 3)  服务器是单线程的,来自全部客户端的全部命令都是串行执行的,所以不用担忧并发修改(串行操做固然仍是有并发问题)的问题,编程模型简单;缓存

  4. 4)  支持消息订阅/通知机制,能够用做消息队列;

  5. 5)  Key、Value 最大长度容许 512M;

四、redis 的缺点:

  1. 1)  Redis 是单线程的,所以单个 Redis 实例只能使用一个 CPU 核,不能充分发挥服务器的性能。能够在一台服务器上运行多个 Redis 实例,不一样实例监听不一样端口,再互相组成集群。

  2. 2)  作缓存性能不如 Memcached;

五、Memcached 的优势:
       1) 多线程,能够充分利用 CPU 多核的性能;

       2) 作缓存性能最高;

六、Memcached 的缺点:

  1. 1)  只能保存键值对数据,键值对只能是字符串,若是有对象数据只能本身序列化成 json字符串;

  2. 2)  数据保存在内存中,重启后会丢失;

  3. 3)  Key 最大长度 255 个字符,Value 最长 1M。

七、总结
      Memcached 只能当缓存服务器用,也是最合适的;Redis 不只能够作缓存服务器(性能没有 Memcached 好),还能够存储业务数据。

八、redis 命令行管理客户端:

       1)直接启动 redis 安装目录下的 redis-cli 便可。不用管恶心的自动提示。 执行 set name yzk,就是设置键值对 name=yzk 执行 get name 就是查找名字是 name 的值;  keys *是查找全部的 key  key *n*是查找全部名字中含有 n 的 key

       2) 和 Redis 同样,Redis 也是不一样系统放到 Redis 中的数据都是不隔离的,所以设定 Key 的 时候也要选择好 Key。
       3) Redis 服务器默认建了 16 个数据库,Redis 的想法是让你们把不一样系统的数据放到不一样 的数据库中。可是建议你们不要这样用,由于 Redis 是单线程的,不一样业务都放到同一个 Redis 实例的话效率就不高,建议放到不一样的实例中。

          所以尽可能只用默认的 db0数据库命令行下能够用 select0、select1 这样的指令切换数据库,最高为15。试试在不一样数据 库下新建、查询数据。

       4) 了解的经常使用的几个命令就能够。全部对数据的操做均可以经过命令行进行,后面讲 的.net 操做 Redis 的驱动其实就是对这些命令的封装。

九、redis GUI 管理客户端

     GUI 客户端很是多,我的推荐使用 RedisDesktopManager安装后点击【Connect to Redis Server】链接服务器。展开节点能够看到全部的 Key,双击 Key 能够查看 Key 的值。在根节点上点右键,选择 【Console】,这样就能够输入命令。

十、.net 操做 Redis

     用 StackExchange.Redis ,而不是 ServiceStack.Redis,由于 StackExchange.Redis 依赖组件 少,并且操做更接近原生的 redis 操做,ServiceStack 封装的太厉害,并且有过收费的“前科”。

Install-Package StackExchange.Redis
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(),尽可能用异步方法。

      注意看到访问的参数、返回值是 RedisKey、RedisValue 类型,进行了运算符重载,能够和 string、 byte[]之间进行隐式转换。

十一、Key 操做
       Key 操做:由于 Redis 里全部数据类型都是用 KeyValue 保存,所以 Key 操做针对全部数据类型, KeyDelete(RedisKey key):根据 Key 删除;KeyExists(RedisKey key)

      判断 Key 是否存在,尽可能不要用, 由于会有并发问题;KeyExpire(RedisKey key, TimeSpan?  expiry)、KeyExpire(RedisKey key, DateTime? expiry)设置过时时间;
十二、数据类型

       Redis 支持的数据结构:string、list、set、sortedset、hash、geo(redis 3.2 以上版本)。对应 的 Redis 客户端里的方法都是 StringXXX、HashXXX、GeoXXX 等方法。

      不一样数据类型的操做方 法不能混用,好比不能用 ListXXX 写入的值用 StringXXX 去读取或者写 入等操做。
1三、String 类型

      能够用 StringGet、StringSet 来读写键值对,是基础操做StringAppend(RedisKey key, RedisValue value):向 Key 的 Value 中附加内容,不存在则新建; 能够用做计数器:db.StringIncrement("count", 2.5);

      给 count 这个计数器增长一个值,若是不存在则从 0 开始加;db.StringDecrement("count",1)计数器减值;获取仍是用 StringGet()获取字符串类型的 值。好比能够用这个来计算新闻点击量、点赞量,效率很是高。     

private static string XinWen_Prefix = "WWW_XinWen_";

public async Task<ActionResult> Index(int id)

{
using (ConnectionMultiplexer redis = await ConnectionMultiplexer.ConnectAsync("localhost:6379")) {

IDatabase db = redis.GetDatabase();//默认是访问 db0 数据库,能够经过方法参数指定数字访 问不一样的数据库

//以 ip 地址和文章 id 为 key
string hasClickKey = XinWen_Prefix + Request.UserHostAddress + "_" + id;

//若是以前这个 ip 给这个文章贡献过点击量,则不重复计算点击量 if(await db.KeyExistsAsync(hasClickKey)==false)
{

await db.StringIncrementAsync(XinWen_Prefix + "XWClickCount" + id, 1); //记录一下这个 ip 给这个文章贡献过点击量,有效期一天 db.StringSet(hasClickKey, "a", TimeSpan.FromDays(1));

}

RedisValue clickCount = await db.StringGetAsync(XinWen_Prefix + "XWClickCount" + id); XinWenModel model = new XinWenModel();
model.ClickCount = Convert.ToInt32(clickCount);
return View(model);

}

return View(); }

1四、list 类型

      Redis 中用 List 保存字符串集合。 好比能够把聊天记录保存到 List 中;商品的物流信息记录。也  能够当成双向队列或者双向栈用,list 长度是无限。

      ListLeftPush(RedisKey key, RedisValue value)从左侧压栈;RedisValue ListLeftPop(RedisKey key) 从左侧弹出;

      ListRightPush(RedisKey key, RedisValue value ) 从右侧压栈;RedisValue ListRightPop(RedisKey key) 从右侧弹出;

      RedisValue ListGetByIndex(RedisKey key, long index)获取 Key 为 key 的 List 中第 index 个元素的值; long ListLength(RedisKey key) 获取 Key 为 key 的 List 中元素个数;尽可能不要用 ListGetByIndex、 ListLength 由于会有并发问题;。

     若是是读取而不 Pop,则使用 ListRange:RedisValue[] ListRange(RedisKey key, long start = 0, long stop = -1)。不传 start、end 表示获取全部数据。指定以后则获取某个范围。

      能够把 Redis 的 list 当成消息队列使用,好比向注册用户发送欢迎邮件的工做,能够在注册的流 程中把要发送邮件的邮箱放到 list 中,另外一个程序从 list 中 pop 获取邮件来发送。

      生产者、消费者模式。把生产过程和消费过程隔离。

1五、set 类型
      如你们所知,set 是一种元素不重复的集合。 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)获取集合中的元素;

      若是使用 set 保存封禁用 id 等,就不用作重复性判断了。

      注意 set 不是按照插入顺序遍历的,而是按照本身的一个存储方式来遍历,由于没有保存插入的 顺序。

1六、sortedset
     若是对于数据遍历顺序有要求,可使用 sortedset,他会按照打分来进行遍历。 SortedSetAdd(RedisKey key, RedisValue member, double score) 在 key 这个 sortedset 中增长member,而且给这个 member 打分,若是 member 已经存在,则覆盖以前的打分;       doubleSortedSetIncrement(RedisKeykey,RedisValuemember,doublevalue) 给key中member这一项增长 value 分;

     double SortedSetDecrement(RedisKey key, RedisValue member, double value):给 key 中 member 这一项减 value 分; 

     SortedSetEntry[] SortedSetRangeByRankWithScores(RedisKey key, long start = 0, long stop = -1,Orderorder=Order.Ascending) 根据排序返回sortedset中的元素以及元素的打分,start、stop用来分页 查询、order 用来指定排序规则。

     测试:

db.SortedSetIncrement("Hotwords", "test", 1);

db.SortedSetIncrement("Hotwords", "test", 1); db.SortedSetIncrement("Hotwords", "test", 1); db.SortedSetIncrement("Hotwords", "杨中科", 1); db.SortedSetIncrement("Hotwords", "侯宝林", 1); db.SortedSetIncrement("Hotwords", "侯宝林", 1);
SortedSetEntry[] items = db.SortedSetRangeByRankWithScores("Hotwords"); foreach(var item in items)

{
Console.WriteLine(item.Element+"="+item.Score);

}

 RedisValue[] SortedSetRangeByRank(RedisKey key, long start = 0, long stop = -1, Order order =Order.Ascending) 根据打分排序返回值,能够根据序号查询其中一部分;

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

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

    sortedset 应用场景:

      1) 用户每搜一次一个关键词,就给这个关键词加一分;展现热搜的时候就把前 N 个获取出来就好了;

      2) 高积分用户排行榜;

      3) 热门商品;

       4) 给宝宝投票;

1七、Hash

      至关于 value 又是一个“键值对集合”或者值是另一个 Dictionary。 没想到有什么应用场景。

1八、Geo 类型

Geo 是 Redis 3.2 版本后新增的数据类型,用来保存兴趣点(POI,point of interest)的坐标信息。

能够实现计算两 POI 之间的距离、获取一个点周边指定距离的 POI。 下面添加兴趣点数据,”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"));

GeoRemove(RedisKey key, RedisValue member)删除一个点

查询两个 POI 之间的举例:double? dist = db.GeoDistance("ShopsGeo", "1", "5", GeoUnit.Meters);// 最后一个参数为距离单位根据点的主键获取坐标:GeoPosition? pos = db.GeoPosition("ShopsGeo", "1")

获取一个 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); }

  获取一个坐标(这个坐标不必定是 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

1九、Redis 的批量操做

若是一次性操做不少,会很慢,那么可使用批量操做,两种方式: 1)几乎全部的操做都支持数组类型,这样就能够一次性操做多条数据:好比

GeoAdd(RedisKey key, GeoEntry[] values)、SortedSetAdd(RedisKey key, SortedSetEntry[] values) 2) 若是一次性的操做不是简单的同类型操做,那么就要使用批量模式:
IBatch batch = db.CreateBatch();
db.GeoAdd("ShopsGeo1", new GeoEntry(116.34039, 39.94218, "1"));

db.StringSet("abc", "123"); batch.Execute();

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

20、redis 分布式锁

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

      

RedisValue token = Environment.MachineName;

//实际项目秒杀此处可换成商品 ID

if (db.LockTake("mylock", token, TimeSpan.FromSeconds(10)))//第三个参数为锁超时时间,锁占 用最多 10 秒钟,超过 10 秒钟若是尚未 LockRelease,则也自动释放锁,避免了死锁
{

try {

} finally {

db.LockRelease("mylock", token); }

} else {

Console.WriteLine("得到锁失败"); 
}
相关文章
相关标签/搜索