服务器之家:专注于服务器技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - Redis - Redis缓存详解

Redis缓存详解

2020-04-20 16:00神牛步行3 Redis

本文主要介绍了Redis缓存从搭建到使用的相关知识,具有一定的参考价值,下面跟着小编一起来看下吧

下面来正式分享今天的文章吧:

。搭建Redis服务端,并用客户端连接

。封装缓存父类,定义Get,Set等常用方法

。定义RedisCache缓存类,执行Redis的Get,Set方法

。构造出缓存工厂调用方法

下面一步一个脚印的来分享:

。搭建Redis服务端,并用客户端连接

首先,咋们去这个地址下载安装文件https://github.com/dmajkic/redis/downloads,我这里的版本是:redis-2.4.5-win32-win64里面有32位和64位的执行文件,我这里服务器是64位的下面给出截图和用到部分程序的说明:

Redis缓存详解

现在,咋们直接可以用鼠标双击redis-server.exe这个应用程序,这样就打开了redis服务窗体(您也可以下载一个windows服务承载器,把redis服务运行在windows的服务中,就不用担心每次关闭redis服务黑色窗体后无法访问redis了),运行起来是这样:

Redis缓存详解

有红色框的信息就表示成功了,这里redis服务监听的端口默认是6379,要修改端口或者更多的配置信息请找到redis.conf配置文件,具体配置信息介绍可以来这里http://www.shouce.ren/api/view/a/6231

再来,打开客户端连接服务端,咋们退到64bit文件夹的目录中,鼠标移到64bit文件夹上并且安装Shift键,同时点击鼠标的右键,选中"在此处打开命令窗口"这样快速进入到了该文件夹的cmd命令窗口中(当然不同的操作系统不同,这里演示的是windows的操作;还有其他进入的方式这里不做介绍,因为个人感觉这是最快的);然后,在命令窗口中录入redis-cli.exe -h localhost -p 6379回车来访问服务端,效果图:

Redis缓存详解

再来看下服务端窗体截图:

Redis缓存详解

没错这样客户端就连接上服务端了,可以简单在客户端执行下set,get命令:

Redis缓存详解

如果是客户端要访问远程的redis服务端,只需要把localhost换成可访问的ip就行了如果还需要密码等更多配置请去上面的那个地址链接;

 。封装缓存父类,定义Get,Set等常用方法

先来,上父类的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class BaseCache : IDisposable
 {
 protected string def_ip = string.Empty;
 protected int def_port = 0;
 protected string def_password = string.Empty;
 public BaseCache()
 {
 }
 public virtual void InitCache(string ip = "", int port = 0, string password = "")
 {
 }
 public virtual bool SetCache<T>(string key, T t, int timeOutMinute = 10) where T : class,new()
 {
  return false;
 }
 public virtual T GetCache<T>(string key) where T : class,new()
 {
  return default(T);
 }
 public virtual bool Remove(string key)
 {
  return false;
 }
 public virtual bool FlushAll()
 {
  return false;
 }
 public virtual bool Any(string key)
 {
  return false;
 }
 public virtual void Dispose(bool isfalse)
 {
  if (isfalse)
  {
  }
 }
 //手动释放
 public void Dispose()
 {
  this.Dispose(true);
  //不自动释放
  GC.SuppressFinalize(this);
 }
 }

 这里定义的方法没有太多的注释,更多的意思我想看方法名称就明白了,这个父类主要实现了IDisposable,实现的Dispose()中主要用来释放资源并且自定义了一个 public virtual void Dispose(bool isfalse)方法,这里面有一句是GC.SuppressFinalize(this);按照官网介绍的意思是阻塞自动释放资源,其他的没有什么了,继续看下面的

。定义RedisCache缓存类,执行Redis的Get,Set方法

