redis key string (data structure papers)

String (string)

Saving text, binary or digital data.
Redis most simple data structure can store both text (such as "hello world"), and can store a number (such as integer and floating-point number 3.14 10086), binary data can also be stored (for example 10010100).
Redis value of these types are provided with appropriate operating commands, allowing users to do different treatment for different values.
Here Insert Picture Description

Basic Operations

Is a bond string set value, obtain the value of the key string, obtaining the length of the string value, and the like.

It is a bond setting value string

SET key value
to the key value of the key string is value, which represents the set command returns OK successful.

If the string key key already exists, overwriting the original value of the old with the new value. Complexity is O (1).

 redis> SET msg "hello world"
 OK
 redis> SET msg "goodbye" # 覆盖原来的值 "hello world"
 OK
SET key value [NX|XX]

SET command also supports an optional NX options and XX option:
• If given NX option, the command only if the key key does not exist, only to set the operation; if the key key already exists, then the SET command is not ... NX do the movements (do not overwrite the old value).
XX • If given the option, the command only if the key key already exists, just to set the operation; key if the key does not exist, SET ... XX command to do the action (will overwrite the old value).
In the case of the given options and NX XX option, SET command returns OK when the setup is successful, returns nil when setup failed.

 redis> SET nx-str "this will fail" XX # 键不存在,指定 XX 选项导致设置失败
 (nil)
 redis> SET nx-str "this will success" NX # 键不存在,所以指定 NX 选项是可行的
 OK
 redis> SET nx-str "this will fail" NX # 键已经存在,指定 NX 选项导致设置失败
 (nil)
 redis> SET nx-str "this will success again!" XX # 键已经存在,指定 XX 选项是可行的
 OK
Gets the value of the string

The GET key
return key stored in the key string value. Complexity is O (1).

 redis> SET msg "hello world"
 OK
 redis> GET msg
 hello world
 redis> SET number 10086
 OK
 redis> GET number
 10086
Example: to use Redis cache

We can use Redis to cache some of the commonly used ones, or require resource-intensive content, by content into Redis inside (that is, memory inside), the program can obtain the content at breakneck speed. For example, for a site, the more time-consuming if a page is frequently accessed, or create page resources (such as the need to access the database multiple times, generating a long time, and so on), then we can use Redis this page cached, reduce the burden on the site, reducing the delay value of the site.

 @app.route("/")
 def index():
 cached_content = cache.get('index') # 尝试从缓存里面获取被缓存的页面
 if cached_content: # 缓存存在,直接返回页面
 return cached_content
 else:
 content = fetch_and_create_index() # 页面没有被缓存,访问数据库并重新生成页面
 cache.put('index', content) # 缓存页面,方便下次取出
 return content # 返回页面
Cache API program and its implementation

Here Insert Picture Description
Specific implement caching program, please refer to cache.py.
After the cache will be achieved automatically according to the time of failure.

//cache.py
# coding: utf-8

class Cache:

    def __init__(self, client):
        self.client = client

    def put(self, name, content):
        self.client.set(name, content)

    def get(self, name):
        return self.client.get(name)
Provided only in the case where the key does not exist

SETNX key value
only in the case where the key does not exist in the keys, the key value of the key is value, which effect and as SET key value NX. NX, meaning "Not eXists" (does not exist). Key does not exist and the setting is successful, the command returns 1; key already exists because when the result set fail command returns 0. Complexity is O (1).

 redis> SETNX new-key "i am a new key!"
 1
 redis> SETNX new-key "another new key here!" # 键已经存在,设置失败
 0
 redis> GET new-key # 键的值没有改变
 i am a new key!
Simultaneously acquiring a plurality of strings or set value of the key

Here Insert Picture Description

Example: set or get personal information

