2.Redis数据库基础数据类型介绍与使用

2022-09-28 17:18:48 浏览数 (1)

[TOC]

0x01 Redis 数据类型

描述: Redis常见五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(Sorted set 有序集合)

其还支持其它类型例如Bit arrays (or simply bitmaps)(处理位数组一样处理字符串值)、HyperLogLogs(概率数据)、Streams类型(抽象日志)

参考地址: https://redis.io/topics/data-types-intro

Key 管理

描述:键命令用于管理 redis 的键

代码语言:javascript复制
> keys p*  #查找所有符合给定模式 pattern 的 key 
1) "python"
2) "php"

> set test redis #设置键
> del test  #删除键
(integer) 1

> type php     #返回 key 所储存的值的类型
string
> exists test  #检查给定 key 是否存在。
(integer) 1
> dump test  #序列化给定 key,返回序列化之后的值
"x00x05redistx00x15xa2xf8=xb6xa9xdex90"
> MOVE test 1   #将 test移动到数据库 1 中(select 1)
(integer) 1


> expire python 600  #expire test1 60 #设置 key 的过期时间,key 过期后将不再可用。单位以秒计
(integer) 1
> PTTL python  # 以毫秒为单位返回 key 的剩余的过期时间
(integer) 592072
> TTL python   # 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)
(integer) 583
> EXPIREAT runoobkey 1293840000  #以 UNIX 时间戳(unix timestamp)格式设置 key 的过期时间
(integer) 1
> pexpire test 6000  #设置key过期毫秒数
(integer) 1
> PEXPIREAT runoobkey 1555555555005  #unix类型毫秒数
(integer) 1
> persist test   #移除 key 的生存时间


> randomkey            # 从当前数据库中随机返回一个 key "php"
> rename key python    # 修改 key 的名称 OK 如果存储则覆盖
> renamenx key newksy  # 仅当 newkey 不存在时,将 key 改名为 newkey 。存在则返回0
(integer) 1

String (字符串)

描述: String 是 redis 最基本的类型(是二进制安全的) 且字符串类型的值最大能存储 512MB 。

基于语法:

代码语言:javascript复制
(1)SET key value    # 设置指定 key 的值
(2)GET key          # 获取指定 key 的值。
(3)GETRANGE key start end   # 返回 key 中字符串值的子字符
(4)GETSET key value         # 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
(5)GETBIT key offset        # 对 key 所储存的字符串值,获取指定偏移量上的位(bit)。
(6)MGET key1 [key2..]      # 获取所有(一个或多个)给定 key 的值。
(7)SETBIT key offset value  # 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。
(8)SETEX key seconds value  # 将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。
(9)SETNX key value             # 只有在 key 不存在时设置 key 的值。
(10)SETRANGE key offset value  # 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。
(11)STRLEN key  # 返回 key 所储存的字符串值的长度。
(12)MSET key value [key value ...]    # 同时设置一个或多个 key-value 对。
(13)MSETNX key value [key value ...]  # 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
(14)PSETEX key milliseconds value     # 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。
(15)INCR key    # 将 key 中储存的数字值增一。
(16)INCRBY key increment  # 将 key 所储存的值加上给定的增量值(increment) 。
(17)INCRBYFLOAT key increment  # 将 key 所储存的值加上给定的浮点增量值(increment) 。
(18)DECR key    # 将 key 中储存的数字值减一。
(19)DECRBY key decrement  # key 所储存的值减去给定的减量值(decrement) 。
(20)APPEND key value      # 如果 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。

使用示例:

代码语言:javascript复制
###### 字符串基础 #######
> SET name "runoob"
> GET name   # "runoob"
> type name  # string

> set test stringdemo  # ok
> getrange test 0 4    # "strin" 截取字符串
> strlen test          # 该键得value字符串长度(integer) 10
> getset stringdemo stringdemo  # "test" 获取值然后再设置键值

> setex demo 15 "value15seconds"     # OK 设置键值15s后销毁  #"seconds15"
> psetex key4 10000 "value10seconds" # OK 设置键值10s后销毁  (毫秒)

> setnx demo1 demo1          # (integer) 1 只有在 key 不存在时设置 key 的值。
> setrange demo1 2 weiyigeek # (integer) 11  从0开始偏移插入在2处 "deweiyigeek"
> APPEND key1 valuakey1      # (integer) 15 在key得value后添加valuakey1字符串
> get key1                   # "value1valuakey1"
 
