Redis
1. 设置密码
-
config set requirepass 123456
-
config get requirepass
2. 登陆
2.1 linux
-
开启服务,使用 redis-conf 的配置 :
./redis-server ../redis-conf
-
开启数据库,使用密码 123456 登陆
./redis-cli -h 127.0.0.1 -p 6379 -a 123456
2.2 windows
redis-cli -h 127.0.0.1 -p 6379 123456
3. spring-data-redis
3.1 spring 和 redis 整合
spring的强大,我们都知道。springspring-data-redis就是spring提供的和redis整合的包装jar。
3.1.1 maven
目前最新的版本是1.7.2
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.2.RELEASE</version>
</dependency>
3.1.2 配置RestTemplate
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="usePool" value="true" />
<property name="hostName" value="${redis.hostName}" />
<property name="port" value="${redis.port}" />
<property name="password" value="${redis.password}" />
<property name="database" value="1" />
<property name="poolConfig" ref="jedisPoolConfig" />
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
3.1.3 注入和使用RestTemplate
public class Example {
// inject the actual template
@Autowired
private RedisTemplate<String, String> template;
// inject the template as ListOperations
// can also inject as Value, Set, ZSet, and HashOperations
@Resource(name="redisTemplate")
private ListOperations<String, String> listOps;
public void addLink(String userId, URL url) {
listOps.leftPush(userId, url.toExternalForm());
// or use template directly
redisTemplate.boundListOps(userId).leftPush(url.toExternalForm());
}
}
3.2 数据类型
redis有五种数据结构,分别是:string,hash,list,set,zset。spring提供了这5种方式,下图是我从官方文档中截取的。提供了对应5中类型的包装类。这5个类都可以由RedisTemplate获取得到。

