1.简介
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),可以使用list的push操作将任务存到List,
然后使用pop操作将任务取出执行,在列表头部或者末尾操作数据非常高效,不受队列长度的影响。
列表最多可存储 232- 1 元素 (4294967295, 每个列表可存储40多亿)。
2.使用场景
redis 的 list 数据类型对于大部分使用者来说,是实现队列服务的最经济,最简单的方式。
另外,因为 list 结构的数据查询两端附近的数据性能非常好,所以适合一些需要获取最新数据的场景,比如新闻类应用的 “最近新闻”,获取前N个用户列表等。
3.list数据结构示意图
list类型在存储数据时,是以key-LinkList<value>格式存储的,如下图所示:
如下是对list封装后的api方法,RedisListService.cs,代码如下:
using MyRedis.Redis.Interface;
using ServiceStack.Redis;
using System;
using System.Collections.Generic;
namespace MyRedis.Redis.Service
{
public class RedisListService : RedisBase
{
#region Queue队列(先进先出)
/// <summary>
/// 入队
/// </summary>
/// <param name="listId">集合Id</param>
/// <param name="value">入队的值</param>
public void EnqueueItemOnList(string listId, string value)
{
base.iClient.EnqueueItemOnList(listId, value);
}
/// <summary>
/// 出队
/// </summary>
/// <param name="listId">集合Id</param>
/// <returns>出队的值</returns>
public string DequeueItemFromList(string listId)
{
return base.iClient.DequeueItemFromList(listId);
}
/// <summary>
/// 出队(阻塞)
/// </summary>
/// <param name="listId">集合Id</param>
/// <param name="timeOut">阻塞时间(超时时间)</param>
/// <returns>出队的值</returns>
public string BlockingDequeueItemFromList(string listId, TimeSpan? timeOut)
{
return base.iClient.BlockingDequeueItemFromList(listId, timeOut);
}
/// <summary>
/// 从多个list中出队(阻塞)
/// </summary>
/// <param name="listIds">集合Id</param>
/// <param name="timeOut">阻塞时间(超时时间)</param>
/// <returns>返回出队的 listId & Item</returns>
public ItemRef BlockingDequeueItemFromLists(string[] listIds, TimeSpan? timeOut)
{
return base.iClient.BlockingDequeueItemFromLists(listIds, timeOut);
}
#endregion Queue队列(先进先出)
#region Stack栈(后进先出)
/// <summary>
/// 入栈
/// </summary>
/// <param name="listId">集合Id</param>
/// <param name="value">入栈的值</param>
public void PushItemToList(string listId, string value)
{
base.iClient.PushItemToList(listId, value);
}
/// <summary>
/// 入栈,并设置过期时间
/// </summary>
/// <param name="listId">集合Id</param>
/// <param name="value">入栈的值</param>
/// <param name="expireAt">过期时间</param>
public void PushItemToList(string listId, string value, DateTime expireAt)
{
base.iClient.PushItemToList(listId, value);
base.iClient.ExpireEntryAt(listId, expireAt);
}
/// <summary>
/// 入栈,并设置过期时间
/// </summary>
/// <param name="listId">集合Id</param>
/// <param name="value">入栈的值</param>
/// <param name="expireIn">过期时间</param>
public void PushItemToList(string listId, string value, TimeSpan expireIn)
{
base.iClient.PushItemToList(listId, value);
base.iClient.ExpireEntryIn(listId, expireIn);
}
/// <summary>
/// 出栈
/// </summary>
/// <param name="listId">集合Id</param>
/// <returns>出栈的值</returns>
public string PopItemFromList(string listId)
{
return base.iClient.PopItemFromList(listId);
}
/// <summary>
/// 出栈(阻塞)
/// </summary>
/// <param name="listId">集合Id</param>
/// <param name="timeOut">阻塞时间(超时时间)</param>
/// <returns>出栈的值</returns>
public string BlockingPopItemFromList(string listId, TimeSpan? timeOut)
{
return base.iClient.BlockingPopItemFromList(listId, timeOut);
}
/// <summary>
/// 从多个list中出栈一个值(阻塞)
/// </summary>
/// <param name="listIds">集合Id</param>
/// <param name="timeOut">阻塞时间(超时时间)</param>
/// <returns>返回出栈的 listId & Item</returns>
public ItemRef BlockingPopItemFromLists(string[] listIds, TimeSpan? timeOut)
{
return base.iClient.BlockingPopItemFromLists(listIds, timeOut);
}
/// <summary>
/// 从fromListId集合出栈并入栈到toListId集合
/// </summary>
/// <param name="fromListId">出栈集合Id</param>
/// <param name="toListId">入栈集合Id</param>
/// <returns>返回移动的值</returns>
public string PopAndPushItemBetweenLists(string fromListId, string toListId)
{
return base.iClient.PopAndPushItemBetweenLists(fromListId, toListId);
}
/// <summary>
/// 从fromListId集合出栈并入栈到toListId集合(阻塞)
/// </summary>
/// <param name="fromListId">出栈集合Id</param>
/// <param name="toListId">入栈集合Id</param>
/// <param name="timeOut">阻塞时间(超时时间)</param>
/// <returns>返回移动的值</returns>
public string BlockingPopAndPushItemBetweenLists(string fromListId, string toListId, TimeSpan? timeOut)
{
return base.iClient.BlockingPopAndPushItemBetweenLists(fromListId, toListId, timeOut);
}
#endregion Stack栈(后进先出)
#region 赋值
/// <summary>
/// 向list头部添加value值
/// </summary>
public void PrependItemToList(string listId, string value)
{
base.iClient.PrependItemToList(listId, value);
}
/// <summary>
/// 向list头部添加value值,并设置过期时间
/// </summary>
public void PrependItemToList(string listId, string value, DateTime expireAt)
{
base.iClient.PrependItemToList(listId, value);
base.iClient.ExpireEntryAt(listId, expireAt);
}
/// <summary>
/// 向list头部添加value值,并设置过期时间
/// </summary>
public void PrependItemToList(string listId, string value, TimeSpan expireIn)
{
base.iClient.PrependItemToList(listId, value);
base.iClient.ExpireEntryIn(listId, expireIn);
}
/// <summary>
/// 向list中添加value值
/// </summary>
public void AddItemToList(string listId, string value)
{
base.iClient.AddItemToList(listId, value);
}
/// <summary>
/// 向list中添加value值,并设置过期时间
/// </summary>
public void AddItemToList(string listId, string value, DateTime expireAt)
{
base.iClient.AddItemToList(listId, value);
base.iClient.ExpireEntryAt(listId, expireAt);
}
/// <summary>
/// 向list中添加value值,并设置过期时间
/// </summary>
public void AddItemToList(string listId, string value, TimeSpan expireIn)
{
base.iClient.AddItemToList(listId, value);
base.iClient.ExpireEntryIn(listId, expireIn);
}
/// <summary>
/// 向list中添加多个value值
/// </summary>
public void AddRangeToList(string listId, List<string> values)
{
base.iClient.AddRangeToList(listId, values);
}
/// <summary>
/// 向list中添加多个value值,并设置过期时间
/// </summary>
public void AddRangeToList(string listId, List<string> values, DateTime expireAt)
{
base.iClient.AddRangeToList(listId, values);
base.iClient.ExpireEntryAt(listId, expireAt);
}
/// <summary>
/// 向list中添加多个value值,并设置过期时间
/// </summary>
public void AddRangeToList(string listId, List<string> values, TimeSpan expireIn)
{
base.iClient.AddRangeToList(listId, values);
base.iClient.ExpireEntryIn(listId, expireIn);
}
#endregion 赋值
#region 获取值
/// <summary>
/// 获取指定list中包含的数据数量
/// </summary>
public long GetListCount(string listId)
{
return base.iClient.GetListCount(listId);
}
/// <summary>
/// 获取指定list中包含的所有数据集合
/// </summary>
public List<string> GetAllItemsFromList(string listId)
{
return base.iClient.GetAllItemsFromList(listId);
}
/// <summary>
/// 获取指定list中下标从startingFrom到endingAt的值集合
/// </summary>
public List<string> GetRangeFromList(string listId, int startingFrom, int endingAt)
{
return base.iClient.GetRangeFromList(listId, startingFrom, endingAt);
}
#endregion 获取值
#region 删除
/// <summary>
/// 移除指定list中,listId/value,与参数相同的值,并返回移除的数量
/// </summary>
public long RemoveItemFromList(string listId, string value)
{
return base.iClient.RemoveItemFromList(listId, value);
}
/// <summary>
/// 从指定list的尾部移除一个数据,并返回移除的数据
/// </summary>
public string RemoveEndFromList(string listId)
{
return base.iClient.RemoveEndFromList(listId);
}
/// <summary>
/// 从指定list的头部移除一个数据,并返回移除的数据
/// </summary>
public string RemoveStartFromList(string listId)
{
return base.iClient.RemoveStartFromList(listId);
}
#endregion 删除
#region 其它
/// <summary>
/// 清理数据,保持list长度
/// </summary>
/// <param name="listId">集合Id</param>
/// <param name="keepStartingFrom">保留起点</param>
/// <param name="keepEndingAt">保留终点</param>
public void TrimList(string listId, int keepStartingFrom, int keepEndingAt)
{
base.iClient.TrimList(listId, keepStartingFrom, keepEndingAt);
}
#endregion 其它
#region 发布订阅
/// <summary>
/// 发布
/// </summary>
/// <param name="channel">频道</param>
/// <param name="message">消息</param>
public void Publish(string channel, string message)
{
base.iClient.PublishMessage(channel, message);
}
/// <summary>
/// 订阅
/// </summary>
/// <param name="channel">频道</param>
/// <param name="actionOnMessage"></param>
public void Subscribe(string channel, Action<string, string, IRedisSubscription> actionOnMessage)
{
var subscription = base.iClient.CreateSubscription();
subscription.OnSubscribe = c =>
{
Console.WriteLine($"订阅频道{c}");
Console.WriteLine();
};
//取消订阅
subscription.OnUnSubscribe = c =>
{
Console.WriteLine($"取消订阅 {c}");
Console.WriteLine();
};
subscription.OnMessage += (c, s) =>
{
actionOnMessage(c, s, subscription);
};
Console.WriteLine($"开始启动监听 {channel}");
subscription.SubscribeToChannels(channel); //blocking
}
/// <summary>
/// 取消订阅
/// </summary>
/// <param name="channel">频道</param>
public void UnSubscribeFromChannels(string channel)
{
var subscription = base.iClient.CreateSubscription();
subscription.UnSubscribeFromChannels(channel);
}
#endregion 发布订阅
}
}
上面的代码创建完成后,项目的基础就构建完成了,下面开始学习list数据类型常用api的使用。
在控制台应用程序中的ServiceStackTest.cs类中,调用Redis的api方法。代码如下:
using MyRedis.Redis.Service;
using System;
using System.Collections.Generic;
using System.Threading;
namespace MyRedis
{
/// <summary>
/// ServiceStackTest api 封装测试
/// </summary>
public class ServiceStackTest
{
public static void Show()
{
#region RedisListService
using (RedisListService listService = new RedisListService())
{
#region List队列操作
listService.EnqueueItemOnList("QueueList", "打印任务1"); //入队
listService.EnqueueItemOnList("QueueList", "打印任务2");
listService.EnqueueItemOnList("QueueList", "打印任务3");
listService.EnqueueItemOnList("QueueList", "打印任务4");
long q = listService.GetListCount("QueueList");
Console.WriteLine("打印任务按照顺序打印开始");
for (int i = 0; i < q; i++)
{
Console.WriteLine("QueueList出队值:{0}", listService.DequeueItemFromList("QueueList"));
}
Console.WriteLine("打印任务按照顺序打印完成");
#endregion
#region 栈操作
listService.PushItemToList("StackList", "入栈操作1"); //入栈
listService.PushItemToList("StackList", "入栈操作2");
listService.PushItemToList("StackList", "入栈操作3");
listService.PushItemToList("StackList", "入栈操作4");
Console.WriteLine("开始出栈");
long p = listService.GetListCount("StackList");
for (int i = 0; i < p; i++)
{
Console.WriteLine("StackList出栈值:{0}", listService.PopItemFromList("StackList"));
}
Console.WriteLine("出栈完成");
#endregion
}
#endregion
}
}
}
执行结果:
上面代码中包含两个实例,队列的使用和栈的使用。
对于队列的使用,有多个客户端需要打印机打印任务,那么使用队列进行任务的排队,然后按照排队顺序开始打印。
对于栈的使用,实现了入栈和出栈,先进后出的顺序完成。
总结:队列是先进先出操作,栈是先进后出操作,并且操作完成后,数据被删除。
使用list可以实现数据分页和发布订阅等待功能。
更多关于list数据类型的说明,可以参考文章:https://www.cnblogs.com/xyh9039/p/14022264.html