详解Redis中的List类型

本系列将和大家分享Redis分布式缓存,本章主要简单介绍下Redis中的List类型,以及如何使用Redis解决博客数据分页、生产者消费者模型和发布订阅等问题。

Redis List的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用这个数据结构。

List类型主要用于队列和栈,先进先出,后进先出等。

存储形式:key--LinkList<value>

首先先给大家Show一波Redis中与List类型相关的API:

using System;
using System.Collections.Generic;
using ServiceStack.Redis;

namespace TianYa.Redis.Service
{
 /// <summary>
 /// Redis List的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,
 /// Redis内部的很多实现,包括发送缓冲队列等也都是用这个数据结构。
 /// </summary>
 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._redisClient.EnqueueItemOnList(listId, value);
  }

  /// <summary>
  /// 出队
  /// </summary>
  /// <param name="listId">集合Id</param>
  /// <returns>出队的值</returns>
  public string DequeueItemFromList(string listId)
  {
   return base._redisClient.DequeueItemFromList(listId);
  }

  /// <summary>
  /// 出队(阻塞)
  /// </summary>
  /// <param name="listId">集合Id</param>
  /// <param name="timeOut">阻塞时间(超时时间)</param>
  /// <returns>出队的值</returns>
  public string BlockingDequeueItemFromList(string listId, TimeSpan? timeOut)
  {
   return base._redisClient.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._redisClient.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._redisClient.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._redisClient.PushItemToList(listId, value);
   base._redisClient.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._redisClient.PushItemToList(listId, value);
   base._redisClient.ExpireEntryIn(listId, expireIn);
  }

  /// <summary>
  /// 出栈
  /// </summary>
  /// <param name="listId">集合Id</param>
  /// <returns>出栈的值</returns>
  public string PopItemFromList(string listId)
  {
   return base._redisClient.PopItemFromList(listId);
  }

  /// <summary>
  /// 出栈(阻塞)
  /// </summary>
  /// <param name="listId">集合Id</param>
  /// <param name="timeOut">阻塞时间(超时时间)</param>
  /// <returns>出栈的值</returns>
  public string BlockingPopItemFromList(string listId, TimeSpan? timeOut)
  {
   return base._redisClient.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._redisClient.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._redisClient.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._redisClient.BlockingPopAndPushItemBetweenLists(fromListId, toListId, timeOut);
  }

  #endregion Stack栈(后进先出)

  #region 赋值

  /// <summary>
  /// 向list头部添加value值
  /// </summary>
  public void PrependItemToList(string listId, string value)
  {
   base._redisClient.PrependItemToList(listId, value);
  }

  /// <summary>
  /// 向list头部添加value值,并设置过期时间
  /// </summary>
  public void PrependItemToList(string listId, string value, DateTime expireAt)
  {
   base._redisClient.PrependItemToList(listId, value);
   base._redisClient.ExpireEntryAt(listId, expireAt);
  }

  /// <summary>
  /// 向list头部添加value值,并设置过期时间
  /// </summary>
  public void PrependItemToList(string listId, string value, TimeSpan expireIn)
  {
   base._redisClient.PrependItemToList(listId, value);
   base._redisClient.ExpireEntryIn(listId, expireIn);
  }

  /// <summary>
  /// 向list中添加value值
  /// </summary>
  public void AddItemToList(string listId, string value)
  {
   base._redisClient.AddItemToList(listId, value);
  }

  /// <summary>
  /// 向list中添加value值,并设置过期时间
  /// </summary>
  public void AddItemToList(string listId, string value, DateTime expireAt)
  {
   base._redisClient.AddItemToList(listId, value);
   base._redisClient.ExpireEntryAt(listId, expireAt);
  }

  /// <summary>
  /// 向list中添加value值,并设置过期时间
  /// </summary>
  public void AddItemToList(string listId, string value, TimeSpan expireIn)
  {
   base._redisClient.AddItemToList(listId, value);
   base._redisClient.ExpireEntryIn(listId, expireIn);
  }

  /// <summary>
  /// 向list中添加多个value值
  /// </summary>
  public void AddRangeToList(string listId, List<string> values)
  {
   base._redisClient.AddRangeToList(listId, values);
  }

  /// <summary>
  /// 向list中添加多个value值,并设置过期时间
  /// </summary>
  public void AddRangeToList(string listId, List<string> values, DateTime expireAt)
  {
   base._redisClient.AddRangeToList(listId, values);
   base._redisClient.ExpireEntryAt(listId, expireAt);
  }

  /// <summary>
  /// 向list中添加多个value值,并设置过期时间
  /// </summary>
  public void AddRangeToList(string listId, List<string> values, TimeSpan expireIn)
  {
   base._redisClient.AddRangeToList(listId, values);
   base._redisClient.ExpireEntryIn(listId, expireIn);
  }

  #endregion 赋值

  #region 获取值

  /// <summary>
  /// 获取指定list中包含的数据数量
  /// </summary>
  public long GetListCount(string listId)
  {
   return base._redisClient.GetListCount(listId);
  }

  /// <summary>
  /// 获取指定list中包含的所有数据集合
  /// </summary>
  public List<string> GetAllItemsFromList(string listId)
  {
   return base._redisClient.GetAllItemsFromList(listId);
  }

  /// <summary>
  /// 获取指定list中下标从startingFrom到endingAt的值集合
  /// </summary>
  public List<string> GetRangeFromList(string listId, int startingFrom, int endingAt)
  {
   return base._redisClient.GetRangeFromList(listId, startingFrom, endingAt);
  }

  #endregion 获取值

  #region 删除

  /// <summary>
  /// 移除指定list中,listId/value,与参数相同的值,并返回移除的数量
  /// </summary>
  public long RemoveItemFromList(string listId, string value)
  {
   return base._redisClient.RemoveItemFromList(listId, value);
  }

  /// <summary>
  /// 从指定list的尾部移除一个数据,并返回移除的数据
  /// </summary>
  public string RemoveEndFromList(string listId)
  {
   return base._redisClient.RemoveEndFromList(listId);
  }

  /// <summary>
  /// 从指定list的头部移除一个数据,并返回移除的数据
  /// </summary>
  public string RemoveStartFromList(string listId)
  {
   return base._redisClient.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._redisClient.TrimList(listId, keepStartingFrom, keepEndingAt);
  }

  #endregion 其它

  #region 发布订阅

  /// <summary>
  /// 发布
  /// </summary>
  /// <param name="channel">频道</param>
  /// <param name="message">消息</param>
  public void Publish(string channel, string message)
  {
   base._redisClient.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._redisClient.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._redisClient.CreateSubscription();
   subscription.UnSubscribeFromChannels(channel);
  }

  #endregion 发布订阅
 }
}