> mset key1 value1 key2 value2  # 设置多个键值 OK
> mget key1 key2                # 获取多个键值
1) "value1"
2) "value2"

> msetnx key2 value2 key3 value3  # 设置多个键值当键不存在则返回1,否则存在不建立键值并返回0

###### 字符串计算 #######
> set key5 1  #OK
> incr key5
(integer) 2
> incr key5
(integer) 3
> incrby key5 3
(integer) 6
> incrbyfloat key5 4.5   #注意浮点类型计算只能在incrbyfloat才行
"10.5"

> set key6 10 #OK
> decr key6
(integer) 9
> decrby key6 5
(integer) 4
> strlen key1
(integer) 15


# getbit/setbit 用于对 key 所储存的字符串值,获取指定设置偏移量上的位(bit)。
# 对不存在的 key 或者不存在的 offset 进行 GETBIT, 返回 0
redis> EXISTS bit
(integer) 0
redis> GETBIT bit 10086
(integer) 0

# 对已存在的 offset 进行 GETBIT
redis> SETBIT bit 10086 1
(integer) 0
redis> GETBIT bit 10086
(integer) 1

Hash (哈希)

描述:hash 是一个键值(key=>value)对集合,是一个string类型的 field 和 value 的映射表,hash 特别适合用于存储对象,每个 hash 可以存储 2^32 -1键值对(40多亿)。

基础语法:

代码语言:javascript复制
(1) HDEL key field1 [field2]         # 删除一个或多个哈希表字段
(2) HEXISTS key field                 # 查看哈希表 key 中,指定的字段是否存在。
(3) HGET key field                    # 获取存储在哈希表中指定字段的值。
(4) HGETALL key                       # 获取在哈希表中指定 key 的所有字段和值
(5) HINCRBY key field increment       # 为哈希表 key 中的指定字段的整数值加上增量 increment 。
(6) HINCRBYFLOAT key field increment  # 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。
(7) HKEYS key    # 获取所有哈希表中的字段
(8) HLEN key     # 获取哈希表中字段的数量
(9) HMGET key field1 [field2]                # 获取所有给定字段的值
(10)HMSET key field1 value1 [field2 value2 ]  # 同时将多个 field-value (域-值)对设置到哈希表 key 中。
(11)HSET key field value    # 将哈希表 key 中的字段 field 的值设为 value 。
(12)HSETNX key field value  # 只有在字段 field 不存在时,设置哈希表字段的值。
(14)HVALS key               # 取哈希表中所有值
(15)HSCAN key cursor [MATCH pattern] [COUNT count]  # 迭代哈希表中的键值对。

使用示例:

代码语言:javascript复制
####### 哈希表基础使用 #######
#Redis HMSET, HGET 命令,HMSET 设置了两个 field=>value 对, HGET 获取对应 field 对应的 value。
> HSET hash1 field1 "hello"  #(integer) 1
> HMSET hash1 field2 "world"  field3 "redis"  #OK
> hkeys hash1     # 获取键里存储的字段
1) "field1"
2) "field2"
3) "field3"
> hlen hash1     # (integer) 3  键里得字段数量

> hexists hash1 field1  # (integer) 1
> hget hash1 field1     # "hello"  查看键里面得字段值
> hmget hash1 field1 field2  #获取在哈希表中指定 key与字段得值
1) "hello"
2) "world"
> HSETNX hash1 field4 "study"  # (integer) 1 不存在则建立存在则报错
> hvals hash1      # 获取哈希表中所有值
1) "hello"
2) "world"
3) "redis"
4) "study"
> hgetall hash1    # 获取在哈希表中指定 key 的所有字段和值
1) "field1"
2) "hello"
3) "field2"
4) "world"
5) "field3"
6) "redis"


> hdel hash1 field4  # 删除hash表键中的字段
(integer) 1
> hdel hash1 field5
(integer) 0


####### 哈希表的计算 #######
> hset hash2 field1 1
(integer) 1
> hincrby hash2 field1 3
(integer) 4
> hincrbyfloat hash2 field1 3
"7"
> hincrbyfloat hash2 field1 3.5
"10.5"

List (列表)

描述: Redis 列表是简单的字符串列表,按照插入顺序排序(类似于栈);你可以添加一个元素到列表的头部(左边)或者尾部(右边)一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

基础语法:

