1.搭建集群

Linux系统的Redis各版本下载路径:https://download.redis.io/releases/,建议下载5.0以上的版本,下载后进行解压安装

(1)单机版

安装环境

  1. yum install gcc-c++ -y

关闭防火墙

  1. systemctl stop firewalld #关闭
  2. systemctl disable firewalld #永久关闭

解压

  1. # 进入到redis安装目录,进行解压即可
  2. cd /home/linux/redis
  3. tar -xvf redis-5.0.13.tar.gz

修改配置文件

  1. cd /home/linux/redis/redis-5.0.13/
  2. vi redis.conf

修改的地方如下

  1. bind 192.168.168.100 #绑定的ip,本机ip
  2. #requirepass redis123 #密码,可以不加

进入redis-5.0.13进行编译

  1. cd /home/linux/redis/redis-5.0.13/
  2. make
  3. cd src
  4. make install



编译成功后会有对应的src、conf等文件夹

为了方便管理,将Redis文件中的conf配置文件和常用的命令移动到统一文件中创建bin和etc文件夹

  1. cd /home/linux/redis/
  2. mkdir bin etc

将redis.conf文件移动到home/linux/redis/etc目录下,将/src下的一些文件移到bin下

  1. cd /home/linux/redis/redis-5.0.13/
  2. mv redis.conf /home/linux/redis/etc
  3. cd /home/linux/redis/redis-5.0.13/src
  4. mv redis-trib.rb mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-server redis-sentinel /home/redis-6.0.6/bin

启动

  1. bin/redis-server etc/redis.conf

客户端连接测试

(2)集群版

因为redis集群至少需要6台(3主3从),为了减少工作量我们选择在一台虚拟机上来完成搭建都任务。

建立文件夹

  1. cd /home/linux/redis_cluster
  2. mkdir 7001 7002 7003 7004 7005 7006

copy文件到目录下

  1. cd redis-5.0.13/
  2. cp etc/redis.conf /home/linux/redis_cluster/7001
  3. cp etc/redis.conf /home/linux/redis_cluster/7002
  4. cp etc/redis.conf /home/linux/redis_cluster/7003
  5. cp etc/redis.conf /home/linux/redis_cluster/7004
  6. cp etc/redis.conf /home/linux/redis_cluster/7005
  7. cp etc/redis.conf /home/linux/redis_cluster/7006

修改配置文件

  1. cd /home/linux/redis_cluster/7006
  2. vi redis.conf

文件需要修改的内容如下

  1. 1port 7001 //设置端口号
  2. 2dir /usr/local/redis_cluster/7001/ //数据存放位置,必须要指定
  3. 3cluster-enabled yes //启动集群模式
  4. 4cluster-config-file nodes-7001.conf //集群节点信息文件
  5. 6cluster-node-timeout 5000
  6. 7appendonly yes //开启AOF模式,默认的持久化上RDB
  7. 8 appendfsync always //可以不加

其他7002....类似修改配置文件

测试启动

  1. cd redis-5.0.13/bin
  2. redis-server /home/soft/redis_cluster/7001/redis.conf

其他的也启动

启动完之后新开窗口加入集群

创建redis集群(5.0版本以后直接使用redis-cli)

  1. redis-cli --cluster create 192.168.168.103:7001 192.168.168.103:7002 192.168.168.103:7003 192.168.168.103:7004 192.168.168.103:7005 192.168.168.103:7006 --cluster-replicas 1

//-a *** 密码 如果redis.conf中配置了密码创建集群时需要添加

2、springboot整合redis(单机、哨兵、集群)

依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <parent>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-parent</artifactId>
  8. <version>2.3.0.RELEASE</version>
  9. <relativePath/> <!-- lookup parent from repository -->
  10. </parent>
  11. <groupId>com.gblfy</groupId>
  12. <artifactId>springboot2-redis</artifactId>
  13. <version>0.0.1-SNAPSHOT</version>
  14. <name>springboot2-redis</name>
  15. <description>Spring Boot整合redia 单机 哨兵 集群 三种方式</description>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. </properties>
  19. <dependencies>
  20. <dependency>
  21. <groupId>org.springframework.boot</groupId>
  22. <artifactId>spring-boot-starter-web</artifactId>
  23. </dependency>
  24. <!--redis Start-->
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-data-redis</artifactId>
  28. </dependency>
  29. <!-- redis lettuce 依赖commons-pool -->
  30. <dependency>
  31. <groupId>org.apache.commons</groupId>
  32. <artifactId>commons-pool2</artifactId>
  33. </dependency>
  34. <!--redis End-->
  35. <dependency>
  36. <groupId>org.springframework.boot</groupId>
  37. <artifactId>spring-boot-starter-test</artifactId>
  38. <scope>test</scope>
  39. </dependency>
  40. <!--工具包-->
  41. <dependency>
  42. <groupId>org.apache.commons</groupId>
  43. <artifactId>commons-lang3</artifactId>
  44. <version>3.9</version>
  45. </dependency>
  46. <dependency>
  47. <groupId>commons-collections</groupId>
  48. <artifactId>commons-collections</artifactId>
  49. <version>3.2.2</version>
  50. </dependency>
  51. <dependency>
  52. <groupId>org.projectlombok</groupId>
  53. <artifactId>lombok</artifactId>
  54. <optional>true</optional>
  55. </dependency>
  56. <!--序列化-->
  57. <dependency>
  58. <groupId>com.google.code.gson</groupId>
  59. <artifactId>gson</artifactId>
  60. </dependency>
  61. </dependencies>
  62. <build>
  63. <plugins>
  64. <plugin>
  65. <groupId>org.springframework.boot</groupId>
  66. <artifactId>spring-boot-maven-plugin</artifactId>
  67. </plugin>
  68. </plugins>
  69. </build>
  70. </project>