使用如下:

/// <summary>
/// Redis List的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,
/// Redis内部的很多实现,包括发送缓冲队列等也都是用这个数据结构。
/// 队列/栈/生产者消费者模型/发布订阅
/// </summary>
public static void ShowList()
{
 using (RedisListService service = new RedisListService())
 {
  service.FlushAll();
  service.AddItemToList("article", "张三");
  service.AddItemToList("article", "李四");
  service.AddItemToList("article", "王五");
  service.PrependItemToList("article", "赵六");
  service.PrependItemToList("article", "钱七");

  var result1 = service.GetAllItemsFromList("article"); //一次性获取所有的数据
  var result2 = service.GetRangeFromList("article", 0, 3); //可以按照添加顺序自动排序,而且可以分页获取
  Console.WriteLine($"result1={JsonConvert.SerializeObject(result1)}");
  Console.WriteLine($"result2={JsonConvert.SerializeObject(result2)}");

  Console.WriteLine("=====================================================");

  //栈:后进先出
  service.FlushAll();
  service.PushItemToList("article", "张三"); //入栈
  service.PushItemToList("article", "李四");
  service.PushItemToList("article", "王五");
  service.PushItemToList("article", "赵六");
  service.PushItemToList("article", "钱七");

  for (int i = 0; i < 5; i++)
  {
   Console.WriteLine(service.PopItemFromList("article")); //出栈
  }

  Console.WriteLine("=====================================================");

  //队列:先进先出,生产者消费者模型
  //MSMQ---RabbitMQ---ZeroMQ---RedisList 学习成本、技术成本
  service.FlushAll();
  service.EnqueueItemOnList("article", "张三"); //入队
  service.EnqueueItemOnList("article", "李四");
  service.EnqueueItemOnList("article", "王五");
  service.EnqueueItemOnList("article", "赵六");
  service.EnqueueItemOnList("article", "钱七");

  for (int i = 0; i < 5; i++)
  {
   Console.WriteLine(service.DequeueItemFromList("article")); //出队
  }
  //分布式缓存,多服务器都可以访问到,多个生产者,多个消费者,任何产品只被消费一次
 }
}