首先,咋们分别定义类RedisCache,MemcachedCache(这里暂未实现对memcache缓存的操作),并且继承BaseCache,重写Set,Get方法如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/// <summary>
 /// Redis缓存
 /// </summary>
 public class RedisCache : BaseCache
 {
 public RedisClient redis = null;
 public RedisCache()
 {
  //这里去读取默认配置文件数据
  def_ip = "172.0.0.1";
  def_port = 6379;
  def_password = "";
 }
 #region Redis缓存
 public override void InitCache(string ip = "", int port = 0, string password = "")
 {
  if (redis == null)
  {
  ip = string.IsNullOrEmpty(ip) ? def_ip : ip;
  port = port == 0 ? def_port : port;
  password = string.IsNullOrEmpty(password) ? def_password : password;
  redis = new RedisClient(ip, port, password);
  }
 }
 public override bool SetCache<T>(string key, T t, int timeOutMinute = 10)
 {
  var isfalse = false;
  try
  {
  if (string.IsNullOrEmpty(key)) { return isfalse; }
  InitCache();
  isfalse = redis.Set<T>(key, t, TimeSpan.FromMinutes(timeOutMinute));
  }
  catch (Exception ex)
  {
  }
  finally { this.Dispose(); }
  return isfalse;
 }
 public override T GetCache<T>(string key)
 {
  var t = default(T);
  try
  {
  if (string.IsNullOrEmpty(key)) { return t; }
  InitCache();
  t = redis.Get<T>(key);
  }
  catch (Exception ex)
  {
  }
  finally { this.Dispose(); }
  return t;
 }
 public override bool Remove(string key)
 {
  var isfalse = false;
  try
  {
  if (string.IsNullOrEmpty(key)) { return isfalse; }
  InitCache();
  isfalse = redis.Remove(key);
  }
  catch (Exception ex)
  {
  }
  finally { this.Dispose(); }
  return isfalse;
 }
 public override void Dispose(bool isfalse)
 {
  if (isfalse && redis != null)
  {
  redis.Dispose();
  redis = null;
  }
 }
 #endregion
 }
 /// <summary>
 /// Memcached缓存
 /// </summary>
 public class MemcachedCache : BaseCache
 {
 }

这里,用到的RedisClient类是来自nuget包引用的,这里nuget包是:

Redis缓存详解

然后,来看下重写的InitCache方法,这里面有一些ip,port(端口),password(密码)参数,这里直接写入在cs文件中没有从配置文件读取,大家可以扩展下;这些参数通过RedisClient构造函数传递给底层Socket访问需要的信息,下面简单展示下RedisClient几个的构造函数:

?
1
2
3
4
5
6
public RedisClient();
 public RedisClient(RedisEndpoint config);
 public RedisClient(string host);
 public RedisClient(Uri uri);
 public RedisClient(string host, int port);
 public RedisClient(string host, int port, string password = null, long db = 0);

至于Get,Set方法最终都是使用RedisClient对象访问的,个人觉得需要注意的是Set方法里面的过期时间参数,目前还没有试验这种情况的效果:

?通过这几种方法设置过期时间后,快到过期时间的时候如果此时有使用这个缓存key那么过期时间是否会往后自动增加过期时间有效期,这里暂时没有试验(这里是由于前面项目中的.net core框架中的memecache缓存都有这种设置,想来redis应该也有吧)

这里,需要重写下public override void Dispose(bool isfalse)方法,因为调用完RedisClient后需要释放,我们通过Dispose统一来手动释放,而不是直接在调用的时候使用using()

。构造出缓存工厂调用方法

接下来,咋们需要定义一个缓存工厂,因为上面刚才定义了一个RedisCache和MemcachedCache明显这里会有多个不同缓存的方法调用,所用咋们来定义个工厂模式来调用对应的缓存;这里的工厂模式没有使用直接显示创建new RedisCache(),new MemcachedCache()对象的方法,而是使用了反射的原理,创建对应的缓存对象;

先来,定义个枚举,枚举里面的声明的名字要和咋们缓存类的名称相同,代码如下:

?
1
2
3
4
5
public enum CacheType
 {
 RedisCache,
 MemcachedCache
 }

再来,定义个工厂来CacheRepository(缓存工厂),并且定义方法Current如下代码:

?
1
2
3
4
5
6
7
public static BaseCache Current(CacheType cacheType = CacheType.RedisCache)
 {
 var nspace = typeof(BaseCache);
 var fullName = nspace.FullName;
 var nowspace = fullName.Substring(0, fullName.LastIndexOf('.') + 1);
 return Assembly.GetExecutingAssembly().CreateInstance(nowspace + cacheType.ToString(), true) as BaseCache;
 }

*:通过传递枚举参数,来确定反射CreateInstance()方法需要用到的typeName参数,从而来定义需要访问的那个缓存对象,这里要注意的是加上了一个命名空间nowspace,因为缓存类可能和工厂类不是同一个命名空间,但是通常会和缓存基类是同命名空间所以在方法最开始的时候截取获取了缓存类需要的命名空间(这里看自身项目来定吧);