application.yml

  1. server:
  2. port: 80
  3. spring:
  4. profiles:
  5. active:

(1)单机版模式

  1. spring:
  2. redis:
  3. host: 192.168.0.114
  4. port: 6379
  5. ### Redis数据库索引(默认为0)
  6. database: 0
  7. ### 连接超时时间(毫秒)
  8. timeout: 60000ms
  9. password:
  10. lettuce:
  11. pool:
  12. ### 最大连接数(使用负值表示没有限制) 默认8
  13. max-active: 8
  14. ### 最小空闲连接 默认8
  15. min-idle: 0
  16. ### 连接池中的最大空闲连接 默认8
  17. max-idle: 8
  18. ### 连接池最大阻塞等待时间(使用负值表示没有限制)
  19. max-wait: -1ms

(2)哨兵模式

  1. #哨兵
  2. spring:
  3. redis:
  4. sentinel:
  5. master: mymaster
  6. nodes:
  7. - 192.168.0.114:26379
  8. - 192.168.0.114:26380
  9. - 192.168.0.114:26381
  10. lettuce:
  11. pool:
  12. ### 最大连接数(使用负值表示没有限制) 默认8
  13. max-active: 8
  14. ### 最小空闲连接 默认8
  15. min-idle: 0
  16. ### 连接池中的最大空闲连接 默认8
  17. max-idle: 8
  18. ### 连接池最大阻塞等待时间(使用负值表示没有限制)
  19. max-wait: -1ms
  20. ### 连接超时时间(毫秒)
  21. timeout: 60000ms
  22. password:

(3)集群模式

  1. #集群
  2. spring:
  3. redis:
  4. cluster:
  5. nodes:
  6. - 192.168.0.114:7001
  7. - 192.168.0.114:7002
  8. - 192.168.0.114:7003
  9. - 192.168.0.114:7004
  10. - 192.168.0.114:7005
  11. - 192.168.0.114:7006
  12. lettuce:
  13. pool:
  14. ### 最大连接数(使用负值表示没有限制) 默认8
  15. max-active: 8
  16. ### 最小空闲连接 默认8
  17. min-idle: 0
  18. ### 连接池中的最大空闲连接 默认8
  19. max-idle: 8
  20. ### 连接池最大阻塞等待时间(使用负值表示没有限制)
  21. max-wait: -1ms
  22. ### 连接超时时间(毫秒)
  23. timeout: 60000ms
  24. password:

redis配置

  1. import com.fasterxml.jackson.annotation.JsonAutoDetect;
  2. import com.fasterxml.jackson.annotation.PropertyAccessor;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.data.redis.connection.RedisConnectionFactory;
  7. import org.springframework.data.redis.core.RedisTemplate;
  8. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  9. import org.springframework.data.redis.serializer.StringRedisSerializer;
  10. /**
  11. * redis配置类
  12. *
  13. * @author gblfy
  14. * @date 2020-04-19
  15. */
  16. @Configuration
  17. public class RedisConfig {
  18. @Bean
  19. @SuppressWarnings("all")
  20. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
  21. RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
  22. redisTemplate.setConnectionFactory(redisConnectionFactory);
  23. Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
  24. ObjectMapper objectMapper = new ObjectMapper();
  25. objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
  26. objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
  27. jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  28. StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
  29. // key采用String的序列化方式
  30. redisTemplate.setKeySerializer(stringRedisSerializer);
  31. // hash的key也采用String的序列化方式
  32. redisTemplate.setHashKeySerializer(stringRedisSerializer);
  33. // valuevalue采用jackson序列化方式
  34. redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
  35. // hash的value采用jackson序列化方式
  36. redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
  37. //开启事务支持
  38. redisTemplate.setEnableTransactionSupport(true);
  39. redisTemplate.afterPropertiesSet();
  40. return redisTemplate;
  41. }
  42. }