运行结果如下所示:

下面我们就来看下如何使用上面的API来解决一些具体的问题:

一、博客数据分页

应用场景:

  博客网站每天新增的随笔和文章可能都是几千几万的,表里面是几千万数据。首页要展示最新的随笔,还有前20页是很多人访问的。

  这种情况下如果首页分页数据每次都去查询数据库,那么就会有很大的性能问题。

解决方案:

  每次写入数据库的时候,把 ID_标题 写入到Redis的List中(后面搞个TrimList,只要最近的200个)。

  这样的话用户每次刷页面就不需要去访问数据库了,直接读取Redis中的数据。

  第一页(当然也可以是前几页)的时候可以不体现总记录数,只拿最新数据展示,这样就能避免访问数据库了。

还有一种就是水平分表了,数据存到Redis的时候可以保存 ID_表名称_标题

使用List主要是解决数据量大,变化快的数据分页问题。

二八原则:80%的访问集中在20%的数据,List里面只用保存大概的量就够用了。

using TianYa.Redis.Service;

namespace MyRedis.Scene
{
 /// <summary>
 /// 博客数据分页
 ///
 /// 应用场景:
 ///  博客网站每天新增的随笔和文章可能都是几千几万的,表里面是几千万数据。首页要展示最新的随笔,还有前20页是很多人访问的。
 ///  这种情况下如果首页分页数据每次都去查询数据库,那么就会有很大的性能问题。
 ///
 /// 解决方案:
 ///  每次写入数据库的时候,把 ID_标题 写入到Redis的List中(后面搞个TrimList,只要最近的200个)。
 ///  这样的话用户每次刷页面就不需要去访问数据库了,直接读取Redis中的数据。
 ///  第一页(当然也可以是前几页)的时候可以不体现总记录数,只拿最新数据展示,这样就能避免访问数据库了。
 ///
 /// 还有一种就是水平分表了,数据存到Redis的时候可以保存 ID_表名称_标题
 ///
 /// 使用List主要是解决数据量大,变化快的数据分页问题。
 /// 二八原则:80%的访问集中在20%的数据,List里面只用保存大概的量就够用了。
 /// </summary>
 public class BlogPageList
 {
  public static void Show()
  {
   using (RedisListService service = new RedisListService())
   {
    service.AddItemToList("newBlog", "10001_IOC容器的实现原理");
    service.AddItemToList("newBlog", "10002_AOP面向切面编程");
    service.AddItemToList("newBlog", "10003_行为型设计模式");
    service.AddItemToList("newBlog", "10004_结构型设计模式");
    service.AddItemToList("newBlog", "10005_创建型设计模式");
    service.AddItemToList("newBlog", "10006_GC垃圾回收");

    service.TrimList("newBlog", 0, 200); //保留最新的201个(一个List最多只能存放2的32次方-1个)
    var result1 = service.GetRangeFromList("newBlog", 0, 9); //第一页
    var result2 = service.GetRangeFromList("newBlog", 10, 19); //第二页
    var result3 = service.GetRangeFromList("newBlog", 20, 29); //第三页
   }
  }
 }
}

二、生产者消费者模型

分布式缓存,多服务器都可以访问到,多个生产者,多个消费者,任何产品只被消费一次。(使用队列实现)

其中一个(或多个)程序写入,另外一个(或多个)程序读取消费。按照时间顺序,数据失败了还可以放回去下次重试。

下面我们来看个例子:

Demo中添加了2个控制台应用程序,分别模拟生产者和消费者:

using System;
using TianYa.Redis.Service;