很多网站都会给你一个地方,填写自己的个人信息、联系信息、个人简介等等,比如右图就是某个网站上的个人信息 设置页面。通过将每项信息储存在一个字符串键里面(比如电子邮件在 huangz::email 键、个人网站在 huangz::homepage 键、公司在huangz::company 键,等等),我们可以通过 调用 MSET 来一次性设置多个项,并使用MGET 来一次性获取多个项的信息。MSET huangz::email"[email protected]" huangz::homepage “http://huangz.me/” huangz::company “FakeCompany” huangz::position"Programmer" huangz::location “广东” huangz::sign "time waits for no one"MGET huangz::email huangz::homepage huangz::company huangz::position …
Here Insert Picture Description

键的命名

因为 Redis 的数据库不能出现两个同名的键,所以我们通常会使用 field1::field2::field3 这样的格式来区分同一类型的多个字符串键。 举个例子,像前面储存个人信息例子,因为网站里面不可能只有 huangz 一个用户,所以我们不能用email 键来直接储存 huangz 的邮件地址,而是使用 huangz::email ,这样 huangz 的邮件地址就不会和其他用户的邮件地址发生冲突 —— 比如用户名为 peter 的用户可以将它的邮件地址储存到peter::email 键,而用户名为 jack 的用户也可以将它的邮件地址储存到 jack::email 键,大家各不相关,互不影响。一些更为复杂的键名例子: user::10086::info ,ID 为 10086 的用户的信息; news::sport::cache ,新闻网站体育分类的缓存; message::123321::content ,ID 为 123321 的消息的内容。:: 是比较常用的分割符,你也可以 选择自己喜欢的其他分割符来命名键,比如斜线 huangz/email 、竖线 huangz|email 、或者面向对象风格的 huangz.email 。

一次设置多个不存在的键

MSETNX key value [key value …]
只有在所有给定键都不存在的情况下, MSETNX 会为所有给定键设置值,效果和同时执行多个SETNX 一样。如果给定的键至少有一个是存在的,那么 MSETNX 将不执行任何设置操作。返回 1 表示设置成功,返回 0 表示设置失败。复杂度为 O(N) , N 为给定的键数量。

redis> MSETNX nx-1 "hello" nx-2 "world" nx-3 "good luck"
 1
 redis> SET ex-key "bad key here"
 OK
 redis> MSETNX nx-4 "apple" nx-5 "banana" ex-key "cherry" nx-6 "durian"
 0

因为 ex-key 键已经存在,所以第二个 MSETNX 会执行失败,所有键都不会被设置。

设置新值并返回旧值

GETSET key new-value
将字符串键的值设置为 new-value ,并返回字符串键在设置新值之前储存的旧值(old value)。
复杂度为 O(1) 。

 redis> SET getset-str "i'm old value" # 先给字符串键设置一个值
 OK
 redis> GETSET getset-str "i'm new value" # 更新字符串键的值,并返回之前储存的旧值
 i'm old value
 redis> GET getset-str # 确认一下,新值已被设置
 i'm new value
用伪代码表示 GETSET 的定义
def GETSET(key, new-value):
 old-value = GET(key) # 记录旧值
 SET(key, new-value) # 设置新值
 return old-value # 返回旧值
追加内容到字符串末尾

APPEND key value
将值 value 推入到字符串键 key 已储存内容的末尾。O(N), 其中 N 为被推入值的长度。

 redis> SET myPhone "nokia"
 OK
 redis> APPEND myPhone "-1110"
 (integer) 10
 redis> GET myPhone
 "nokia-1110

Here Insert Picture Description

返回值的长度

STRLEN key
返回字符串键 key 储存的值的长度。
因为 Redis 会记录每个字符串值的长度,所以获取该值的复杂度为 O(1) 。

 redis> SET msg "hello"
 OK
 redis> STRLEN msg
 (integer) 5
 redis> APPEND msg " world"
 (integer) 11
 redis> STRLEN msg
 (integer) 11

Here Insert Picture Description

索引和范围

字符串的正数索引和负数索引,范围取值,范围设置。

索引

