一.String(字符串)
字符串类型是Redis最基础的数据结构。首先键都是字符串类型,而且其他几种数据结构都是在字符串类型基础上构建的,所以字符串类型能为其他四种数据结构的学习奠定基础。如图2-7所示,字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。
1.1常用命令
1.1.1 单个key操作
1 | set key value [ex seconds] [px milliseconds] [nx|xx] |
EX second
:设置键的过期时间为second
秒。SET key value EX second
效果等同于SETEX key second value
。PX millisecond
:设置键的过期时间为millisecond
毫秒。SET key value PX millisecond
效果等同于PSETEX key millisecond value
。NX
:只在键不存在时,才对键进行设置操作。SET key value NX
效果等同于SETNX key value
。XX
:只在键已经存在时,才对键进行设置操作。
案例:
1 | 设置key并添加过期时间 |
1.1.2 批量操作
1 | 批量赋值,取值,节省网络开销 |
案例
1 | 127.0.0.1:6379> mset key1 value1 key2 value2 key3 value3 |
1.1.3 计数
1 | 127.0.0.1:6379> set num 1 |
1.2 内部编码
int 编码:保存long 型的64位有符号整数
1
2
3
4
5
6
7
8
9
10赋值int数字(小于20长度) 则内部编码使用int
127.0.0.1:6379> set count 1234567891234567891
OK
127.0.0.1:6379> object encoding count
"int"
赋值int数字(大于等于20长度) 则内部编码使用embstr
127.0.0.1:6379> set count 12345678912345678912
OK
127.0.0.1:6379> object encoding count
"embstr"embstr 编码:保存长度小于44字节的字符串。
1
2
3短字符串(小于44位) 则内部编码使用embstr
127.0.0.1:6379> object encoding hello
"embstr"
raw 编码:保存长度大于44字节的字符串。
1
2
3
4
5长字符串(大于等于44位) 则内部编码使用raw
127.0.0.1:6379> set hello "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
OK
127.0.0.1:6379> object encoding hello
"raw"
1.3 应用场景
缓存功能
Redis作为缓存层,MySQL作为存储层,绝大部分请求的数据都是从Redis中获取
1
2
3
4
5
6
7
8
9
10user_cache_key = "user:userinfo:"+userId
//从缓存中获取用户信息
userInfo = redis.get(user_cache_key)
if(userInfo == null){
//未获取到信息则从数据库中获取
userInfo = mysql.query(userId)
//放入缓存
redis.set(user_cache_key,userInfo)
}
retrun userInfo计数
许多应用都会使用Redis作为计数的基础工具 ,如:短信发送次数限制,一分钟内登录次数等
1
2
3
4
5
6
7
8send_message_limit_key = "user:login:sendMessageCount:"+username
value = redis.get(send_message_limit_key)
//判断用户登录发送短信次数是否超过限制
if(value > limit){
retrun errorJson;
}
//未超过限制,则增加一次
redis.incr(send_message_limit_key)共享Session
分布式系统中将用户登录信息放在各个服务器中是不现实的,可以使用Redis将用户的Session进行集中管理,在这种模式下只要保证Redis是高可用和扩展性的,每次用户更新或者查询登录信息都直接从Redis中集中获取。
1
2
3
4
5
6//判断用户是否登录
loginUser = redis.get(sessionid)
if(loginUser == null){
return LOGIN;
}
.....分布式锁
在分布式环境中我们可以基于redis实现分布式锁,利用redis单线程和setnx命令实现分布式锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
二.Hash(哈希表)
2.1常用命令
2.1.1 单个key操作
1 | HSET key field value |
案例
1 | 127.0.0.1:6379> hset user:userinfo username james |
2.1.2 批量设置
1 | HMSET key field value [field value ...] |
案例
1 | 批量设置用户信息(username,age,mobile) |
2.2 内部编码
哈希类型的内部编码有两种:
- ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。
- hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)
1 | 设置短字符串 |
2.3 应用场景
hash 类型十分适合存储对象类数据,相对于在 string 中介绍的把对象转化为 json 字符串存储,hash 的结构可以任意添加或删除‘字段名’,更加高效灵活。
1 | hmset user:1 username james email james@163.com mobile 15858585858 |
三 . List(列表)
3.1 常用命令
3.1.1单个key操作
1 | 从左边向列表中插入数据 |
案例
1 | 127.0.0.1:6379> lpush list a b c d e |
3.2 内部编码
关于list的内部编码,不同版本的方法有所区别,先看Redis 3.2版本之前的做法:
- ziplist (压缩列表,当list中的元素个数小于list-max-ziplist-entries,默认512,且每个元素的大小都小于list-max-ziplist-value,默认64字节)
- linkedlist(链表,当list中的元素不满足ziplist的条件时,内部实现使用linkedlist)
在Redis 3.0版本的源码的redis.conf
文件中可以看到:
1 | list-max-ziplist-entries 512 |
在Redis 3.2版本以及之后的版本,引入了一种叫做quicklist
的数据结构,quicklist
综合了ziplist
和linkedlist
的优点。从外部看,quicklist
也是一个linkedlist
,不过它的每个节点都是一个ziplist
。
1 | 127.0.0.1:6379> lpush liststr a b c d |
3.3 应用场景
- lpush+lpop=Stack(栈)
- lpush+rpop=Queue(队列)
- lpsh+ltrim=Capped Collection(有限集合)
- lpush+brpop=Message Queue(消息队列)
四. Set(集合)
4.1 常用命令
1 | 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略 |
案例
1 | 127.0.0.1:6379> sadd k1 v1 v2 v3 |
4.2 内部编码
集合类型的内部编码有两种:
- intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实现,从而减少内存的使用。
- hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现
1 | 当保存整数类型且少于512 内部编码使用intset |
4.3 应用场景
集合类型比较典型的使用场景是标签(tag)。例如一个用户可能对娱乐、体育比较感兴趣,另一个用户可能对历史、新闻比较感兴趣,这些兴趣点就是标签。有了这些数据就可以得到喜欢同一个标签的人,以及用户的共同喜好的标签,这些数据对于用户体验以及增强用户黏度比较重要。例如一个电子商务的网站会对不同标签的用户做不同类型的推荐,比如对数码产品比较感兴趣的人,在各个页面或者通过邮件的形式给他们推荐最新的数码产品,通常会为网站带来更多的利益 。
1 | 1.给用户添加标签 |
其他场景
- sadd=Tagging(标签)
- spop/srandmember=Random item(生成随机数,比如抽奖)
- sadd+sinter=Social Graph(社交需求)
五.SortedSet(有序集合)
5.1 常用命令
1 | 将一个或多个 member 元素及其 score 值加入到有序集 key 当中 |
案例
1 | 127.0.0.1:6379> zadd user 21 user1 |
5.2 内部编码
有序集合类型的内部编码有两种:
ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。
skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的读写效率会下降
1
2
3
4
5
6
7
8
9
10默认使用ziplist
127.0.0.1:6379> zadd user 61 user:age
(integer) 1
127.0.0.1:6379> zadd user 23 user2:age
(integer) 1
127.0.0.1:6379> object encoding user
"ziplist"
元素个数超过128 内部编码使用skiplist
127.0.0.1:6379> zadd user 16 k1 28 k2 23 k3...24 k130
"skiplist"
5.3 应用场景
有序集合比较典型的使用场景就是排行榜系统。例如视频网站需要对用户上传的视频做排行榜,榜单的维度可能是多个方面的:按照时间、按照播放数量、按照获得的赞数。本节使用赞数这个维度,记录每天用户上传视频的排行榜。主要需要实现以下4个功能 :
1 | 1.添加用户赞数 |