Redis学习笔记
工具:
GUI客户端管理工具 redis-desktop-manager
用StackExchange.Redis二次封装的组件:依赖组减少,更接近原生redis操作。
Install-Package StackExchange.Redis
简介
Redis是一个支持数据结构更多的键值对数据库。不仅支持字符串等基本类型还支持类对象,更可以是Set、List、计数器等高级的数据结构
Memcached也可以保存类似于Set、List这样结构的数据,但如果要向List中添加元素,则需要把数据全部拿出来,添加数据,保存回去,不仅效率低,而且有并发访问的问题。
Redis内置的Set、List等可以直接支持添加、删除元素的操作,效率很高,操作是原子性的。
Memcached是将数据保存在内存中,重启就会消失;Redis会把数据持久化到硬盘中,Redis重启后数据还在。
对比:
Redis的优点:
- 支持 string、list、set、geo 等复杂的数据结构。
- 高命中的数据运行时是在内存中,数据最终还是可以保存到磁盘中,这样服务器重启之后数据还在。
- 服务器是单线程的,来自所有客户端的所有命令都是串行执行的,因此不用担心并发修改(串行操作当然还是有并发问题)的问题,编程模型简单;
- 支持消息订阅/通知机制,可以用作消息队列;
- Key、Value 最大长度允许 512M;
Redis缺点:
- Redis 是单线程的,因此单个 Redis 实例只能使用一个 CPU 核,不能充分发挥服务器的性能。可以在一台服务器上运行多个 Redis 实例,不同实例监听不同端口,再互相组成集群
- 做缓存性能不如 Memcached
Memcached的优点:
- 多线程,可以充分利用CPU多核的性能。
- 做缓存性能最高。
Memcached的缺点:
- .只能保存键值对数据,键值对只能是字符串,如果有对象数据只能自己序列化成 json字符串;
- 数据保存在内存中,重启后会丢失;
- Key 最大长度 255 个字符,Value 最长 1M。
总结:
memcached只能当缓存服务器用也最适合。Redis不仅能可以做缓存还可以保存业务数据
.net操作Redis
Install-Package StackExchange.Redis
1 using StackExchange.Redis; 2 3 private async void button1_Click(object sender, EventArgs e) 4 { 5 using (ConnectionMultiplexer redis= ConnectionMultiplexer.Connect("localhost:6379")) 6 { 7 IDatabase db= redis.GetDatabase(); 8 db.StringSet("name", "cjl"); 9 await db.StringSetAsync("age", "30"); 10 } 11 MessageBox.Show("写入成功"); 12 } 13 14 //支持设置过期时间:db.StringSet("name", "rupeng.com", TimeSpan.FromSeconds(10)) 15 //获取数据:string s = db.StringGet("Name")如果查不到则返回 null
Redis中所有方法几乎都支持异步
key-value的参数类型是:RedisKey、RedisValue,这是对string类型进行了重载可以在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 去读取或者写入等操作。
string类型
可以用 StringGet、StringSet 来读写键值对,是基础操作
StringAppend(RedisKey key, RedisValue value):向 Key 的 Value 中附加内容,不存在则新建;
计数器:db.StringIncrement("count", 2.5);给 count 这个计数器增加一个值,如果不存在则从 0 开始加;db.StringDecrement("count",1)计数器减值;
获取还是用 StringGet()获取字符串类型的值。
比如:可以用这个来计算新闻点击量、点赞量,效率非常高。
1 public async Task<ActionResult> Index(int id) 2 { 3 //id用于区分不同新闻 4 using (ConnectionMultiplexer redis=await ConnectionMultiplexer.ConnectAsync("localhost:6379")) 5 { 6 IDatabase db=redis.GetDatabase(); 7 //如果做防恶意点击需要获取用户的IP地址即可 8 await db.StringIncrementAsync("News_cc" + id, 1); 9 RedisValue cCount= await db.StringGetAsync("News_cc" + id); 10 NewsModel model = new NewsModel(); 11 model.ClickCount = Convert.ToInt32(cCount); 12 return View(model); 13 } 14 15 }
List类型
Redis 中用 List 保存字符串集合。
比如可以把聊天记录保存到 List 中;商品的物流信息记录。也可以当成双向队列或者双向栈用,list 长度是无限。不用担心左右侧同时使用不够长的问题
1 ListLeftPush(RedisKey key, RedisValue value)//从左侧压栈;
2 RedisValue ListLeftPop(RedisKey key)//从左侧弹出;
3 ListRightPush(RedisKey key, RedisValue value )// 从右侧压栈;RedisValue ListRightPop(RedisKey key) //从右侧弹出;
4 RedisValue ListGetByIndex(RedisKey key, long index)//获取Key为key的List中第index个元素的值;
5 long ListLength(RedisKey key)// 获取 Key 为 key 的 List 中元素个数;尽量不要用 ListGetByIndex、ListLength 因为会有并发问题。
6 //如果是读取而不 Pop,则使用:
7 RedisValue[] ListRange(RedisKey key, long start = 0, long stop = -1)。
8 //不传 start、end 表示获取所有数据。指定之后则获取某个范围。
可以把 Redis 的 list 当成消息队列使用,
比如向注册用户发送欢迎邮件的工作,可以在注册的流程中把要发送邮件的邮箱放到 list 中,另一个程序从 list 中 pop 获取邮件来发送。
生产者、消费者模式。把生产过程和消费过程隔离
以网站注册成功发送邮件流程为实例:用户注册成功后,需要向用户发送一个邮件确认注册信息,这个过程中当用户注册成功则向redis中的List存入用户信息,
此时redis所在服务器有另外一个程序在监听(定时任务,windows服务)List中是否有数据,有则pop数据,也就是向用户发送注册信息的过程
Set及SortedSet数据类型
Set是一种元素不重复的集合
注意 set 不是按照插入顺序遍历的,而是按照自己的一个存储方式来遍历,因为没有保存插入的顺序。
1 SetAdd(RedisKey key, RedisValue value)//向 set 中增加元素
2 bool SetContains(RedisKey key, RedisValue value)// 判断 set 中是否存在某个元素;
3 long SetLength(RedisKey key) //获得 set 中元素的个数;
4 SetRemove(RedisKey key, RedisValue value)//从 set 中删除元素;
5 RedisValue[] SetMembers(RedisKey key)//获取集合中的元素;
6 //如果使用 set 保存封禁用 id 等,就不用做重复性判断了。
应用场景: 用户拉黑
SortedSet
如果对于数据遍历顺序有要求,可以使用 sortedset,他会按照打分来进行遍历。
1 SortedSetAdd(RedisKey key, RedisValue member, double score) //在 key 这个 sortedset 中增加member,并且给这个 member 打分,如果 member 已经存在,则覆盖之前的打分;
2 double SortedSetIncrement(RedisKey key, RedisValue member, double value) //给 key中 member 这一项增加 value 分;
3 double SortedSetDecrement(RedisKey key, RedisValue member, double value)://给 key 中 member 这一项减 value 分;
4 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 这个范围的打分;
sortedSet的应用场景:
用户每搜一次一个关键词,就给这个关键词加一分,展示热搜的时候就把前 N 个获取出来就行了
高积分用户排行榜;
热门商品
给宝宝投票
Hash
相当于value又是一个键值对集合,或者value是另一个Dictionary
Geo类型
Geo 是 Redis 3.2 版本后新增的数据类型,用来保存兴趣点(POI,point of interest)的坐标信息。
可以实现计算两 POI 之间的距离、获取一个点周边指定距离的 POI
//下面添加兴趣点数据,”1”、”2”是点的主键,点的名称、地址、电话等存到其他表中
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);
}
Redis的批量操作
如果一次性操作很多,会很慢,那么可以使用批量操作。
① 几乎所有的操作都支持 数组类型,这样就可以一次性操作多条数据:
GeoAdd(RedisKey key, GeoEntry[] values);
SortedSetAdd(RedisKey key, SortedSetEntry[] values);
② 如果一次性的操作不是简单的同类型操作,那么就要使用批量模式:
1 IBatch batch = db.CreateBatch(); 2 db.GeoAdd("ShopsGeo1", new GeoEntry(116.34039, 39.94218, "1")); 3 db.StringSet("abc", "123"); 4 batch.Execute();
会把当前连接的 CreateBatch()、Execute()之间的操作一次性提交给服务器。
Redis中的分布式锁
类似于sql中的乐观锁;
多线程中的 lock 等的作用范围是当前的程序范围内的,如果想跨多台服务器的锁(尽量避免这样搞),就要使用分布式锁。

浙公网安备 33010602011771号