namespace TianYa.Producer
{
 /// <summary>
 /// 模拟生产者
 /// </summary>
 class Program
 {
  static void Main(string[] args)
  {
   Console.WriteLine("生产者程序启动了。。。");
   using (RedisListService service = new RedisListService())
   {
    Console.WriteLine("开始生产test产品");
    for (int i = 1; i <= 20; i++)
    {
     service.EnqueueItemOnList("test", $"产品test{i}");
    }

    Console.WriteLine("开始生产task产品");
    for (int i = 1; i <= 20; i++)
    {
     service.EnqueueItemOnList("task", $"产品task{i}");
    }
    Console.WriteLine("模拟生产结束");

    while (true)
    {
     Console.WriteLine("************请输入数据************");
     string testTask = Console.ReadLine();
     service.EnqueueItemOnList("test", testTask);
    }
   }
  }
 }
}
using System;
using System.Threading;
using TianYa.Redis.Service;

namespace TianYa.Consumer
{
 /// <summary>
 /// 模拟消费者
 /// </summary>
 class Program
 {
  static void Main(string[] args)
  {
   Console.WriteLine("消费者程序启动了。。。");
   using (RedisListService service = new RedisListService())
   {
    while (true)
    {
     var result = service.BlockingDequeueItemFromLists(new string[] { "test", "task" }, TimeSpan.FromHours(1));
     Thread.Sleep(100);
     Console.WriteLine($"消费者消费了 {result.Id} {result.Item}");
    }
   }
  }
 }
}

接下来我们使用.NET Core CLI来启动2个消费者实例和1个生产者实例,运行结果如下所示:

像这种异步队列在项目中有什么价值呢?

PS:此处事务是一个很大问题,真实项目中需根据实际情况决定是否采用异步队列。

三、发布订阅

发布订阅:

  发布一个数据,全部的订阅者都能收到。

  观察者,一个数据源,多个接收者,只要订阅了就可以收到的,能被多个数据源共享。

  观察者模式:微信订阅号---群聊天---数据同步。。。

下面我们来看个小Demo:

/// <summary>
/// 发布订阅
///  发布一个数据,全部的订阅者都能收到。
///  观察者,一个数据源,多个接收者,只要订阅了就可以收到的,能被多个数据源共享。
///  观察者模式:微信订阅号---群聊天---数据同步。。。
/// </summary>
public static void ShowPublishAndSubscribe()
{
 Task.Run(() =>
 {
  using (RedisListService service = new RedisListService())
  {
   service.Subscribe("TianYa", (c, message, iRedisSubscription) =>
   {
    Console.WriteLine($"注册{1}{c}:{message},Dosomething else");
    if (message.Equals("exit"))
     iRedisSubscription.UnSubscribeFromChannels("TianYa");
   });//blocking
  }
 });
 Task.Run(() =>
 {
  using (RedisListService service = new RedisListService())
  {
   service.Subscribe("TianYa", (c, message, iRedisSubscription) =>
   {
    Console.WriteLine($"注册{2}{c}:{message},Dosomething else");
    if (message.Equals("exit"))
     iRedisSubscription.UnSubscribeFromChannels("TianYa");
   });//blocking
  }
 });
 Task.Run(() =>
 {
  using (RedisListService service = new RedisListService())
  {
   service.Subscribe("Twelve", (c, message, iRedisSubscription) =>
   {
    Console.WriteLine($"注册{3}{c}:{message},Dosomething else");
    if (message.Equals("exit"))
     iRedisSubscription.UnSubscribeFromChannels("Twelve");
   });//blocking
  }
 });
 using (RedisListService service = new RedisListService())
 {
  Thread.Sleep(1000);
  service.Publish("TianYa", "TianYa1");
  Thread.Sleep(1000);
  service.Publish("TianYa", "TianYa2");
  Thread.Sleep(1000);
  service.Publish("TianYa", "TianYa3");

  Thread.Sleep(1000);
  service.Publish("Twelve", "Twelve1");
  Thread.Sleep(1000);
  service.Publish("Twelve", "Twelve2");
  Thread.Sleep(1000);
  service.Publish("Twelve", "Twelve3");

  Thread.Sleep(1000);
  Console.WriteLine("**********************************************");

  Thread.Sleep(1000);
  service.Publish("TianYa", "exit");
  Thread.Sleep(1000);
  service.Publish("TianYa", "TianYa6");
  Thread.Sleep(1000);
  service.Publish("TianYa", "TianYa7");
  Thread.Sleep(1000);
  service.Publish("TianYa", "TianYa8");

  Thread.Sleep(1000);
  service.Publish("Twelve", "exit");
  Thread.Sleep(1000);
  service.Publish("Twelve", "Twelve6");
  Thread.Sleep(1000);
  service.Publish("Twelve", "Twelve7");
  Thread.Sleep(1000);
  service.Publish("Twelve", "Twelve8");

  Thread.Sleep(1000);
  Console.WriteLine("结束");
 }
}

