欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Redis开发与运维读书笔记-第二章-字符串数据类型介绍(二)

程序员文章站 2024-02-28 14:13:04
...

下面从redis几种数据结构的角度来进行理解:

一.字符串

字符串是redis数据类型的基础,首先键都是字符串类型.字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB

(一) 相关命令

常用命令:

(1)设置值(set key value [ex seconds] [px milliseconds] [nx|xx])

set key value [ex seconds] [px milliseconds] [nx|xx]

下面操作设置键为hello,值为world的键值对,返回结果为OK代表设置成功:

127.0.0.1:6379> set hello world 
OK

set命令有几个选项:
·ex seconds:为键设置秒级过期时间
·px milliseconds:为键设置毫秒级过期时间
·nx:键必须不存在,才可以设置成功,用于添加。
·xx:与nx相反,键必须存在,才可以设置成功,用于更新。
除了set选项,Redis还提供了setex和setnx两个命令:它们的作用和ex和nx选项是一样的。下面的例子说明了set、setnx、set xx的区别。

Redis开发与运维读书笔记-第二章-字符串数据类型介绍(二)

setnx和setxx在实际使用中有什么应用场景吗?以setnx命令为例子,由于 Redis的单线程命令处理机制,如果有多个客户端同时执行setnx key value, 根据setnx的特性只有一个客户端能设置成功,setnx可以作为分布式锁的一种 实现方案,Redis官方给出了使用setnx实现分布式锁的方法:http://redis.io/topics/distlock

(2)获取值(get key)

Redis开发与运维读书笔记-第二章-字符串数据类型介绍(二)

(3)批量设置值(mset key value [key value ...])

下面操作通过mset命令一次性设置4个键值对:

127.0.0.1:6379> mset a 1 b 2 c 3 d 4 
OK

(4)批量获取值(mget key [key ...])

下面操作批量获取了键a、b、c、d的值:

127.0.0.1:6379> mget a b c d 
1) "1" 
2) "2" 
3) "3" 
4) "4"

如果有些键不存在,那么它的值为nil(空),结果是按照传入键的顺序返回

127.0.0.1:6379> mget a b c f 
1) "1" 
2) "2" 
3) "3" 
4) (nil)

要注意的是每次批 量操作所发送的命令数不是无节制的,如果数量过多可能造成Redis阻塞或者网络拥塞。

(5)计数(incr key)

incr命令用于对值做自增操作,返回结果分为三种情况:
·值不是整数,返回错误。
·值是整数,返回自增后的结果。
·键不存在,按照值为0自增,返回结果为1,继续对该键进行自增,返回2。

除了incr命令,Redis提供了decr(自减)、incrby(自增指定数字)、 decrby(自减指定数字)、incrbyfloat(自增浮点数)

不常用命令:

(1)追加值(append key value)

append可以向字符串尾部追加值,例如:

127.0.0.1:6379> get key "redis" 
127.0.0.1:6379> append key world (integer) 10 
127.0.0.1:6379> get key "redisworld"

(2)字符串长度(strlen key)

例如,当前值为redisworld,所以返回值为10:

127.0.0.1:6379> get key 
"redisworld" 
127.0.0.1:6379> strlen key 
(integer) 10

下面操作返回结果为6,因为每个中文占用3个字节

127.0.0.1:6379> set hello "世界"
OK 
127.0.0.1:6379> strlen hello 
(integer) 6

(3)设置并返回原值(getset key value)

getset和set一样会设置值,但是不同的是,它同时会返回键原来的值,例如:

127.0.0.1:6379> getset hello world 
(nil) 
127.0.0.1:6379> getset hello redis 
"world"

(4)设置指定位置的字符(setrange key offeset value)

下面操作将值由pest变为了best:

127.0.0.1:6379> set redis pest 
OK 
127.0.0.1:6379> setrange redis 0 b 
(integer) 4 
127.0.0.1:6379> get redis 
"best"

(5)获取部分字符串(getrange key start end)

start和end分别是开始和结束的偏移量,偏移量从0开始计算,例如下面操作获取了值best的前两个字符。

127.0.0.1:6379> getrange redis 0 1 
"be

小结:

设值:set name zhangsan (说明:多次设置name会覆盖)
命令:
  setnx name lx: (not exist) 如果name不存在,则设值。如果name存在,则不设值并返回0;
  setex name 10 lx :(expired) 设置name的值为lx,过期时间为10秒,10秒后name清除(key也清除)
  setrange string range value 替换字符串
