1.前言
一直以为自己会redis,但是从没有系统的学过,对于redis的理解还是只停留在set key、get key这些基础的命令上。现在查漏补缺,重新学习一遍,增加一下对redis的了解。
2.Redis的key
redis的key除了set 和 get之外。还有exists, expire, keys, expireat, ttl等很多,常用的如下:
命令 | 说明 | demo |
exists | 检查当前库里面是否存在某个key |
127.0.0.1:6379> exists name |
expire |
设置key的过期时间,单位秒 pexpire单位是毫秒 pexpireat同毫秒 |
127.0.0.1:6379> expire name 2000 (integer) 1 |
expireat | 设置key的过期时间,单位时间戳,这个在某些特殊场景还是挺有用的 | 127.0.0.1:6379> EXPIREAT name 1576234458 (integer) 1 127.0.0.1:6379> ttl name (integer) 86369 |
persist | 取消key的过期时间,key将永久保存 | 127.0.0.1:6379> persist name (integer) 1 127.0.0.1:6379> ttl name (integer) -1 |
keys | 返回符合条件的key(生产环境慎用 keys *,或者禁用) | 127.0.0.1:6379> keys na* 1) "name" |
ttl | 查看key的剩余过期时间 | 127.0.0.1:6379> ttl name (integer) 1773 |
rename | 重命名一个key,如果newname已存在,则会覆盖 | 127.0.0.1:6379> get name "smallking" 127.0.0.1:6379> set name2 "tom" OK 127.0.0.1:6379> rename name2 name OK 127.0.0.1:6379> get name "tom" |
incr | 自增+1,如果 key 不存在,那么 key 的值会先被初始化为 0 | 127.0.0.1:6379> set i 1 OK 127.0.0.1:6379> incr i (integer) 2 127.0.0.1:6379> get i "2" |
3.Redis hash
hash是redis的数据类型之一,比较适合用来存储对象。语法如下:
命令 | 说明 | demo |
hmset |
添加一个或者多个键值对到key对应的hash表中。以前认为hmset是hashMapSet的缩写,后来看到还有一个hset,所以hmset应该hash multi set的缩写。一次可以设置多个,而hset只能设置一个 |
127.0.0.1:6379> hmset user01 id 001 name smallking age 18 OK |
hmget | 得到指定key对应hash表的多个key-value | 127.0.0.1:6379> HMGET user01 id name 1) "001" 2) "smallking" |
hset和hget | 同hmset和hmget不同的是,一次只能操作hash的一个键值对 | 127.0.0.1:6379> hset user01 email [email protected] (integer) 1 127.0.0.1:6379> hget user01 email "[email protected]" |
hgetall | 得到一个hash表的所有字段 | 127.0.0.1:6379> hgetall user01 1) "id" 2) "001" 3) "name" 4) "smallking" 5) "age" 6) "18" 7) "email" 8) "[email protected]" |
hkeys | 得到指定hash表的所有key | 127.0.0.1:6379> hkeys user01 1) "id" 2) "name" 3) "age" 4) "email" |
hvals | 得到指定hash表的所有value | 127.0.0.1:6379> HVALS user01 1) "001" 2) "smallking" 3) "18" 4) "[email protected]" |
除了表中的这些,redis的hash还有其他一些命令,很多命令都是见名知意。hash可以用来存储对象,但是如果将对象转为json字符串,以key-jsonstr存在redis里更方便,一般不经常修改的对象,可以存jsonstr,但是如果对象要经常修改的话,还是存hash比较方便。
4.Redis list
一直都将redis的list当成一个java的ArrayList<String> 来看,其实它是个linkedList<String>。当我们想到一个list的时候,list.add()增加元素,list.get(index) 获得元素,list.size()获取列表长度,这些功能redis的list都具备:
命令 | 说明 | demo |
lpush | 在list的头部添加元素 | 127.0.0.1:6379> lpush idlist 1 2 3 4 5 (integer) 5 |
llen | llen即 list length 获取list的长度 | 127.0.0.1:6379> llen idlist (integer) 5 |
lrange | 获取list的一段值 | 127.0.0.1:6379> lrange idlist 0 100 1) "5" 2) "4" 3) "3" 4) "2" 5) "1" |
lindex | 获取指定下标的值 | 127.0.0.1:6379> lindex idlist 3 "2" |
lpop | 移除并返回第一个元素 | 127.0.0.1:6379> lpop idlist "5" 127.0.0.1:6379> lrange idlist 0 100 1) "4" 2) "3" 3) "2" 4) "1" |
lpush | 在头部插入一个元素 | 127.0.0.1:6379> lpush idlist 10 (integer) 5 127.0.0.1:6379> lrange idlist 0 100 1) "10" 2) "4" 3) "3" 4) "2" 5) "1" |
redis的list除了以上操作,还有通过BLPOP/BRPOP来实现简单的消息队列,这两个命令都是阻塞的。当程序中有简单消息队列的需要,但是对于消息的可靠性性要求不高的时候,可以使用BLPOP/BRPOP来简单快速的实现消息队列需求。简单实现如下:
生产者
import com.smallking.redis.speed.JedisManager;
import redis.clients.jedis.Jedis;
public class Producer {
public static void main(String[] args) {
Jedis jedis = JedisManager.instance().getJedis();
Long lpush = jedis.lpush("message", "消息内容:" + Math.random());
System.out.println("list大小:" + lpush);
}
}
消费者
import java.util.List;
import com.smallking.redis.speed.JedisManager;
import redis.clients.jedis.Jedis;
public class Comsumer {
public static void main(String[] args) {
Jedis jedis = JedisManager.instance().getJedis();
while(true) {
List<String> list = jedis.blpop(0, "message");
for(String str : list) {
System.out.println("处理消息" + str);
}
}
}
}
redis的list通过lrange命令还可以实现定时计算的,并且计算之后就不会更改的排行榜,如去年的班级成绩排名,上个月的出勤率等,但是如果是经常变化的list就不推荐用list了,后面会说到可以用sorted set。还有一些时间线很明显的数据也可以通过list的lpush来实现。
5.Redis set
redis的set存储的元素是无序且唯一的。除了对set的基础增删改查之外,我们还应该灵活运用set的交并插计算来应对一些场景。
命令 | 说明 | demo |
sadd | 为一个set集合添加一个或者多个元素 | 127.0.0.1:6379> sadd user 1 2 3 (integer) 3 |
smembers | 获得一个set集合的所有元素 | 127.0.0.1:6379> SMEMBERS user 1) "1" 2) "2" 3) "3" |
srem | 移除一个或者多个元素 | 127.0.0.1:6379> SREM user 1 2 (integer) 2 |
sdiff | 求所给集合的差集 | 127.0.0.1:6379> sadd set1 1 2 3 4 (integer) 4 127.0.0.1:6379> sadd set2 2 5 6 7 (integer) 4 127.0.0.1:6379> sdiff set1 set2 1) "1" 2) "3" 3) "4" |
sunion | 求所给集合的并集 | 127.0.0.1:6379> sunion set1 set2 1) "1" 2) "2" 3) "3" 4) "4" 5) "5" 6) "6" 7) "7" |
sinter | 求交集 | 127.0.0.1:6379> sinter set1 set2 1) "2" |
redis的set集合可以给我们解决很多问题,例如:
- set的唯一性可以用来做uv统计,一天一个用户多次访问本网站算做一次uv,则可以用日期作为key,当用户访问网站的时候直接sadd当前客户的ID就好了。
- set的sinter可以用来做好友推荐,需求:现在有2个用户,如果这两个用户的共同好友超过3人,则推荐他们加好友。我们可以用用户的id作为key,将用户的好友存储在set里面,sinter userid1 userid2就得到了2个人的共同好友,判断共同好友的数量,决定是否推荐。
- 当业务需要求交集并集差集的时候,可以朝着redis set上面想一想,也许一下子就变的简单了呢。
6.Redis sorted set
sorted set是有序的集合,它给每个set元素都加了一个score分数,通过这个分数来进行排序。
命令 | 说明 | demo |
zadd | 添加元素 如果元素已则会更新元素的score | 127.0.0.1:6379> zadd ganmerank 200.5 xiaowang 300 xiaozhang 400 xiaoli (integer) 3 |
zrange | 按照下标查询元素 | 127.0.0.1:6379> zrange ganmerank 0 100 withscores 1) "xiaowang" 2) "200.5" 3) "xiaozhang" 4) "300" 5) "xiaoli" 6) "400" |
zrangebyscore | 按照分数来查询元素 | 127.0.0.1:6379> zrangebyscore gamerank 1 1000 withscores 1) "xiaowang" 2) "200.5" 3) "xiaozhang" 4) "300" 5) "xiaoli" 6) "400" |
zrem | 删除元素 | 127.0.0.1:6379> zrange gamerank 0 200 1) "xiaowang" 2) "xiaoli" 127.0.0.1:6379> zrem gamerank xiaoli (integer) 1 127.0.0.1:6379> zrange gamerank 0 -1 1) "xiaowang" |
ZINCRBY | 给某个元素加分 | 127.0.0.1:6379> zrange gamerank 0 -1 withscores 1) "xiaowang" 2) "200.5" 127.0.0.1:6379> ZINCRBY gamerank 1 xiaowang "201.5" |
sorted set 很适用一些实时变化的排行场景,比如某论坛的文章排序规则是按照阅读量,那么可以用文章的id作为sorted set的元素,阅读量作为score。当一个文章阅读量发生变化的时候ZINCRBY +1 就好了。