Java并发编程学习-日记12、Redis 和 Java

Redis的配置项清单如下:

(1)port:端口配置项,查看和设置Redis监听端口,默认端口为6379。

(2)bind:主机地址配置项,查看和绑定的主机地址,默认地址的值为127.0.0.1。

(3)timeout:连接空闲多长要关闭连接,表示客户端闲置一段时间后,要关闭连接。如果指定为0,表示时长不限制。这个选项的默认值为0,表示默认不限制连接的空闲时长。

(4)dbfilename:指定保存缓存数据库的本地文件名,默认值为dump.rdb。

(5)dir:指定保存缓存数据的本地文件所存放的目录,默认值为安装目录。

(6)rdbcompression:指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变得巨大。

(7)save:指定在多长时间内,有多少次Key-Value更新操作,就将数据同步到本地数据库文件。save配置项的格式为save<seconds><changes>:seconds表示时间段的长度,changes表示变化的次数。

(8)requirepass:设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH<password>命令提供密码,默认这个选项是关闭"

(9)slaveof:在主从复制的模式下,设置当前节点为slave(从)节点时,设置master(主)节点的IP地址及端口,在Redis启动时,它会自动从master(主)节点进行数据同步。如果已经是slave(从)服务器,则会丢掉旧数据集,从新的master主服务器同步缓存数据。

设置为slave节点命令的格式为:slaveof<masterip><masterport>

(10)masterauth:在主从复制的模式下,当master(主)服务器节点设置了密码保护时,slave(从)服务器连接master(主)服务器的密码。

 master服务器节点设置密码的格式为:masterauth<master-password>

(11)databases:设置缓存数据库的数量,默认数据库数量为16个。databases配置选项,可以设置多个缓存数据库,不同的数据库存放不同应用的缓存数据。当清除缓存数据时,使用flushdb命令,只会清除当前数据库中的数据,而不会影响到其他数据库;而flushall命令,则会清除这个Redis实例所有数据库(从0-15)的数据。

在Java编程中,配置连接Redis的uri连接字符串时,可以指定到具体的数据库,格式为: redis://用户名:密码@host:port/Redis库。

通过安装目录下的redis-cli命令客户端,可以连接到Redis本地服务。如果需要在远程Redis服务上执行命令,我们使用的也是redis-cli命令。Windows/Linux命令的格式为: redis-cli -h host -p port -a password。

Redis中有5种数据类型:String(字符串类型)、Hash(哈希类型)、List(列表类型)、Set(集合类型)、Zset(有序集合类型)。

Redis,建议使用冒号作为superkey和subkey直接的分隔符,如下: superkey:subkey:subsubkey:subsubsubkey:……。

Key的命名规范使用冒号分割,大致的优势如下:

(1)方便分层展示。Redis的很多客户端可视化管理工具,如Redis Desktop Manager,是以冒号作为分类展示的,方便快速查到要查阅的Redis Key对应的Value值。

(2)方便删除与维护。可以对于某一层次下面的Key,使用通配符进行批量查询和批量删除。

Jedis开源库提供了一个负责管理Jedis连接对象的池,名为JedisPool类,位于redis.clients.jedis包中。

  • JedisPoolConfig配置类重要参数:

(1)maxTotal:资源池中最大的连接数,默认值为8。

(2)maxIdle:资源池允许最大空闲的连接数,默认值为8。

(3)minIdle:资源池确保最少空闲的连接数,默认值为0。如果JedisPool开启了空闲连接的有效性检测,如果空闲连接无效,就销毁。销毁连接后,连接数量就少了,如果小于minIdle数量,就新建连接,维护数量不少于minIdle的数量。minIdle确保了线程池中有最小的空闲Jedis实例的数量。

(4)blockWhenExhausted:当资源池用尽后,调用者是否需要等待,默认true。当为true时,maxWaitMillis才会生效。

(5)maxWaitMillis:资源池连接用尽后最大等待时间,默认值-1,表示永远不超时,不建议使用默认值。

(6)testOnBorrow:向资源池借用连接时,是否做有效性检测(ping命令),如果是无效连接,会被移除,默认值为false,表示不做检测。如果为true,则得到的Jedis实例均是可用的。在业务量小的应用场景,建议设置为true,确保连接可用;在业务量很大的应用场景,建议设置为false(默认值),少一次ping命令的开销,有助于提升性能。

(7)testOnReturn:向资源池归还连接时,是否做有效性检测(ping命令),如果是无效连接,会被移除,默认值为false,表示不做检测。同样,在业务量很大的应用场景,建议设置为false(默认值),少一次ping命令的开销。