代码语言:javascript复制
(1) BLPOP key1 [key2 ] timeout  #移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
(2) BRPOP key1 [key2 ] timeout  #移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
(3) BRPOPLPUSH source destination timeout  #从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
(4) LINDEX key index #通过索引获取列表中的元素
(5)	LINSERT key BEFORE|AFTER pivot value  #在列表的元素前或者后插入元素
(6) LLEN key #获取列表长度
(7)	LPOP key #移出并获取列表的第一个元素
(8)	LPUSH key value1 [value2]  #将一个或多个值插入到列表头部 (后进排头)
(9) LPUSHX key value  #将一个值插入到已存在的列表头部
(10) LRANGE key start stop #获取列表指定范围内的元素
(11) LREM key count value  #移除列表元素
(12) LSET key index value  #通过索引设置列表元素的值
(13) LTRIM key start stop   #对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
(14) RPOP key #移除列表的最后一个元素,返回值为移除的元素。
(15) RPOPLPUSH source destination  #移除列表的最后一个元素,并将该元素添加到另一个列表并返回
(16) RPUSH key value1 [value2] #在列表中添加一个或多个值 (按前后顺序添加)
(17) RPUSHX key value  #为已存在的列表添加值

基础格式

代码语言:javascript复制
[1]> lpush list1 1 2 3      # 每次从头开始插入元素 向右边移动 头部添加
(integer) 3
[1]> lpushx list1 0
(integer) 4
[1]> llen list1             # 列表长度
(integer) 4

[1]> rpush list2 0 1 2 3 4  # 按顺序插入
(integer) 5
[1]> rpushx list2 5  # 在尾部添加
(integer) 6
[1]> lset list2 5 7  # 将第六的一个元素值改成7 (注意这里索引修改必须是存在)
OK

#下标 0 表示列表的第一个元素,以 1 表示列表的第二个元素,
#使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 
[1]> lrange list1 0  4
1) "0"
2) "3"
3) "2"
4) "1"
[1]> lrange list2 0  5
1) "0"
2) "1"
3) "2"
4) "3"
5) "4"
6) "7"


[1]> lpop list1    # 弹出头部元素
"0"
[1]> rpop list1   # 弹出尾部元素
"1"
[1]> blpop list1 10  #移出并获取列表的第一个元素
1) "list1"
2) "3"
[1]> brpop list1 10 #移出并获取列表的最后一个元素
1) "list1"
2) "2"

[1]> lindex list2 0 # 通过索引获取列表中的元素
"0"
[1]> lindex list2 5
"5"

#从列表中取出最后一个元素,并插入到另外一个列表的头部
redis> BRPOPLPUSH list1 list2 500   # msg非空列表
"hello moto"                        # 弹出元素的值
(3.38s)                             # 等待时长
redis> LLEN list2
(integer) 1
redis> LRANGE list2 0 0
1) "hello moto"
redis> BRPOPLPUSH msg list2 1 #msg为空列表
(nil)
(1.34s)

[1]> rpoplpush list2 list3  #移除列表的最后一个元素,并将该元素添加到另一个列表并返回
"4"

#【重点】在列表的元素前或者后插入元素
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"


#[重点]让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除 。
redis > RPUSH mylist "hello"
(integer) 1
redis > RPUSH mylist "hello"
(integer) 2
redis > RPUSH mylist "foo"
(integer) 3
redis > RPUSH mylist "bar"
(integer) 4
redis > LTRIM mylist 1 -1
OK
redis > LRANGE mylist 0 -1
1) "hello"
2) "foo"
3) "bar"


#[重点]移除列表元素 返回被移除元素的数量。列表不存在时返回 0 。
  # COUNT 的值可以是以下几种:
  # count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
  # count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
  # count = 0 : 移除表中所有与 VALUE 相等的值。
redis> RPUSH mylist "hello"
(integer) 1
redis> RPUSH mylist "hello"
(integer) 2
redis> RPUSH mylist "foo"
(integer) 3
redis> RPUSH mylist "hello"
(integer) 4
redis> LREM mylist -2 "hello"
(integer) 2
redis> LRANGE mylist 0 -1
1) "hello"
2) "foo"

Set (集合)

描述: Set是string类型的无序集合,集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1),集合成员是唯一的所以集合中不能出现重复的数据; 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

基础语法:

代码语言:javascript复制
(1) SADD key member1 [member2] #向集合添加一个或多个成员
(2) SCARD key   #获取集合的成员数(类似于llen)
(3) SDIFF key1 [key2]  #返回给定所有集合的差集
(4) SDIFFSTORE destination key1 [key2]  #返回给定所有集合的差集并存储在 destination 中
(5) SINTER key1 [key2]  #返回给定所有集合的交集
(6) SINTERSTORE destination key1 [key2]  #返回给定所有集合的交集并存储在 destination 中
(7) SISMEMBER key member  #判断 member 元素是否是集合 key 的成员
(8) SMEMBERS key  #返回集合中的所有成员
(9) SMOVE source destination member  #将 member 元素从 source 集合移动到 destination 集合
(10) SPOP key #移除并返回集合中的一个随机元素
(11) SRANDMEMBER key [count]  #返回集合中一个或多个随机数
(12) SREM key member1 [member2] #移除集合中一个或多个成员
(13) SUNION key1 [key2]   #返回所有给定集合的并集
(14) SUNIONSTORE destination key1 [key2]  #所有给定集合的并集存储在 destination 集合中
(15) SSCAN key cursor [MATCH pattern] [COUNT count]  #迭代集合中的元素

基础示例:

代码语言:javascript复制
#sadd key member #添加一个 string 元素到 key 对应的 set 集合中成功返回1,如果元素已经在集合中返回 0,如果 

[2]> sadd set1 1 2 3 4 5 6  #(integer) 添加 6 个元素
[2]> sadd set2 1 3 5 7 9   #(integer) 添加 5 个元素

[2]> scard set1   #集合中元素的个数
(integer) 6
[2]> sismember set1 1  #判断元素是不是在该集合中
(integer) 1
[2]> sismember set1 0
(integer) 0


[2]> sdiff set1 set2  #差集
1) "2"
2) "4"
3) "6"
[2]> sdiffstore set3 set1 set2  # 将set1与set2差集存入set3中 (integer) 3

[2]> sinter set1 set2  #交集
1) "1"
2) "3"
3) "5"
[2]> sinterstore set4 set1 set2 # 将set1与set2交集存入set4中 (integer) 3

[2]> smove set1 set4 2  #将集合set1中的元素为2的移动到set4集合中 (integer) 1
[2]> smembers set4  #显示集合中的元素
1) "1"
2) "2"
3) "3"
4) "5"

[2]> spop set1  #移除并返回集合中的一个随机元素  "4"
[2]> srandmember set1 10  #显示集合中指定的个数的元素值
1) "1"
2) "3"
3) "5"
4) "6"
[2]> srandmember set1 1
1) "6"


[2]> srem set1 1 3     # 删除set1集合中的1和3两个元素  (integer) 2

[2]> sunion set1 set2  # 并集
1) "1"
2) "3"
3) "5"
4) "6"
5) "7"
6) "9"
[2]> sunionstore set5 set1 set2  # (integer) 6 并集 同上

[2]> sscan set5 1 match *   # 迭代集合中的元素
1) "0"
2) 1) "1"
   2) "3"
   3) "5"
   4) "6"
   5) "7"
   6) "9"

Zset (sorted set 有序集合)

描述: Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员,集合是通过哈希表实现的;

  • 不同的是每个元素都会关联一个double类型的分数
  • redis正是通过分数来为集合中的成员进行从小到大的排序
  • zset的成员是唯一的,但分数(score)却可以重复
  • 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

其实在redis sorted sets里面当items内容大于64的时候同时使用了hash和skiplist两种设计实现。这也会为了排序和查找性能做的优化,所以如上可知:

  • 添加和删除都需要修改skiplist,所以复杂度为O(log(n))。
  • 但是如果仅仅是查找元素的话可以直接使用hash,其复杂度为O(1)
  • 其他的range操作复杂度一般为O(log(n))
  • 当然如果是小于64的时候,因为是采用了ziplist的设计,其时间复杂度为O(n)

基础语法:

