redis+memoryCache 现实内存缓存解决redis缓存存取大对象较慢的问题

Redis 作为内存数据库,拥有非常高的性能,单个实例的 QPS 能够达到 10W 左右。
Redis 在写入数据时,需要为新的数据分配内存,当从 Redis 中删除数据时,它会释放对应的内存空间。
如果一个 Key 写入的数据非常大,Redis 在分配内存时也会比较耗时。同样的,当删除这个 Key 的数据时,释放内存也会耗时比较久。
你需要检查你的业务代码,是否存在写入大 Key 的情况,需要评估写入数据量的大小,业务层应该避免一个 Key 存入过大的数据量。
那么有没有什么办法可以扫描现在 Redis 中是否存在大 Key 的数据吗?

Redis 也提供了扫描大 Key 的方法:

  1. redis-cli -h $host -p $port --bigkeys -i 0.01

项目上遇到过 存了几千条数据(5M以上)到redis 导致 从redis获取就花费了 近两秒

这里主要是针对微服务,因为微服务同一个程序可能有多个实例,此时 通过 redis 同步缓存memoryCache的key,删除了redis里面的key就可以使所有实现的缓存失效,无需同步删除所有实例的缓存(如果担心缓存时间过长,造成内存泄漏可以加一个定时任务 隔一段时间清理下redis上不存在的key)

具体实现如下(java原理一样):

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Threading;
  5. using Microsoft.Extensions.Caching.Memory;
  6. using Microsoft.Extensions.DependencyInjection;
  7. using Service.Common.Autofac.Attributes;
  8. using Service.Common.helpers;
  9. using Utils.Common.utils;
  10. namespace Service.Common.Services
  11. {
  12. /// <summary>
  13. /// 内存缓存 主要是缓存部分大对象 不要所有的缓存都是用这个缓存
  14. /// 不能依赖这个缓存 如果缓存查询不到还是要从数据库查询
  15. /// 每个服务都会缓存一份
  16. /// </summary>
  17. [Service(lifetime = ServiceLifetime.Singleton)]
  18. public class CacheService
  19. {
  20. //前缀
  21. private static String redisKeyPrefix = "CacheServiceKey_";
  22. //内存缓存
  23. private IMemoryCache memoryCache;
  24. //redis服务
  25. private RedisService redisService;
  26. /// <summary>
  27. /// 构造方法
  28. /// </summary>
  29. /// <param name="memoryCache"></param>
  30. /// <param name="redisService"></param>
  31. public CacheService(IMemoryCache memoryCache, RedisService redisService)
  32. {
  33. this.memoryCache = memoryCache;
  34. this.redisService = redisService;
  35. }
  36. /// <summary>
  37. /// 获取对象
  38. /// </summary>
  39. /// <typeparam name="T"></typeparam>
  40. /// <param name="key"></param>
  41. /// <returns></returns>
  42. public T Get<T>(string key)
  43. {
  44. if (String.IsNullOrWhiteSpace(key))
  45. {
  46. return default(T);
  47. }
  48. key = $"{redisKeyPrefix}{key}";
  49. Object result;
  50. if (redisService.Exists(key) && memoryCache.TryGetValue(key, out result))
  51. {
  52. return (T)result;
  53. }
  54. return default(T);
  55. }
  56. /// <summary>
  57. /// 设置缓存
  58. /// </summary>
  59. /// <param name="key"></param>
  60. /// <param name="value"></param>
  61. /// <param name="expireMinutes"></param>
  62. public bool Set(string key, Object value, int expireMinutes = 1440)
  63. {
  64. TimeSpan timeSpan = TimeSpan.FromMinutes(expireMinutes);
  65. return Set(key, value, timeSpan);
  66. }
  67. /// <summary>
  68. /// 设置缓存
  69. /// </summary>
  70. /// <param name="key"></param>
  71. /// <param name="value"></param>
  72. /// <param name="timeSpan"></param>
  73. public bool Set(string key, object value, TimeSpan timeSpan)
  74. {
  75. if (String.IsNullOrWhiteSpace(key) || value == null)
  76. {
  77. return false;
  78. }
  79. key = $"{redisKeyPrefix}{key}";
  80. memoryCache.Set(key, value, timeSpan);
  81. redisService.Set(key, "1", timeSpan);
  82. return true;
  83. }
  84. /// <summary>
  85. /// 判断在缓存中是否存在该key的缓存数据
  86. /// </summary>
  87. /// <param name="key"></param>
  88. /// <returns></returns>
  89. public bool Exists(string key)
  90. {
  91. key = $"{redisKeyPrefix}{key}";
  92. return redisService.Exists(key) && memoryCache.TryGetValue(key, out _);
  93. }
  94. /// <summary>
  95. /// 移除指定key的缓存
  96. /// </summary>
  97. /// <param name="key"></param>
  98. /// <returns></returns>
  99. public bool Remove(string key)
  100. {
  101. if (String.IsNullOrWhiteSpace(key))
  102. {
  103. return true;
  104. }
  105. key = $"{redisKeyPrefix}{key}";
  106. redisService.Remove(key);
  107. memoryCache.Remove(key);
  108. return true;
  109. }
  110. }
  111. }

版权声明:本文为qq_17056391原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。