简介:
其实就是一个基于java语言的redis-cli客户端。
Redis是一种运行在内存当中键值数据库,而且是以字符串类型为中心的,当前它能够支持多重数据类型,包括字符串,
散列,列表,集合,有序集合,基数和地理位置等。
在Spring项目当中使用Redis:
我们需要通过连接池创建一个RedisConnectionFactory对象,通过它我们就能够创建RedisConnection接口对象,
但是当我们使用一条连接时,我们要先从RedisConnectionFactory工厂获取,然后在使用完成后还要自己去关闭。
Spring为简化操作,提供RedisTemplate
redisTemplate.opsForvalue().set("key1","value1");当存储这样的一对键值对时,从Redis客户端输入
key*key1,得到的并不是value1。因为Redis是基于字符串存储的NoSQl,而java是基于对象的语言,对象是无法储存到
Redis中的,不过java中提供了序列化机制,可以将对象转换成二进制字符串。
所以就有了Redis键序列化器,
RedisSerializer stringRedisSerializer=redisTemplate.getStringSerializer;
设置后能够从复杂的编码得到简单的字符串。
在SpringBoot中使用Redis:
1.首先要在官网下载Redis,添加服务到本地,(设置开机自动启动该服务)
2.导入依赖
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3.导入依赖后,SpringBoot已将帮我把RedisTemplateFactory,RedisTemplate,StringRedisTemplate
的常用对象添加到IOC容器当中,因此我们只需要从IOC容器中获取该对象即可。
可以通过 redis-cli.exe -h 127.0.0.1 -p 6379命令查找key对应的value和设置键值对
Redis的一些特殊用法:
Redis除了操作那些数据类型的功能外,还能支持事务,流水线,发布订阅和Lua语等功能
在高并发的场景中,往往我们需要保证数据的一致性,这是考虑使用Redis事务,或者是利用Redis执行
Lua的原子性来达到数据一致性的目的。
使用Redis事务:
在Redis中使用事务,通常的命令组合是watch,multi,exec。也就是要在一个Redis连接中执行多个命令,
这时我们可以考虑使用SessionCallBack来达到这个目的。
watch:是可以监控Redis的一些键;
multi:是开始事务,开启事务后,该客户端的命令不会马上立即被执行,而是存放在一个队列中,结果返回null
exec:执行事务,只是他在队列命令执行前会判断被watch监控的Redis的键的数据是否发生过改变,如果他认为发生了
改变,(即使是相同的值覆盖也认为是改变)那么Redis就会取消事务。
测试代码:
1.SpringBoot中对Redis中String类型的使用
依赖:
<!--添加mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!--添加DBCP数据源-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
常用配置 :
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
# 添加redis到服务 redis-server --service-install redis.windows.conf
# 开启服务 redis-server --service-start
spring.redis.port=6379
# 默认无密码
spring.redis.password=
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.max-wait=2000ms
spring.redis.jedis.pool.min-idle=5
#缓存管理器配置
#如果底层的缓存管理器支持创建,以逗号分隔列表缓存名称
spring.cache.cache-names=redisCache
#是否允许Redis缓存空值
spring.cache.redis.cache-null-values=true
#Redis的键前缀
spring.cache.redis.key-prefix=
#缓存超时时间戳,配置为0则不设置超时时间 十分钟
spring.cache.redis.time-to-live=600000ms
#是否启用Redis的键前缀
spring.cache.redis.use-key-prefix=true
# 缓存类型 默认情况下,Spring会自动根据上下文探测
spring.cache.type=REDIS
控制层:
@Controller
@RequestMapping("/redis")
public class RedisController {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/stringAndHash")
@ResponseBody
public Map<String,Object> testStringAndHash(){
redisTemplate.opsForValue().set("key1","value1");
//默认使用JDK的序列化器,所以Redis保存的不是整数,不能进行运行
redisTemplate.opsForValue().set("int_key1","1");
//使用运算
stringRedisTemplate.opsForValue().set("int","1");
//获取底层Redis连接
// RedisProperties.Jedis jedis;
// jedis= (RedisProperties.Jedis) stringRedisTemplate.getConnectionFactory().getConnection().getNativeConnection();
// jedis.
//减一操作 redisTemplate不支持
Map<String,String> hash=new HashMap();
hash.put("field1","value1");
hash.put("field2","value2");
//存入一个散列数据类形
stringRedisTemplate.opsForHash().putAll("hash",hash);
stringRedisTemplate.opsForHash().put("hash","field3","value3");
//绑定散列操作的key,这样可以连续对一个散列数据类型进行操作
BoundHashOperations hashOps= stringRedisTemplate.boundHashOps("hash");
hashOps.delete("field1","field2");
hashOps.put("field4","value4");
Map map=new HashMap();
map.put("Success",true);
return map;
}
@Autowired
private MybatisUserService mybatisUserService;
@RequestMapping("/insertUser")
@ResponseBody
public User insertUser(){
User user=new User();
user.setName("redis");
user.setNote("新成员");
mybatisUserService.insertUser(user);
return user;
}
@RequestMapping("/findUser")
@ResponseBody
public User findUser(int id)
{
return mybatisUserService.findUser(id);
}
@RequestMapping("/updateUser")
@ResponseBody
public User updateUser(Integer id,String name){
return mybatisUserService.updateUser(id,name);
}
@RequestMapping("/deleteUser")
@ResponseBody
public int deleteUser(Integer id){
return mybatisUserService.deleteUser(id);
}
}
组件 :
@Component
public class RedisComponent {
//通过stringRedisTemplate进行序列化键值,这样只能支持支持字符串,并不能支持java对象的储存。
//不设置时,RedisTemplate默认使用JdkSerializationRedisSerializer 进行序列化键值。存储到
//服务器,这是存入的便是一个经过序列化后的特殊字符,对于我们跟踪不友好!
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void set(String key,String value){
//拿到存储操作对象
ValueOperations<String,String> ops=stringRedisTemplate.opsForValue();
if(stringRedisTemplate.hasKey(key))
{
System.out.println("Key="+key+"value="+value);
}
else{
ops.set(key, value);
System.out.println("添加成功!");
}
}
public String get(String key){
return stringRedisTemplate.opsForValue().get(key);
}
public void delete(String key){
stringRedisTemplate.delete(key);
System.out.println("删除成功!");
}
}
业务层
//插入用户,最后Mybatis会回填ID,取结果id缓存用户
@Override
@Transactional
@CachePut(value = "redisCache",key = "'redis_user_'+#result.id")
//当方法返回值为int类型时会报找不到id异常
//value配置对应缓存名称redisCache 键配置:#result表示返回对象user
//而且返回类型必须为User不然会报异常
public User insertUser(User user) {
mybatisUserDao.insertUser(user);
return user;
}
@Override
@Transactional
@CachePut(value = "redisCache",key = "'redis_user_'+#result.id",condition = "#result!='null'")
//如果返回结果为空,则不使用缓存
public User updateUser(Integer id,String name) {
User user=this.findUser(id);//该方法的缓存注解在此处会失效
if(user==null)
{
System.out.println("查找用户失败!!!");
return null;
}
user.setName("更改后的名字");
user.setNote("更改后的身份");
mybatisUserDao.updateUser(user);//不要忘了。。
return user;
}
@Override
@Transactional
@Cacheable(value = "redisCache" ,key = "'redis_user_'+#id")
//参数中不是User对象不能设置为#result.id
public User findUser(Integer id) {
return mybatisUserDao.findUser(id);
}
@Override
@Transactional
@CacheEvict(value = "redisCache",key ="'redis_user_'+#id",beforeInvocation = false)
//默认值为false 表示在该方法之后移除缓存
public int deleteUser(Integer id) {
return mybatisUserDao.deleteUser(id);
}
2.Jedis总结:
# Jedis直连
## 执行流程
1. 创建Jedis对象
Jedis jedis=new Jedis("127.0.0.1",6379);
2. 通过Jedis执行命令
3. 返回Jedis执行结果
4. 关闭Jedis连接
## 创建Jedis的方式
```java
/**
* @param host Redis节点所在机器的IP或域名
* @param port Redis服务的端口号
* @param connectionTimeout 客户端连接超时时间(毫秒)
* @param soTimeout 客户端读写超时时间(毫秒)
*/
public Jedis(String host , int port , int connectionTimeout , int soTimeout)
```
具体参考API文档http://tool.oschina.net/apidocs/apidoc?api=jedis-2.1.0
# Jedis连接池
## 执行流程
1. 创建一个JedisPool对象
2. 从资源池中获取一个Jedis对象
3. 通过Jedis执行命令
4. 返回Jedis执行结果
5. 关闭Jedis连接,将Jedis还给资源池
## 创建Jedis连接池的方式
```java
JedisPoolConfig config = new JedisPoolConfig();
JedisPool jedisPool = new JedisPool(config , "127.0.0.1" , 6379);
Jedis jedis = jedisPool.getResource();
jedis.close();
```
最好是使用try catch方式
# Jedis直连 VS Jedis连接池
| | 优点 | 缺点 |
| :---------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
| Jedis直连 | 使用简单<br/>适用于少量长期连接的场景 | 存在每次新建/关闭TCP连接的开销<br/>资源无法控制,存在连接泄露的风险<br/>Jedis对象线程不安全 |
| Jedis连接池 | Jedis对象预先生成,降低使用开销<br/>连接池的形式保护和控制资源的使用 | 相对于直连,使用相对麻烦<br/>尤其在资源的管理上需要许多参数保证<br/>一旦参数不合理会出现很多问题 |
Jedis连接池配置
1.commons-pool资源数配置参数:
2.commons-pool借还参数
JedisPoolConfig jedisPoolConfig=new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(10); jedisPoolConfig.setMaxWaitMillis(1000); JedisPool jedisPool=new JedisPool(jedisPoolConfig,"127.0.0.1",6379); for(int i=0;i<10;i++){ Jedis jedis=null; try { jedis=jedisPool.getResource(); System.out.println(jedis.ping()+(i+1)); }catch (Exception e){ e.printStackTrace(); }finally { jedis.close(); //当我们不在finally中进行资源的关闭时, // 由于连接池中的jedis都在被使用,就会出现连接超时的错误 } } jedisPool.getResource().ping();
复杂一点的jedis连接池配置。https://blog.csdn.net/u010473656/article/details/78838254
在springboot中配置参考上文很简单。