Redis指令详解-string val相关指令

目录

目标

将val视为bit数据

BITCOUNT

BITFIELD

    bit顺序

    子命令

    类型

    offset

    OVERFLOW溢出

性能

BITOP

BITPOS

SETBIT

GETBIT key offset

将val视为数字

DECR key

INCR key

    场景

INCRBY key increment

INCRBYFLOAT

DECRBY key decrement

将val视为字符串

SET

    选项

    排它锁

GET key

APPEND

SETRANGE

    场景:高效的随机访问string数组

GETRANGE

STRLEN key

GETSET key value

MGET

MSET

MSETNX

PSETEX

SETEX

SETNX


目标

列举redis中与string val相关的指令,将指令分为bit、string、数字3组,包括作用、使用时需要注意的地方等。可用于高效计算实时指标、排它锁、计数器、限制器。

将val视为bit数据

BITCOUNT

BITCOUNT key [startCharIndex endCharIndex]
    O(N)
    将String val视为bit数组形式,返回bit为1的bit数量,即群体统计,可指定字符index范围,index为0表示第一个字符,index为-1表示最后一个字符

BITFIELD

BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
    since 3.2
     O(1) 
    将key对应string val视为bit数组,使用offset(based-0)和特定bit宽度确定field且进行操作,支持在一个命令中对多个field执行不同的操作。如从位偏移1000开始,对5位的无符号整数进行读取、设置、增加、减少并可设置上溢下溢行为。
    为什么提供该指令
        出于高效使用内存的目的,可以将很多小整数存储在一个大bitmap中,这样也避免出现大批key。


    bit顺序

  •         offset是从左向右计数,左侧第一个bit为offset-0.
  •         offset和宽度确定field后,该field左为高位,右为低位。

    子命令

  •         GET <type> <offset> -- 返回特定宽度的 bit field
  •         SET <type> <offset> <value> -- 设置特定宽度 bit field,返回旧数据.
  •         INCRBY <type> <offset> <increment> --针对特定宽度bit field进行增加或减少操作,返回最新数据.
  •         OVERFLOW [WRAP|SAT|FAIL] 仅对紧跟其后的一个INCRBY子命令其作用,指定溢出的处理方式

    类型

  •         使用i表示有符号整数,u表示无符号整数,后边使用数字表示占用bit位数,如i6 6bit有符号整数,u2 2bit无符号整数。
  •         redis仅支持最多64bit有符号整数,最多63bit无符号整数(redis无法复制64bit无符号整数).

    offset

  •         无前缀整数,表示based-0的bit偏移量,如1000,表示偏移量为1000的bit
  •         前缀#,表示offset=bit宽度乘以#后的数字, i8 #0 表示offset=8*0, i8 #i表示offset=8*1。当使用string val的存储整数数组数据时,可以使用该方式#后为元素index。

    OVERFLOW溢出

        允许用户针对增加或减少操作,选择溢出的处理方式
        wrap 环绕式,默认方式
            无符号整数,对溢出进行取模处理,如u2最大整数个数为2^2=4,当前数据为3,则加1后为4,溢出采用4%4,则结果为0
            有符号整数,对溢出的处理为在正max和负min之间变化,如i8,当前为正max127,加1后变为负min-128,之后再加1,变为正max127
        SAT 饱和式
            当增加时,上限为正max,如i8当前120,加10,结果为上限127,继续增加结果不变
            当减少时,下限为负min
        FAIL 操作失败式
            溢出时不对数据进行任何操作,仅返回NULL以告知client发生溢出
        每个overflow命令仅对紧跟overflow之后一个increby命令起作用,未指定overflow,则默认使用wrap方式。
        举例
            > BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1
1) (integer) 1
2) (integer) 1
> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1
1) (integer) 2
2) (integer) 2
> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1
1) (integer) 3
2) (integer) 3
> BITFIELD mykey incrby u2 100 1 OVERFLOW SAT incrby u2 102 1
1) (integer) 0 // 第一个incryby默认使用wrap溢出模式
2) (integer) 3

性能

        bitfield一般情况是很高效的,如果在短string定位很远的bit,将导致内存分配。

