redis基础数据类型--String

                                             String类型的使用

前言

      要想学好redis,那么string这个基本数据类型就一定要掌握,它是最基础的数据类型,如果是当当使用redis的string类型进行数据操作的话,那么使用redis就好像使用memcache(基于php框架开发的开源的分布式内存对象缓存系统)。但是我们知道redis的数据结构不只有string这个一个,后续的课程会继续讲解redis的其他数据类型。

介绍

     redis的String类型是包含了许多种类型的特殊类型,并且是二进制安全的,我们可以使用redis的string类型来存储比如序列化后的对象、比如一张图片进行二进制存储(值的长度不能超过512MB)、比如是一个字符串、数值等等。

使用

     redis像其他开源项目一样,也提供命令行的方式,方便我们进行操作,并且提供了redis的客户端,我们可以在redis客户端上输入命令进行操作。下面讲解string的各种命令:

(1)、append

        如果key已经存在,并且值为字符串,那么这个命令会把value追加到原来值(value)的结尾。 如果key不存在,那么它将首先创建一个空字符串的key,再执行追加操作,这种情况 APPEND 将类似于 SET 操作。

        语法:append key value(key为要追加的key,value是追加的值)

        返回值(int类型,为追加后的字符串长度)

例子

redis> exists mykey
(integer) 0
redis> append mykey "Hello"
(integer) 5
redis> append mykey " World"
(integer) 11
redis> get mykey
"Hello World"
redis>

(2)、bitcount 

      bitcount是统计字符串中被设置为1的bit数,一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。start 和 end 参数的设置和 GETRANGE 命令类似,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,以此类推。不存在的 key 被当成是空字符串来处理,因此对一个不存在的 key 进行 BITCOUNT 操作,结果为 0 。

       语法:bitcount key [start end],指定额外的start或者end参数,让技术只在特定的位上进行。

       返回值(int类型,为被设置为 1 的位的数量。)

例子

redis> set mykey "foobar"
OK
redis> bitcount mykey
(integer) 26
redis> bitcount mykey 0 0
(integer) 4
redis> bitcount mykey 1 1
(integer) 6
redis>

        模式:使用 bitmap 实现用户上线次数统计

       假设现在我们希望记录自己网站上的用户的上线频率,比如说,计算用户 A 上线了多少天,用户 B 上线了多少天,诸如此类,以此作为数据,从而决定让哪些用户参加 beta 测试等活动 —— 这个模式可以使用 setbit 和 bitcount 来实现。

       比如说,每当用户在某一天上线的时候,我们就使用 setbit ,以用户名作为 key ,将那天所代表的网站的上线日作为 offset 参数,并将这个 offset 上的为设置为 1 。

       举个例子,如果今天是网站上线的第 100 天,而用户 peter 在今天阅览过网站,那么执行命令setbit peter 100 1 ;如果明天 peter 也继续阅览网站,那么执行命令 setbit peter 101 1 ,以此类推。

       当要计算 peter 总共以来的上线次数时,就使用 bitcount 命令:执行 bitcount peter ,得出的结果就是 peter 上线的总天数。

       更详细的实现可以参考博文 Fast, easy, realtime metrics using Redis bitmaps (需要翻墙)

        性能:

前面的上线次数统计例子,即使运行 10 年,占用的空间也只是每个用户 10*365 比特位(bit),也即是每个用户 456 字节。对于这种大小的数据来说, bitcount 的处理速度就像 get 和 incr 这种 O(1) 复杂度的操作一样快。

如果你的 bitmap 数据非常大,那么可以考虑使用以下两种方法:

  • 将一个大的 bitmap 分散到不同的 key 中,作为小的 bitmap 来处理。使用 Lua 脚本可以很方便地完成这一工作。
  • 使用 bitcount 的 start 和 end 参数,每次只对所需的部分位进行计算,将位的累积工作(accumulating)放到客户端进行,并且对结果进行缓存 (caching)。

(3)、bitop

     对一个或多个保存二进制位的字符串 key 进行位元操作。

