一:目录
- redis的安装和启动
- jedis的使用
- Hello World
- JedisPool的使用
- Spring整合
二:Redis下载、安装和启动
1、下载和安装redis
Windows x64下载地址:https://github.com/ServiceStack/redis-windows
下载完直接解压就好
2、启动Redis服务
使用终端切换到redis的目录下,然后 使用命令启动 redis-server.exe redis.windows.conf
3、启动客户端
使用终端切换到redis的目录下,然后 使用命令启动 redis-cli.exe
三:jedis的使用
1、在pom.xml 中引入spring(需要用到spring-test来测试)和jedis相关的依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.2.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- springframework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
2、测试Jedis相关的方法
这里只是列举出jedis常用的一些方法,jedis的方法名和redis的命令是一样的,了解了命令就自然知道jedis中相关的方法了,这里并不介绍redis中的每个命令
SpringTestCase
package com.mengdee.manager.redis;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@ContextConfiguration(locations = {"classpath:conf/spring/spring-base.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringTestCase extends AbstractJUnit4SpringContextTests{
}
JedisTest
package com.mengdee.manager.redis;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
public class JedisTest extends SpringTestCase{
private Jedis jedis;
@Before
public void setup(){
jedis = new Jedis("127.0.0.1", 6379);
}
/**
* redis 字符串string
*
* 常用操作:添加键值对 、获取键对应的值、删除键、批量添加键值对、对数字型的值+1或者-1
*/
@Test
public void testString() {
//-----添加数据----------
String key = "name";
jedis.set(key,"mengdee");
System.out.println(jedis.get(key));
jedis.append(key, " is good");
System.out.println(jedis.get(key));
jedis.del(key);
System.out.println(jedis.get(key));
// 批量添加键值对,注意值都是字符串类型,注意这里是一次性添加三个键值对,他们的关系是并列关系(即使三个键值对而不是一个键值对的值是三个值)
jedis.mset("name","mengdee","age","23","nickname","mengday");
jedis.incr("age"); //进行加1操作
System.out.println(jedis.get(key) + "-" + jedis.get("age") + "-" + jedis.get("nickname"));
}
/**
* redis map
* map 包含多个键值对,通常存储Java中的对象使用map数据类型存储
*/
@Test
public void testMap() {
//-----添加数据----------
Map<String, String> userMap = new HashMap<String, String>();
userMap.put("id", "1");
userMap.put("username", "mengdee");
userMap.put("age", "20");
String key = "user:1";
jedis.hmset(key, userMap);
// 一次获取多个字段的值
List<String> values = jedis.hmget(key, "id", "username", "age");
System.out.println(values);
// 通常使用hgetAll获取到的Map转换成Java中的对象
Map<String, String> userMap2 = jedis.hgetAll(key);
Set<Entry<String, String>> entrySet = userMap2.entrySet();
for (Entry<String, String> entry : entrySet) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
System.out.println(jedis.hmget(key, "age"));
System.out.println(jedis.hlen(key));
System.out.println(jedis.exists(key));
System.out.println(jedis.hkeys(key));
System.out.println(jedis.hvals(key));
jedis.hdel(key,"age");
}
/**
* jedis list(一般存储像Java中的数组或者List这样的数据类型)
*/
@Test
public void testList(){
//开始前,先移除所有的内容
String key = "java";
jedis.del(key);
jedis.lpush(key, "gradle");
jedis.lpush(key, "springmvc");
jedis.lpush(key, "mybatis");
jedis.rpush(key, "spring boot");
System.out.println(jedis.lrange(key, 0, -1));
jedis.del(key);
jedis.rpush(key, "spring");
jedis.rpush(key, "struts");
jedis.rpush(key, "hibernate");
System.out.println(jedis.lrange(key, 0, -1));
}
/**
* jedis set
*
* 注意set操作是无须的
*/
@Test
public void testSet(){
//添加
String key = "users";
jedis.sadd(key,"zhangsan");
jedis.sadd(key,"lisi");
jedis.sadd(key,"mengdee");
jedis.sadd(key,"mengday");
jedis.sadd(key,"X");
jedis.srem(key,"X");
System.out.println(jedis.smembers(key));
System.out.println(jedis.sismember(key, "mengdee"));
System.out.println(jedis.srandmember(key));
System.out.println(jedis.scard(key));
}
}
3、JedisPool的使用
上面示例是没有使用线程池的,在实际开发中肯定要使用线程池,要不然每次都要建立连接,下面只是简单的实现方式,demo版,不能实际在项目中使用,在项目中实际使用还需要使用更优的实现
package com.mengdee.manager.utils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public final class RedisUtil {
//Redis服务器IP
private static String ADDR = "127.0.0.1";
//Redis的端口号
private static int PORT = 6379;
//访问密码
private static String AUTH = "admin";
//控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
private static int MAX_IDLE = 200;
private static int TIMEOUT = 10000;
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
private static boolean TEST_ON_BORROW = true;
private static JedisPool jedisPool = null;
/**
* 初始化Redis连接池
*/
static {
try {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(MAX_IDLE);
config.setTestOnBorrow(TEST_ON_BORROW);
jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取Jedis实例
* @return
*/
public synchronized static Jedis getJedis() {
try {
if (jedisPool != null) {
Jedis resource = jedisPool.getResource();
return resource;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 释放jedis资源
* @param jedis
*/
public static void returnResource(final Jedis jedis) {
if (jedis != null) {
jedisPool.returnResource(jedis);
}
}
}
使用JedisUtil获取Jedis对象,操作缓存
@Test
public void testRedisPool() {
JedisUtil.getJedis().set("newname", "中文测试");
System.out.println(RedisUtil.getJedis().get("newname"));
}
第二部分:Spring整合
开发中可以单独使用Jedis中相关的方法操作缓存,也可以使用spring提供的相关的注解
1、 在pom.xml中引入相关依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.2.3.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<!-- springframework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.1.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
2、在src/main/resources/conf/settings/application.properties下配置redis相关配置
redis.master.ip=localhost
redis.master.port=6379
redis.pool.maxActive=1024
redis.pool.maxIdle=200
redis.pool.maxWait=1000
redis.pool.testOnBorrow=true
redis.pool.testOnReturn=true
3、在src/main/resources/conf/spring 下配置spring-base.xml和spring-redis.xml
spring-base.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 加载配置文件数据库连接文件 -->
<context:property-placeholder location="classpath:conf/settings/*.properties" />
<context:component-scan base-package="com.mengdee.**.dao,com.mengdee.**.service"/>
</beans>
spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<!-- 以前项目中的配置,注意需要添加Spring Data Redis等jar包 -->
<description>redis配置</description>
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.pool.maxIdle}"/>
<property name="maxTotal" value="${redis.pool.maxActive}"/>
<property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
<property name="testOnReturn" value="${redis.pool.testOnReturn}"/>
</bean>
<!-- JedisConnectionFactory -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.master.ip}"/>
<property name="port" value="${redis.master.port}"/>
<property name="poolConfig" ref="jedisPoolConfig"/>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
p:connectionFactory-ref="jedisConnectionFactory">
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
</bean>
<!--spring cache-->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
c:redisOperations-ref="redisTemplate">
<!-- 默认缓存10分钟 -->
<property name="defaultExpiration" value="600"/>
<property name="usePrefix" value="true"/>
<!-- cacheName 缓存超时配置,半小时,一小时,一天 -->
<property name="expires">
<map key-type="java.lang.String" value-type="java.lang.Long">
<entry key="halfHour" value="1800"/>
<entry key="hour" value="3600"/>
<entry key="oneDay" value="86400"/>
<!-- shiro cache keys -->
<entry key="authorizationCache" value="1800"/>
<entry key="authenticationCache" value="1800"/>
<entry key="activeSessionCache" value="1800"/>
</map>
</property>
</bean>
<!-- cache注解,和spring-ehcache.xml中的只能使用一个 -->
<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>
</beans>
4、Service 类
User
package com.mengdee.manager.entity;
import java.io.Serializable;
// 一定要实现Serializable,否则报错
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private int age;
public User() {
super();
}
public User(Long id, String username, int age) {
super();
this.id = id;
this.username = username;
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", age=" + age + "]";
}
// getter && setter
}
UserService
package com.mengdee.manager.service;
import com.mengdee.manager.entity.User;
public interface UserService {
public User getUserInfo(String username);
public void deleteUser(String username);
public User updateUser(User user);
}
UserServiceImpl
package com.mengdee.manager.service;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import com.mengdee.manager.entity.User;
@Service
public class UserServiceImpl implements UserService {
@Cacheable("users")
@Override
public User getUserInfo(String username) {
System.out.println("从数据库中查询用户信息。。。");
long id = (int)((Math.random()*9+1)*100000);
return new User(id, username, 20);
}
@CacheEvict("users")
@Override
public void deleteUser(String username) {
System.out.println("删除用户:"+username);
}
// 使用@CachePut比许返回要缓存的对象,使用value作为键的前缀(users),使用冒号作为分隔符(:),使用key的值追加到前面的组合,如"users:mengdee"
// 方法的返回值作为键值对的值缓存起来,如果方法没有返回值,那么就相当于没有更新缓存
@CachePut(value="users", key="#user.getUsername()")
@Override
public User updateUser(User user) {
System.out.println("更新用户");
return user;
}
}
5、测试
UserServiceTest
package com.mengdee.manager.redis;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.mengdee.manager.service.UserService;
public class UserServiceTest extends SpringTestCase{
@Autowired
private UserService userService;
@Test
public void testGetUserInfo(){
String username = "mengdee";
// 第一次执行了方法体
userService.getUserInfo(username);
// 第二次没有执行方法体,直接从缓存中获取的
User userInfo = userService.getUserInfo(username);
System.out.println(userInfo); // User [id=565857, username=mengdee, age=20]
}
@Test
public void testDeleteUser(){
String username = "mengdee";
User userInfo = userService.getUserInfo(username);
System.out.println(userInfo);
userService.getUserInfo(username);
userService.deleteUser(username);
}
@Test
public void testUpdateUser(){
String username = "mengdee";
User userInfo = userService.getUserInfo(username);
System.out.println(userInfo);
userInfo.setAge(200);
userInfo.setId(10L);
userService.updateUser(userInfo);
User userInfo2 = userService.getUserInfo(username);
System.out.println(userInfo2);
}
}
使用redis-cli可以看到User对象缓存的键是“users:mengdee”, 从中可以看出key的默认规则是@Cacheable中的value值跟冒号分隔,然后跟参数的实际值
注意
1、注意配置key的失效时间,既可以野数据的存在,也最大化利用内存,一般不要失效时间配置为永久
2、在使用redis的时候,对于键的命名规则非常重要,常用的做法是实体名字符串跟冒号跟ID值,如“user:1”, “user:2”, 或者其他命名规则在使用Spring的 @Cacheable 的时候更要注意,因为当两个方法的key相同时,两个方法的返回值会相互覆盖,例如以下两个方法
@Cacheable("user")
User getUser(String id);
@Cacheable("user")
User getUserDetail(String id);
当执行第一个方法后换成user对象,再执行第二个方法还会换成user对象,因为这两个方法的key最终生成的是完全一样的,所以第二个方法其实会更新缓存,这两个方法返回虽然都是User对象,很可能返回user对象的字段是不同的,这样在使用user对象时很可能拿不到值,解决方法就是key的生成规则一定不能一样,可以在key的生成规则中加入方法名作为区分,当然这不是一个很好的方式,因为方法名往往是比较长的,key越长越占内存,key应该尽可能的简短来节省内存