*:Assembly.GetExecutingAssembly()这个是用来获取当前应用程序集的路径,这里就避免了咋们使用Assembly.Load()方法还需要传递程序集的路径地址了

好了满上上面要求后,咋们可以在测试页面调用代码如:CacheRepository.Current(CacheType.RedisCache).SetCache<MoFlightSearchResponse>(keyData, value);就如此简单,咋们使用redis-cli.exe客户端来看下缓存起来的数据:

Redis缓存详解

怎么样,您们的是什么效果呢,下面给出整体代码

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#region CacheRepository 缓存工厂(默认存储Session中)
 /// <summary>
 /// 缓存枚举
 /// </summary>
 public enum CacheType
 {
 BaseCache,
 RedisCache,
 MemcachedCache
 }
 /// <summary>
 /// 缓存工厂(默认存储Session中)
 /// </summary>
 public class CacheRepository
 {
 /// <summary>
 /// 缓存工厂(默认存储Session中, CacheKey = "SeesionKey")
 /// </summary>
 /// <param name="cacheType">缓存类型</param>
 /// <returns></returns>
 public static BaseCache Current(CacheType cacheType = CacheType.RedisCache)
 {
 var nspace = typeof(BaseCache);
 var fullName = nspace.FullName;
 var nowspace = fullName.Substring(0, fullName.LastIndexOf('.') + 1);
 
 return Assembly.GetExecutingAssembly().CreateInstance(nowspace + cacheType.ToString(), true) as BaseCache;
 }
 }
 /// <summary>
 /// 缓存基类(默认存储Session中)
 /// </summary>
 public class BaseCache : IDisposable
 {
 protected string def_ip = string.Empty;
 protected int def_port = 0;
 protected string def_password = string.Empty;
 protected string CacheKey = "SeesionKey";
 public BaseCache()
 {
 }
 /// <summary>
 /// 获取自定义SessionId值
 /// </summary>
 /// <param name="key">key:使用唯一的登陆账号</param>
 /// <returns>hash值的SessionId</returns>
 public virtual string GetSessionId(string key)
 {
 return Md5Extend.GetSidMd5Hash(key);
 }
 public virtual void InitCache(bool isReadAndWriter = true, string ip = "", int port = 0, string password = "")
 {
 }
 public virtual bool SetCache<T>(string key, T t, int timeOutMinute = 10, bool isSerilize = false) where T : class,new()
 {
 var isfalse = false;
 try
 {
 key = key ?? CacheKey;
 if (t == null) { return isfalse; }
 
 var session_json = JsonConvert.SerializeObject(t);
 HttpContext.Current.Session.Timeout = timeOutMinute;
 HttpContext.Current.Session.Add(key, session_json);
 isfalse = true;
 }
 catch (Exception ex)
 {
 throw new Exception(ex.Message);
 }
 return isfalse;
 }
 public virtual T GetCache<T>(string key = null, bool isSerilize = false) where T : class,new()
 {
 var t = default(T);
 try
 {
 key = key ?? CacheKey;
 var session = HttpContext.Current.Session[key];
 if (session == null) { return t; }
 
 t = JsonConvert.DeserializeObject<T>(session.ToString());
 }
 catch (Exception ex)
 {
 throw new Exception(ex.Message);
 }
 return t;
 }
 public virtual bool Remove(string key = null)
 {
 var isfalse = false;
 try
 {
 key = key ?? CacheKey;
 HttpContext.Current.Session.Remove(key);
 isfalse = true;
 }
 catch (Exception ex)
 {
 throw new Exception(ex.Message);
 }
 return isfalse;
 }
 /// <summary>
 /// 增加缓存时间
 /// </summary>
 /// <returns></returns>
 public virtual bool AddExpire(string key, int nTimeMinute = 10)
 {
 return true;
 }
 public virtual bool FlushAll()
 {
 return false;
 }
 public virtual bool Any(string key)
 {
 return false;
 }
 public virtual bool SetHashCache<T>(string hashId, string key, T t, int nTimeMinute = 10) where T : class,new()
 {
 return false;
 }
 public virtual List<string> GetHashKeys(string hashId)
 {
 return null;
 }
 public virtual List<string> GetHashValues(string hashId)
 {
 return null;
 }
 public virtual T GetHashValue<T>(string hashId, string key) where T : class,new()
 {
 var t = default(T);
 return t;
 }
 public virtual bool RemoveHashByKey(string hashId, string key)
 {
 return false;
 }
 public virtual void Dispose(bool isfalse)
 {
 if (isfalse)
 {
 }
 }
 //手动释放
 public void Dispose()
 {
 this.Dispose(true);
 //不自动释放
 GC.SuppressFinalize(this);
 }
 }
 /// <summary>
 /// Redis缓存
 /// </summary>
 public class RedisCache : BaseCache
 {
 public IRedisClient redis = null;
 public RedisCache()
 {
 //这里去读取默认配置文件数据
 def_ip = "127.0.0.1";
 def_port = 6379;
 def_password = "";
 }
 #region Redis缓存
 public static object _lockCache = new object();
 public override void InitCache(bool isReadAndWriter = true, string ip = "", int port = 0, string password = "")
 {
 if (redis == null)
 {
 ip = string.IsNullOrEmpty(ip) ? def_ip : ip;
 port = port == 0 ? def_port : port;
 password = string.IsNullOrEmpty(password) ? def_password : password;
 //单个redis服务
 //redis = new RedisClient(ip, port, password);
 //集群服务 如果密码,格式如:pwd@ip:port
 var readAndWritePorts = new List<string> { "shenniubuxing3@127.0.0.1:6379" };
 var onlyReadPorts = new List<string> {
  "shenniubuxing3@127.0.0.1:6378",
  "shenniubuxing3@127.0.0.1:6377"
 };
 var redisPool = new PooledRedisClientManager(
  readAndWritePorts,
  onlyReadPorts,
  new RedisClientManagerConfig
  {
  AutoStart = true,
  //最大读取链接
  MaxReadPoolSize = 20,
  //最大写入链接
  MaxWritePoolSize = 10
  })
 {
  //每个链接超时时间
  ConnectTimeout = 20,
  //连接池超时时间
  PoolTimeout = 60
 };
 lock (_lockCache)
 {
  redis = isReadAndWriter ? redisPool.GetClient() : redisPool.GetReadOnlyClient();
 }
 }
 }
 public override bool AddExpire(string key, int nTimeMinute = 10)
 {
 var isfalse = false;
 try
 {
 if (string.IsNullOrEmpty(key)) { return isfalse; }
 InitCache();
 //isfalse = redis.ExpireEntryIn(key, TimeSpan.FromMinutes(nTimeMinute));
 isfalse = redis.ExpireEntryAt(key, DateTime.Now.AddMinutes(nTimeMinute));
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return isfalse;
 }
 public override bool SetCache<T>(string key, T t, int timeOutMinute = 10, bool isSerilize = false)
 {
 var isfalse = false;
 try
 {
 if (string.IsNullOrEmpty(key)) { return isfalse; }
 InitCache();
 if (isSerilize)
 {
  var data = JsonConvert.SerializeObject(t);
  var bb = System.Text.Encoding.UTF8.GetBytes(data);
  isfalse = redis.Set<byte[]>(key, bb, TimeSpan.FromMinutes(timeOutMinute));
 }
 else { isfalse = redis.Set<T>(key, t, TimeSpan.FromMinutes(timeOutMinute)); }
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return isfalse;
 }
 public override T GetCache<T>(string key, bool isSerilize = false)
 {
 var t = default(T);
 try
 {
 if (string.IsNullOrEmpty(key)) { return t; }
 InitCache(false);
 if (isSerilize)
 {
  var bb = redis.Get<byte[]>(key);
  if (bb.Length <= 0) { return t; }
  var data = System.Text.Encoding.UTF8.GetString(bb);
  t = JsonConvert.DeserializeObject<T>(data);
 }
 else { t = redis.Get<T>(key); }
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return t;
 }
 public override bool Remove(string key)
 {
 var isfalse = false;
 try
 {
 if (string.IsNullOrEmpty(key)) { return isfalse; }
 InitCache();
 isfalse = redis.Remove(key);
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return isfalse;
 }
 public override bool SetHashCache<T>(string hashId, string key, T t, int nTimeMinute = 10)
 {
 var isfalse = false;
 try
 {
 if (string.IsNullOrEmpty(hashId) || string.IsNullOrEmpty(key) || t == null) { return isfalse; }
 InitCache();
 var result = JsonConvert.SerializeObject(t);
 if (string.IsNullOrEmpty(result)) { return isfalse; }
 isfalse = redis.SetEntryInHash(hashId, key, result);
 if (isfalse) { AddExpire(key, nTimeMinute); }
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return isfalse;
 }
 public override List<string> GetHashKeys(string hashId)
 {
 var hashKeys = new List<string>();
 try
 {
 if (string.IsNullOrEmpty(hashId)) { return hashKeys; }
 InitCache();
 hashKeys = redis.GetHashKeys(hashId);
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return hashKeys;
 }
 public override List<string> GetHashValues(string hashId)
 {
 var hashValues = new List<string>();
 try
 {
 if (string.IsNullOrEmpty(hashId)) { return hashValues; }
 
 InitCache();
 hashValues = redis.GetHashValues(hashId);
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return hashValues;
 }
 public override T GetHashValue<T>(string hashId, string key)
 {
 var t = default(T);
 try
 {
 if (string.IsNullOrEmpty(hashId) || string.IsNullOrEmpty(key)) { return t; }
 InitCache();
 var result = redis.GetValueFromHash(hashId, key);
 if (string.IsNullOrEmpty(result)) { return t; }
 t = JsonConvert.DeserializeObject<T>(result);
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return t;
 }
 public override bool RemoveHashByKey(string hashId, string key)
 {
 var isfalse = false;
 try
 {
 if (string.IsNullOrEmpty(hashId) || string.IsNullOrEmpty(key)) { return isfalse; }
 InitCache();
 isfalse = redis.RemoveEntryFromHash(hashId, key);
 }
 catch (Exception ex)
 {
 }
 finally { this.Dispose(); }
 return isfalse;
 }
 public override void Dispose(bool isfalse)
 {
 if (isfalse && redis != null)
 {
 redis.Dispose();
 redis = null;
 }
 }
 #endregion
 }
 /// <summary>
 /// Memcached缓存
 /// </summary>
 public class MemcachedCache : BaseCache
 {
 }
 #endregion

 这次分享的Redis缓存从搭建到使用希望给您们有帮助,还请多多支持点赞,谢谢。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持服务器之家!

原文链接:http://www.cnblogs.com/wangrudong003/p/5785116.html

延伸 · 阅读

精彩推荐
  • Redis就这?Redis持久化策略——AOF

    就这?Redis持久化策略——AOF

    今天为大家介绍Redis的另一种持久化策略——AOF。注意:AOF文件只会记录Redis的写操作命令,因为读命令对数据的恢复没有任何意义...

    头发茂密的刘叔4052021-12-14
  • RedisRedis存取序列化与反序列化性能问题详解

    Redis存取序列化与反序列化性能问题详解

    这篇文章主要给大家介绍了关于Redis存取序列化与反序列化性能问题的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    这名字已经存在9742021-02-24
  • Redis聊一聊Redis与MySQL双写一致性如何保证

    聊一聊Redis与MySQL双写一致性如何保证

    一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。本文给大家分享Redis与MySQL双写一致性该如何保证,感兴趣的朋友一...

    mind_programmonkey6432021-08-12
  • RedisRedis数据结构之链表与字典的使用

    Redis数据结构之链表与字典的使用

    这篇文章主要介绍了Redis数据结构之链表与字典的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友...

    白泽来了4052021-08-03
  • Redis在ssm项目中使用redis缓存查询数据的方法

    在ssm项目中使用redis缓存查询数据的方法

    本文主要简单的使用Java代码进行redis缓存,即在查询的时候先在service层从redis缓存中获取数据。如果大家对在ssm项目中使用redis缓存查询数据的相关知识感...

    caychen8962019-11-12
  • RedisLinux Redis 的安装步骤详解

    Linux Redis 的安装步骤详解

    这篇文章主要介绍了 Linux Redis 的安装步骤详解的相关资料,希望大家通过本文能掌握如何安装Redis,需要的朋友可以参考下 ...

    carl-zhao3822019-11-08
  • Redisredis启动,停止,及端口占用处理方法

    redis启动,停止,及端口占用处理方法

    今天小编就为大家分享一篇redis启动,停止,及端口占用处理方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 ...

    澄海单挑狂5152019-11-14
  • RedisRedis分布式锁升级版RedLock及SpringBoot实现方法

    Redis分布式锁升级版RedLock及SpringBoot实现方法

    这篇文章主要介绍了Redis分布式锁升级版RedLock及SpringBoot实现,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以...

    等不到的口琴7802021-07-25