redis工具类

  1. import org.springframework.beans.factory.annotation.Autowired;
  2. import org.springframework.data.redis.core.RedisTemplate;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.util.CollectionUtils;
  5. import java.util.List;
  6. import java.util.Map;
  7. import java.util.Set;
  8. import java.util.concurrent.TimeUnit;
  9. /**
  10. * Redis工具类
  11. *
  12. * @author gblfy
  13. * @date 2020-04-19
  14. */
  15. @Component
  16. public final class RedisUtils {
  17. @Autowired
  18. private RedisTemplate<String, Object> redisTemplate;
  19. // =============================common============================
  20. /**
  21. * 指定缓存失效时间
  22. *
  23. * @param key 键
  24. * @param time 时间(秒)
  25. * @return
  26. */
  27. public boolean expire(String key, long time) {
  28. try {
  29. if (time > 0) {
  30. redisTemplate.expire(key, time, TimeUnit.SECONDS);
  31. }
  32. return true;
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. return false;
  36. }
  37. }
  38. /**
  39. * 根据key 获取过期时间
  40. *
  41. * @param key 键 不能为null
  42. * @return 时间(秒) 返回0代表为永久有效
  43. */
  44. public long getExpire(String key) {
  45. return redisTemplate.getExpire(key, TimeUnit.SECONDS);
  46. }
  47. /**
  48. * 判断key是否存在
  49. *
  50. * @param key 键
  51. * @return true 存在 false不存在
  52. */
  53. public boolean hasKey(String key) {
  54. try {
  55. return redisTemplate.hasKey(key);
  56. } catch (Exception e) {
  57. e.printStackTrace();
  58. return false;
  59. }
  60. }
  61. /**
  62. * 删除缓存
  63. *
  64. * @param key 可以传一个值 或多个
  65. */
  66. @SuppressWarnings("unchecked")
  67. public void del(String... key) {
  68. if (key != null && key.length > 0) {
  69. if (key.length == 1) {
  70. redisTemplate.delete(key[0]);
  71. } else {
  72. redisTemplate.delete(CollectionUtils.arrayToList(key));
  73. }
  74. }
  75. }
  76. // ============================String=============================
  77. /**
  78. * 普通缓存获取
  79. *
  80. * @param key 键
  81. * @return 值
  82. */
  83. public Object get(String key) {
  84. return key == null ? null : redisTemplate.opsForValue().get(key);
  85. }
  86. /**
  87. * 普通缓存放入
  88. *
  89. * @param key 键
  90. * @param value 值
  91. * @return true成功 false失败
  92. */
  93. public boolean set(String key, Object value) {
  94. try {
  95. redisTemplate.opsForValue().set(key, value);
  96. return true;
  97. } catch (Exception e) {
  98. e.printStackTrace();
  99. return false;
  100. }
  101. }
  102. /**
  103. * 普通缓存放入并设置时间
  104. *
  105. * @param key 键
  106. * @param value 值
  107. * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
  108. * @return true成功 false 失败
  109. */
  110. public boolean set(String key, Object value, long time) {
  111. try {
  112. if (time > 0) {
  113. redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
  114. } else {
  115. set(key, value);
  116. }
  117. return true;
  118. } catch (Exception e) {
  119. e.printStackTrace();
  120. return false;
  121. }
  122. }
  123. /**
  124. * 递增
  125. *
  126. * @param key 键
  127. * @param delta 要增加几(大于0)
  128. * @return
  129. */
  130. public long incr(String key, long delta) {
  131. if (delta < 0) {
  132. throw new RuntimeException("递增因子必须大于0");
  133. }
  134. return redisTemplate.opsForValue().increment(key, delta);
  135. }
  136. /**
  137. * 递减
  138. *
  139. * @param key 键
  140. * @param delta 要减少几(小于0)
  141. * @return
  142. */
  143. public long decr(String key, long delta) {
  144. if (delta < 0) {
  145. throw new RuntimeException("递减因子必须大于0");
  146. }
  147. return redisTemplate.opsForValue().increment(key, -delta);
  148. }
  149. // ================================Map=================================
  150. /**
  151. * HashGet
  152. *
  153. * @param key 键 不能为null
  154. * @param item 项 不能为null
  155. * @return 值
  156. */
  157. public Object hget(String key, String item) {
  158. return redisTemplate.opsForHash().get(key, item);
  159. }
  160. /**
  161. * 获取hashKey对应的所有键值
  162. *
  163. * @param key 键
  164. * @return 对应的多个键值
  165. */
  166. public Map<Object, Object> hmget(String key) {
  167. return redisTemplate.opsForHash().entries(key);
  168. }
  169. /**
  170. * HashSet
  171. *
  172. * @param key 键
  173. * @param map 对应多个键值
  174. * @return true 成功 false 失败
  175. */
  176. public boolean hmset(String key, Map<String, Object> map) {
  177. try {
  178. redisTemplate.opsForHash().putAll(key, map);
  179. return true;
  180. } catch (Exception e) {
  181. e.printStackTrace();
  182. return false;
  183. }
  184. }
  185. /**
  186. * HashSet 并设置时间
  187. *
  188. * @param key 键
  189. * @param map 对应多个键值
  190. * @param time 时间(秒)
  191. * @return true成功 false失败
  192. */
  193. public boolean hmset(String key, Map<String, Object> map, long time) {
  194. try {
  195. redisTemplate.opsForHash().putAll(key, map);
  196. if (time > 0) {
  197. expire(key, time);
  198. }
  199. return true;
  200. } catch (Exception e) {
  201. e.printStackTrace();
  202. return false;
  203. }
  204. }
  205. /**
  206. * 向一张hash表中放入数据,如果不存在将创建
  207. *
  208. * @param key 键
  209. * @param item 项
  210. * @param value 值
  211. * @return true 成功 false失败
  212. */
  213. public boolean hset(String key, String item, Object value) {
  214. try {
  215. redisTemplate.opsForHash().put(key, item, value);
  216. return true;
  217. } catch (Exception e) {
  218. e.printStackTrace();
  219. return false;
  220. }
  221. }
  222. /**
  223. * 向一张hash表中放入数据,如果不存在将创建
  224. *
  225. * @param key 键
  226. * @param item 项
  227. * @param value 值
  228. * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
  229. * @return true 成功 false失败
  230. */
  231. public boolean hset(String key, String item, Object value, long time) {
  232. try {
  233. redisTemplate.opsForHash().put(key, item, value);
  234. if (time > 0) {
  235. expire(key, time);
  236. }
  237. return true;
  238. } catch (Exception e) {
  239. e.printStackTrace();
  240. return false;
  241. }
  242. }
  243. /**
  244. * 删除hash表中的值
  245. *
  246. * @param key 键 不能为null
  247. * @param item 项 可以使多个 不能为null
  248. */
  249. public void hdel(String key, Object... item) {
  250. redisTemplate.opsForHash().delete(key, item);
  251. }
  252. /**
  253. * 判断hash表中是否有该项的值
  254. *
  255. * @param key 键 不能为null
  256. * @param item 项 不能为null
  257. * @return true 存在 false不存在
  258. */
  259. public boolean hHasKey(String key, String item) {
  260. return redisTemplate.opsForHash().hasKey(key, item);
  261. }
  262. /**
  263. * hash递增 如果不存在,就会创建一个 并把新增后的值返回
  264. *
  265. * @param key 键
  266. * @param item 项
  267. * @param by 要增加几(大于0)
  268. * @return
  269. */
  270. public double hincr(String key, String item, double by) {
  271. return redisTemplate.opsForHash().increment(key, item, by);
  272. }
  273. /**
  274. * hash递减
  275. *
  276. * @param key 键
  277. * @param item 项
  278. * @param by 要减少记(小于0)
  279. * @return
  280. */
  281. public double hdecr(String key, String item, double by) {
  282. return redisTemplate.opsForHash().increment(key, item, -by);
  283. }
  284. // ============================set=============================
  285. /**
  286. * 根据key获取Set中的所有值
  287. *
  288. * @param key 键
  289. * @return
  290. */
  291. public Set<Object> sGet(String key) {
  292. try {
  293. return redisTemplate.opsForSet().members(key);
  294. } catch (Exception e) {
  295. e.printStackTrace();
  296. return null;
  297. }
  298. }
  299. /**
  300. * 根据value从一个set中查询,是否存在
  301. *
  302. * @param key 键
  303. * @param value 值
  304. * @return true 存在 false不存在
  305. */
  306. public boolean sHasKey(String key, Object value) {
  307. try {
  308. return redisTemplate.opsForSet().isMember(key, value);
  309. } catch (Exception e) {
  310. e.printStackTrace();
  311. return false;
  312. }
  313. }
  314. /**
  315. * 将数据放入set缓存
  316. *
  317. * @param key 键
  318. * @param values 值 可以是多个
  319. * @return 成功个数
  320. */
  321. public long sSet(String key, Object... values) {
  322. try {
  323. return redisTemplate.opsForSet().add(key, values);
  324. } catch (Exception e) {
  325. e.printStackTrace();
  326. return 0;
  327. }
  328. }
  329. /**
  330. * 将set数据放入缓存
  331. *
  332. * @param key 键
  333. * @param time 时间(秒)
  334. * @param values 值 可以是多个
  335. * @return 成功个数
  336. */
  337. public long sSetAndTime(String key, long time, Object... values) {
  338. try {
  339. Long count = redisTemplate.opsForSet().add(key, values);
  340. if (time > 0)
  341. expire(key, time);
  342. return count;
  343. } catch (Exception e) {
  344. e.printStackTrace();
  345. return 0;
  346. }
  347. }
  348. /**
  349. * 获取set缓存的长度
  350. *
  351. * @param key 键
  352. * @return
  353. */
  354. public long sGetSetSize(String key) {
  355. try {
  356. return redisTemplate.opsForSet().size(key);
  357. } catch (Exception e) {
  358. e.printStackTrace();
  359. return 0;
  360. }
  361. }
  362. /**
  363. * 移除值为value的
  364. *
  365. * @param key 键
  366. * @param values 值 可以是多个
  367. * @return 移除的个数
  368. */
  369. public long setRemove(String key, Object... values) {
  370. try {
  371. Long count = redisTemplate.opsForSet().remove(key, values);
  372. return count;
  373. } catch (Exception e) {
  374. e.printStackTrace();
  375. return 0;
  376. }
  377. }
  378. // ===============================list=================================
  379. /**
  380. * 获取list缓存的内容
  381. *
  382. * @param key 键
  383. * @param start 开始
  384. * @param end 结束 0 到 -1代表所有值
  385. * @return
  386. */
  387. public List<Object> lGet(String key, long start, long end) {
  388. try {
  389. return redisTemplate.opsForList().range(key, start, end);
  390. } catch (Exception e) {
  391. e.printStackTrace();
  392. return null;
  393. }
  394. }
  395. /**
  396. * 获取list缓存的长度
  397. *
  398. * @param key 键
  399. * @return
  400. */
  401. public long lGetListSize(String key) {
  402. try {
  403. return redisTemplate.opsForList().size(key);
  404. } catch (Exception e) {
  405. e.printStackTrace();
  406. return 0;
  407. }
  408. }
  409. /**
  410. * 通过索引 获取list中的值
  411. *
  412. * @param key 键
  413. * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
  414. * @return
  415. */
  416. public Object lGetIndex(String key, long index) {
  417. try {
  418. return redisTemplate.opsForList().index(key, index);
  419. } catch (Exception e) {
  420. e.printStackTrace();
  421. return null;
  422. }
  423. }
  424. /**
  425. * 将list放入缓存
  426. *
  427. * @param key 键
  428. * @param value 值
  429. * @param time 时间(秒)
  430. * @return
  431. */
  432. public boolean lSet(String key, Object value) {
  433. try {
  434. redisTemplate.opsForList().rightPush(key, value);
  435. return true;
  436. } catch (Exception e) {
  437. e.printStackTrace();
  438. return false;
  439. }
  440. }
  441. /**
  442. * 将list放入缓存
  443. *
  444. * @param key 键
  445. * @param value 值
  446. * @param time 时间(秒)
  447. * @return
  448. */
  449. public boolean lSet(String key, Object value, long time) {
  450. try {
  451. redisTemplate.opsForList().rightPush(key, value);
  452. if (time > 0)
  453. expire(key, time);
  454. return true;
  455. } catch (Exception e) {
  456. e.printStackTrace();
  457. return false;
  458. }
  459. }
  460. /**
  461. * 将list放入缓存
  462. *
  463. * @param key 键
  464. * @param value 值
  465. * @param time 时间(秒)
  466. * @return
  467. */
  468. public boolean lSet(String key, List<Object> value) {
  469. try {
  470. redisTemplate.opsForList().rightPushAll(key, value);
  471. return true;
  472. } catch (Exception e) {
  473. e.printStackTrace();
  474. return false;
  475. }
  476. }
  477. /**
  478. * 将list放入缓存
  479. *
  480. * @param key 键
  481. * @param value 值
  482. * @param time 时间(秒)
  483. * @return
  484. */
  485. public boolean lSet(String key, List<Object> value, long time) {
  486. try {
  487. redisTemplate.opsForList().rightPushAll(key, value);
  488. if (time > 0)
  489. expire(key, time);
  490. return true;
  491. } catch (Exception e) {
  492. e.printStackTrace();
  493. return false;
  494. }
  495. }
  496. /**
  497. * 根据索引修改list中的某条数据
  498. *
  499. * @param key 键
  500. * @param index 索引
  501. * @param value 值
  502. * @return
  503. */
  504. public boolean lUpdateIndex(String key, long index, Object value) {
  505. try {
  506. redisTemplate.opsForList().set(key, index, value);
  507. return true;
  508. } catch (Exception e) {
  509. e.printStackTrace();
  510. return false;
  511. }
  512. }
  513. /**
  514. * 移除N个值为value
  515. *
  516. * @param key 键
  517. * @param count 移除多少个
  518. * @param value 值
  519. * @return 移除的个数
  520. */
  521. public long lRemove(String key, long count, Object value) {
  522. try {
  523. Long remove = redisTemplate.opsForList().remove(key, count, value);
  524. return remove;
  525. } catch (Exception e) {
  526. e.printStackTrace();
  527. return 0;
  528. }
  529. }
  530. }

