Redis String存储结构
Redis是一个非常优秀的key-value存储系统,我自己非常喜欢用redis做内存存储。redis本身提供了非常多的数据结构存储,能够满足很多业务的存储场景,今天先从日常使用最多的String结构入手谈谈,记录下我自己平时的使用场景。
String大概是工作中使用最多的场景了,String的内部存储默认是字符串。我在工作中最常使用的场景莫过于把结果集序列化成json,然后利用string的set命令存储了。这种存储方式最常见,也最省事,是偷懒型程序员的最爱。例如持久化的用户榜单信息,我就喜欢将整个榜单序列化成json,然后存入redis。
public List<RankInfo> getRankList(){
String json = redis.string.get(“key”);
if(!StringUtiils.isEmpty(json)){
return new Gson().fromjson(json,new
TypeToken<List<User>>() {});
} else {
List<RankInfo> list = db.getData();
if(!CollectionUtils.isEmpty(list)) {
redis.string.set(“key”,new Gson().toJson(list));
redis.expire(“key”,5000);
}
return list;
}
}
上面这段代码可是我最喜欢的代码片段,简单,快速,又可以偷懒。当然用string存储也是有缺点的,那就是会浪费内存,因为string存储的key是散落在各个角落的,每个存储的key的大小不确定,这样可能会造成内存碎片,造成不必要的内存浪费。所以如果存储的内存块非常大,我们是需要考虑存储结构的优化的,这个后期再讲。
自增(INCRBY)自减(DECRBY)操作也是string结构的常用命令。incrby命令的常用业务场景就是可以用来判断用户的某些行为验证。比如某个业务场景有这样一种规则:每个用户一小时内只能投票两次,这时候incrby命令就非常有用。
public void vote(int userId){
String key = “vote”+userId;
redis.string.incrby(key,1);
long ttl = redis.ttl;
if(ttl==-1 | ttl ==-2){
redis.expire(key,3600);
}
}
public int getUserVoteCount(int userId){
String key = “vote” + userId;
String value = redis.string.get(key);
if(value!=null){
return Integer.parseInt(value);
} else {
return 0;
}
}
以上我们通过incrby命令就可以非常简单的完成了用户投票的场景。此外incry还可以用来解决集群情况下分布式锁的业务场景,这个放在后期的mutex设计中来讲。
String存储结构的过期时间。过期时间其实非常好理解,只是其中有个地方需要特别注意下,这个问题之前可是让我栽了大坑。一个string类型的key,当你第一次设置了过期时间之后,如果第二次重新做了set操作,那么这个key的过期时间已经不存在了,什么意思呢?可以看以下这段代码:
public void test(){
redis.string.set(“key”,”1”);
redis.expire(“key”,2000);
//这一步将key的value设置为2的时候,key的过期时间已经不存在了
redis.string.set(“key”,”2”);
}
如果有业务场景需要保留过期时间的,那么需要重新设置过期时间,可以利用ttl命令计算出key的剩余时间,再将ttl取得的剩余时间重新设置为key的过期时间。此外对key的修改操作,比如:incrby,rename等操作不会使得过期时间失效。hash存储结构的hset命令不会出现string过期时间消失的情况,因为hset命令针对的是field操作,并不是对整个key进行重写操作。具体文档可以看这里。
string作为redis的基础数据结构,我平时用的最多的就是以上几种场景,平时只需要知道string的场景和注意的要点就可以了。