取值: get name
删值:del name
批量写:mset k1 v1 k2 v2 ...
批量读:mget k1 k2 k3
一次性设值和读取(返回旧值,写上新值):getset name lx
数值类型自增减:incr,decr,incrby,decrby
字符串拼接:append key value
字符串长度:strlen key

(二)内部编码

字符串类型的内部编码有3种:
·int:
8个字节的长整型。
·embstr:小于等于39个字节的字符串。
·raw:大于39个字节的字符串。
Redis会根据当前值的类型和长度决定使用哪种内部编码实现。

整数类型示例如下:

127.0.0.1:6379> set key 8653 
OK 
127.0.0.1:6379> object encoding key 
"int"

短字符串示例如下:

#小于等于39个字节的字符串:embstr 
127.0.0.1:6379> set key "hello,world" 
OK 
127.0.0.1:6379> object encoding key 
"embstr"

长字符串示例如下:

#大于39个字节的字符串:raw 
127.0.0.1:6379> set key "one string greater than 39 byte........." 
OK 
127.0.0.1:6379> object encoding key "raw" 
127.0.0.1:6379> strlen key 
(integer) 40

(三)典型使用场景

1.缓存功能

下图是比较典型的缓存使用场景,其中Redis作为缓存层,MySQL作 为存储层,绝大部分请求的数据都是从Redis中获取。由于Redis具有支撑高并发的特性,所以缓存通常能起到加速读写和降低后端压力的作用.

Redis开发与运维读书笔记-第二章-字符串数据类型介绍(二)

下面伪代码模拟上图的访问过程:
1)该函数用于获取用户的基础信息:

UserInfo getUserInfo(long id){ ... }

2)首先从Redis获取用户信息

// 定义键 
userRedisKey = "user:info:" + id; 
// 从Redis获取值 
value = redis.get(userRedisKey); 
if (value != null) {    
// 将值进行反序列化为UserInfo并返回结果    
userInfo = deserialize(value);     
return userInfo; 
}

3)如果没有从Redis获取到用户信息,需要从MySQL中进行获取,并将 结果回写到Redis,添加1小时(3600秒)过期时间:

// 从MySQL获取用户信息 
userInfo = mysql.get(id); 
// 将userInfo序列化,并存入Redis 
redis.setex(userRedisKey, 3600, serialize(userInfo)); 
// 返回结果 
return userInfo

整个功能的伪代码如下:

UserInfo getUserInfo(long id){    
userRedisKey = "user:info:" + id ;   
value = redis.get(userRedisKey);    
UserInfo userInfo;    
if (value != null) {        
    userInfo = deserialize(value);     
} else {        
        userInfo = mysql.get(id);        
        if (userInfo != null)            
        redis.setex(userRedisKey, 3600, serialize(userInfo)); 
   }    
    return userInfo;
}

2.计数

许多应用都会使用Redis作为计数的基础工具,它可以实现快速计数、查询缓存的功能,同时数据可以异步落地到其他数据源。例如有的视频播放数系统就是使用Redis作为视频播放数计数的基础组件,用户每 播放一次视频,相应的视频播放数就会自增1:

long incrVideoCounter(long id) {    
    key = "video:playCount:" + id;    
    return redis.incr(key); 
}

3.共享Session

如下图所示,一个分布式Web服务将用户的Session信息(例如用户登录信息)保存在各自服务器中,这样会造成一个问题,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务器上,用户刷新一次访问可能会发现需要重新登录,这个问题是用户无法容忍的

Redis开发与运维读书笔记-第二章-字符串数据类型介绍(二)

为了解决这个问题,可以使用Redis将用户的Session进行集中管理,如下图所示,在这种模式下只要保证Redis是高可用和扩展性的,每次用户 更新或者查询登录信息都直接从Redis中集中获取。

Redis开发与运维读书笔记-第二章-字符串数据类型介绍(二)

4.限速

很多应用出于安全的考虑,会在每次进行登录时,让用户输入手机验证码,从而确定是否是用户本人。但是为了短信接口不被频繁访问,会限制用 户每分钟获取验证码的频率,例如一分钟不能超过5次,此功能可以使用Redis来实现,下面的伪代码给出了基本实现思路:

phoneNum = "138xxxxxxxx"; 
key = "shortMsg:limit:" + phoneNum; 
// SET key value EX 60 NX 
isExists = redis.set(key,1,"EX 60","NX"); 
if(isExists != null || redis.incr(key) <=5){    
    // 通过 
}else{    
    // 限速 
}

上述就是利用Redis实现了限速功能,例如一些网站限制一个IP地址不 能在一秒钟之内访问超过n次也可以采用类似的思路