BITOP

    BITOP operation destkey key [key ...]
    O(N)
    since 2.6.0
    对多个source key进行位操作,如AND、OR、XOR、NOT(仅支持一个source key),并将结果存储到destkey。
    举例
        BITOP AND destkey srckey1 srckey2 srckey3 ... srckeyN
        BITOP OR destkey srckey1 srckey2 srckey3 ... srckeyN
        BITOP XOR destkey srckey1 srckey2 srckey3 ... srckeyN
        BITOP NOT destkey srckey
    长短不一的string如何处理
        以最长string长度为准,短string使用0补位。
        对于不存在的key,视为所有位均为0
    返回dest key对应string val的字符长度
    使用bitmap进行实时计算
    性能
        如果命令中source key数量很多、key对应val很长时,会成为耗时操作。
        为了避免阻塞master server,可以将read-only配置关闭,使用salve node处理。

BITPOS

    BITPOS key bit(1or0) [byteStartIndex] [byteEndIndex]
    O(N)
    since 2.8.7
    将key对应string val视为bit数组,在字节index范围内定位第一个为1或0的bit,返回从offset-0开始的绝对位置。
    关于bit数组,从左向右based-0计数bit offset,第0个字节为第0-7bit,第二字节为第8-15bit,依次类推;字节index支持负数,-1表示最后一个字节。
    返回值

  •         若定位bit 1,但string val为empty或所有位为0,则返回-1
  •         若定位bit 0,但string val所有位均为1且不指定endIndex,该命令认为val右侧均为0,,所以返回val之后的第一bit的offset
            set testK1 '\xff'
             "OK"
             bitpos testK1 0
             8 // 0-7bit均为1,则返回8

 

  •         若定位bit 0,但string val所有位均为1且指定start和endIndex,返回-1
            set testK1 '\xff'
            "OK"
            bitpos testK1 0 0 0
            -1


SETBIT

    SETBIT key offset value(1or0)
    O(1)
    将string val视为bit数组,设置offset index的bit数据,返回bit的原始数据。
    offset必须位于[0,2^32),即bitmap最大为512M
    key不存在则创建key和满足offset的val。如offset为2^32-1,则将分配完整的内存空间,即使存储的数据很小。

GETBIT key offset

    O(1)
    将string val视为bit数组,返回offset index的bit数据。key不存在或offset越界返回0

 

将val视为数字

DECR key

    O(1)
    将key对应string val减一,返回最新val。
    如果val类型不对或string无法表示为64bit有符号整数,则error
    如果key不存在,则创建key,val初始值为0

INCR key

    O(1)
    将key对应string val加一,返回最新val。
    如果val类型不对或string无法表示为64bit有符号整数,则error
    如果key不存在,则创建key,val初始值为0

    场景

        计数器counter
            针对某个维度,进行计数

  •             统计每个用户每天的访问量

                使用用户ID和日期作为key,用户每次请求则加一

  •             INCR和EXPIRE组合

                如最近时间段内pv

  •             INCR和GETSET

                使用GETSET获取当前counter值并重置

  •             INCR INCRBY DECR DECRBY

                数据可以幅度变化,如游戏中用户分数

       限制器

            仍然是计数器,但是用来限制操作的执行次数。
            如限制每个IP每秒钟最多访问10次,以下是redis官网的实现,个人认为在高并发时无法准确限制。

  •     每秒每IP维度提供一个计数器并设置超时
FUNCTION LIMIT_API_CALL(ip)
ts = CURRENT_UNIX_TIME()
keyname = ip+":"+ts
current = GET(keyname)
IF current != NULL AND current > 10 THEN
    ERROR "too many requests per second"
ELSE
    MULTI
        INCR(keyname,1)
        EXPIRE(keyname,10)
    EXEC
    PERFORM_API_CALL()
END

事务形式保证一起执行

  •       IP维护的计数器并设置超时,不可采用
FUNCTION LIMIT_API_CALL(ip):
current = GET(ip)
IF current != NULL AND current > 10 THEN
    ERROR "too many requests per second"
ELSE
    value = INCR(ip)
    IF value == 1 THEN
        EXPIRE(ip,1)
    END
    PERFORM_API_CALL()
END