bitop命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数:

  • bitop and destkey key1 key2 key3 ... keyn,对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
  • bitop or destkey key1 key2 key3 ... keyn,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
  • bitop xor destkey key1 key2 key3 ... keyn,对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
  • bitop not destkey key1,对给定 key 求逻辑非,并将结果保存到 destkey 。

除了 NOT 操作之外,其他操作都可以接受一个或多个 key 作为输入。

执行结果将始终保持到 destkey 里面。 

     语法:bitop operation destkey key[key ...] (operation 表示位操作,分别有and、or、 xor、not,destkey为位操作后保存的key,key是一个数组,是需要进行位操作的key,可以一个也可以多个)

     返回值(int类型 保存到 destkey 的字符串的长度,和输入 key 中最长的字符串长度相等。

例子:

redis> set key1 "foobar"
OK
redis> set key2 "abcdef"
OK
redis> bitop and dest key1 key2
(integer) 6
redis> get dest
"`bc`ab"
redis>

       模式:使用 bitop 实现用户上线次数统计

      bitop是对bitcount命令很好的补充。不同的bitmaps进行组合操作可以获得目标bitmap以进行人口统计操作。

      Fast easy realtime metrics using Redis bitmaps这篇文章介绍了一个有趣的用例。

       性能: bitop可能是一个缓慢的命令它的时间复杂度是O(N)。 在处理长字符串时应注意一下效率问题。

(4)、incr     

       对存储在指定key的数值执行原子的加1操作。

       如果指定的key不存在,那么在执行incr操作之前,会先将它的值设定为0

       如果指定的key中存储的值不是字符串类型(fix:)或者存储的字符串类型不能表示为一个整数,那么执行这个命令时服务器会返回一个错误(eq:(error) ERR value is not an integer or out of range)。

       这个操作仅限于64位的有符号整型数据。        

       注意: 由于redis并没有一个明确的类型来表示整型数据,所以这个操作是一个字符串操作。执行这个操作的时候,key对应存储的字符串被解析为10进制的64位有符号整型数据

       事实上,Redis 内部采用整数形式(Integer representation)来存储对应的整数值,所以对该类字符串值实际上是用整数保存,也就不存在存储整数的字符串表示(String representation)所带来的额外消耗。

       语法:incr key

       返回值:(int类型 执行递增操作后key对应的值。

例子:

redis> set mykey "10"
OK
redis> incr mykey
(integer) 11
redis> get mykey
"11"
redis> 

 实例:计数器

Redis的原子递增操作最常用的使用场景是计数器。

使用思路是:每次有相关操作的时候,就向Redis服务器发送一个incr命令。

例如这样一个场景:我们有一个web应用,我们想记录每个用户每天访问这个网站的次数。

web应用只需要通过拼接用户id和代表当前时间的字符串作为key,每次用户访问这个页面的时候对这个key执行一下incr命令。

这个场景可以有很多种扩展方法:

  • 通过结合使用incr和expire命令,可以实现一个只记录用户在指定间隔时间内的访问次数的计数器
  • 客户端可以通过 getset 命令获取当前计数器的值并且重置为0
  • 通过类似于 decr 或者 incrby 等原子递增/递减的命令,可以根据用户的操作来增加或者减少某些值 比如在线游戏,需要对用户的游戏分数进行实时控制,分数可能增加也可能减少。

实例:限速器

限速器是一种可以限制某些操作执行速率的特殊场景。

传统的例子就是限制某个公共api的请求数目。

假设我们要解决如下问题:限制某个api每秒每个ip的请求次数不超过10次。

我们可以通过incr命令来实现两种方法解决这个问题。

实例:限速器1

更加简单和直接的实现如下:

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每秒生成一个可以记录请求数的计数器。

但是这些计数器每次递增的时候都设置了10秒的过期时间,这样在进入下一秒之后,redis会自动删除前一秒的计数器。

注意上面伪代码中我们用到了 multi 和 exec 命令,将递增操作和设置过期时间的操作放在了一个事务中, 从而保证了两个操作的原子性。

实例: 限速器 2

另外一个实现是对每个ip只用一个单独的计数器(不是每秒生成一个),但是需要注意避免竟态条件。 我们会对多种不同的变量进行测试。

function limit_api_call(ip):
current = get(ip)
if current != null and current > 10 then
   error "too many requests per secord"
else
   value = incr(ip)
   if value == 1 then
      expire(value,1)
   end
   perform_api_call()
end

上述方法的思路是,从第一个请求开始设置过期时间为1秒。如果1秒内请求数超过了10个,那么会抛异常。

否则,计数器会清零。

上述代码中,可能会进入竞态条件,比如客户端在执行INCR之后,没有成功设置EXPIRE时间。这个ip的key 会造成内存泄漏,直到下次有同一个ip发送相同的请求过来。

把上述incr和expire命令写在lua脚本并执行eval命令可以避免上述问题(只有redis版本>=2.6才可以使用)

local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
    redis.call("expire",KEYS[1],1)
end

还可以通过使用redis的list来解决上述问题避免进入竞态条件。

实现代码更加复杂并且利用了一些redis的新的feature,可以记录当前请求的客户端ip地址。这个有没有好处 取决于应用程序本身。

function limit_api_call(ip):
cuurent = llen(ip)
if current > 10 then
   error "too many requests per secord"
else
   if exists(ip) == false
      multi 
           rpush(ip,ip)
           expire(ip,1)
      exec
   else
      rpushx(ip,ip)
   end
   perform_api_call()
end

The rpushx command only pushes the element if the key already exists.

rpushx命令会往list中插入一个元素,如果key存在的话

上述实现也可能会出现竞态,比如我们在执行exists指令之后返回了false,但是另外一个客户端创建了这个key。

(5)、decr

        对key对应的数字做减1操作。

        如果key不存在,那么在操作之前,这个key对应的值会被置为0。

        如果key有一个错误类型的value或者是一个不能表示成数字的字符串,就返回错误。

        这个操作最大支持在64位有符号的整型数字。

        语法:decr key

        返回值:(int类型 执行递减操作后key对应的值。

例子:

redis> set mykey "10"
OK
redis> decr mykey
(integer) 9
redis> SET mykey "234293482390480948029348230948"
OK
redis> decr mykey
ERR value is not an integer or out of range
redis> 

(6)、decrby 

        将key对应的数字减decrement。如果key不存在,操作之前,key就会被置为0。如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。这个操作最多支持64位有符号的正型数字。

        语法:decrby key decrement(decrement为需要减多少的数值)

        返回值:(int类型 减少之后的value值。)

例子:

redis> set mykey "10"
OK
redis> decrby mykey 5
(integer) 5
redis> 

(7)、get 

        返回key的value。如果key不存在,返回特殊值nil。如果key的value不是string,就返回错误,因为get命令只处理string类型的 value。

        语法:get key

        返回值:(key对应的value,或者nil(key不存在时)

例子:

redis> get nonexisting
(nil)
redis> set mykey "Hello"
OK
redis> get mykey
"Hello"
redis> 

(8)、getbit

       返回key对应的string在offset处的bit值 当offset超出了字符串长度的时候,这个字符串就被假定为由0比特填充的连续空间。当key不存在的时候,它就认为是一个空字符串,所以offset总是超出范围,然后value也被认为是由0比特填充的连续空间。到内存分配。

       语法:getbit key offset

       返回值:(int类型 在offset处的bit值

例子:

redis> setbit mykey 7 1
(integer) 0
redis> getbit mykey 0
(integer) 0
redis> getbit mykey 7
(integer) 1
redis> getbit mykey 100
(integer) 0
redis> 

(8)、 getrange

       返回key对应的字符串value的子串,这个子串是由start和end位移决定的(两者都在string内)。可以用负的位移来表示从string尾部开始数的下标。所以-1就是最后一个字符,-2就是倒数第二个,以此类推。

       这个函数处理超出范围的请求时,都把结果限制在string内。

       :getrange key start end

       返回值:(string语法类型)

例子

redis> set mykey "This is a string"
OK
redis> getrange mykey 0 3
"This"
redis> getrange mykey -3 -1
"ing"
redis> getrange mykey 0 -1
"This is a string"
redis> getrange mykey 10 100
"string"
redis> 

(9)、incrby

       将key对应的数字加increment。如果key不存在,操作之前,key就会被置为0。如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。这个操作最多支持64位有符号的正型数字。

       语法:incrby key increment

       返回值:(int类型 增加之后的value值。)

例子:

redis> set mykey "10"
OK
redis> incrby mykey 5
(integer) 15
redis> 

(10)、incrbyfloat

通过指定浮点数key来增长浮点数(存放于string中)的值. 当键不存在时,先将其值设为0再操作.下面任一情况都会返回错误:

  • key 包含非法值(不是一个string).
  • 当前的key或者相加后的值不能解析为一个双精度的浮点值.(超出精度范围了)

如果操作命令成功, 相加后的值将替换原值存储在对应的键值上, 并以string的类型返回. string中已存的值或者相加参数可以任意选用指数符号,但相加计算的结果会以科学计数法的格式存储. 无论各计算的内部精度如何, 输出精度都固定为小数点后17位.

        语法:incrbyfloat key increment

        返回值:(String类型,当前key增加increment后的值。)

例子:

redis> set mykey 10.50
OK
redis> incrbyfloat mykey 0.1
"10.6"
redis> set mykey 5.0e3
OK
redis> incrbyfloat mykey 2.0e2
"5200"
redis> 

(11)、mget

        返回所有指定的key的value。对于每个不对应string或者不存在的key,都返回特殊值nil。正因为此,这个操作从来不会失败。

        语法:mget key[key...] (key为数组,可以一个或者多个)

        返回值:(list链表结构 指定的key对应的values的list)

例子:

redis> set key1 "Hello"
OK
redis> set key2 "World"
OK
redis> mget key1 key2 nonexisting
1) "Hello"
2) "World"
3) (nil)
redis> 

(12)、mset

        可以设置多个key和value,当使用mset命令设置已经存在value的key时,mset命令会用新的value去取代原来的value,mset是原子的,所以所有给定的keys是一次性set的。

        语法:mset key value[key value ...]

        返回值:总是ok,因为是原子性的,不会失败

例子:

redis> mset key1 "Hello" key2 "World"
OK
redis> get key1
"Hello"
redis> get key2
"World"
redis> 

(13)、msetnx

        可以设置多个key和value,当使用msetnx设置一个已经存在的key时,那么操作是不会成功的。对应给定的keys到他们相应的values上。只要有一个key已经存在,msetnx一个操作都不会执行。 由于这种特性,msetnx可以实现要么所有的操作都成功,要么一个都不执行,这样可以用来设置不同的key,来表示一个唯一的对象的不同字段。msetnx是原子的,所以所有给定的keys是一次性set的。

       语法:msetnx key value[key value...]

       返回值

  • 1 如果所有的key被set
  • 0 如果没有key被set(至少其中有一个key是存在的)

例子

redis> msetnx key1 "Hello" key2 "there"
(integer) 1
redis> msetnx key2 "there" key3 "world"
(integer) 0
redis> mset key1 key2 key3
1) "Hello"
2) "there"
3) (nil)
redis> 

(14)、psetnx

    设置key对应字符串value,并且设置key在给定的seconds时间之后超时过期(毫秒),可以使用pttl命令查看时间

    语法:psetnx key milliseconds value 

    返回值:总是ok

例子

redis> psetnx mykey 1000 "Hello"
OK
redis> pttl mykey
(integer) 999
redis> get mykey
"Hello"
redis> 

(15)、set 

将键set设定为指定的“字符串”值。如果 key 已经保存了一个值,那么这个操作会直接覆盖原来的值,并且忽略原始类型。当set命令执行成功之后,之前设置的过期时间都将失效。

        语法:set key value [ex seconds] [px milliseconds] [xx] [nx] ex

  • exseconds – 设置键key的过期时间,单位时秒
  • px milliseconds – 设置键key的过期时间,单位时毫秒
  • nx– 只有键key不存在的时候才会设置key的值
  • xx– 只有键key存在的时候才会设置key的值

         返回值:如果set命令正常执行那么回返回OK,否则如果加了nx 或者 xx选项,但是没有设置条件。那么会返回nil。

例子

redis> set mykey "Hello"
OK
redis> get mykey
"Hello"
redis> 

猜你喜欢

转载自my.oschina.net/u/3872757/blog/1922776