源码参考:​https://gitee.com/gb_90/SpringBoot2_Practical_Column​

3、整合redisson

参考:https://blog.csdn.net/qq_27579471/article/details/103921489

redisson优势

提供了看门狗

Redisson提供了一个监控锁的看门狗(watch dog),它的作用是在Redisson实例被关闭前,不断(默认每10s)的延长锁(redis中的目标key)的有效期(默认续期到30s),也就是说,如果一个拿到锁的线程一直没有完成逻辑,那么看门狗会帮助线程不断的延长锁的超时时间,锁不会因为超时而被释放。加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认会在30s内自动过期,不会产生死锁问题;

默认情况下,看门狗的续期时间是30s,也可以通过修改Config.lockWatchdogTimeout来另行指定。

另外Redisson还提供了可以指定leaseTime参数的加锁方法来指定加锁的时间。超过这个时间后锁便自动解开了,不会延长锁的有效期。

提供了多种锁

redisson 还有公平锁、读写锁的实现。

  1. public interface RedissonClient {
  2. /**
  3. * 获取锁(默认的非公平锁)
  4. */
  5. RLock getLock(String name);
  6. /**
  7. * 获取公平锁
  8. */
  9. RLock getFairLock(String name);
  10. /**
  11. * 获取读写锁
  12. */
  13. RReadWriteLock getReadWriteLock(String name);
  14. }
  1. public interface RLock extends Lock, RExpirable, RLockAsync {
  2. void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException;
  3. boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
  4. void lock(long leaseTime, TimeUnit unit);
  5. boolean forceUnlock();
  6. boolean isLocked();
  7. boolean isHeldByCurrentThread();
  8. int getHoldCount();
  9. }

