2.2、字符串

命令

  1. 常用命令

    (1)设置值

    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两个命令:

    setex key seconds key
    setnx key value
    

    它们的作用和ex和nx选项是一样的。下面的列子说明了set、setnx、set xx
    的区别。
    当前键hello不存在:

    127.0.0.1:6379> exists hello
    (integer) 0
    

    设置键位hello,值为world的键值对:

    127.0.0.1:6379> set hello world
    OK
    

    因为键hello已存在,所以setnx失败,返回结果为0:

    127.0.0.1:6379> setnx hello redis
    (integer) 0
    

    因为键hello已存在,所以set xx成功,返回结果为OK:

    127.0.0.1:6379> set hello jedis xx
    OK
    

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

    (2)获取值

    get key

    下面操作获取键hello的值:

    127.0.0.1:6379> get hello
    "world"
    

    如果要获取的键不存在,则返回nil(空):

    127.0.0.1:6379> get not_exist_key
    (nil)
    

    (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)
    

    批量操作命令可以有效提高开发效率,假如没有mget这样的命令,要执行n次
    get命令需要按照图2-8的方式来执行,具体耗时如下:

    • n次get时间=n次网络时间+n次命令时间

    使用mget命令后,要执行n次get命令操作具体耗时如下:

    • n次get时间=1次网络时间+n次命令时间

    Redis可以支撑每秒数万的读写操作,但是这指的是Redis服务端的处理能力,
    对于客户端来说,一次命令除了命令时间还是有网络时间,假设网络时间为1
    毫秒,命令时间为0.1毫秒(按照每秒处理1万条命令算),那么执行1000次
    get命令和1次mget命令的区别如下表:

    操作 时间
    1000次get 10001+10000.1=1100毫秒=1.1秒
    1次mget(组装了1000个键值对) 11+10000.1=101毫秒=0.101秒

    学会使用批量操作,有助于提高业务处理效率,但是要注意的是每次批量操作
    所发送的命令数不是无节制的,如果数量过多可能造成Redis阻塞或者网络拥
    塞。

    (5)技术

    incr key

    incr命令用于对值做自增操作,返回结果分为三种情况:

    • 值不是整数,返回错误。
    • 值是整数,返回自增后的结果。
    • 键不存在,按照值为0自增,返回结果为1.

    例如对一个不存在的键执行incr操作后,返回结果是1:

    127.0.0.1:6379> exists key
    (integer) 0
    127.0.0.1:6379> incr key
    (integer) 1
    

    再次对键执行incr命令,返回结果是2:

    127.0.0.1:6379> incr key
    (integer) 2
    

    如果值不是整数,那么会返回错误:

    127.0.0.1:6379> set hello world
    OK
    127.0.0.1:6379> incr hello
    (error) ERR value is not an integer or out of range
    

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

    decr key
    incrby key increment
    decrby key decrement
    incrbyfloat key increment
    

    很多存储系统和编程语言内部使用CAS机制实现技术功能,会有一定的CPU消
    耗,但在Redis只给你完全不存在这个问题,因为Redis是单线程架构,任何命
    令到了Redis服务端都要顺序执行。

  2. 不常用命令

    (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> sterange 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 key value O(1)
    get key O(1)
    del key [key ...] O(k),k是键的个数
    mset key value [key value ...] O(k),k是键的个数
    mget key [key ...] O(k),k是键的个数
    incr key O(1)
    decr key O(1)
    incrby key increment O(1)
    decrby key decrement O(1)
    incrbyfloat key increment O(1)
    append key value O(1)
    strlen key O(1)
    setrange key offset value O(1)
    getrange key start end O(n),n是字符串长度,由于获取字符串非常快,所以如果字符串不是很长,可以视同为O(1)

内部编码

  1. 字符串类型的内部编码有3种:

    • int: 8个字节的长整型。
    • embstr: 小于等于39个字节的字符串。
    • raw: 大于39个字节的字符串。

    Redis会根据当前值的类型和长度决定使用哪种内部编码实现。
    整数类型示例如下:

    127.0.0.1:6379> ste 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具有支撑高并发的特性,所以缓存通常能起到加速读写和降低
    后端压力的作用。
    下面伪代码模拟了一个访问过程:

    (1)该函数用于获取用户的基础信息:

    UserInfo getUserInfo(long id){
        ...
    }
    

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

    //定义键
    userRedisKey = "user:info:" + id;
    //从Redis获取值
    value = redis.get(usrRedisKey);
    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, seriallize(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);
}
  1. 共享session

    一个分布式Web服务将用户的Session信息保存在各自服务器中,这样会造成
    一个问题,出于负载均衡的考虑,分布式服务会将用户的访问均衡到不同服务
    器上,用户刷新一次访问可能会发现需要重新登录,这个问题是用户无法容忍
    的。为了解决这个问题,可以使用Redis将用户的Session进行集中管理,将
    Web服务的Session交于Redis集中管理,在这种模式下只要保证Redis高可用和
    扩展性的,每次用户更新或者查询登录信息都直接从Redis中集中获取。

  2. 限速

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容