(8)testWhileIdle:如果为true,表示用一个专门的线程对空闲的连接进行有效性的检测扫描,如果有效性检测失败,即表示无效连接,会从资源池中移除。

(9)timeBetweenEvictionRunsMillis:表示两次空闲连接扫描的活动之间,要睡眠的毫秒数,默认为30000毫秒,也就是30秒钟。

(10)minEvictableIdleTimeMillis:表示一个Jedis连接至少停留在空闲状态的最短时间,然后才能被空闲连接扫描线程进行有效性检测,默认值为60000毫秒,即60秒。

(11)numTestsPerEvictionRun:表示空闲检测线程每次最多扫描的Jedis连接数,默认值为-1,表示扫描全部的空闲连接。

(12)jmxEnabled:是否开启jmx监控,默认值为true,建议开启。

RedisPool实例:

public class JredisPoolBuilder {

    private static JedisPool pool = null;

    static {

        buildPool();  //创建连接池

        hotPool();//预热连接池

    }

    //创建连接池

    private static JedisPool buildPool() {

        if (pool == null) {

            long start = System.currentTimeMillis();

            JedisPoolConfig config = new JedisPoolConfig();

            config.setMaxTotal(50);

            config.setMaxIdle(50);

            config.setMaxWaitMillis(1000 * 10);

            // 在borrow一个jedis实例时,是否提前进行validate操作;

            // 如果为true,则得到的jedis实例均是可用的;

            config.setTestOnBorrow(true);

            //new JedisPool(config, ADDR, PORT, TIMEOUT, AUTH);

            pool = new JedisPool(config, "127.0.0.1", 6379, 10000);

            long end = System.currentTimeMillis();

            Logger.info("buildPool  毫秒数:", end - start);

        }

        return pool;

    }

    //获取连接

    public synchronized static Jedis getJedis() {

        return pool.getResource();

    }

    //连接池的预热

    public static void hotPool() {

        long start = System.currentTimeMillis();

        List<Jedis> minIdleJedisList = new ArrayList<Jedis>(MAX_IDLE);

        Jedis jedis = null;

        for (int i = 0; i < MAX_IDLE; i++) {

            try {

                jedis = pool.getResource();

                minIdleJedisList.add(jedis);

                jedis.ping();

            } catch (Exception e) {

                Logger.error(e.getMessage());

            } finally {

            }

        }

        for (int i = 0; i < MAX_IDLE; i++) {

            try {

                jedis = minIdleJedisList.get(i);

                jedis.close();

            } catch (Exception e) {

                Logger.error(e.getMessage());

            } finally {

            }

        }

        long end = System.currentTimeMillis();

        Logger.info("hotPool  毫秒数:", end - start);

    }

}

  • 一般的Java开发,都会使用了Spring框架,可以使用spring-data-redis开源库来简化Redis操作的代码逻辑,做到最大程度的业务聚焦。

1)使用spring-data-redis库的第一步是,要在Maven的pom文件中加上spring-data-redis库的依赖。

2)使用spring-data-redis库的第二步,即配置spring-data-redis库的连接池实例和RedisTemplate模板实例。spring-data-redis库在JedisPool提供连接池的基础上封装了自己的连接池——RedisConnectionFactory连接工厂;并且spring-data-redis封装了一个短期、非线程安全的连接类,名为RedisConnection连接类。

  1. RedisConnection的API命令操作的对象都是字节级别的Key键和Value值。为了更进一步地减少开发的工作,spring-data-redis库在RedisConnection连接类的基础上,针对不同的缓存类型,设计了五大数据类型的命令API集合,用于完成不同类型的数据缓存操作,并封装在RedisTemplate模板类。
  2. RedisTemplate模板类位于核心包org.springframework.data.redis.core中,它封装了五大数据类型的命令API集合:

(1)ValueOperations字符串类型操作API集合。

(2)ListOperations列表类型操作API集合。

(3)SetOperations集合类型操作API集合。

(4)ZSetOperations有序集合类型API集合。

(5)HashOperations哈希类型操作API集合。

  1. RedisConnection连接类更加底层,它负责二进制层面的Redis操作,Key、Value都是二进制字节数组。而RedisTemplate模板类,在RedisConnection的基础上,使用在spring-redis.xml中配置的序列化、反序列化的工具类,完成上层类型(如String、Object、POJO类等)的Redis操作。
  2. Spring的Redis缓存注解

(1)@CachePut作用是设置缓存。先执行方法,并将执行结果缓存起来。Redis缓存中的Key键即为@CachePut注解配置的key属性值,一般是一个字符串,或者是结果为字符串的一个SpEL(SpringEL)。一般来说,可以给@CachePut设置三个属性,Value、Key和Condition。