字符串的索引(index)以 0 为开始,从字符串的开头向字符串的结尾依次递增,字符串第一个字符的索引为 0 ,字符串最后一个字符的索引 为 N-1 ,其中 N 为字符串的长度。除了(正数)索引之外,字符串 还有负数索引:负数索引以 -1 为开始,从字符串的结尾向字符串的开头依次递减,字符串的最后一个字符的索引 为 -N ,其中 N 为字符串的长度。
Here Insert Picture Description

范围设置

SETRANGE key index value
从索引 index 开始,用 value 覆写(overwrite)给定键 key 所储存的字符串值。只接受正数索引。命令返回覆写之后,字符串 值的长度。复杂度为 O(N), N 为 value 的长度。

 redis> SET msg "hello"
 OK
 redis> SETRANGE msg 1 "appy"
 (integer) 5
 redis> GET msg
 "happy

Here Insert Picture Description

范围取值

GETRANGE key start end
返回键 key 储存的字符串值中,位于 start 和 end 两个索引之间的内容(闭区间,start 和 end 会被包括在内)。和 SETRANGE 只接受正数索引不同, GETRANGE 的索引可以是正数或者 负数。复杂度为 O(N) , N 为被选中内容的长度。

 redis> SET msg "hello world"
 OK
 redis> GETRANGE msg 0 4
 "hello"
 redis> GETRANGE msg -5 -1
 "world

Here Insert Picture Description

数字操作

增加或者减少字符串键储存的数字值。

设置和获取数字

只要储存在字符串键里面的值可以被解释为 64 位整数,或者 IEEE-754 标准的 64 位浮点数,那么用户就可以对这个字符串键执行针对数字值的命令。
Here Insert Picture Description

增加或者减少数字的值

对于一个保存着数字的字符串 键 key ,我们可以使用 INCRBY 命令来增加它的值,或者使用 DECRBY 命令来减少它的值。
Here Insert Picture Description
如果执行 INCRBY 或者 DECRBY 时,键 key 不存在,那么命令会将 键 key 的 值初始化为 0 ,然后再执行增加或者减少操作。

INCRBY / DECRBY 示例
 redis> INCRBY num 100 # 键 num 不存在,命令先将 num 的值初始化为 0 ,
 (integer) 100 # 然后再执行加 100 操作
redis> INCRBY num 25 # 将值再加上 25
(integer) 125
redis> DECRBY num 10 # 将值减少 10
(integer) 115
redis> DECRBY num 50 # 将值减少 50
(integer) 65
增一和减一

因为针对数字值的增一和减一操作非常常 见,所以 Redis 特别为这两个操作创建了 INCR 命令和DECR 命令。
Here Insert Picture Description

redis> SET num 10
OK
redis> INCR num
(integer) 11
redis> DECR num
(integer) 10
示例:计数器(counter)

Here Insert Picture Description
很多网站都使用了计数器来记录页面被访问的次数。
每当用户访问页面时,程序首先将页面访问计数器的值增一,然后将计数器当前的值返回给用户观看,以便用户通过页面的访问次数来判断页面内容的受关注程度。使用字符串键以及 INCR 、GET 等命令,我们也可以实现这样的计数器。

计数器 API 及其实现

Here Insert Picture Description

c = Counter('page-counter', redis_client) # 创建一个名为 page-counter 的计数器
c.incr() # => 1
c.incr() # => 2

计数器实现的完整源码请查看 counter.py 文件。

//counter.py
# encoding: utf-8

class Counter:

    def __init__(self, key, client):
        self.key = key
        self.client = client

    def incr(self, n=1):
        counter = self.client.incr(self.key, n)
        return int(counter)

    def decr(self, n=1):
        counter = self.client.decr(self.key, n)
        return int(counter)

    def reset(self, n=0):
        counter = self.client.getset(self.key, n)
        if counter is None:
            counter = 0
        return int(counter)

    def get(self):
        counter = self.client.get(self.key)
        if counter is None:
示例:id 生成器