添加依赖,注意redisson和springboot的版本对应

  1. <dependency>
  2. <groupId>org.redisson</groupId>
  3. <artifactId>redisson-spring-boot-starter</artifactId>
  4. <version>3.15.0</version>
  5. </dependency>

分析源码

redisson-spring-boot-starter这个包下只有2个类:RedissonProperties和RedissonAutoConfiguration。阅读源码发现RedissonAutoConfiguration会自动注册RedissonConnectionFactory和RedissonClient。



redisson-spring-boot-starter支持三种配置方式

方式一:完全兼容spring-boot-starter-data-redis的配置,即只需在application.yml中使用spring.redis来配置redis,redisson-spring-boot-starter会根据Redis的配置自动配置redisson。(已测试可行)

方式二:redisson的spring.redis.redisson.file方式,即在application.yml中使用spring.redis.redisson.file=classpath:redisson.yml来指定redisson的配置文件路径,然后在redisson.yml中配置redisson。

方式三:redisson的spring.redis.redisson.config方式,即在application.yml中使用spring.redis.redisson.config=xxx来直接配置Redisson。

多种方式的优先级:spring.redis.redisson.config > spring.redis.redisson.file> spring.redis。详细参考org.redisson.spring.starter.RedissonAutoConfiguration#redisson()。