(2)@CacheEvict的作用是删除缓存。在执行方法前,删除缓存。使用@CacheEvict注解清除缓存时,可以通过合理配置清除指定Cache名称下的所有Key。

(3)@Cacheable的作用更多是查询缓存。首先检查注解中的Key键是否在缓存中,如果是,则返回Key的缓存值,不再执行方法;否则,执行方法并将方法结果缓存。

(4)@Caching注解,是一个缓存处理的组合注解。通过@Caching,可以一次指定多个Spring Cache注解的组合。@Caching注解拥有三个属性:cacheable、put和evict。

Redis缓存的一些操作实例:

public class CacheOperationService {

    private RedisTemplate redisTemplate;

    public void setRedisTemplate(RedisTemplate redisTemplate) {

        this.redisTemplate = redisTemplate;

    }

    // --------------RedisTemplate 基础操作  --------------------

    /**

     * 取得指定格式的所有的key

     * @param patens 匹配的表达式

     * @return key 的集合

     */

    public Set getKeys(Object patens) {

        try {

            return redisTemplate.keys(patens);

        } catch (Exception e) {

            e.printStackTrace();

            return null;

        }

    }

 

    /**

     * 指定缓存失效时间

     *

     * @param key  键

     * @param time 时间(秒)

     * @return

     */