代码语言:javascript复制
(1) ZADD key score1 member1 [score2 member2]  #向有序集合添加一个或多个成员,或者更新已存在成员的分数
(2) ZCARD key #获取有序集合的成员数
(3) ZCOUNT key min max  #计算在有序集合中指定区间分数的成员数
(4) ZINCRBY key increment member  #有序集合中对指定成员的分数加上增量 increment
(5) ZINTERSTORE destination numkeys key [key ...]  #计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
(6) ZLEXCOUNT key min max  #在有序集合中计算指定字典区间内成员数量
(7) ZRANGE key start stop [WITHSCORES]  #通过索引区间返回有序集合成指定区间内的成员,[分数从低到高]
(8) ZRANGEBYLEX key min max [LIMIT offset count]  #通过字典区间返回有序集合的成员
(9) ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT]  #通过分数返回有序集合指定区间内的成员
(10)ZRANK key member  #返回有序集合中指定成员的索引
(11)ZREM key member [member ...]  #移除有序集合中的一个或多个成员
(12)ZREMRANGEBYLEX key min max  #移除有序集合中给定的字典区间的所有成员
(13)ZREMRANGEBYRANK key start stop #移除有序集合中给定的排名区间的所有成员
(14)ZREMRANGEBYSCORE key min max  #移除有序集合中给定的分数区间的所有成员
(15)ZREVRANGE key start stop [WITHSCORES]  #返回有序集中指定区间内的成员,通过索引,[分数从高到底]
(16)ZREVRANGEBYSCORE key max min [WITHSCORES]  #返回有序集中指定分数区间内的成员,分数从高到低排序
(17)ZREVRANK key member #返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
(18)ZSCORE key member  #返回有序集中,成员的分数值
(19)ZUNIONSTORE destination numkeys key [key ...]  #计算给定的一个或多个有序集的并集,并存储在新的 key 中
(20)ZSCAN key cursor [MATCH pattern] [COUNT count]  #迭代有序集合中的元素(包括元素成员和元素分值)

基础示例:

代码语言:javascript复制
#有序集合(重点)
[3]> zadd zset 1 redis 1 python 1 php  # (integer) 3 建立有序集合
[3]> zadd zset 2 java 2 javascript 2 node.js  #继续添加有序集合
(integer) 3
[3]> zcard zset    #获取有序集合的成员数  (integer) 6
[3]> zcount zset 0 2  #计算在有序集合中指定区间分数的成员数 (integer) 6

[3]> zincrby zset 1 redis  # increment "2" 有序集合中对指定成员的分数加上增量
[3]> zrank zset redis      #(integer) 5  返回有序集合中指定成员的索引

[3]> zincrby zset 7 redis  #redis元素分数继续 7  "9"
[3]> zlexcount zset 0 6   #这时有序集合分数区间只有 (integer) 5

[3]> zrange zset 0 -1 withscores #显示有序列表中的所有元素 withscores(显示分数)
 1) "php"
 2) "1"
 3) "python"
 4) "1"
 5) "java"
 6) "2"
 7) "javascript"
 8) "2"
 9) "node.js"
10) "2"
11) "redis"
12) "9"

[3]> zrangebyscore zset 0 2  #通过分数返回有序集合指定区间内的成员
1) "php"
2) "python"
3) "java"
4) "javascript"
5) "node.js"
[3]> zrangebyscore zset 9 9
1) "redis"

[3]> zadd zset 0 php 1 php2 2 php3 3 php4  #添加有序集合元素(integer) 4
[3]> zrevrange zset 0 5 withscores  ##返回有序集中指定区间内的成员,通过索引,分数从高到底 
 1) "php4"
 2) "3"
 3) "php3"
 4) "2"
 5) "node.js"
 6) "2"
 7) "php2"
 8) "1"
 9) "php"
10) "0"
[3]> zrevrangebyscore zset 0 -1 #返回有序集中指定分数区间内的成员,分数从高到低排序
1) "php"

[3]> zrevrank zset php  #返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序
(integer) 4
[4]> zscore zset node.js #返回有序集中,成员的分数值
"2"

[3]> zrem zset redis  #删除有序列表中指定元素 返回1  (integer) 1
[3]> zrem zset redis  #不存在列表中返回0 (integer) 0

[3]> zremrangebyrank zset 0 1  #移除有序集合中给定的字典区间的所有成员 (integer) 2
[3]> zremrangebyrank zset 0 1  #(integer) 2 后面的成员回向前面补齐


