Redis缓存中使用Kryo序列化工具并且修改内容同时更新缓存

背景

缓存做为加速数据访问的途径在许多的系统开发中已经是必不可少。在实际的使用中存在这样一个场景:

对象的的内容非常庞大,一次数据查询量比较大,在并发访问时频繁访问数据库会造成性能瓶颈,实际使用中就会使用缴存,对对象进行更新操作的时候需要对更新的内容入库。但是不希望删除缓存中的对象,而是只更新缓存中的内容,这样就可以减少数据库的访问。提升应用的访问性能

基于此场景,本示例做了一个用例,可以实用于真实例项目中。

项目路径【Redis使用Kryo序列化工具】

实现

下面只讲解了部分关键代码,完整理示例见项目路径
代码结构

这里写图片描述

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example.spring.boot.redis</groupId>
    <artifactId>004-Redis使用Kryo序列化工具</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
        <relativePath/>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.8.4</version>
        </dependency>

        <dependency>
            <groupId>com.esotericsoftware</groupId>
            <artifactId>kryo</artifactId>
            <version>4.0.0</version>
        </dependency>

        <dependency>
            <groupId>de.javakaffee</groupId>
            <artifactId>kryo-serializers</artifactId>
            <version>0.41</version>
        </dependency>

    </dependencies>
</project>

AppConfig.java

package com.example.spring.boot.redis.common;

import com.example.spring.boot.redis.entity.RedisConfig;
import com.example.spring.boot.redis.mapper.RedisConfigMapper;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.sql.DataSource;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;


/**
 * Author: 王俊超
 * Date: 2017-05-07 10:02
 * All Rights Reserved !!!
 */
@Configuration

@MapperScan(basePackages = "com.example.spring.boot.redis.mapper")
public class AppConfig {


    /**
     * 设置mybatis会话工厂
     * @param ds
     * @return
     * @throws Exception
     */
    @Primary
    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(DataSource ds) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(ds);

        // 设置mybatis xml文件扫描路径
        factory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath*:mapper/*Mapper.xml"));
        return factory.getObject();
    }

    /**
     * Redis连接工厂
     * @param mapper 这个非常重要,必须在mapper被创建了之后才能创建Redis连接工厂
     * @return
     */
    @Primary
    @Bean("redisConnectionFactory")
    public RedisConnectionFactory redisConnectionFactory(RedisConfigMapper mapper) {

        // 获取redis连接信息
        List<RedisConfig> redisConfigs = mapper.getRedisConfig();
        List<String> clusterNodes = new ArrayList<>();
        for (RedisConfig rc : redisConfigs) {
            clusterNodes.add(rc.getUrl() + ":" + rc.getPort());
        }

        // 获取Redis集群配置信息
        RedisClusterConfiguration rcf = new RedisClusterConfiguration(clusterNodes);

        return new JedisConnectionFactory(rcf);
    }

    /**
     * 创建redis模板
     *
     * @param redisConnectionFactory
     * @return
     * @throws UnknownHostException
     */
    @Primary
    @Bean("redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        // redis value使用的序列化器
        template.setValueSerializer(new KryoRedisSerializer<>());
        // redis key使用的序列化器
        template.setKeySerializer(new KryoRedisSerializer<>());
        template.setHashKeySerializer(new KryoRedisSerializer<>());
        template.setHashValueSerializer(new KryoRedisSerializer<>());

        template.afterPropertiesSet();
        return template;
    }
}

KryoRedisSerializer.java

package com.example.spring.boot.redis.common;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.util.Arrays;

/**
 * Author: 王俊超
 * Date: 2017-05-31 21:43
 * All Rights Reserved !!!
 */
public class KryoRedisSerializer<T> implements RedisSerializer<T> {
    private Kryo kryo = new Kryo();

    @Override
    public byte[] serialize(T t) throws SerializationException {

        System.out.println("[serialize]" + t);

        byte[] buffer = new byte[2048];
        Output output = new Output(buffer);
        kryo.writeClassAndObject(output, t);
        return output.toBytes();
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {

        System.out.println("[deserialize]" + Arrays.toString(bytes));

        Input input = new Input(bytes);
        @SuppressWarnings("unchecked")
        T t = (T) kryo.readClassAndObject(input);
        return t;
    }

}

RedisClient.java

package com.example.spring.boot.redis.common;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;


/**
 * Author: 王俊超
 * Date: 2017-06-04 19:57
 * All Rights Reserved !!!
 */
@Component("redisClient")
public class RedisClient {
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    /**
     * 取redis连接
     *
     * @return
     */
    private RedisConnection getConnection() {
        return redisTemplate.getConnectionFactory().getConnection();
    }