运行结果如下所示:

至此本文就全部介绍完了,如果觉得对您有所启发请记得点个赞哦!!!

Demo源码:

链接: https://pan.baidu.com/s/1_kEMCtbf2iT5pLV7irxR5Q 提取码: v4sr

此文由博主精心撰写转载请保留此原文链接:https://www.cnblogs.com/xyh9039/p/14022264.html

到此这篇关于详解Redis中的List类型的文章就介绍到这了,更多相关Redis List类型内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Redis List列表的详细介绍

    Redis List列表的详细介绍 Redis列表是简单的字符串列表,按照插入顺序排序.你可以添加一个元素导列表的头部(左边)或者尾部(右边) 一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素). 实例 redis 127.0.0.1:6379> LPUSH runoobkey redis (integer) 1 redis 127.0.0.1:6379> LPUSH runoobkey mongodb (integer) 2 redis 127

  • 解决Spring session(redis存储方式)监听导致创建大量redisMessageListenerContailner-X线程问题

    待解决的问题 Spring session(redis存储方式)监听导致创建大量redisMessageListenerContailner-X线程 解决办法 为spring session添加springSessionRedisTaskExecutor线程池. /** * 用于spring session,防止每次创建一个线程 * @return */ @Bean public ThreadPoolTaskExecutor springSessionRedisTaskExecutor(){ T

  • Redis list 类型学习笔记与总结

    redis 版本 复制代码 代码如下: [root@localhost ~]# redis-server --version Redis server v=2.8.19 sha=00000000:0 malloc=jemalloc-3.6.0 bits=32 build=e2559761bd460ca0 list 是一个链表结构,主要功能是 push(类似 PHP 的 array_push() 方法). pop(类似 PHP 的 array_pop() 方法).获取一个范围的所有值 等, 操作

  • Python操作redis实例小结【String、Hash、List、Set等】

    本文实例总结了Python操作redis方法.分享给大家供大家参考,具体如下: 这里介绍详细使用 1.String 操作 redis中的String在在内存中按照一个name对应一个value来存储 set() #在Redis中设置值,默认不存在则创建,存在则修改 r.set('name', 'zhangsan') '''参数: set(name, value, ex=None, px=None, nx=False, xx=False) ex,过期时间(秒) px,过期时间(毫秒) nx,如果设

  • Redis教程(三):List数据类型

    一.概述: 在Redis中,List类型是按照插入顺序排序的字符串链表.和数据结构中的普通链表一样,我们可以在其头部(left)和尾部(right)添加新的元素.在插入时,如果该键并不存在,Redis将为该键创建一个新的链表.与此相反,如果链表中所有的元素均被移除,那么该键也将会被从数据库中删除.List中可以包含的最大元素数量是4294967295.       从元素插入和删除的效率视角来看,如果我们是在链表的两头插入或删除元素,这将会是非常高效的操作,即使链表中已经存储了百万条记录,该操作

  • 详解Redis中的List类型

    本系列将和大家分享Redis分布式缓存,本章主要简单介绍下Redis中的List类型,以及如何使用Redis解决博客数据分页.生产者消费者模型和发布订阅等问题. Redis List的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用这个数据结构. List类型主要用于队列和栈,先进先出,后进先出等. 存储形式:key--LinkList<value> 首先先给大家Show一波Redis中与List类型相

  • 详解Redis中key的命名规范和值的命名规范

    数据库中得热点数据key命名惯例 表名:主键名:主键值:字段名 例如 user:id:0001:name 例如 user:id:0002:name 例如 order:id:s2002:price 上面的key对应的值则可以是 存放的方式 key value 优点 单独的key:value形式 order:id:s2002:price 2000 方便简单的操作,例如incr自增或自减 json格式 user:id:0001 {id:0001,name:"张三"} 方便一次性存和取数据,但

  • 一文详解Redis中的持久化

    目录 1. 前言 2. RDB 2.1 手动触发 2.2 自动触发 3. bgsave大致流程 4. RDB持久化方式的优缺点 5. AOF 6. AOF的使用方式 7. AOF流程剖析 7.1 命令写入 7.2 文件同步 7.3 重写机制 7.4 重启加载 8. 问题定位与优化 8.1 关于fork操作 8.2 关于子进程开销 8.3 关于AOF追加阻塞 1. 前言 为什么要进行持久化?:持久化功能有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复. 持久

  • 详解Mybatis-plus中更新date类型数据遇到的坑

    最近一年的项目都是在使用Mybatis-plus,感觉挺好用的,也没遇到很多问题,但是在最近项目上线之后,遇到了一些新的需要,在进行新版本开发的时候就开始遇到坑了,今天来说一下更新数据中有date类型数据的时候会出现的问题. 实体类部分字段如下: @Data @Builder @NoArgsConstructor @AllArgsConstructor public class ProductPo implements Serializable { /** * 产品主键,自增 */ privat

  • 详解redis中的锁以及使用场景

    分布式锁 什么是分布式锁? 分布式锁是控制分布式系统之间同步访问共享资源的一种方式. 为什么要使用分布式锁? ​ 为了保证共享资源的数据一致性. 什么场景下使用分布式锁? ​ 数据重要且要保证一致性 如何实现分布式锁? 主要介绍使用redis来实现分布式锁 redis事务 redis事务介绍: ​ 1.redis事务可以一次执行多个命令,本质是一组命令的集合. ​ 2.一个事务中的所有命令都会序列化,按顺序串行化的执行而不会被其他命令插入 ​ **作用:**一个队列中,一次性.顺序性.排他性的执

  • 详解javaScript中Number数字类型的使用

    目录 前言 Number数字 自带属性值 基础使用 总结 源码地址 前言 Number和Math都属于JavaScript中的内置对象,Number数字类型作为基础数据类型,我们在开发过程中会经常用到,包括数字精度的格式化,还有字符串转换成数字等操作. Number数字 自带属性值 Number.EPSILON 两个可表示(representable)数之间的最小间隔. Number.MAX_SAFE_INTEGER JavaScript 中最大的安全整数 (2^53 - 1). Number.

  • 详解Python中的枚举类型

    目录 什么是枚举类型 为什么要使用枚举 如何使用枚举 从字典创建枚举 最后的话 你好,我是 征哥,今天分享一下 Python 中的枚举类型,为什么需要枚举类型,及如何使用. 什么是枚举类型 枚举(Enum)是一种数据类型,是绑定到唯一值的符号表示.您可以使用它来创建用于变量和属性的常量集.它们类似于全局变量,但是,它们提供了更有用的功能,例如分组和类型安全.Python 在 3.4 版本中添加了标准库 enum. 为什么要使用枚举 使用枚举有以下好处: 代码更容易阅读,更容易维护. 减少由转换或

  • 详解Redis中Lua脚本的应用和实践

    引言 前段时间组内有个投票的产品,上线前考虑欠缺,导致被刷票严重.后来,通过研究,发现可以通过 redis lua 脚本实现限流,这里将 redis lua 脚本相关的知识分享出来,讲的不到位的地方还望斧正. redis lua 脚本相关命令 这一小节的内容是基本命令,可粗略阅读后跳过,等使用的时候再回来查询 redis 自 2.6.0 加入了 lua 脚本相关的命令,EVAL.EVALSHA.SCRIPT EXISTS.SCRIPT FLUSH.SCRIPT KILL.SCRIPT LOAD,

  • 详解Redis中的双链表结构

    Redis中双链表实现的基本结构: 1.节点结构 typedef struct listNode { struct listNode *prev; //前向节点 struct listNode *next; //后向节点 void *value; //该节点的值 } listNode; 2.双向链表结构 typedef struct list { listNode *head; //头节点 listNode *tail; //尾节点 void *(*dup)(void *ptr); //复制函数

  • 详解java中的byte类型

    介绍 byte,即字节,由8位的二进制组成.在Java中,byte类型的数据是8位带符号的二进制数. 在计算机中,8位带符号二进制数的取值范围是[-128, 127],所以在Java中,byte类型的取值范围也是[-128, 127]. 取值范围分析 一直在想为什么不是 -128 到 128呢?今天分析了一下这个问题. 首先我们得明白一件事情,那就是运算规则: ####################################################################

随机推荐