""""
# Zlexcount 命令在计算有序集合中指定字典区间内成员数量
""""
redis > ZADD myzset 0 a 0 b 0 c 0 d 0 e
(integer) 5
redis > ZADD myzset 0 f 0 g
(integer) 2
redis > ZLEXCOUNT myzset -  
(integer) 7
redis > ZLEXCOUNT myzset [b [f
(integer) 5

""""
# [重点]:通过字典区间返回有序集合的成员。 
""""
redis > ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g
(integer) 7
redis > ZRANGEBYLEX myzset - [c
1) "a"
2) "b"
3) "c"
redis > ZRANGEBYLEX myzset - (c
1) "a"
2) "b"
redis > ZRANGEBYLEX myzset [aaa (g
1) "b"
2) "c"
3) "d"
4) "e"
5) "f"

"""
# [重点]:给定的一个或多个有序集的交集,其中给定 key 的数量必须以 numkeys 参数指定,并将该交集(结果集)储存到 destination 。 
"""
# 有序集 mid_test
redis > ZADD mid_test 70 "Li Lei"
(integer) 1
redis > ZADD mid_test 70 "Han Meimei"
(integer) 1
redis > ZADD mid_test 99.5 "Tom"
(integer) 1

# 另一个有序集 fin_test
redis > ZADD fin_test 88 "Li Lei"
(integer) 1
redis > ZADD fin_test 75 "Han Meimei"
(integer) 1
redis > ZADD fin_test 99.5 "Tom"
(integer) 1

redis > ZINTERSTORE sum_point 2 mid_test fin_test # 交集 (integer) 3
# 显示有序集内所有成员及其分数值
redis > ZRANGE sum_point 0 -1 WITHSCORES     
1) "Han Meimei"
2) "145"
3) "Li Lei"
4) "158"
5) "Tom"
6) "199"

HyperLogLog (基数统计)

描述: 在2.8.9版本添加了HyperLogLog结构是是用来做基数统计的算法;

Q:什么是基数? 答:比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5;基数估计就是在误差可接受的范围内,快速计算基数。

HyperLogLog 的优点:

  • 输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
  • 每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数
  • 根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素

基础语法:

代码语言:javascript复制
1) PFADD key element [element ...]  # 添加指定元素到 HyperLogLog 中。
2) PFCOUNT key [key ...]              #  返回给定 HyperLogLog 的基数估算值。
3) PFMERGE destkey sourcekey [sourcekey ...]  #将多个 HyperLogLog 合并为一个 HyperLogLog

基础示例:

代码语言:javascript复制
[5]> pfadd hyper "redis" "mongodb" "mysql" "python"
(integer) 1
[5]> pfadd log "java" "javascript" "web"
(integer) 1
[5]> pfcount hyper log  #基数估算值 
(integer) 7
[5]> pfcount hyper   #基数估算值 
(integer) 4 
[5]> pfmerge descLog hyper log   #将两个keys进行合并到descLog
OK
[5]> pfcount descLog
(integer) 7

0x02 Redis 进阶学习

1.选择数据库号

描述: Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念。

Tips: Redis是一个字典结构的存储服务器,而实际上一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。

这与我们熟知的在一个关系数据库实例中可以创建多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库。

每个数据库对外都是一个从0..15(默认16个)开始的递增数字命名,

Redis默认支持16个数据库(可以通过配置文件支持更多无上限), 可以通过配置databases来修改这一数字。

代码语言:javascript复制
databases 16

客户端与Redis建立连接后会自动选择0号数据库,当然我们可以指定库连接。

代码语言:javascript复制
redis-cli -n 1
127.0.0.1:6379[1]> ping

可以随时使用SELECT命令更换数据库,

代码语言:javascript复制
redis> SELECT 1  # 选择1号数据库
OK
redis [1] > GET foo
(nil)

补充说明: 描述:然而这些以数字命名的数据库又与我们理解的数据库有所区别

  • 首先Redis不支持自定义数据库的名字,每个数据库都以编号命名,开发者必须自己记录哪些数据库存储了哪些数据。
  • 另外Redis也不支持为每个数据库设置不同的访问密码,所以一个客户端要么可以访问全部数据库,要么连一个数据库也没有权限访问。
  • 最重要的一点是多个数据库之间并不是完全隔离的,比如FLUSHALL命令可以清空一个Redis实例中所有数据库中的数据。

综上所述 redis 数据库 更像是一种命名空间,而不适宜存储不同应用程序的数据。

比如:可以使用0号数据库存储某个应用生产环境中的数据,使用1号数据库存储测试环境中的数据,但不适宜使用0号数据库存储A应用的数据而使用1号数据库B应用的数据,不同的应用应该使用不同的Redis实例存储数据

2.Redis 发布订阅