简单使用

配置了redis和redisson的信息之后,就可以在项目中使用@Autowired注入org.redisson.api.RedissonClient(redisson提供的默认实现类是org.redisson.Redisson,且redisson-spring-boot-starter会自动往IOC容器中注册org.redisson.Redisson)。

Redis操作注入RedisTemplate进行使用。

Redission分布式锁可以引入RedissionClient进行使用。

  1. @RestController
  2. public class RedissonController {
  3. @Autowired
  4. private RedissonClient redissonClient;
  5. @GetMapping(value = "/redisson/{key}")
  6. public String redissonTest(@PathVariable("key") String lockKey) {
  7. RLock lock = redissonClient.getLock(lockKey);
  8. try {
  9. lock.lock();
  10. Thread.sleep(10000);
  11. } catch (Exception e) {
  12. } finally {
  13. lock.unlock();
  14. }
  15. return "已解锁";
  16. }
  17. }

优化一下 封装redissonClient

  1. package com.xx.core.redisson.lock;
  2. /**
  3. * Redisson分布式锁接口
  4. * <p>
  5. * RLock的实现有可重入非公平锁(RedissonLock)、可重入公平锁(RedissonFairLock)、联锁(RedissonMultiLock)、 红锁(RedissonRedLock)、 读锁(RedissonReadLock)、 写锁(RedissonWriteLock)等
  6. */
  7. public interface DistributedLocker {
  8. RLock lock(String lockKey);
  9. RLock lock(String lockKey, long timeout);
  10. RLock lock(String lockKey, TimeUnit unit, long timeout);
  11. boolean tryLock(String lockKey, TimeUnit unit, long leaseTime);
  12. boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);
  13. void unlock(String lockKey);
  14. void unlock(RLock lock);
  15. }
  1. /**
  2. * Redisson分布式锁实现-使用可重入非公平锁(RedissonLock)
  3. */
  4. @Component
  5. public class RedissonDistributedLocker implements DistributedLocker {
  6. @Autowired
  7. private RedissonClient redissonClient;
  8. public RLock lock(String lockKey) {
  9. RLock lock = this.redissonClient.getLock(lockKey);
  10. lock.lock();
  11. return lock;
  12. }
  13. public RLock lock(String lockKey, long timeout) {
  14. RLock lock = this.redissonClient.getLock(lockKey);
  15. lock.lock(timeout, TimeUnit.SECONDS);
  16. return lock;
  17. }
  18. public RLock lock(String lockKey, TimeUnit unit, long timeout) {
  19. RLock lock = this.redissonClient.getLock(lockKey);
  20. lock.lock(timeout, unit);
  21. return lock;
  22. }
  23. public boolean tryLock(String lockKey, TimeUnit unit, long leaseTime) {
  24. RLock lock = this.redissonClient.getLock(lockKey);
  25. try {
  26. return lock.tryLock(0L, leaseTime, unit);
  27. } catch (InterruptedException var7) {
  28. return false;
  29. }
  30. }
  31. public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime){
  32. RLock lock = redissonClient.getLock(lockKey);
  33. try {
  34. return lock.tryLock(waitTime, leaseTime, unit);
  35. } catch (InterruptedException e) {
  36. return false;
  37. }
  38. }
  39. public void unlock(String lockKey){
  40. RLock lock = redissonClient.getLock(lockKey);
  41. try {
  42. if (lock.isLocked()) {
  43. lock.unlock();
  44. }
  45. } catch (IllegalMonitorStateException localIllegalMonitorStateException) {}
  46. }
  47. public void unlock(RLock lock) {
  48. try {
  49. if (lock.isLocked()) {
  50. lock.unlock();
  51. }
  52. } catch (IllegalMonitorStateException var3) {}
  53. }
  54. }