很多网站在创建新条目的时候,都会使用 id 生成器来为条目创建唯一标识符。
举个例子,对于一个论坛来说,每注册一个新用户,论坛都会为这个新用户创建一个用户 id ,比如12345 ,然后访问 /user/12345 就可以看到这个用户的个人页面。又比如说,当论坛里的用户创建一个新帖子的时候,论坛都会为这个新帖子创建一个帖子 id ,比如10086 ,然后访问 /topic/10086 就可以看到这个帖子的内容。被创建的 id 通常都是连续的,比如说,如果最新创建的 id 为 1003 ,那么下一个生成的 id 就会是1004 ,再下一个 id 就是 1005 ,以此类推。
Here Insert Picture Description

id 生成器 API 及其实现

Here Insert Picture Description

generator = IdGenerator('user-id', redis_client) # 创建一个用户 id 生成器
generator.init(10000) # 保留前一万个 id
generator.gen() # => 10001
generator.gen() # => 10002

id 生成器的源代码可以在 id_generator.py 找到。

//id_generator.py
# coding: utf-8

class IdGenerator:

    def __init__(self, key, client):
        self.key = key
        self.client = client

    def init(self, n):
        self.client.set(self.key, n)

    def gen(self):
        new_id = self.client.incr(self.key)
        return int(new_id)
浮点数的自增和自减

INCRBYFLOAT key increment
为字符串键 key 储存的值加上浮点数增量 increment ,命令返回操作执行之后,键 key 的值。没有相应的 DECRBYFLOAT ,但可以通过给定负值来达到 DECRBYFLOAT 的效果。复杂度为 O(1) 。

 redis> SET num 10
 OK
 redis> INCRBYFLOAT num 3.14
 "13.14"
 redis> INCRBYFLOAT num -2.04 # 通过传递负值来达到做减法的效果
 "11.1"
注意事项

即使字符串键储存的是数字值,它也可以执行 APPEND、STRLEN、SETRANGE 和 GETRANGE 。当用户针对一个数字值执行这些命令的时候,Redis 会先将数字值转换为字符串,然后再执行命令。

 redis> SET number 123
 OK
 redis> STRLEN number # 转换为 "123" ,然后计算这个字符串的长度
 3
 redis > APPEND number 456 # 转换为 "123" ,然后与 "456" 进行拼接
 6
 redis> GET number
 123456

二进制数据操作

设置和获取字符串储存的二进制数据,执行二进制位运算。

设置和获取二进制数据

SET 、GET 、SETNX、 APPEND 等命令同样可以用于设置二进制数据。

# 因为 Redis 自带的客户端 redis-cli 没办法方便的设置二进制数据
# 所以这里使用 Python 客户端来进行
 >>> import redis
 >>> r = redis.Redis()
 >>> r.set('bits', 0b10010100) # 将字符串键 bits 的值设置为二进制 10010100
 True
 >>> bin(int(r.get('bits'))) # 获取字符串键 bits 储存的二进制值(需要进行转换)
 '0b10010100'
 >>> r.append('bits', 0b111) # 将 0b111 (也即是十进制的 7)推入到 bits 已有二进制位的末尾
 4L
 >>> bin(int(r.get('bits'))) # 推入之前的值为 0b10010100 = 148
 '0b10111001111' # 推入之后的值为 0b10111001111 = 1487
二进制位的索引

和储存文字时一样,字符串键在储存二进制位时,索引也是从 0 开始的。但是和储存文字时,索引从左到右依次递增不同,当字符串键储存的是二进制位时,二进制位的索引会从左到右依次递减。Here Insert Picture Description

设置二进制位的值

SETBIT key index value
将给定索引上的二进制位的值设置为 value ,命令返回被设置的位原来储存的旧值。 复杂度为 O(1) 。

redis> SETBIT bits 2 1
 (integer) 0

Here Insert Picture Description

获取二进制位的值