Q: 什么是pub/sub? 答:Pub/Sub功能(means Publish, Subscribe)即发布及订阅功能,基于事件的系统中,Pub/Sub是目前广泛使用的通信模型,它采用事件作为基本的通信机制,提供大规模系统所要求的松散耦合的交互模式;

Tips: 同样 Redis 的 pub/sub 是一种消息通信模式,主要的目的是解除消息发布者和消息订阅者之间的耦合,Redis 作为一个 pub/sub的 server, 在订阅者和发布者之间起到了消息路由的功能。

描述: 类似于MQTT协议一样,Redis也支持发布订阅(pub/sub)消息通信模式(发送者(pub)发送消息,订阅者(sub)接收消息),Redis客户端可以订阅任意数量的频道

WeiyiGeek.redis发布订阅

基础语法:

  • (1) PSUBSCRIBE pattern [pattern …] #订阅一个或多个符合给定模式的频道。(可以采用通配符)
  • (5) SUBSCRIBE channel [channel …] #订阅给定的一个或多个频道的信息。
  • (2) PUBSUB subcommand [argument [argument …]] #查看订阅与发布系统状态。
  • (3) PUBLISH channel message #将信息发送到指定的频道。
  • (6) UNSUBSCRIBE [channel [channel …]] #指退订给定的频道。
  • (4) PUNSUBSCRIBE [pattern [pattern …]] #退订所有给定模式的频道。

基础实例:

代码语言:javascript复制
# 终端1 (订阅频道)
[6]> subscribe redisChat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
1) "message"
2) "redisChat"
3) "Redis pub/sub"
1) "message"
2) "redisChat"
3) "Hello World!MQTT"

# 终端2 (发布频道)
192.168.1.100:6379> publish redisChat "Redis pub/sub"
(integer) 1
192.168.1.100:6379> publish redisChat "Hello World!MQTT"
(integer) 1
192.168.1.100:6379> publish redisChat "Hello World!Redis Channel"
(integer) 1

192.168.1.100:6379> pubsub CHANNELS redis* #命令用于查看订阅与发布系统状态
1) "redisChat"

# 终端3 (订阅频道)
[6]> psubscribe redis*  #通配符(匹配redis以及redis字符串拼接的发布频道)
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redis*"
3) (integer) 1
"Hello World!Redis  Channel"

3.Redis 事物处理

描述: 事务可以一次执行多个命令, 并且带有以下两个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

  • 1) 开始事务
  • 2) 命令入队
  • 3) 执行事务

以下命令是实现事务的基石:

代码语言:javascript复制
(1)MULTI    # 标记一个事务块的开始 == `BEGIN TRANSACTION`
(2)EXEC     # 提交执行所有事务块内的命令 == `COMMIT`
(3)DISCARD  # 取消事务,放弃执行事务块内的所有命令 == `ROLLBACK`
(4)WATCH key [key ...] # 监视一个(或多个) key,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断
(5)UNWATCH 取消 WATCH 命令对所有 key 的监视。

Redis事务特征:

  • 1.在事务执行中所有命令都将会被串行化的顺序执行(效率低但是由于操作的是内存所以忽略不计),并且在该事务执行期间Redis不会再为其它客户端的请求提供任何服务(保证了事务中的所有命令被原子执行);
  • 2.在事务执行失败后其后面的命令任然会继续执行,此点与关系型数据库事务相比有些许不同
  • 3.在事务执行中命令MULTI可以看做关系型数据库中BEGIN TRANSACTION,而EXEC与DISCARD命令来提交/回滚事务内的所有操作等同于COMMIT 与 ROLLBACK 语句;
  • 4.在事务执行过程中如果Client与Server出现通讯故障并导致网络断开,其后所执行的语句将不会被服务器指向,但是如果网络中断事件发生在EXEC命令之后则任然执行;
  • 5.在事务执行中使用Append-Only模式此时Redis会调用系统函数write将该事务内的所有写操作在本次调用中全部写入硬盘;但是如果在写入过程中出现系统崩溃导致数据写不完整,此时在Redis重启后会进行一致性检测如果发现问题将会提示;

PSTips: 当Redis进行错误提示我们可以利用Redis-check-aof工具帮助我们定位到数据不一致的错误,并将已经写入部分数据进行回滚,之后重启数据库即可;

示例1.事务处理提交