    /**
     * 获取缓存的key
     *
     * @param id
     * @return
     */
    private <T> byte[] getKey(T id) {
        // 在spring data redis中,使用@CachePut,@CacheEvict,@Cacheable其缓存名都是
        // CachePut,@CacheEvict,@Cacheable中的cacheNames(即value)值+“:”+key
        // 因为本示例中使用了kryo对象作为redis的对象序列化的工具所以
        // 对cacheName:key要使用序列化,先将“cacheNames:”的值序列化,再对key进行序列化,
        // 将这两部分的byte数组合并起来做为key
        RedisSerializer serializer = redisTemplate.getKeySerializer();
        byte[] idBytes = serializer.serialize(id);
        byte[] prefixBytes = (RedisConst.PERSON_CACHE_NAME + ":").getBytes();
        byte[] key = new byte[prefixBytes.length + idBytes.length];
        System.arraycopy(prefixBytes, 0, key, 0, prefixBytes.length);
        System.arraycopy(idBytes, 0, key, prefixBytes.length, idBytes.length);

        System.out.println(new String(key));
        return key;
    }

    /**
     * 删除redis中的对象
     *
     * @param t
     */
    public <T> void delete(T t) {
        getConnection().del(getKey(t));
    }

    /**
     * 更新缓存中的对象,也可以在redis缓存中存入新的对象
     *
     * @param id
     * @param t
     * @param <T>
     */
    public <T> void set(Long id, T t) {
        byte[] key = getKey(id);
        RedisSerializer serializer = redisTemplate.getValueSerializer();
        byte[] val = serializer.serialize(t);
        getConnection().set(key, val);

    }

    /**
     * 从缓存中取对象
     *
     * @param id
     * @param <T>
     * @return
     */
    public <T> T getObject(Long id) {
        byte[] key = getKey(id);
        byte[] result = getConnection().get(key);
        return (T) redisTemplate.getValueSerializer().deserialize(result);
    }


}

RedisServiceImpl.java

package com.example.spring.boot.redis.service.impl;

import com.example.spring.boot.redis.common.RedisClient;
import com.example.spring.boot.redis.common.RedisConst;
import com.example.spring.boot.redis.entity.Person;
import com.example.spring.boot.redis.mapper.PersonMapper;
import com.example.spring.boot.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * Author: 王俊超
 * Date: 2017-05-07 09:58
 * All Rights Reserved !!!
 */
@Service
@Transactional(readOnly = true)
public class RedisServiceImpl implements RedisService {
    @Autowired
    private PersonMapper personRepository;

    @Autowired
    private RedisClient redisClient;

    /**
     * 创建对象,并且将person对象入缓存,key是person对象的id
     *
     * @param person
     * @return
     */
    @Override
    @CachePut(value = RedisConst.PERSON_CACHE_NAME, key = "#person.id")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public Person save(Person person) {
        personRepository.save(person);
        System.out.println("为id、key为:" + person.getId() + "数据做了缓存");
        return person;
    }

    /**
     * 从缓存中删除person对象,key是person对象的id
     *
     * @param id
     */
    @Override
    @CacheEvict(value = RedisConst.PERSON_CACHE_NAME) //2
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void remove(Long id) {
        System.out.println("删除了id、key为" + id + "的数据缓存");
        //这里不做实际删除操作
    }

    /**
     * 查询对象,并且将person对象入缓存,key是person对象的id
     *
     * @param person
     * @return
     */
    @Override
    @Cacheable(value = RedisConst.PERSON_CACHE_NAME, key = "#person.id") //3
    public Person findOne(Person person) {
        Person p = personRepository.findOne(person.getId());
        System.out.println("为id、key为:" + p.getId() + "数据做了缓存");
        return p;
    }

    /**
     * 更新对象,并且将对象入缓存,减少入缓存需要重新查询
     *
     * @param person
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void update(Person person) {
        personRepository.update(person);
        redisClient.set(person.getId(), person);
    }
}

测试

使用手工方式向redsi服务器中插入数据
这里写图片描述

这里写图片描述
使用手工方式取数据
这里写图片描述
使用redis托管方式取数据
这里写图片描述
使用手工方式删除数据
这里写图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值