redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。
Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。
redis的官网地址,非常好记,是redis.io。(特意查了一下,域名后缀io属于国家域名,是british Indian Ocean territory,即英属印度洋领地)目前,Vmware在资助着redis项目的开发和维护。
下面是官方的bench-mark数据:
测试完成了50个并发执行100000个请求。
设置和获取的值是一个256字节字符串。
Linux box是运行Linux 2.6,这是X3320 Xeon 2.5 ghz。
文本执行使用loopback接口(127.0.0.1)。
结果:读的速度是110000次/s,写的速度是81000次/s
redis提供五种数据类型:string,hash,list,set及zset(sorted set)。
string(字符串)
string是最简单的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value,其上支持的操作与Memcached的操作类似。但它的功能更丰富。
redis采用结构sdshdr和sds封装了字符串,字符串相关的操作实现在源文件sds.h/sds.c中。
list(双向链表)
list是一个链表结构,主要功能是push、pop、获取一个范围的所有值等等。操作中key理解为链表的名字。
dict(hash表)
set是集合,和我们数学中的集合概念相似,对集合的操作有添加删除元素,有对多个集合求交并差等操作。操作中key理解为集合的名字。
dict中table为dictEntry指针的数组,数组中每个成员为hash值相同元素的单向链表。set是在dict的基础上实现的,指定了key的比较函数为dictEncObjKeyCompare,若key相等则不再插入。
zset(排序set)
zset是set的一个升级版本,他在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次指定后,zset会自动重新按新的值调整顺序。可以理解了有两列的mysql表,一列存value,一列存顺序。操作中key理解为zset的名字。
zset利用dict维护key -> value的映射关系,用zsl(zskiplist)保存value的有序关系。zsl实际是叉数不稳定的多叉树,每条链上的元素从根节点到叶子节点保持升序排序。
配置代码:
@Configuration public class RedisConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration cfg = new RedisStandaloneConfiguration(); cfg.setHostName("127.0.0.1"); // cfg.setPassword(); cfg.setPort(6379); cfg.setDatabase(0); JedisConnectionFactory factory = new JedisConnectionFactory(cfg); return factory; } @Bean(name = "jedisTemplate") public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(this.redisConnectionFactory()); return template; } }
接着需要一个缓存的模型类:
/** * Created by alan.luo on 2017/8/4. */ public class HashCache implements Serializable { /** * 缓存的对象 */ private Object value; /** * 过期时间 */ private long expired; public HashCache(Object val, long expired){ if (expired <= 0){ expired = RedisProvider.DEFAULT_TIME_OUT; } this.value = val; this.expired = expired; } public static HashCache newInstance(Object val,long expired){ return new HashCache(val,expired); } public boolean check(){ return System.currentTimeMillis() > this.expired; } public Object getValue() { return value; } public void setValue(Object value) { this.value = value; } public long getExpired() { return expired; } public void setExpired(long expired) { this.expired = expired; } }
最后就是现实redis缓存操作:
/** * Created by alan on 2018/6/19. */ @Component public class RedisProvider implements ICache { public static final long DEFAULT_TIME_OUT = 3600; private String id = "1001"; @Resource private RedisTemplate<String,Object> template; public RedisProvider() { } public RedisProvider(String id){ this.id = id; } @Override public Object get(String key) { HashCache cache = (HashCache) template.opsForHash().get(id, key); if (cache != null && cache.check()) { return cache.getValue(); } template.opsForHash().delete(id, key); return null; } @Override public void put(String key, Object val, long millis) { HashCache cache = HashCache.newInstance(val, millis); template.opsForHash().put(id, key, cache); } @Override public boolean has(String key) { return template.opsForHash().hasKey(id, key); } @Override public boolean remove(String key) { return template.opsForHash().delete(id, key) > 0; } @Override public long count() { return template.opsForHash().size(id); } @Override public void clear() { Set<Object> keys = template.opsForHash().keys(id); for (Object b : keys) { this.remove(b.toString()); } } @Override public Map<String, Object> getAll() { Map<String, Object> data = new HashMap<>(); Set<Object> keys = template.opsForHash().keys(id); for (Object b : keys) { Object val = this.get(b.toString()); if (val != null) { data.put(b.toString(), val); } } return data; } @Override public void checkAll(boolean isClear) { this.getAll(); } }
这里全部都是使用模板的opsForHash()方法进行存贮。当然,如果你不是Spring项目,那么可以直接整合redis.clients包就可以实现了,现在来看看最简单的实现代码:
首先是模型类:
/** * Created by alan.luo on 2017/8/4. */ public class DCacheData implements Serializable { /** * 缓存的对象 */ private Object val; /** * 过期时间 */ private long expired; public DCacheData(Object val,long expired){ setVal(val); setExpired(expired); } public Object getVal() { return val; } public void setVal(Object val) { this.val = val; } public long getExpired() { return expired; } public void setExpired(long expired) { this.expired = expired; } /** * 判断时间是否正确 * @return */ public boolean checkExpired(){ return System.currentTimeMillis() < getExpired(); } } 接着是实现类:/** * Created by alan.luo on 2017/8/4. * <p> * CacheRedis redis = new CacheRedis("testKey"); * redis.put("redisTest","redisTest1",10); * redis.put("redisTest1","redisTest1",0); * redis.remove("redisTest2"); * redis.put("redisUserList",Object,50); * <p> * redis.count() * redis.get("redisTest") * redis.get("redisTest2") * redis.get("redisUserList") */ public class CacheRedisHandler implements ICache { protected JedisPool jedisPool = null; protected String id = null; public CacheRedisHandler(String id) { jedisPool = new JedisPool(Constant.Redis.IP, Constant.Redis.PORT); // GenericObjectPoolConfig config = new GenericObjectPoolConfig(); // config.setMaxIdle(ConstantRedis.maxIdle); // config.setMaxTotal(ConstantRedis.maxTotal); // config.setMaxWaitMillis(ConstantRedis.maxWaitMillis); // config.setMinIdle(8); // config.setTestOnBorrow(ConstantRedis.testOnBorrow); // jedisPool = new JedisPool(config,ConstantRedis.IP,ConstantRedis.PORT,2000,ConstantRedis.PASSWORD); this.id = id; } public Object execute(IRedisCallback callback) { Jedis jedis = jedisPool.getResource(); try { return callback.doWithRedis(jedis); } finally { jedis.close(); } } public String getId() { return id; } public Object get(final String key) { return execute(new IRedisCallback() { @Override public Object doWithRedis(Jedis jedis) { if (jedis.hexists(getId().getBytes(), key.getBytes())) { try { DCacheData val = (DCacheData) SerializeUtils.unSerialize(jedis.hget(id.getBytes(), key.getBytes())); if (val.checkExpired()) { return val.getVal(); } }catch (Exception e){ e.printStackTrace(); } jedis.hdel(getId().getBytes(), key.getBytes()); } return null; } }); } @Override public boolean put(final String key, Object val, long millis) { if (millis <= 0) { millis = 86400; } millis = millis * 1000; millis = System.currentTimeMillis() + millis; final DCacheData data = new DCacheData(val, millis); return (boolean) execute(new IRedisCallback() { @Override public Object doWithRedis(Jedis jedis) { long in = jedis.hset(getId().getBytes(), key.getBytes(), SerializeUtils.serialize(data)); return in == 1 ? true : false; } }); } @Override public boolean has(final String key) { return (boolean) execute(new IRedisCallback() { @Override public Object doWithRedis(Jedis jedis) { return jedis.hexists(getId().getBytes(), key.getBytes()); } }); } @Override public boolean remove(final String key) { return (boolean) execute(new IRedisCallback() { @Override public Object doWithRedis(Jedis jedis) { long in = jedis.hdel(getId().getBytes(), key.getBytes()); return in == 1 ? true : false; } }); } @Override public int count() { return (int) execute(new IRedisCallback() { @Override public Object doWithRedis(Jedis jedis) { return jedis.hgetAll(getId().getBytes()).size(); } }); } @Override public void clear() { execute(new IRedisCallback() { @Override public Object doWithRedis(Jedis jedis) { jedis.del(getId().getBytes()); return null; } }); } /** * 获取所有的缓存数据 * @return Map<String,Object>对象 */ @Override public Object getAll() { final Map<String,Object> data = new HashMap<>(); return execute(new IRedisCallback() { @Override public Object doWithRedis(Jedis jedis) { Set<byte[]> keys = jedis.hkeys(getId().getBytes()); for (byte[] b : keys) { DCacheData val = (DCacheData) SerializeUtils.unSerialize(jedis.hget(getId().getBytes(), b)); if (val == null || !val.checkExpired()) { jedis.hdel(getId().getBytes(),b); }else { data.put(new String(b),val.getVal()); } } return data; } }); } /** * 清除无用的缓存 */ @Override public void checkAll() { execute(new IRedisCallback() { @Override public Object doWithRedis(Jedis jedis) { Set<byte[]> keys = jedis.hkeys(getId().getBytes()); for (byte[] b : keys) { DCacheData val = (DCacheData) SerializeUtils.unSerialize(jedis.hget(getId().getBytes(), b)); if (!val.checkExpired()) { jedis.hdel(getId().getBytes(),b); } } return null; } }); } }
代码贴完了,接着就是测试:
@RequestMapping(value = "/testRedis") public Map<String, Object> testRedis(HttpServletRequest request) { Map<String, Object> hash = new HashMap<>(); redisProvider.put("test001", "val001", 5); redisProvider.put("test002", "val002", 10); out(redisProvider.get("test001").toString()); out(redisProvider.get("test002").toString()); out(redisProvider.count() + ""); out(redisProvider.remove("test002") + ""); out(redisProvider.has("test002") + ""); out(redisProvider.count() + ""); out(redisProvider.count() + ""); out(redisProvider.has("test001") + ""); redisProvider.put("test003", "val003", 5); redisProvider.put("test004", "val004", 10); hash = redisProvider.getAll(); return hash; }