代码语言:javascript复制
# 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令
192.168.1.100:6379[7]> multi  #开启事务
192.168.1.100:6379[7]> set book-name "redis 入门到放弃"
QUEUED
192.168.1.100:6379[7]> get book-name
QUEUED
192.168.1.100:6379[7]> sadd tag "redis" "数据库" "基础入门"
QUEUED
192.168.1.100:6379[7]> smembers tag
QUEUED
192.168.1.100:6379[7]> exec  #触发事务(将建立的k-v写入)
1) OK
2) "redis xe5x85xa5xe9x97xa8xe5x88xb0xe6x94xbexe5xbcx83"
3) (integer) 3
4) 1) "xe6x95xb0xe6x8dxaexe5xbax93"
   2) "redis"
   3) "xe5x9fxbaxe7xa1x80xe5x85xa5xe9x97xa8xefxbcxe2x80"

示例2.事务处理回滚

代码语言:javascript复制
> SELECT 1
OK
[1]> set name WeiyiGeek
OK
[1]> get name
"WeiyiGeek"
[1]> MULTI
OK 
[1]> set name Redis  #在事务中重新设置name键值
QUEUED
[1]> get name
QUEUED
[1]> DISCARD  #不提交事务即回滚
OK
[1]> get name  #显示事务开启前的name键值
"WeiyiGeek"

示例3.失败命令自动剔除执行案例

代码语言:javascript复制
> set num 10
OK
> get num
"10"
> MULTI
OK
> INCRBY num 5
QUEUED
> INCRBY num x  #异常加值
QUEUED
> INCRBY num 5  # 15   5
QUEUED
> exec
1) (integer) 15
2) (error) ERR value is not an integer or out of range  #当事务提交后执行所有操作当操作异常时候将被忽略;
3) (integer) 20
> get num
"20"

0x03 使用场景

描述:复习Redis的五种数据类型:String、List、Hash、Set、Sorted-set, 前面我们简单的了解了一下Redis各个使用操作,这一部分我们主要来说说Redis的应用场景;

List 类型

(1) 取最新N个数据操作 描述:比如一篇文章的评论只显示最新的5000千条评论的ID放在Redis的List集合之中,并将超出集合部分从数据库中获取;

代码语言:javascript复制
# 向List集合中插入值
LPUSH latest.comments ID
# 保存最近5000千个ID值
LTRIM latest.comments 0 5000

(2) 构建队列系统 描述:可以使用list可以构建队列,使用sorted set 甚至可以构建有优先级的队列系统;

Set 类型

(1) Uniq操作获取某段时间所有数据排重置 描述:将输入放入set集合即可,所有的数据将会自动排重;

Zset 类型

(1) 排行榜应用TOP N操作 描述:此处需要按照次数进行排序,所以此时我们需要采用sorted set类型,将您需要排序的key名称以及其具体数据设置成相应的value,每次只需要执行ZADD命令即可;

(2) 需要精准设定过期时间应用 描述:可以将上面说到的sorted set类型的score值设置成为过期时间的时间戳,就可以通过过期时间排序以及定时清除过期数据;

Incr、Decr

(1) 事物统计 描述:可以统计在线人数由于Redis命令是原子性的,您可以轻松利用INCR与DECR命令来构建计算统计系统

means Publish, Subscribe

(1) 在线聊天室 描述: 可以利用订阅与发布实现一个,类似于在网页中在线聊天程序。

示例演示

代码语言:javascript复制
# List 列表: 可以用来存储用户UID
# hash 哈希:用来存储用户的信息
# set  集合用来查看共同好友(唯一性)

# 字符串 #
> set Weiyi Geek  # OK
> get Weiyi       #"Geek"

# HASH #
> hmset hash love1 "Python" love2 "C  "   #注意键不能同名 OK

# 列表 #
> lpush list redis
(integer) 1
> lpush list python
(integer) 2
> lrange list 0 10
1) "python"
2) "redis"

# 集合 #
> sadd run redis
(integer) 1
> sadd run redis1
(integer) 1
> sadd run redis2
(integer) 1
> smembers run
1) "redis2"
2) "redis1"
3) "redis"

# 有序集合 #
> zadd test 0 redis
(integer) 1
> zadd test 0 python
(integer) 1
> zrangebyscore runoob 0 1000  #当请求没有键不会报错(注意)
(empty list or set)
> zrangebyscore test 0 1000
1) "python"
2) "redis"

WeiyiGeek.数据类型应用场景

0 人点赞