在Spring中使用Redis

一、NoSQL

1、NoSQL的优点:

  • 高并发、效率高
  • 拓展性强
  • 数据结构灵活

2、NoSQL的类型:

  • 键值对存储:Redis (内容缓存,高并发访问效率高)
  • 列存储:Hbase
  • 文档数据库:MongoDB
  • 图形数据库

二、Redis

1、概述

  • 使用C语言进行开发的一个高性能的键值对型数据库

  • 每秒11万次的读,8W次的写操作,高吞吐量

2、应用场景

  • 缓存
  • 任务队列
  • 分布式集群的session分离

3、安装

  • sudo brew install redis
  • cd 进入 /usr/local/bin
  • sudo ./redis-server

    启动服务端

  • sudo ./redis-cli
  • 在客户端执行Redis语法代码
    启动客户端

4、持久化

  • RDB:指定间隔多少时间将Redis中的内容存储到硬盘
  • AOF:记录日志,记录每个操作,服务器启动之后进行数据库重新构建

三、Spring Boot使用Redis

1、增加依赖spring-boot-starter-data-redis

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在Spring Boot里面的依赖spring-boot-starter-data-redis已经集成了jedis,common-pool等等的包,所以就不要增加这些依赖,容易版本冲突导致应用无法起来,如下图所示。
Redis依赖

2、进行配置注入

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
@Configuration
public class Config {

private Logger LOG = LoggerFactory.getLogger(Config.class);

@Bean
Jedis jedis(){
Jedis jedis = new Jedis("127.0.0.1",6379);
return jedis;
}

@Bean
JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName("127.0.0.1");
jedisConnectionFactory.setPort(6379);
return new JedisConnectionFactory();
}

@Bean
public RedisTemplate<String, String> redisTemplate() {
RedisTemplate<String, String> template = new RedisTemplate<String, String>();
template.setConnectionFactory(jedisConnectionFactory());
template.setKeySerializer(new StringRedisSerializer());
return template;
}
}

3、代码测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Resource
private RedisTemplate redisTemplate;

@Autowired
private Jedis jedis;

@Test
public void testRedis() {
redisTemplate.opsForValue().set("name", "jaychou");
}

@Test
public void testJedis() {
jedis.set("age", "12");
String age = jedis.get("age");
jedis.close();
Assert.assertEquals(age, "12");
}

执行完毕之后可以在你的本机的Redis客户端查看到写入的内容。或者可以通过brew cask install rdm安装RDM(Redis Desktop Manager)可视化工具进行查看,如下图:
Redis可视化工具

具体RedisTemplate的使用姿势:如何使用RedisTemplate访问Redis数据结构

三、使用Redis 实现分布式锁

在分布式锁的实现上,基于数据库的乐观锁,基于zookeeper的分布式锁,同样基于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
/**
* Created by buzheng
* 使用Redis实现分布式锁服务
*/
public class RedisLock {


private Logger logger = LoggerFactory.getLogger(RedisLock.class);

/**
* redis
*/
@Autowired
private StringRedisTemplate redisTemplate;


/**
* 尝试获取锁
*
* @param redisConnection
* @param lockSeconds
* @return
*/
private boolean lock(RedisConnection redisConnection, int lockSeconds, byte[] lockKey) {
long nowTime = System.currentTimeMillis();
long expireTime = nowTime + lockSeconds * 1000;
if (redisConnection.setNX(lockKey, longToBytes(expireTime))) {
redisConnection.expire(lockKey, lockSeconds);
return true;
} else {
byte[] oldValue = redisConnection.get(lockKey);
if (oldValue != null && bytesToLong(oldValue) < nowTime) {
//说明锁已经过期了 可以获得锁
byte[] oldValueRetry = redisConnection.getSet(lockKey, longToBytes(expireTime));
if (Arrays.equals(oldValue, oldValueRetry)) {
//双重验证标明可以获得锁
redisConnection.expire(lockKey, lockSeconds);
return true;
} else {
//标明此时已经有人先获取锁了
return false;
}
}
}
return false;
}

/**
* 通过轮询的方式获取分布式锁
*
* @param lockSeconds 锁时间
* @param waitTimeMillions 休眠时间
* @param exeCounts 最大尝试次数
* @param lockKey 锁的key
* @return
*/
public boolean tryLock(final int lockSeconds, final long waitTimeMillions, final long exeCounts, byte[] lockKey) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
int tryCount = 0;
while (true) {
tryCount++;
if (tryCount > exeCounts) {
return false;
}
try {
if (tryLock(lockSeconds, lockKey)) {
return true;
}
} catch (Exception e) {
logger.error("try get lock error", e);
}
try {
Thread.sleep(waitTimeMillions);
} catch (Exception e) {
logger.error("tryLock interrupted", e);
return false;
}
}
}
});
}

/**
* 释放锁
*/
private void unLock(byte[] lockKey) {
String lock = new String(lockKey);
redisTemplate.delete(lock);
}

/**
* 尝试一次性获取锁
*
* @param lockSeconds
* @param lockKey
* @return
*/
public boolean tryLock(final int lockSeconds, byte[] lockKey) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
try {
return lock(redisConnection, lockSeconds, lockKey);
} catch (Exception e) {
logger.error("try lock is failed", e);
return false;
}
}
});
}


public byte[] longToBytes(long value) {
ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE);
buffer.putLong(value);
return buffer.array();
}

public long bytesToLong(byte[] bytes) {
return ByteBuffer.wrap(bytes).getLong();
}


}
坚持原创技术分享,您的支持将鼓励我继续创作!