再优化一下,使用函数式DistributedLocker 接口新增tryLockAndRun()方法

  1. public interface DistributedLocker {
  2. /**
  3. * @param lockKey
  4. * @param unit
  5. * @param waitTime
  6. * @param leaseTime
  7. * @param supplier 获取锁后要执行的业务逻辑
  8. * @param scene 业务逻辑的场景,用于打印日志
  9. * @param <T>
  10. * @return
  11. */
  12. <T> T tryLockAndRun(@Nonnull String lockKey, @Nonnull TimeUnit unit, long waitTime, long leaseTime, @Nonnull Supplier<T> supplier, String scene);
  13. }
  1. public class RedissonDistributedLocker implements DistributedLocker {
  2. @Override
  3. public <T> T tryLockAndRun(@Nonnull String lockKey, @Nonnull TimeUnit unit, long waitTime, long leaseTime, @Nonnull Supplier<T> supplier, String scene) {
  4. final long start = SystemClock.now();
  5. // 获取分布式锁,最长等待时间:10秒,20秒后自动释放。注意锁与事务的顺序:获取分布式锁 -> 开启事务 -> 执行业务 -> 提交事务 -> 释放分布式锁!!!
  6. final boolean tryLock = this.tryLock(lockKey, unit, waitTime, leaseTime);
  7. final long end = SystemClock.now();
  8. if (!tryLock) {
  9. log.error("[{}]获取分布式锁失败,lockKey = {},耗时{}ms", scene, lockKey, end - start);
  10. throw new RequestResultException(JsonCommonCodeEnum.E0004);
  11. }
  12. // 注意:一定是获取锁成功后,才进行try{}finally{释放锁}
  13. try {
  14. log.info("[{}]获取分布式锁成功,lockKey = {},耗时{}ms", scene, lockKey, end - start);
  15. return supplier.get();
  16. } finally {
  17. this.unlock(lockKey);
  18. }
  19. }
  20. }

4、问题处理

