基本的Redis key的操作都已经熟悉了之后,便可以开始针对Redis提供的各种可操作的数据结构进行学习和了解。本文依旧是学习Redis的各种命令。
待补充--下述命令的讲解及理解;Jedis对应操作;Redis的事务等
首先先学习下Redis String下最常用的Set命令:
String的基本逻辑结构为key->value
SET
SET ${key} ${value} [${exipire seconds}EX|${expire milliseconds}PX] [NX|XX]
将一个key设置一个值,如果对应的key已经存在,则覆盖该key的值;注:操作前一定要check你要操作的key具体类型,或者明确具体类型,若你期望操作的key对应的结构为非String数据结构,则会直接覆盖成你新的数据结构。因此在key的定义上也要规避这种重复命名的情况
时间复杂度O(1),当操作成功时会返回ok(为什么会有失败呢?因为有下面的几个可选参数)
因为我们在Client未开始事务以及脚本操作的情况下,命令个是逐条到达Redis Server的,在两条命令中间可能会有其他插入的命令,来造成数据不安全,不一致的现象,因此,我们不建议设置key值,并设置过期时间通过如下方式:
redis.set(key,value)
redis.expire(key,seconds)
这里存在两个问题,1、非原子性操作;2、两次通信交互,可能存在链接等待时间
因此,聪明的Redis设计者提供了如下几个可选参数:
EX (设置秒过期)设置一个值的时候,同时设置这个key的过期时间,单位是秒,与PX不同时出现
PX (设置毫秒过期)设置一个值的时候,同事设定这个key的过期时间,单位是毫秒,与EX不同时出现
EX/PX在命令中的位置为value后,如:SET mykey "myvalue" 1000 EX
NX (key不存在时设置)设置一个值之前,先检查对应的key是否存在,若key不存在则设置,若key存在时则不设置返回设置失败,与XX不同时出现
XX (key存在时设置)设置一个值之前,先检查对应的key是否存在,若key存在则覆盖设置,若key不存在则不设置返回设置失败,与NX不同事出现
NX/XX在命令中的位置为最后位置,如:SET mykey "myvalue" 1000 EX NX
NX一般可利用Redis的单线程原子性原理,来实现分布式锁,设置成功则持有锁,设置失败则未持有锁,为了避免锁的无限期存在,需要结合EX同时使用
XX可以用于分布式强制覆盖,在部分场景,我们只对存在的数据进行二次处理,但是不做数据初始化,对于这种场景,我们适用于XX来做覆盖。
SETNX
SETNX ${key} ${value} 对不存在的key设置一个value,若key已存在则设置失败,时间复杂度为O(1)
同理 SET ${key} ${value} NX
推荐使用SET命令代替,SET命令可以同步设置PX/EX避免永久锁的出现
SETEX
SETEX ${key} ${seconds} ${value} 设置key一个value,同步设置其过期时间,时间复杂度O(1)
同理 SET${key} ${value} ${seconds} EX
推荐使用SET命令代替
PSETEX
SETEX的毫秒版本,PSETEX ${key} ${milliseconds} ${value}
推荐使用SET命令代替
MSET
MSET ${key1} ${value1} [${key2} ${value2} ...${keyn}${valuen}] 批量进行多key的同时赋值,时间复杂度为O(N),N为操作的key数
这个命令可以理解为多次操作SET,即 MSET key1 "value1" key2 "value2",可以转换为:
SET key1 "value1"
SET key2 "value2"
但是,不推荐这样使用。
MSET自身是原子性,批量设置非常合适,可以避免网络异常问题等,适用需要全部成功或全部失败的情况。
且这里的通讯交互仅一次,可有效避免重复的获取通讯链接的等待时间
批量操作key一般推荐是20个左右一次操作,避免对server进行阻塞,降低redisserver的性能,引起其他需要快速返回的请求
MSETNX
MSETNX ${key1} ${value1} [${key2} ${value2} ...${keyn}${valuen}] 批量进行多key的同时赋值,仅所有key均不存在时可操作成功,若有key存在则操作失败,且全部失败,具有原子性。时间复杂度为O(N),N为操作的key数
MSETNX 适用于 多资源条件锁,当你在执行某个操作的时候,你需要多个条件资源,如:
你准备写一个数据合并消费程序,其中由多个程序为你生产数据,他们使用不同的通道,为了保证通道数据传输准确,你期望在获取数据的时候不能生产,生产数据的时候不能读取,且你期望各生产者能够最大力度的生产消息,自己可以接受消费后置,则可以推荐使用这个MSETNX命令,可以做到多个资源的同时上锁,如果某一个key无法上锁,也不会修改其他可操作的数据值,即这是一个等待代价很高的锁。
但。正如我上面所说的,我们期望每个所都带有一定的过期时间,使用这个命令,无法保证每个锁都是带有过期时间的。
批量操作key一般推荐是20个左右一次操作,避免对server进行阻塞,降低redisserver的性能,引起其他需要快速返回的请求
SETBIT
SETBIT ${key} ${offset} ${value} 在指定偏移量上设置其二进制数,形成一个bitmap;时间复杂度为O(1), offset的取值在0~2^32-1(512MB之内)
注意的操作offset相当于是高位开始计数,默认按字节分割(即八位),想要观察自己设置的结果,可以用setbit 操作完成后使用get获取一下
如:setbit mybit 0 1 生成的结果为: 1000 0000
setbit mybit 8 1 生成的结果为: 0000 0000 1000 0000
因为SETBIT的操作形式,如果OFFSET设置过大,会在首次操作时,让Redis生成这个bitmap较久,第二次则不会重新分配空间,因此想要使用请注意offset的大小
该命令可以结合BITCOUNT,BITOP,GETBIT等用于行为操作统计或分析:
如:统计当日用户浏览某页面UV,key为当日生成key,offset为uid等。
学好二进制这些东西就能很容易的理解,和找到巧妙的使用场景啦~
SETRANGE
SETRANGE ${key} ${offset} ${value} 在指定offset偏移量上进行value的字符串覆写,对于无字符串覆盖部分,Redis使用\x00占位填充
SETBIT是针对偏移量上的二进制进行覆写来理解的话,SETRANGE就是在指定偏移量上进行字符串的覆写,同SETBIT,可写入的内存限制在了512MB,即OFFSET限定在了0~2^29-1(字符默认占用1个字节,即2^3(8bit))
时间复杂度上为O(M)M为字符串长度(据说在小字符串情况下平摊复杂度是O(1))待读源码
返回值是修改后的字符串长度
GETSET
GETSET ${key} ${value} 覆写一个key的value,并返回旧值,若key不是字符型,则抛错,时间复杂度O(1)
返回值为旧值,当key不存在时返回nil
这个命令是原子性获取原值的命令,可以用于很多,我们做替换型操作的命令,如抢夺,接力等。
也可用于计数归零操作,方便记录归零前数据。
接下来是与SET对应的GET命令:
GET
GET ${key} 获取key对应的value,若key对应的value结构非String类型会返回一个错误,时间复杂度为O(1)
返回值为:key对应的String值,若不存在则为nil,若结构不是String则返回一个错误
MGET
MGET ${key1} [${key2}...${keyn}] 同时获取多个key对应的value,返回一个列表,时间复杂度为O(N) N为key的数量
注意:这个命令不会报错,哪怕你的key对应的结构不是string,也不会报错,只会在对应的位置返回nil
因为返回值为一个列表,因此在获取结构时,务必注意保存结构的顺序,根据顺序匹配结果
另外,多key获取数据时,不推荐使用 get key1 get key2 ...多部操作,而是推荐mget操作,减少命令操作对链接获取的等待时间
批量操作key一般推荐是20个左右一次操作,避免对server进行阻塞,降低redisserver的性能,引起其他需要快速返回的请求
GETBIT
GETBIT ${key} ${offset} 主要针对 SETBIT的存储结果进行查询,获取偏移量在offset位置的二进制数据,时间复杂度O(1),返回值 0或1,若类型非String,则返回一个错误
当利用SETBIT生成一个BITMAP的时候,GETBIT是很好的获取对应位数据的命令
GETRANGE
GETRANGE ${key} ${start}${end} 获取key对应字符串的start 偏移量到end偏移量之间的字符串,时间复杂度O(N),N为待返回的字符串长度
start 为开始字符,从左开始计数为0,当为负数时,表示从右开始计数的第n个,如-1,表示右侧第一个字符
end同理,为结束字符
举例说明:
字符串:set myrange "0123456789abcdef"
getrange myrange 0 10 ------>"0123456789a"
getrange myrange 10 15 ------>"abcdef"
getrange myrange 12 20 ------>"cdef"
getrange myrange 0 -1 ------>"0123456789abcdef"
getrange myrange 0 -2 ------>"0123456789abcde"
getrange myrange -1 -2 ------>""
接下来是一些用于叠加器:
INCRBY
INCRBY ${key} ${increment} 将key对应的数值按increment的幅度增加,increment必须是一个整数(可以为负数,负数则较为类似一个decr),如果key不存在则先初始化为0,然后执行incrby操作,即,若key: myincr不存在,执行 INCRBY myincr 10 的执行结果为10,时间复杂度是O(1),返回值就是执行incrby之后的key对应的结果,当key对应的结构不是String,且不是integer类型的数据时,会提示一个错误(即负数也是可以的)。注:该命令的值限制在-2^63~2^63-1(即64bit内)
该命令主要用于计数,分布式任务执行同步,累计步幅,和expire结合使用可以做分时限流,再结合setrange甚至可以完成统计并记录到时间序列上
INCR
INCR ${key} 将key对应的数值按增幅为1的幅度增加,如果key不存在则先初始化为0,然后再执行incr操作,实际上等于incrby ${key} 1,时间复杂度是O(1),返回值就是执行incr之后的key对应的结果,当key对应的结构不是String,且不是integer类型的数据时,会提示一个错误(即负数也是可以的)。注:该命令的值限制在-2^63~2^63-1(即64bit内)
INCRBYFLOAT
INCRBY ${key} ${increment} 将key对应的述职按increment的幅度增加,increment必须是一个可以转换为双精度浮点数的数值(可以为负数),如果key不存在则先初始化为0,然后执行incrbyfloat操作,时间复杂度是O(1),返回值就是执行incrbyfloat之后的key对应的结果,当key对应的结构不是String,且不是数值类型的数据时,会提示一个错误。注:命令的小数要注意小数点最多保留小数点后17位
递减器:
DECRBY
DECRBY ${key} ${increment} 将key对应的数值按increment的幅度减少,increment必须是一个整数(可以为负数,负数则较为类似一个incr),如果key不存在则先初始化为0,然后执行decrby操作,即,若key: mydecr不存在,执行 DECRBY mydecr 10 的执行结果为-10,时间复杂度是O(1),返回值就是执行decrby之后的key对应的结果,当key对应的结构不是String,且不是integer类型的数据时,会提示一个错误(即负数也是可以的)。注:该命令的值限制在-2^63~2^63-1(即64bit内)
可用做最大重试次数,或最大次数限定
DECR
DECR ${key} 将key对应的数值按增幅为1的幅度减少,如果key不存在则先初始化为0,然后再执行decr操作,实际上等于decrby ${key} 1,时间复杂度是O(1),返回值就是执行incr之后的key对应的结果,当key对应的结构不是String,且不是integer类型的数据时,会提示一个错误(即负数也是可以的)。注:该命令的值限制在-2^63~2^63-1(即64bit内)
字符长度:
STRLEN
STRLEN ${key} 返回key对应字符串的字符长度,时间复杂度是O(1),返回值为字符串的字符长度,就算是数值也是按字符串方式获取其长度,若key不存在,则返回0,若key不是字符串,则返回一个错。(Redis底层使用SDS结构,内置字符长度,因此时间复杂度为O(1))
字符拼接:
APPEND
APPEND ${key} ${value} 在key对应的字符串后面追加value字符串,并返回其追加后的字符串长度。时间复杂度平摊O(1)(原因待学习)。返回值:拼接后的字符串的长度。若key不存在则相当于set命令,若key对应的值非String类型,则返回一个错误。
位运算:
BITCOUNT
BITCOUNT ${key} [${start}] [${end}] 获取start到end之间位之和,时间复杂度O(N),N为待返回的字符串长度
start 为开始字符,从左开始计数为0,当为负数时,表示从右开始计数的第n个,如-1,表示右侧第一个字符
end同理,为结束字符
BITOP
BITOP ${OP} ${dest} ${key1} [${key2}...${keyn}] 将key1到keyn 这些key对应的位内容进行OP操作,并将操作结果保存至以dest为key的结果内,同时返回操作结果的长度,时间复杂度为O(N),对大型矩阵的操作要避免在master操作,避免阻塞
OP 包含: AND (并) OR(或)XOR(异或)NOT(非)
注:非情况,仅支持一个key
OK
OK
大家读完一遍之后,发现,哎呀,咋感觉这么乱呢。
其实是上面,只是按照命令操作类型进行了排序,如果还有一些困惑的同学,可以按照下述顺序再进行一次学习:
对于String下面的更多类型来分解:
数值类型:(注意除了incr,decr这类按照数值进行操作,有数值依赖的操作外,其他均将对数值当字符操作)
初始化、覆盖可以使用:SET;
批量初始化、覆盖可以使用:MSET;
获取可以使用:GET;
批量获取可以使用:MGET;
设置过期可以使用EXPIRE;
原子性简单SET & EXPIRE 可以使用SETEX,当然也可以使用 SET EX;
设置毫秒级过期可以使用PEXPIRE;
原子性简单SET & PEXPIRE 可以使用PSETEX,当然也可以使用 SET PX;
设置前检查值是否存在可以使用:EXISTS;
原子性简单EXISTS & SET 可以使用 SETNX, 淡然也可以使用 SET NX;
同时对多个key设置SETNX,可以使用MSETNX;
如果希望SETNX + SETEX 必须使用 SET EX NX;
增加整型数字可以使用:INCR、有步幅设置的可以使用:INCRBY;
增加浮点型数字可以使用:INCRBYFLOAT;
减少整型数字可以使用:DECR、有步幅设置的可以使用:DECRBY;
如果希望设置一个值前,先获取下之前的值,可以 使用GETSET;
位运算,bit类型:(注意bit类型的操作也可被其他操作覆盖修改,要注意避免冲突使用)
设置某个位上的值,可以使用SETBIT;
对应获取某个位上的值,可以使用GETBIT;
获取指定范围内,位值和,可以使用BITCOUNT;
如果希望进行常规的位运算,可以使用BITOP;
上述内容,除了强制要求数值类型的操作外;均可操作这类位运算的结果;
纯字符类型:
初始化、覆盖可以使用:SET;
批量初始化、覆盖可以使用:MSET;
获取可以使用:GET;
批量获取可以使用:MGET;
设置过期可以使用EXPIRE;
原子性简单SET & EXPIRE 可以使用SETEX,当然也可以使用 SET EX;
设置毫秒级过期可以使用PEXPIRE;
原子性简单SET & PEXPIRE 可以使用PSETEX,当然也可以使用 SET PX;
设置前检查值是否存在可以使用:EXISTS;
原子性简单EXISTS & SET 可以使用 SETNX, 淡然也可以使用 SET NX;
同时对多个key设置SETNX,可以使用MSETNX;
如果希望SETNX + SETEX 必须使用 SET EX NX;
如果希望设置一个值前,先获取下之前的值,可以 使用GETSET;
设置字符串内范围值,可以使用SETRANGE;
获取字符串内范围值,可以使用GETRANGE;
字符串拼接覆盖,可以使用APPEND;
高效获取字符长度,可以使用STRLEN;
如果只是常规操作字符串,那么就记好:
SET 默认不过期,后追EXPIRE操作非原子性,使用SET EX(PX)更省心
想用Redis做锁,用好SETNX,后追EXPIRE操作非原子性,使用SET EX(PX)NX更省心
获取数据用GET,不是必须提前用EXISTS
循环操作SET、GET浪费Client的网络和链接,可以用MSET、MGET减少链接次数(注意避免过大操作)
其他类型根据情况可以巧用~
掰掰~