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连接类。
- RedisConnection的API命令操作的对象都是字节级别的Key键和Value值。为了更进一步地减少开发的工作,spring-data-redis库在RedisConnection连接类的基础上,针对不同的缓存类型,设计了五大数据类型的命令API集合,用于完成不同类型的数据缓存操作,并封装在RedisTemplate模板类。
- RedisTemplate模板类位于核心包org.springframework.data.redis.core中,它封装了五大数据类型的命令API集合:
(1)ValueOperations字符串类型操作API集合。
(2)ListOperations列表类型操作API集合。
(3)SetOperations集合类型操作API集合。
(4)ZSetOperations有序集合类型API集合。
(5)HashOperations哈希类型操作API集合。
- RedisConnection连接类更加底层,它负责二进制层面的Redis操作,Key、Value都是二进制字节数组。而RedisTemplate模板类,在RedisConnection的基础上,使用在spring-redis.xml中配置的序列化、反序列化的工具类,完成上层类型(如String、Object、POJO类等)的Redis操作。
- 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; } } |