问题在于EXPIRE可能未执行,导致无法超时

  •        IP维度的队列并设置超时
FUNCTION LIMIT_API_CALL(ip)
current = LLEN(ip)
IF current > 10 THEN
    ERROR "too many requests per second"
ELSE
    IF EXISTS(ip) == FALSE
        MULTI
            RPUSH(ip,ip)
            EXPIRE(ip,1)
        EXEC
    ELSE
        RPUSHX(ip,ip)
    END
    PERFORM_API_CALL()
END

使用list达到计数器的目的,使用事务保证RPUSH和EXPIRE都被执行

  • 思考如何准确限流

为了达到准确限制的目标,必然要求 获取当前值、验证、递增 3个流程在并发情况下同步执行。redis单线程已经保证指令同步执行,那如何保证执行一个请求的多条指令时不被其他请求的指令穿插呢?

我选择使用lua脚本。

local limit = redis.call('get', KEYS[1]);  if ( limit == false or ARGV[1] - limit > 0  ) then local current = redis.call('incr',KEYS[1]) redis.call('expire',KEYS[1],ARGV[2])  return current  else return -1 end

使用redis client执行eval script 1 key limitMax expireSeconds

关于性能差异,该脚本ttl与get指令ttl相差很少。

 

INCRBY key increment

INCRBYFLOAT

    INCRBYFLOAT key increment

DECRBY key decrement

 

将val视为字符串

SET

    SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
    设置key和string val,同时提供排他或仅更新、超时的可选项

    选项

        EX seconds -- Set the specified expire time, in seconds.
        PX milliseconds -- Set the specified expire time, in milliseconds.
        NX -- Only set the key if it does not already exist.
        XX -- Only set the key if it already exist.
    如果设置成功返回OK,如果由于NX或XX限制导致没有执行成功则返回null

    排它锁

        set key val NX EX seconds

  •         val数据选择随机方式生成
  •         验证val相等时才可执行DEL
    •             建议使用eval执行如下脚本
eval " if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end" 1 具体key 具体随机val

GET key

    O(1)
    返回key对应的string val
    如果key 不存在则返回null,如果val不是string类型则error

APPEND

    APPEND key value
     O(1)
    在原string val基础上,追加string。若key不存在则新建。
    返回追加后val的长度

SETRANGE

    SETRANGE key offset value
    从offset开始,对参数value长度的数据进行覆盖
    如果offset超过val长度或key不存在则新建key,与offset的间距使用zero-byte进行补全。

redis> SETRANGE key2 6 "Redis" // key2不存在时
(integer) 11
redis> GET key2
"\u0000\u0000\u0000\u0000\u0000\u0000Redis"

    返回修改后的val长度

    场景:高效的随机访问string数组

        使用SETRANGE和GETRANGE命令,可以将string作为高效的随机访问string数组,且具有O(1)时间复杂度。

GETRANGE

    GETRANGE key start end
    O(N) N:返回string的长度
    返回key对应string val的子字符串,start和end可以为负,越界则使用边界。

STRLEN key

    返回string val的长度

GETSET key value

    O(1)
    原子性操作,对key设置新string val,并返回旧string val。若key对应类型不是string则error。若key不存在则null
    GETSET和INCR组合使用,对事件发生次数进行计数,某种情况时获取计数并重置为0.

MGET

    MGET key [key ...]
    以数组形式返回多个key的stirng val,当val不是string类型或key不存在,在返回数组对应位置返回null

MSET

    MSET key value [key value ...]
    原子性操作,保证设置或更新多个key的val都被执行
    返回OK

MSETNX

    MSETNX key value [key value ...]
    原子性操作,保证设置多个key的val都被执行,不会更新已存在key的val。
    如果没有重复key,都是新建key和val,则返回1
    如果至少存在一个key,则什么也不做,返回0

PSETEX

    PSETEX key milliseconds value
    原子性指令,同时设置val与超时,单位毫秒,但不具有排他性

SETEX

    SETEX key seconds value
    原子性指令,同时设置val与超时,单位秒,但不具有排他性

SETNX

    SETNX key value
    仅当key不存在时才执行操作
    如果key不存在,则执行且返回1
    如果key存在,则不执行操作,返回0

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值