    public boolean expire(String key, long time) {

        try {

            if (time > 0) {

                redisTemplate.expire(key, time, TimeUnit.SECONDS);

            }

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 根据key 获取过期时间

     *

     * @param key 键 不能为null

     * @return 时间(秒) 返回0代表为永久有效

     */

    public long getExpire(String key) {

        return redisTemplate.getExpire(key, TimeUnit.SECONDS);

    }

 

    /**

     * 判断key是否存在

     *

     * @param key 键

     * @return true 存在 false不存在

     */

    public boolean hasKey(String key) {

        try {

            return redisTemplate.hasKey(key);

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 删除缓存

     *

     * @param key 可以传一个值 或多个

     * @return 删除的个数

     */

    public void del(String... key) {

        if (key != null && key.length > 0) {

            if (key.length == 1) {

                redisTemplate.delete(key[0]);

            } else {

                redisTemplate.delete(CollectionUtils.arrayToList(key));

            }

        }

    }

    // --------------RedisTemplate 操作 String --------------------

    /**

     * 普通缓存获取

     *

     * @param key 键

     * @return 值

     */

    public Object get(String key) {

        return key == null ? null : redisTemplate.opsForValue().get(key);

    }

 

    /**

     * 普通缓存放入

     *

     * @param key   键

     * @param value 值

     * @return true成功 false失败

     */

    public boolean set(String key, Object value) {

        try {

            redisTemplate.opsForValue().set(key, value);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

 

    }

 

    /**

     * 普通缓存放入并设置时间

     *

     * @param key   键

     * @param value 值

     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期

     * @return true成功 false 失败

     */

    public boolean set(String key, Object value, long time) {

        try {

            if (time > 0) {

                redisTemplate.opsForValue()

                        .set(key, value, time, TimeUnit.SECONDS);

            } else {

                set(key, value);

            }

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 递增

     *

     * @param key   键

     * @param delta 自增因子,要增加几(大于0)

     * @return 自增的结果

     */

    public long incr(String key, long delta) {

        if (delta < 0) {

            throw new RuntimeException("自增因子必须大于0");

        }

        return redisTemplate.opsForValue().increment(key, delta);

    }

 

    /**

     * 自减

     *

     * @param key   键

     * @param delta 自减,  要减少几(小于0)

     * @return

     */

    public long decr(String key, long delta) {

        if (delta < 0) {

            throw new RuntimeException("自减因子必须大于0");

        }

        return redisTemplate

                .opsForValue().increment(key, -delta);

    }

 

    // --------------RedisTemplate 操作 Map --------------------

 

    /**

     * HashGet

     *

     * @param key   键 不能为null

     * @param field 项 不能为null

     * @return 值

     */

    public Object hget(String key, String field) {

        return redisTemplate.opsForHash().get(key, field);

    }

 

    /**

     * 获取hashKey对应的所有键值

     *

     * @param key 键

     * @return 对应的多个键值

     */

    public Map<Object, Object> hmget(String key) {

        return redisTemplate.opsForHash().entries(key);

    }

 

    /**

     * HashSet

     *

     * @param key 键

     * @param map 对应多个键值

     * @return true 成功 false 失败

     */

    public boolean hmset(String key, Map<String, Object> map) {

        try {

            redisTemplate.opsForHash().putAll(key, map);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * HashSet 并设置时间

     *

     * @param key  键

     * @param map  对应多个键值

     * @param time 时间(秒)

     * @return true成功 false失败

     */

    public boolean hmset(String key, Map<String, Object> map, long time) {

        try {

            redisTemplate.opsForHash().putAll(key, map);

            if (time > 0) {

                expire(key, time);

            }

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 向一张hash表中放入数据,如果不存在将创建

     *

     * @param key   键

     * @param field 项

     * @param value 值

     * @return true 成功 false失败

     */

    public boolean hset(String key, String field, Object value) {

        try {

            redisTemplate.opsForHash().put(key, field, value);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 向一张hash表中放入数据,如果不存在将创建

     *

     * @param key   键

     * @param field 项

     * @param value 值

     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间

     * @return true 成功 false失败

     */

    public boolean hset(String key, String field, Object value, long time) {

        try {

            redisTemplate.opsForHash().put(key, field, value);

            if (time > 0) {

                expire(key, time);

            }

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 删除hash表中的值

     *

     * @param key   键 不能为null

     * @param field 项 可以使多个 不能为null

     */

    public void hdel(String key, Object... field) {

        redisTemplate.opsForHash().delete(key, field);

    }

 

    /**

     * 判断hash表中是否有该项的值

     *

     * @param key   键 不能为null

     * @param field 项 不能为null

     * @return true 存在 false不存在

     */

    public boolean hHasKey(String key, String field) {

        return redisTemplate.opsForHash().hasKey(key, field);

    }

 

    /**

     * hash自增 如果不存在,就会创建一个 并把新增后的值返回

     *

     * @param key   键

     * @param field 项

     * @param by    要增加几(大于0)

     * @return

     */

    public double hincr(String key, String field, double by) {

        return redisTemplate.opsForHash().increment(key, field, by);

    }

 

    /**

     * hash自减

     *

     * @param key   键

     * @param field 项

     * @param by    要减少记(小于0)

     * @return

     */

    public double hdecr(String key, String field, double by) {

        return redisTemplate.opsForHash().increment(key, field, -by);

    }

 

    // --------------RedisTemplate 操作 Set --------------------

 

    /**

     * 根据key获取Set中的所有值

     *

     * @param key 键

     * @return

     */

    public Set<Object> sGet(String key) {

        try {

            return redisTemplate.opsForSet().members(key);

        } catch (Exception e) {

            e.printStackTrace();

            return null;

        }

    }

 

    /**

     * 根据value从一个set中查询,是否存在

     *

     * @param key   键

     * @param value 值

     * @return true 存在 false不存在

     */

    public boolean sHasKey(String key, Object value) {

        try {

            return redisTemplate.opsForSet().isMember(key, value);

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 将数据放入set缓存

     *

     * @param key    键

     * @param values 值 可以是多个

     * @return 成功个数

     */

    public long sSet(String key, Object... values) {

        try {

            return redisTemplate.opsForSet().add(key, values);

        } catch (Exception e) {

            e.printStackTrace();

            return 0;

        }

    }

 

    /**

     * 将set数据放入缓存

     *

     * @param key    键

     * @param time   时间(秒)

     * @param values 值 可以是多个

     * @return 成功个数

     */

    public long sSetAndTime(String key, long time, Object... values) {

        try {

            Long count = redisTemplate.opsForSet().add(key, values);

            if (time > 0)

                expire(key, time);

            return count;

        } catch (Exception e) {

            e.printStackTrace();

            return 0;

        }

    }

 

    /**

     * 获取set缓存的长度

     *

     * @param key 键

     * @return

     */

    public long sGetSetSize(String key) {

        try {

            return redisTemplate.opsForSet().size(key);

        } catch (Exception e) {

            e.printStackTrace();

            return 0;

        }

    }

 

    /**

     * 移除值为value的

     *

     * @param key    键

     * @param values 值 可以是多个

     * @return 移除的个数

     */

    public long setRemove(String key, Object... values) {

        try {

            Long count = redisTemplate.opsForSet().remove(key, values);

            return count;

        } catch (Exception e) {

            e.printStackTrace();

            return 0;

        }

    }

    // --------------RedisTemplate 操作list --------------------

 

    /**

     * 获取list缓存的内容

     *

     * @param key   键

     * @param start 开始

     * @param end   结束 0 到 -1代表所有值

     * @return

     */

    public List<Object> lGet(String key, long start, long end) {

        try {

            return redisTemplate.opsForList().range(key, start, end);

        } catch (Exception e) {

            e.printStackTrace();

            return null;

        }

    }

 

    /**

     * 获取list缓存的长度

     *

     * @param key 键

     * @return

     */

    public long lGetListSize(String key) {

        try {

            return redisTemplate.opsForList().size(key);

        } catch (Exception e) {

            e.printStackTrace();

            return 0;

        }

    }

 

    /**

     * 通过索引 获取list中的值

     *

     * @param key   键

     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推

     * @return

     */

    public Object lGetIndex(String key, long index) {

        try {

            return redisTemplate.opsForList().index(key, index);

        } catch (Exception e) {

            e.printStackTrace();

            return null;

        }

    }

 

    /**

     * 将list放入缓存

     *

     * @param key   键

     * @param value 值

     * @return

     */

    public boolean lSet(String key, Object value) {

        try {

            redisTemplate.opsForList().rightPush(key, value);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 将list放入缓存

     *

     * @param key   键

     * @param value 值

     * @param time  时间(秒)

     * @return

     */

    public boolean lSet(String key, Object value, long time) {

        try {

            redisTemplate.opsForList().rightPush(key, value);

            if (time > 0)

                expire(key, time);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 将list放入缓存

     *

     * @param key   键

     * @param value 值

     * @return

     */

    public boolean lSet(String key, List<Object> value) {

        try {

            redisTemplate.opsForList().rightPushAll(key, value);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 将list放入缓存

     *

     * @param key   键

     * @param value 值

     * @param time  时间(秒)

     * @return

     */

    public boolean lSet(String key, List<Object> value, long time) {

        try {

            redisTemplate.opsForList().rightPushAll(key, value);

            if (time > 0)

                expire(key, time);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 根据索引修改list中的某条数据

     *

     * @param key   键

     * @param index 索引

     * @param value 值

     * @return

     */

    public boolean lUpdateIndex(String key, long index, Object value) {

        try {

            redisTemplate.opsForList().set(key, index, value);

            return true;

        } catch (Exception e) {

            e.printStackTrace();

            return false;

        }

    }

 

    /**

     * 移除N个值为value

     *

     * @param key   键

     * @param count 移除多少个

     * @param value 值

     * @return 移除的个数

     */

    public long lRemove(String key, long count, Object value) {

        try {

            Long remove = redisTemplate.opsForList().remove(key, count, value);

            return remove;

        } catch (Exception e) {

            e.printStackTrace();

            return 0;

        }

    }

}

Redis缓存Spring注解的一些操作实例:

 

@Service

@CacheConfig(cacheNames = "userCache")

public class UserServiceImplWithAnno implements UserService {

 

    public static final String USER_UID_PREFIX = "'userCache:'+";

 

    /**

     * CRUD 之  新增/更新

     *

     * @param user 用户

     */

    @CachePut(key = USER_UID_PREFIX + "T(String).valueOf(#user.uid)")

    @Override

    public User saveUser(final User user) {

        //保存到数据库

        //返回值,将保存到缓存

        Logger.info("user : save to redis");

        return user;

    }

 

    /**

     * 带条件缓存

     *

     * @param user 用户

     * @return 用户

     */

    @CachePut(key = "T(String).valueOf(#user.uid)", condition = "#user.uid>1000")

    public User cacheUserWithCondition(final User user) {

        //保存到数据库

        //返回值,将保存到缓存

        Logger.info("user : save to redis");

        return user;

    }

 

 

    /**

     * CRUD 之   查询

     *

     * @param id id

     * @return 用户

     */

    @Cacheable(key = USER_UID_PREFIX + "T(String).valueOf(#id)")

    @Override

    public User getUser(final long id) {

        //如果缓存没有,则从数据库中加载

        Logger.info("user : is null");

        return null;

    }

 

    /**

     * CRUD 之 删除

     *

     * @param id id

     */

 

    @CacheEvict(key = USER_UID_PREFIX + "T(String).valueOf(#id)")

    @Override

    public void deleteUser(long id) {

 

        //从数据库中删除

        Logger.info("delete  User:", id);

    }

 

    /**

     * 删除userCache中的全部缓存

     */

    @CacheEvict(value = "userCache", allEntries = true)

    public void deleteAll() {

 

    }

    /**

     * 一个方法上,加上三类cache处理

     */

    @Caching(cacheable = @Cacheable(key = "'userCache:'+ #uid"),

            put = @CachePut(key = "'userCache:'+ #uid"),

            evict = {

                    @CacheEvict(key = "'userCache:'+ #uid"),

                    @CacheEvict(key = "'addressCache:'+ #uid"),

                    @CacheEvict(key = "'messageCache:'+ #uid")

            }

    )

    public User updateRef(String uid) {

        //....业务逻辑

        return null;

    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章