GETBIT key index
返回给定索引上的二进制位的值。 复杂度为 O(1) 。

 redis> GETBIT bits 7
 (integer) 1
 redis> GETBIT bits 6
 (integer) 0
 redis> GETBIT bits 4
 (integer) 1

Here Insert Picture Description

计算值为 1 的二进制位的数量

BITCOUNT key [start] [end]
计算并返回字符串键储存的值中,被设置为 1 的二进制位的数量。
一般情况下,给定的整个字符串键都会进行计数操作,但通过指定额外的 start 或 end 参数,可以让计数只在特定索引范围的位上进行。
start 和 end 参数的设置和 GETRANGE 命令类似,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,以此类推。复杂度为 O(N) ,其中 N 为被计算二进制位的数量。

BITCOUNT 示例

Here Insert Picture Description

带有 start 和 end 参数的BITCOUNT 示例

Here Insert Picture Description

二进制位运算

BITOP operation destkey key [key …]
对一个或多个保存二进制位的字符串键执行位元操作,并将结果保存到 destkey 上。operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种:
Here Insert Picture Description
除了 NOT 操作之外,其他操作都可以接受一个或以上数量的 key 作为输入。复杂度为 O(N) , N 为进行计算的二进制位数量的总和。命令的返回值为计算所得结果的字节长度,相当于对 destkey 执行 STRLEN 。

BITOP 示例

假设现在 b1 键储存了二进制 01001101 ,而 b2 键储存了二进制 10110101 。

redis> BITOP AND b1-and-b2 b1 b2 # b1-and-b2 = 00000101
(integer) 1
redis> BITOP OR b1-or-b2 b1 b2 # b1-or-b2 = 11111101
(integer) 1
redis> BITOP XOR b1-xor-b2 b1 b2 # b1-xor-b2 = 11111000
(integer) 1
redis> BITOP NOT not-b1 b1 # not-b1 = 10110010
(integer) 1
示例:实现在线人数统计

一些网站具备了在线人数统计功能,通过这个功能可以看到一段时间以内(比如这个小时,或者这一天),曾经登录过这个网站的会员人数。
Here Insert Picture Description
某网站的在线人数统计结果,显示目前有 289 个会员在线。 通过使用字符串键以及二进制数据处理命令,我们也可以构建一个高效并且节约内存的在线人数统计实现。

在用户 id 和位索引之间进行关联

之前说过,字符串键储存的每个二进制位都有与之对应的索引,比如对于一个 8 位长的二进制值来说,它的各个二进制位的索引值为 0 至 7 。 因为通常网站的每个会员都有一个自己的数字 id ,比如 peter的 id 可能是 3 ,而 jack 的 id 可能是 5 ,所以我们可以在用户id 和二进制位的索引之间进行关联: • 如果 id 为 N 的用户在线,我们就将索引为 N 的二进制位的值设置为 1 。 • 如果索引为 N 的二进制位的值为 0 ,这表示 id 为 N 用户不在线。 • 使用 BITCOUNT 可以统计有多少个用户在线。 • 通过为每段时间分别储存一个二进制值,我们就可以为每 段时间都记录在线用户的数量。(每小时创建一个键或者每天创建一个键,诸如此类。)
Here Insert Picture Description

在线用户统计的 API 及其实现

Here Insert Picture Description

count = OnlineCount(‘2014-8-3 10a.m.’) # 记录 2014 年 8 月 3 日上午 10 点的在线用户数量
count.include(4) # 将 id 为 4 的用户设置为在线
count.include(5) # 将 id 为 5 的用户设置为在线
count.include(7) # 将 id 为 7 的用户设置为在线
count.result() # 返回 3 ,表示有三个用户在线 

在线用户统计程序的完整实现代码可以在 online_count.py 查看。

//online_count.py
# encoding: utf-8

class OnlineCount:

    def __init__(self, when, client):
        self.when = when
        self.client = client

    def include(self, user_id):
        return self.client.setbit(self.when, user_id, 1)

    def result(self):
        return self.client.bitcount(self.when)