3.3 序列化
-
spring-data-redis对key和value都有做序列化。相关序列化的方法在相应jar的org.springframework.data.redis.serializer包下面。
-
spring-data-redis 提供了很多张序列化的方法。序列化方式需要在配置文件中配置,如果没有配置,那么就使用默认的配置方式。
3.3.1 序列化配置
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
<property name="connectionFactory" ref="jedisConnectionFactory" />
<!-- 默认 key 和 value 的序列化方式 -->
<property name="defaultSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
</property>
<!-- 非 hash 的 key 的序列化方式 -->
<property name="keySerializer" >
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<!-- 非 hash 的 value 的序列化方式 -->
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<!-- hash 的 key 的序列化方式 -->
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
<!-- hash 的 value 的序列化方式 -->
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
</property>
</bean>
如果redisTemplate中没有配置defaultSerializer,那么默认会使用JdkSerializationRedisSerializer,我们可以看下RedisTemplate中的afterPropertiesSet()方法下的部分源码:
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
如果没有设置keySerializer和valueSerializer,那么默认的就是defaultSerializer。
3.3.2 序列化策略
StringRedisSerializer
序列化成String类型。当然这种方式是最高效的,redis设计就是这样的。但是这种策略是不能满足我们的。我们是有实体的,这种不好存储实体
JdkSerializationRedisSerializer
序列化成16进制字节序列。这种方式用的比较多吧,虽然是最消耗内存的,但是既然用缓存了,也就不在乎内存了吧!
OxmSerializer
序列化成xml。略过
Jackson2JsonRedisSerializer
序列化成json。这种方式比较笨重,每次都要指定对应的实体。执行效率比>较低。在后面我们会比较为什么执行效率低。
JacksonJsonRedisSerializer
此序列方式在spring-data-redis-1.7.2.RELEASE.jar中已经过时了。取而代之的是上面的方式。都是为了序列换成json的方式
3.3.3 序列化方式
-
这里讨论实体的序列化,如果只是String或者是基本类型的话,也就不需要上面那么多种策略了。
-
redis是key-value的nosql的内存数据库。对于java的实体而言,存储起来就有点麻烦。当然redis的hash是可以存储实体的,但是我们必须指定各个字段所对应的key。保存时将对应的实体属性一个个的放入hash,获取时,又得一个个的封装到实体。我们当然希望有更简单的方式。
-
那么我们能想到的当然是json,xml什么的。xml就算了,我现在用xml的地方不多。所以我直接忽略了(如果以后用到了,那在说吧)。
-
由上策略可知,我们转化的方式就是JdkSerializationRedisSerializer和Jackson2JsonRedisSerializer(xml方式不讨论)。以下就简称为json方式和jdk方式。
3.3.3.1 前期定义
(1). 定义一个实体 StringDemo。自己添加getter和setter以及toString方法。实现Serializer方法,才能序列化。
public class StringDemo implements Serializable{
private static final long serialVersionUID = -6209797447467016710L;
private String id;
private String name;
private int age;
private Date birth;
}
(2). 定义实现方式
@Service
public class RedisStringServiceImpl implements RedisStringService {
@Resource
private RedisTemplate<String, StringDemo> redisTemplate;
private ValueOperations<String, StringDemo> valueOperation;
@Resource(name = "redisTemplate")
private ValueOperations<String, String> valueOps;
@PostConstruct
public void init(){
valueOperation = redisTemplate.opsForValue();
}
/**
* JDK序列化方式序列对象
* applicationContent.xml中valueSeriazer 需要配置成 JdkSerializationRedisSerializer
*/
@Override
public void object2JDKSerializer(StringDemo demo){
valueOperation.set("jdk2Object", demo);
}
/**
* 从 REDIS 中获取对象
* @return
*/
@Override
public StringDemo jdkSerializer2Object(){
return valueOperation.get("jdk2Object");
}
/**
* jackson的方式要指定 class。所以在配置总不好配置,所以就在程序中手动配置。
* <p>
* json的处理方式jackson是一种,还有的直接将实体转换成json,但是泛型的方式也要做相应的修改 <br>
* 至于使用哪一种来序列化。就随便了。fastjson、gson等等
*/
@Override
public void Object2JacksonSerializer(StringDemo demo){
Jackson2JsonRedisSerializer<StringDemo> jackson = new Jackson2JsonRedisSerializer<StringDemo>(StringDemo.class);
redisTemplate.setValueSerializer(jackson);
valueOperation.set("jackson2Object", demo);
}
@Override
public StringDemo jacksonSerializer2Object(){
Jackson2JsonRedisSerializer<StringDemo> jackson = new Jackson2JsonRedisSerializer<StringDemo>(StringDemo.class);
redisTemplate.setValueSerializer(jackson);
return valueOperation.get("jackson2Object");
}
/**
* 使用fastjson的方式序列化
* applicationContent.xml中valueSeriazer 需要配置成 StringRedisSerializer
*/
@Override
public void object2Fastjson(StringDemo demo){
String demoStr = JSON.toJSONString(demo);
valueOps.set("fastjson2Object", demoStr);
}
@Override
public StringDemo fastjson2Object(){
String demoStr = valueOps.get("fastjson2Object");
return JSON.parseObject(demoStr, StringDemo.class);
}
}
(3). 测试类
public class RedisStringServiceTest {
private ApplicationContext ctx;
private RedisStringService redisStringService;
private long start;
private long end;
@Before
public void init() {
ctx = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
redisStringService = ctx.getBean(RedisStringServiceImpl.class);
start = System.currentTimeMillis();
}
@After
public void after(){
end = System.currentTimeMillis();
System.out.println("cost time : " + (end - start) + "ms");
}
@Test
public void testSerializer(){
redisStringService.testSerializer();
}
@Test
/**
* \xAC\xED\x00\x05sr\x007com.roachfu.study.module.string.model.entity.StringDemo\xA9\xD2^\x1FX\x99\xDD\xFA\x02\x00\x04I\x00\x03ageL\x00\x05birtht\x00\x10Ljava/util/Date;L\x00\x02idt\x00\x12Ljava/lang/String;L\x00\x04nameq\x00~\x00\x02xp\x00\x00\x00\x19sr\x00\x0Ejava.util.Datehj\x81\x01KYt\x19\x03\x00\x00xpw\x08\x00\x00\x01W\x03\xBF\xD8\xB5xt\x00\x0510000t\x00\x05roach
* <p>
* 由上得出,以16进制保存的方式将实体的信息都保存了。可见占用的内存比较大。如果实体改变了包名,那么将不能反序列化
* 结果耗时是:35ms左右
*/
public void object2JDKSerializerTest(){
StringDemo demo = new StringDemo();
demo.setId("10000");
demo.setAge(25);
demo.setName("roach");
demo.setBirth(new Date());
redisStringService.object2JDKSerializer(demo);
}
/**
* 平均耗时75ms左右
*/
@Test
public void jdkSerializer2ObjectTest(){
StringDemo demo = redisStringService.jdkSerializer2Object();
System.out.println(demo);
}
/**
* 平均180ms左右
*/
@Test
public void Object2JacksonSerializerTest(){
StringDemo demo = new StringDemo();
demo.setId("10000");
demo.setAge(25);
demo.setName("roach");
demo.setBirth(new Date());
redisStringService.Object2JacksonSerializer(demo);
}
/**
* 平均190ms左右
* <br>
* 在 REDIS 中查看可得知,日期已经转换为时间戳
*/
@Test
public void jacksonSerializer2Object(){
StringDemo demo = redisStringService.jacksonSerializer2Object();
System.out.println(demo);
}
/**
* 85ms左右
* <br>
* 在 REDIS 中查看可得知,日期已经转换为时间戳
*/
@Test
public void object2FastjsonTest(){
StringDemo demo = new StringDemo();
demo.setId("10000");
demo.setAge(25);
demo.setName("roach");
demo.setBirth(new Date());
redisStringService.object2Fastjson(demo);
}
/**
* 85ms左右
*/
@Test
public void fastjson2Object(){
StringDemo demo = redisStringService.fastjson2Object();
System.out.println(demo);
}
}
3.3.3.2 JDK方式序列化实体
-
需要在相应的配置文件当中(例如在applicationContent.xml)配置valueSeriazer为JdkSerializationRedisSerializer。
-
需要定义对应的实体泛型。
-
这种序列化方式,是将value序列化成16进制的字节序列。比如一个实体StringDemo,序列化完之后的值为:
\xAC\xED\x00\x05sr\x007com.roachfu.study.module.string.model.entity.StringDemo\xA9\xD2^\x1FX\x99\xDD\xFA\x02\x00\x04I\x00\x03ageL\x00\x05birtht\x00\x10Ljava/util/Date;L\x00\x02idt\x00\x12Ljava/lang/String;L\x00\x04nameq\x00~\x00\x02xp\x00\x00\x00\x19sr\x00\x0Ejava.util.Datehj\x81\x01KYt\x19\x03\x00\x00xpw\x08\x00\x00\x01W\x03\xBF\xD8\xB5xt\x00\x0510000t\x00\x05roach
- 由上得出,以16进制保存的方式将实体的信息都保存了。可见占用的内存比较大。如果实体改变了包名,那么将不能反序列化。但是这种方式执行的效率比较高,速度比较快。
3.3.3.3 JSON方式序列化实体
将value序列化成JSON的方式又分两种:
(1) jackson序列化方式
-
这是spring-data-redis自带方式,即Jackson2JsonRedisSerializer的实现方式。
-
因为jackson的方式必须指定实体,所以在配置文件中配置会比较繁杂。所以一般都在方法总进行配置。比如
public void Object2JacksonSerializer(StringDemo demo){
Jackson2JsonRedisSerializer<StringDemo> jackson = new Jackson2JsonRedisSerializer<StringDemo>(StringDemo.class);
redisTemplate.setValueSerializer(jackson);
valueOperation.set("jackson2Object", demo);
}
- 如上可知,我们指定序列化StringDemo实体为json数据,在方法中定义了redisTemplate的valueSerializer,然后再存储数据。相比jdk的方式,代码上缺失了优雅性,需要同时设置redisTemplate和valueOperation。执行的速度也比jdk方式慢的多。
(2) 直接将实体序列化成json的方式
-
这里使用fastjson进行javabean和json之间的转换,也可以使用其它的方式(gson,json-lib都是可以的)。
-
这种方式是直接将javabean转换成json,返回的类型是String。valueSerializer需要设置成StringRedisSerializer。获取数据时,在通过fastjson将String转换成实体。
public void object2Fastjson(StringDemo demo){
String demoStr = JSON.toJSONString(demo);
valueOps.set("fastjson2Object", demoStr);
}
- 如上所示:这种方式的代码优雅度比jackson方式高。经过测试,这种方式的执行效率比jackson高,比jdk方式低。
3.3.3.4 总结
通过下面方法的验证,得出下面的结论:
(1) jdk方式执行的速度是最快的,但是由于存储的16进制字节序列,所占用的内存就比其他方式占用的多。
(2) jackson 这种序列化的方式最麻烦,代码的优雅度也相对较低,最惨的是执行的速度是最慢的。
(3) 直接转json方式(此处使用fastjson,其他的方式也是可以的。gson什么的)。写入的时候将javabean转成json,获取的时候又反序列成实体。这种方式比jdk方式慢点,比jackson方式快。
(4) jackson方式完败了(当然我的测试相对片面的),写出来的代码的可读性优雅度也没有其他两种好。
(5) 如果是公司的服务的内存强大,那就使用jdkSerializer这种方式。毕竟快。如果内存有限,那还是手动转json吧!