redis单机、主从、哨兵、集群以及redisson分布式锁的更多相关文章

  1. Redis主从&哨兵集群搭建

    主从集群 在搭建主从集群前,我们先把Redis安装起来: #解压Redis压缩包 [root@master lf]# tar -zxvf redis-6.2.1.tar.gz -- #安装gcc [r ...

  2. C#两大知名Redis客户端连接哨兵集群的姿势

    前言 前面利用<Docker-Compose搭建Redis高可用哨兵集群>, 我们的思路是将Redis.Sentinel.Redis Client App链接到同一个网桥网络,这个网桥内的 ...

  3. 阿里云ECS部署Redis主备哨兵集群遇到的问题

    一.部署 详细部署步骤:https://blog.csdn.net/lihongtai/article/details/82826809 Redis5.0版本需要注意的参数配置:https://www ...

  4. 三千字介绍Redis主从+哨兵+集群

    一.Redis持久化策略 1.RDB 每隔几分钟或者一段时间会将redis内存中的数据全量的写入到一个文件中去. 优点: 因为他是每隔一段时间的全量备份,代表了每个时间段的数据.所以适合做冷备份. R ...

  5. 【Redis学习专题】- Redis主从+哨兵集群部署

    集群版本: redis-4.0.14 集群节点: 节点角色 IP redis-master 10.100.8.21 redis-slave1 10.100.8.22 redis-slave2 10.1 ...

  6. 项目实战11—企业级nosql数据库应用与实战-redis的主从和集群

    企业级nosql数据库应用与实战-redis 环境背景:随着互联网2.0时代的发展,越来越多的公司更加注重用户体验和互动,这些公司的平台上会出现越来越多方便用户操作和选择的新功能,如优惠券发放.抢红包 ...

  7. Redis集合 安装 哨兵集群 配置

    redis相关 redis基础 redis发布订阅 redis持久化RDB与AOF redis不重启,切换RDB备份到AOF备份 redis安全配置 redis主从同步 redis哨兵集群 redis ...

  8. redis系列--深入哨兵集群

    一.前言 在之前的系列文章中介绍了redis的入门.持久化以及复制功能,如果不了解请移步至redis系列进行阅读,当然我也是抱着学习的知识分享,如果有什么问题欢迎指正,也欢迎大家转载.而本次将介绍哨兵 ...

  9. Redis单机安装以及集群搭建

    今天主要来看一下Redis的安装以及集群搭建(我也是第一次搭建). 环境:CentOS 7.1,redis-5.0.7 一.单机安装 1.将Redis安装包放置服务器并解压 2.进入redis安装目录 ...

  10. redis 单机模拟 cluster集群

    一.redis-cluster设计 Redis集群搭建的方式有多种,例如使用zookeeper等,但从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心 ...

随机推荐

  1. Lombok注解及其作用

    Lombok是一个Java库,通过使用注解简化Java类的开发,减少冗余的样板代码.以下是一些常用的Lombok注解及其作用: 1. `@Data`:生成所有属性的getter.setter.`toS ...

  2. Java工具类Result<T>

    枚举类:ResultCodeEnum /** * 统一返回结果状态信息类 * */ @Getter public enum ResultCodeEnum { SUCCESS(200,"成功& ...

  3. 00.XML入门

    0.了解XML Extensible Markup Language 可扩展标记语言 申明信息不算元素,左图中book为根元素,根元素有且仅有一个; 1.初识XML 1.3用IDE创建xml(以ecl ...

  4. 白帽子讲web安全

    世界安全观 Web安全筒史 起初,研究计算机系统和网络的人,被称为"Hacker","Hacker"在中国按照音译,被称为"黑客" 对于现代 ...

  5. P3498 [POI2010]KOR-Beads 题解

    前言: 最近在做哈希的题,发现了这道好题,看题解里很多大佬的方法都很巧妙,自己就发一个较为朴素的方法吧. 题意: 题目传送门 给你一个序列,需要求出数 k,使划分的子串长度为 k 时,不同的子串数量最 ...

  6. 聊聊 ASP.NET 6 整洁架构开发模板

    大家好,我是Edison. 最近看了一些整洁架构(CleanArchitecture)的文章,自己和同事也简单写了一个基于整洁架构的ASP.NET 6开发模板在玩.这里就仅仅抛个砖,案例主要以自己根据 ...

  7. Mysql 5.7 的安装

    Mysql的安装 1 windows两种安装方式,入门选手推荐第二种(win10演示) Mysql官网下载地址:https://dev.mysql.com/downloads/mysql/ 2 开始准 ...

  8. Linux下日志管理工具Logrotate

    背景: 项目上的Nginx和Tomcat已经跑了大半年了,Nginx的access.log和error.log将近1G大小:Tomcat下的catalina.out日志经常跑到打不出日志然后进行手动移 ...

  9. Prometheus-3:一文详解promQL

    读前提示: 本文字数较多且紧凑,最好预留15min一次性看完,好营养,易吸收. promQL详解 Prometheus提供了内置的数据查询语言PromQL(全称为Prometheus Query La ...

  10. 「学习笔记」KMP 算法

    前置知识 前缀 是指从串首开始到某个位置 \(i\) 结束的一个特殊子串. 真前缀 指除了 \(S\) 本身的 \(S\) 的前缀. 举例来说, 字符串 abcabeda 的所有前缀为 {a, ab, ...