关于用户在线统计的更多信息

目前这个实现的优点:
• 将用户设置为在线的速度非常快, O(1) 。 • 即使用户数量非常大,占用的内存也不多:记录一百万用户仅需一百万位,也即是 0.125MB;记录一千万用户仅需一千万位,也即是 1.25 MB 。 • 可以在现有程序的基础上,做进一步的操作。举个例子,我们可以使用 BITOP AND 命令,将多个在线记录作为输入,计算出全勤用户的数量(全勤指的是,用户在所有输入的在线统计记录中,都显示为在线)。目前这个实现的缺点:
• 每次进行统计的复杂度为 O(N) 。 • 没办法轻易地获取所有在线用户的名单,只能遍历整个二进制值,复杂度为 O(N) ,其中 N 为二进制位数量。
进一步的优化:
• 用户量不大并且需要获取在线用户名单的话,可以使用之后介绍的集合数据结构来实现。
• 不需要获取在线用户名单,并且不需要精确的在线统计数量,
可以使用之后介绍的 HyperLogLog 来实现。

照片分享网站/拍照应用

Here Insert Picture Description

示例:使用 Redis 缓存热门图片

图片网站要储存大量的图片(通常放在硬盘里面),而少部分热门的图片会被经常地访问到。为了加快网站获取热门图片的速度,我们可以利用 Redis 能够储存二进制数据这一特性,使用之前构建的缓存程序来缓存图片网站中的热门图片。

cache = Cache(redis_client) # 设置缓存的客户端
file = open('redis-logo.jpg', 'r') # 打开文件
data = file.read() # 读取文件数据
file.close() # 关闭文件
cache.put('redis-logo', data) # 以 redis-logo 为名字,将图片缓存起来
cache.get('redis-logo') # 取出 redis-logo 图片的数据

储存中文时的注意事项

STRLEN、SETRANGE 和 GETRANGE 不适用于中文

注意事项

一个英文字符只需要使用 单个字节来储存,而一个中文字符却需要使用多个字 节来储存。
Here Insert Picture Description
STRLEN、SETRANGE 和 GETRANGE 都是为英文设置的,它们只会在字符为单个字节的情况下正常工作,而一旦我们储存的是类似中文这样的多字节字符,那么这三个命令就不再适用了。

STRLEN 示例
$ redis-cli --raw # 在 redis-cli 中使用中文时,必须打开 --raw 选项,才能正常显示中文
 redis> SET msg "世界你好" # 设置四个中文字符
 OK
 redis> GET msg # 储存中文没有问题
 世界你好
 redis> STRLEN msg # 这里 STRLEN 显示了“世界你好”的字节长度为 12 字节
 12 # 但我们真正想知道的是 msg 键里面包含多少个字符

Here Insert Picture Description

SETRANGE 和 GETRANGE 示例

SETRANGE and GETRANGE situation is also similar: both because the index is based on the command used to orchestrate bytes instead of characters, so call SETRANGE or GETRANGE to handle Chinese, can not get the results we want.

redis> SET msg "世界你好"
 OK
 redis> GETRANGE msg 2 3 # 试图获取 "你好"
 �
 redis> SETRANGE msg 2 "欢迎你" # 试图构建 "世界欢迎你",其中"欢迎你"为 9 字节长
 12
 redis> GET msg
 ��欢迎你�

Here Insert Picture Description

in conclusion

Do not use STRLEN, SETRANGE and GETRANGE to deal with Chinese.
Exception: If you want to know the Chinese are stored contain how many bytes, you can use STRLEN.

review

Each string may save a key value, which can be text, integer, floating point, or binary data.
Do not use STRLEN, SETRANGE and GETRANGE to deal with Chinese.
Here Insert Picture Description

Published 217 original articles · won 125 Like · views 10000 +

Guess you like

Origin blog.csdn.net/qq_39885372/article/details/104231131