キーの文字列(データ構造の論文を)のRedis

文字列(文字列)

テキスト、バイナリまたはデジタルデータを保存します。
Redisの最も単純なデータ構造は、(例えば「ハローワールド」など)の両方のテキストを格納することができ、そして(例えば、整数と浮動小数点数3.14 10086など)番号を格納することができる、バイナリデータはまた、(たとえば10010100のために)保存することができます。
これらのタイプのRedisの値は、ユーザーが異なる値に対して異なる処理を行うためにできるように、適切なオペレーティング・コマンドが設けられています。
ここに画像を挿入説明

基本操作

結合文字列の設定値があり、キーの文字列の値を取得し、文字列値の長さを求め、などが挙げられます。

これは、債券の設定値の文字列です。

SETキー値
キー文字列のキー値には成功したOK setコマンドのリターンを表す値、です。

文字列キーのキーがすでに存在する場合、新しい値で古いの元の値を上書きします。複雑さはO(1)です。

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

SETコマンドは、オプションのNXオプションとXXオプションをサポートしています。
•NXオプションを指定した場合は、コマンドキー、キーが存在しない場合にのみ、動作のみを設定するには、キーのキーがすでに存在する場合は、SETコマンドではありません... NX動きを行う(古い値を上書きしません)。
キーキーが既にだけで操作を設定するには、存在する場合にのみオプション、コマンド指定した場合はXX•;アクションを行うためのキーキーが存在しない場合は、SET ... XXコマンド(古い値を上書きします)。
セットアップが失敗したときに、セットアップが成功し、与えられたオプションおよびNX XXオプション、SETコマンドが返すのOKの場合は、nilを返します。

 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
文字列の値を取得します。

GETキー
、キーの文字列値に格納されてリターンキー。複雑さはO(1)です。

 redis> SET msg "hello world"
 OK
 redis> GET msg
 hello world
 redis> SET number 10086
 OK
 redis> GET number
 10086
例:Redisのキャッシュを使用します

我々は、プログラムが猛スピードでコンテンツを取得することができます(つまり、メモリ内の、ある)のRedisの内部にコンテンツにより、一般的に使用されるものの一部をキャッシュするか、リソースを大量に消費するコンテンツを必要とするのRedisを使用することができます。たとえば、サイトのためのより多くの時間のかかるページが頻繁にアクセスされた場合、またはページのリソースを作成します(このようなデータベースを複数回アクセスする必要として、長い時間を生成する、など)、我々は使用することができますキャッシュされたこのページではRedisを、サイトの遅延値を下げる、サイト上の負担を軽減します。

 @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 # 返回页面
キャッシュAPIプログラムとその実装

ここに画像を挿入説明
特定のキャッシュプログラムを実装し、cache.pyを参照してください。
キャッシュ後の失敗の時間に応じて自動的に達成されるであろう。

//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)
唯一のキーが存在しない場合に提供

SETNXキー値は
キーのみがキーに存在しない場合には、キーのキー値は値、効果及びSETキー値NX通りです。「が存在しない」(存在しない)という意味NX、。キーが存在しないと、設定が正常に行われ、コマンドが返す1;結果セットがコマンド0を返し失敗した場合ので、キーがすでに存在しています。複雑さは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!
同時に、文字列やキーの設定値を複数取得

ここに画像を挿入説明

例:設定情報や個人情報を取得します

很多网站都会给你一个地方,填写自己的个人信息、联系信息、个人简介等等,比如右图就是某个网站上的个人信息 设置页面。通过将每项信息储存在一个字符串键里面(比如电子邮件在 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 …
ここに画像を挿入説明

键的命名

因为 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

ここに画像を挿入説明

返回值的长度

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

ここに画像を挿入説明

索引和范围

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

索引

字符串的索引(index)以 0 为开始,从字符串的开头向字符串的结尾依次递增,字符串第一个字符的索引为 0 ,字符串最后一个字符的索引 为 N-1 ,其中 N 为字符串的长度。除了(正数)索引之外,字符串 还有负数索引:负数索引以 -1 为开始,从字符串的结尾向字符串的开头依次递减,字符串的最后一个字符的索引 为 -N ,其中 N 为字符串的长度。
ここに画像を挿入説明

范围设置

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

ここに画像を挿入説明

范围取值

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

ここに画像を挿入説明

数字操作

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

设置和获取数字

只要储存在字符串键里面的值可以被解释为 64 位整数,或者 IEEE-754 标准的 64 位浮点数,那么用户就可以对这个字符串键执行针对数字值的命令。
ここに画像を挿入説明

增加或者减少数字的值

对于一个保存着数字的字符串 键 key ,我们可以使用 INCRBY 命令来增加它的值,或者使用 DECRBY 命令来减少它的值。
ここに画像を挿入説明
如果执行 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 命令。
ここに画像を挿入説明

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

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

计数器 API 及其实现

ここに画像を挿入説明

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 ,以此类推。
ここに画像を挿入説明

id 生成器 API 及其实现

ここに画像を挿入説明

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 开始的。但是和储存文字时,索引从左到右依次递增不同,当字符串键储存的是二进制位时,二进制位的索引会从左到右依次递减。ここに画像を挿入説明

设置二进制位的值

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

redis> SETBIT bits 2 1
 (integer) 0

ここに画像を挿入説明

获取二进制位的值

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

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

ここに画像を挿入説明

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

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

BITCOUNT 示例

ここに画像を挿入説明

带有 start 和 end 参数的BITCOUNT 示例

ここに画像を挿入説明

二进制位运算

BITOP operation destkey key [key …]
对一个或多个保存二进制位的字符串键执行位元操作,并将结果保存到 destkey 上。operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种:
ここに画像を挿入説明
除了 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
示例:实现在线人数统计

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

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

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

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

ここに画像を挿入説明

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 来实现。

照片分享网站/拍照应用

ここに画像を挿入説明

示例:使用 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 不适用于中文

注意事项

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

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

ここに画像を挿入説明

SETRANGE 和 GETRANGE 示例

SETRANGEとGETRANGE状況も似ています:インデックスがオーケストレーションに使用するコマンドに基づいており、両方のためのコールSETRANGEまたはGETRANGEが中国を処理するので、私たちが望む結果を得ることができない、文字の代わりにバイト。

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

ここに画像を挿入説明

結論

中国に対処するSTRLEN、SETRANGEとGETRANGEを使用しないでください。
例外:あなたが中国を知りたい場合は、STRLENを使用することができますどのように多くのバイトを、含まれて保存されます。

レビュー

各文字列は、テキスト、整数、浮動小数点、またはバイナリデータとすることができるキー値を保存することができます。
中国に対処するSTRLEN、SETRANGEとGETRANGEを使用しないでください。
ここに画像を挿入説明

217元記事公開 ・は 125個のように勝っ ビュー10000 +を

おすすめ

転載: blog.csdn.net/qq_39885372/article/details/104231131