redis第二课-基本操作和redis的数据结构

继上篇搭建完redis之后,现在开始对redis中的基本操作进行讲解。
通过输入

redis-cli -h

可看到:

Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]
  -h <hostname>      Server hostname (default: 127.0.0.1).
  -p <port>          Server port (default: 6379).
  -s <socket>        Server socket (overrides hostname and port).
  -a <password>      Password to use when connecting to the server.
                     You can also use the REDISCLI_AUTH environment
                     variable to pass this password more safely
                     (if both are used, this argument takes predecence).
  -u <uri>           Server URI.
  -r <repeat>        Execute specified command N times.
  -i <interval>      When -r is used, waits <interval> seconds per command.
                     It is possible to specify sub-second times like -i 0.1.
  -n <db>            Database number.
  -x                 Read last argument from STDIN.
  -d <delimiter>     Multi-bulk delimiter in for raw formatting (default: \n).
  -c                 Enable cluster mode (follow -ASK and -MOVED redirections).
  --raw              Use raw formatting for replies (default when STDOUT is
                     not a tty).
  --no-raw           Force formatted output even when STDOUT is not a tty.
  --csv              Output in CSV format.
  --stat             Print rolling stats about server: mem, clients, ...
  --latency          Enter a special mode continuously sampling latency.
                     If you use this mode in an interactive session it runs
                     forever displaying real-time stats. Otherwise if --raw or
                     --csv is specified, or if you redirect the output to a non
                     TTY, it samples the latency for 1 second (you can use
                     -i to change the interval), then produces a single output
                     and exits.
  --latency-history  Like --latency but tracking latency changes over time.
                     Default time interval is 15 sec. Change it using -i.
  --latency-dist     Shows latency as a spectrum, requires xterm 256 colors.
                     Default time interval is 1 sec. Change it using -i.
  --lru-test <keys>  Simulate a cache workload with an 80-20 distribution.
  --replica          Simulate a replica showing commands received from the master.
  --rdb <filename>   Transfer an RDB dump from remote server to local file.
  --pipe             Transfer raw Redis protocol from stdin to server.
  --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.
                     no reply is received within <n> seconds.
                     Default timeout: 30. Use 0 to wait forever.
  --bigkeys          Sample Redis keys looking for keys with many elements (complexity).
  --memkeys          Sample Redis keys looking for keys consuming a lot of memory.
  --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.
                     And define number of key elements to sample
  --hotkeys          Sample Redis keys looking for hot keys.
                     only works when maxmemory-policy is *lfu.
  --scan             List all keys using the SCAN command.
  --pattern <pat>    Useful with --scan to specify a SCAN pattern.
  --intrinsic-latency <sec> Run a test to measure intrinsic system latency.
                     The test will run for the specified amount of seconds.
  --eval <file>      Send an EVAL command using the Lua script at <file>.
  --ldb              Used with --eval enable the Redis Lua debugger.
  --ldb-sync-mode    Like --ldb but uses the synchronous Lua debugger, in
                     this mode the server is blocked and script changes are
                     not rolled back from the server memory.
  --cluster <command> [args...] [opts...]
                     Cluster Manager command and arguments (see below).
  --verbose          Verbose mode.
  --no-auth-warning  Don't show warning message when using password on command
                     line interface.
  --help             Output this help and exit.
  --version          Output version and exit.

Cluster Manager Commands:
  Use --cluster help to list all available cluster manager commands.

Examples:
  cat /etc/passwd | redis-cli -x set mypasswd
  redis-cli get mypasswd
  redis-cli -r 100 lpush mylist x
  redis-cli -r 100 -i 1 info | grep used_memory_human:
  redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3
  redis-cli --scan --pattern '*:12345*'

  (Note: when using --eval the comma separates KEYS[] from ARGV[] items)

When no command is given, redis-cli starts in interactive mode.
Type "help" in interactive mode for information on available commands
and settings.

可以看到有许许多多的指令,其中有一个-n的参数,看描述代表指定连接的数据库,redis默认有16个库,0-15号,可自行选择连接。

#通过输入此命令,即可连接6号库
redis-cli -n 6
#不指定,默认0号库
redis-cli

此时映入眼帘的就是客户端的交互式界面。
那有什么东西呢?不懂就敲

help
127.0.0.1:6379[6]> help
redis-cli 5.0.5
To get help about Redis commands type:
      "help @<group>" to get a list of commands in <group>
      "help <command>" for help on <command>
      "help <tab>" to get a list of possible help topics
      "quit" to exit

To set redis-cli preferences:
      ":set hints" enable online hints
      ":set nohints" disable online hints
Set your preferences in ~/.redisclirc

可以看到,提示我们可以通过敲入@ 然后按tab键,发现会自动联想很多
例如:

help @generic

可以看到通用命令列表:

DEL key [key ...]
  summary: Delete a key
  since: 1.0.0

  DUMP key
  summary: Return a serialized version of the value stored at the specified key.
  since: 2.6.0

  EXISTS key [key ...]
  summary: Determine if a key exists
  since: 1.0.0

  EXPIRE key seconds
  summary: Set a key's time to live in seconds
  since: 1.0.0

  EXPIREAT key timestamp
  summary: Set the expiration for a key as a UNIX timestamp
  since: 1.2.0

  KEYS pattern
  summary: Find all keys matching the given pattern
  since: 1.0.0

  MIGRATE host port key| destination-db timeout [COPY] [REPLACE] [KEYS key]
  summary: Atomically transfer a key from a Redis instance to another one.
  since: 2.6.0

  MOVE key db
  summary: Move a key to another database
  since: 1.0.0

  OBJECT subcommand [arguments [arguments ...]]
  summary: Inspect the internals of Redis objects
  since: 2.2.3

  PERSIST key
  summary: Remove the expiration from a key
  since: 2.2.0

  PEXPIRE key milliseconds
  summary: Set a key's time to live in milliseconds
  since: 2.6.0

  PEXPIREAT key milliseconds-timestamp
  summary: Set the expiration for a key as a UNIX timestamp specified in milliseconds
  since: 2.6.0

  PTTL key
  summary: Get the time to live for a key in milliseconds
  since: 2.6.0

  RANDOMKEY -
  summary: Return a random key from the keyspace
  since: 1.0.0

  RENAME key newkey
  summary: Rename a key
  since: 1.0.0

  RENAMENX key newkey
  summary: Rename a key, only if the new key does not exist
  since: 1.0.0

  RESTORE key ttl serialized-value [REPLACE]
  summary: Create a key using the provided serialized value, previously obtained using DUMP.
  since: 2.6.0

  SCAN cursor [MATCH pattern] [COUNT count]
  summary: Incrementally iterate the keys space
  since: 2.8.0

  SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]
  summary: Sort the elements in a list, set or sorted set
  since: 1.0.0

  TOUCH key [key ...]
  summary: Alters the last access time of a key(s). Returns the number of existing keys specified.
  since: 3.2.1

  TTL key
  summary: Get the time to live for a key
  since: 1.0.0

  TYPE key
  summary: Determine the type stored at key
  since: 1.0.0

  UNLINK key [key ...]
  summary: Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.
  since: 4.0.0

  WAIT numreplicas timeout
  summary: Wait for the synchronous replication of all the write commands sent in the context of the current connection
  since: 3.0.0

  POST ...options...
  summary: Help not available
  since: not known

  SUBSTR key arg arg 
  summary: Help not available
  since: not known

  REPLCONF ...options...
  summary: Help not available
  since: not known

  PFSELFTEST 
  summary: Help not available
  since: not known

  HOST: ...options...
  summary: Help not available
  since: not known

  ASKING 
  summary: Help not available
  since: not known

  LATENCY arg ...options...
  summary: Help not available
  since: not known

  RESTORE-ASKING key arg arg ...options...
  summary: Help not available
  since: not known

  PSYNC arg arg 
  summary: Help not available
  since: not known

  MODULE arg ...options...
  summary: Help not available
  since: not known

  PFDEBUG arg arg ...options...
  summary: Help not available
  since: not known

  XSETID key arg 
  summary: Help not available
  since: not known

  GEORADIUS_RO key arg arg arg arg ...options...
  summary: Help not available
  since: not known

  GEORADIUSBYMEMBER_RO key arg arg arg ...options...
  summary: Help not available
  since: not known

  LOLWUT ...options...
  summary: Help not available
  since: not known

例如:

keys * #可以得到当前库的所有key

Redis中的Key

redis是key,value型的内存数据库,value的类型十分的丰富,而在此之前,我们先说一下key
在这里插入图片描述

key可以看成是一个对象类型,其中有3个主要的属性标识:
type:标识着当前value的类型 ,有了它可以快速判断当前value类型是否可以调用某个操作,而不需要等到计算时再抛异常
encoding:标识value当前的编码,也可用于快速判断,例如string类型的value可根据此值快速判断是否可以进行字符串或者数值的操作
length:一次计算出value的长度后,将其保存下来,而无需每次都进行计算

type key名称 #访问某个key的value的类型
OBJECT  encoding key名称 #访问某个key的value的编码

length通过各类型都有对应的获取长度的方法。

接下来对各个value类型进行一一说明

string

string是redis中最直接最简单的类型,至少,看似是这样的,但其实它内部也可以细分为三种类型:
1、字符串
2、数值
3、bitmap
针对每种类型,都有对应的操作

输入

help @string

可以看到string类型value的所有操作方法

127.0.0.1:6379[6]> help @string

  APPEND key value
  summary: Append a value to a key
  since: 2.0.0

  BITCOUNT key [start end]
  summary: Count set bits in a string
  since: 2.6.0

  BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
  summary: Perform arbitrary bitfield integer operations on strings
  since: 3.2.0

  BITOP operation destkey key [key ...]
  summary: Perform bitwise operations between strings
  since: 2.6.0

  BITPOS key bit [start] [end]
  summary: Find first bit set or clear in a string
  since: 2.8.7

  DECR key
  summary: Decrement the integer value of a key by one
  since: 1.0.0

  DECRBY key decrement
  summary: Decrement the integer value of a key by the given number
  since: 1.0.0

  GET key
  summary: Get the value of a key
  since: 1.0.0

  GETBIT key offset
  summary: Returns the bit value at offset in the string value stored at key
  since: 2.2.0

  GETRANGE key start end
  summary: Get a substring of the string stored at a key
  since: 2.4.0

  GETSET key value
  summary: Set the string value of a key and return its old value
  since: 1.0.0

  INCR key
  summary: Increment the integer value of a key by one
  since: 1.0.0

  INCRBY key increment
  summary: Increment the integer value of a key by the given amount
  since: 1.0.0

  INCRBYFLOAT key increment
  summary: Increment the float value of a key by the given amount
  since: 2.6.0

  MGET key [key ...]
  summary: Get the values of all the given keys
  since: 1.0.0

  MSET key value [key value ...]
  summary: Set multiple keys to multiple values
  since: 1.0.1

  MSETNX key value [key value ...]
  summary: Set multiple keys to multiple values, only if none of the keys exist
  since: 1.0.1

  PSETEX key milliseconds value
  summary: Set the value and expiration in milliseconds of a key
  since: 2.6.0

  SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
  summary: Set the string value of a key
  since: 1.0.0

  SETBIT key offset value
  summary: Sets or clears the bit at offset in the string value stored at key
  since: 2.2.0

  SETEX key seconds value
  summary: Set the value and expiration of a key
  since: 2.0.0

  SETNX key value
  summary: Set the value of a key, only if the key does not exist
  since: 1.0.0

  SETRANGE key offset value
  summary: Overwrite part of a string at key starting at the specified offset
  since: 2.2.0

  STRLEN key
  summary: Get the length of the value stored in a key
  since: 2.2.0

如上所述,这里string类型的value方法其实可以分为三类:

1、string

set
get
append
setrange
getrange
strlen

做个试验,2个key,1个放数值,1个放字母,此时看key上的encoding有什么不同?

127.0.0.1:6379[6]> set k1 a
OK
127.0.0.1:6379[6]> set k2 123
OK
127.0.0.1:6379[6]> OBJECT  encoding k1
"embstr"
127.0.0.1:6379[6]> OBJECT  encoding k2
"int"

发现,encoding有所不同,实际上,虽然存储的都是字符串,但redis对此做了优化,标识字符串里的内容到底是数值还是非数值,
进而可以有一些用于专属于数值或字符的操作,而是否能够使用,无需每次都去判断一下,只需要看这个值就能快速判断了。

继续操作,在k2上追加asd,在看encoding

127.0.0.1:6379[6]> APPEND k2 asd
(integer) 6
127.0.0.1:6379[6]> OBJECT  encoding k2
"raw"

发现变成了raw。 raw又是啥呢? 后续解答。

Redis的存储与显示

redis的底层存储实际上是字节数组,因此是二进制安全的。
默认情况下,客户端只能显示ASCII码的value,而非ASCII的会转换成16进制进行显示。

127.0.0.1:6379[6]> set k3   哈哈
OK
127.0.0.1:6379[6]> get k3
"\xe5\x93\x88\xe5\x93\x88"

当然可以手动指定以当前客户端连接编码进行解码,只需要在连接的时候这样输入:

[root@dream01 ~]# redis-cli --raw
127.0.0.1:6379> get k3
哈哈

发现结果就是可以显示中文了。

2、数值

INCR key
summary: Increment the integer value of a key by one
since: 1.0.0

INCRBY key increment
summary: Increment the integer value of a key by the given amount #如果想要的不是递增而是递减怎么办? increment为负数即可
since: 1.0.0

INCRBYFLOAT key increment
summary: Increment the float value of a key by the given amount
since: 2.6.0

应用场景:

抢购,秒杀,详情页,点赞,评论规避并发下,对数据库的事务操作完全由redis内存操作代替

3、bitmap(最有价值的类型)

要想清楚bitmap的使用,需要先了解一下它的存储结构

如图,1个字节有8位,从左到右位的索引从0开始。
举例说明,比如我们想要存一个"B"进去,B的ascii是42,它的16进制表现为 0010 0010,参照上图,只要在索引为1和索引为6的地方标识位置为1就可以了。

127.0.0.1:6379> SETBIT bkey 1 1
(integer) 0
127.0.0.1:6379> SETBIT bkey 6 1
(integer) 0
127.0.0.1:6379> get bkey
"B"

在指定key的指定的offset下设置位值

SETBIT key offset value

获取指定key在指定字节范围内找到的第一个指定的bit值的offset

BITPOS key bit [start] [end]

获取指定key在指定字节范围内的为1的位总个数

BITCOUNT key [start end]

将若干个key进行指定操作(与,或,非等) ,将得到的结果放入destkey指定的key

BITOP operation destkey key [key ...]

应用场景:

1、有一个用户系统,需要统计用户登录天数,且选取天数窗口随机
如果按照传统办法来说,我们可以选择使用mysql建一张表,每天用户登录都去表里新增一条记录
在redis里面可以这么解决呢?可以运用bitmap,每一个用户对应1个key,然后放置365位,每位代表1天,登录1天就在指定天对应位里置1,如下图
在这里插入图片描述

代码实现:

setbit sean 1 1 #用户sean在第2天里登录
setbit sean 7 1 #用户sean在第8天里登录
setbit sean 364 1 #用户sean在第365天里登录
STRLEN sean 
BITCOUNT sean -2 -1  #选取用户sean 1年里最后16天的总登录天数

2、一知名线上商城618做活动:送礼物,大库需要备货多少礼物,假设有2E用户?
用户分为 僵尸用户/冷热用户/忠诚用户,因此不是有2E用户就送2E商品,需要统计出活跃用户的数量,例如统计1-2号上线用户总数,单个用户连续登录需要去重。
在redis里依旧可以使用bitmap,此时日期作为key,每个用户作为位,如图:
在这里插入图片描述
此时可以将两个key进行与运算,即可完成去重操作,然后使用bitcount统计位值为1的个数,即可统计出活跃用户个数
代码实现:

setbit 20190101   1  1 #1号用户01登录了
setbit 20190102   1  1 #2号用户01登录了
setbit 20190102   7  1  #2号用户08登录了
bitop  or   destkey 20190101  20190102  #01与02进行与运算
BITCOUNT  destkey  0 -1  #得到所有的位值为1的个数,也就是总活跃用户数

正反向索引

redis中有许多的范围操作,因此就涉及到redis的索引
在这里插入图片描述
redis中,是存在正反向索引的,不但可以通过0,1,2进行访问指定偏移量数据,还可以用-1,-2表示从后向前访问。
拿string类型的getrange方法举例

set k1 hel
OK
GETRANGE k1 0 2
hel
GETRANGE k1 0 -1
hel

List

redis中,list的类型其实本质上是使用一个链表的结构进行存储,是一种存储多元素的结构,且它本身放入元素是有顺序性的,且元素是不支持去重的。

此时,首先可以观察一下此时的key,key上面此时会分别有两个属性,head和tail分别指向链表的头部和尾部。
而正如之前说的,list中的元素也有正反向索引之说。

想要查看list有哪些操作:

127.0.0.1:6379> help @list

  BLPOP key [key ...] timeout
  summary: Remove and get the first element in a list, or block until one is available
  since: 2.0.0

  BRPOP key [key ...] timeout
  summary: Remove and get the last element in a list, or block until one is available
  since: 2.0.0

  BRPOPLPUSH source destination timeout
  summary: Pop a value from a list, push it to another list and return it; or block until one is available
  since: 2.2.0

  LINDEX key index
  summary: Get an element from a list by its index
  since: 1.0.0

  LINSERT key BEFORE|AFTER pivot value
  summary: Insert an element before or after another element in a list
  since: 2.2.0

  LLEN key
  summary: Get the length of a list
  since: 1.0.0

  LPOP key
  summary: Remove and get the first element in a list
  since: 1.0.0

  LPUSH key value [value ...]
  summary: Prepend one or multiple values to a list
  since: 1.0.0

  LPUSHX key value
  summary: Prepend a value to a list, only if the list exists
  since: 2.2.0

  LRANGE key start stop
  summary: Get a range of elements from a list
  since: 1.0.0

  LREM key count value
  summary: Remove elements from a list
  since: 1.0.0

  LSET key index value
  summary: Set the value of an element in a list by its index
  since: 1.0.0

  LTRIM key start stop
  summary: Trim a list to the specified range
  since: 1.0.0

  RPOP key
  summary: Remove and get the last element in a list
  since: 1.0.0

  RPOPLPUSH source destination
  summary: Remove the last element in a list, prepend it to another list and return it
  since: 1.2.0

  RPUSH key value [value ...]
  summary: Append one or multiple values to a list
  since: 1.0.0

  RPUSHX key value
  summary: Append a value to a list, only if the list exists
  since: 2.2.0
#LPUSH key value [value ...]
 #summary: Prepend one or multiple values to a list  
 #since: 1.0.0
 #向链表左侧依次向前插入元素(头插法)
 127.0.0.1:6379> LPUSH list1 1 2 3
(integer) 3
127.0.0.1:6379> LRANGE list1 0 -1
1) "3"
2) "2"
3) "1"
#LPOP key
#summary: Remove and get the first element in a list
#since: 1.0.0
#删除并获得链表第一个元素(最左边)
127.0.0.1:6379> LPOP list1
"3"
127.0.0.1:6379> LPOP list1
"2"
127.0.0.1:6379> LPOP list1
"1"
#RPUSH key value [value ...]
#summary: Append one or multiple values to a list
#since: 1.0.0
#向链表右侧依次插入元素(尾插法)
127.0.0.1:6379> rpush list1 1 2 3
(integer) 3
127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "2"
3) "3"
#RPOP key
#summary: Remove and get the last element in a list
#since: 1.0.0
#删除并获得链表第一个元素(最左边)
127.0.0.1:6379> RPOP list1
"3"
127.0.0.1:6379> RPOP list1
"2"
127.0.0.1:6379> RPOP list1
"1"

我们可以看到上面使用相同方向的添加和删除,效果看起来就是先进去的先出来了,是不是很像编程中的一种模型? 没错,就是栈,后入先出。
而反过来想,如果相反呢?

127.0.0.1:6379> rpush list1 1 2 3
(integer) 3
127.0.0.1:6379> lpop list1
"1"
127.0.0.1:6379> lpop list1
"2"
127.0.0.1:6379> lpop list1
"3"

看到效果了吗? 我们实现了另外一种模型, 队列, 先入先出。
因此可以做个总结:list的同向命令可以实现栈,反向命令可以实现队列。

上面我们有用到LRANGE,这里来说明一下:

#LRANGE key start stop
#summary: Get a range of elements from a list
#since: 1.0.0
#获取某个范围内的元素
LRANGE list1 0 -1
1) "1"
2) "2"
3) "3"
  #LINDEX key index
  #summary: Get an element from a list by its index
  #since: 1.0.0
  #从给定索引获取元素
  
127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "666"
3) "3"

127.0.0.1:6379> LINDEX list1 1
"666"
 #LREM key count value
  #summary: Remove elements from a list
  #since: 1.0.0
  #移除count个值为value的元素   这个count可以为正数可以为负数, 正数时从前向后删除|count|个该value元素,负数时从后向前删除|count|个该value元素
  127.0.0.1:6379> LRANGE list1 0 -1
 1) "1"
 2) "666"
 3) "3"
 4) "3"
 5) "3"
 6) "3"
 7) "6"
 8) "7"
 9) "3"
10) "3"
#count为正数
127.0.0.1:6379> LREM list1 2 3
(integer) 2
127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "666"
3) "3"
4) "3"
5) "6"
6) "7"
7) "3"
8) "3"
#count为负数
127.0.0.1:6379> LREM list1 -2 3
(integer) 2
127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "666"
3) "3"
4) "3"
5) "6"
6) "7"
  #LSET key index value
  #summary: Set the value of an element in a list by its index
  #since: 1.0.0
  #设置某个索引的元素
  127.0.0.1:6379> LRANGE list1 0 -1
  1) "1"
  2) "2"
  3) "3"
  127.0.0.1:6379> LSET list1 1 666
  OK
  127.0.0.1:6379> LRANGE list1 0 -1
  1) "1"
  2) "666"
  3) "3"
  #LINSERT key BEFORE|AFTER pivot value
  #summary: Insert an element before or after another element in a list
  #since: 2.2.0
  #从前向后找到某个元素为pivot的,并在它的前面或者后面插入该value值
  127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "666"
3) "3"
4) "3"
5) "6"
6) "7"
#在666前面插入123
127.0.0.1:6379> LINSERT list1 before 666 123
(integer) 7
127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "123"
3) "666"
4) "3"
5) "3"
6) "6"
7) "7"
#在666后面插入123
127.0.0.1:6379> LINSERT list1 after 666 123
(integer) 8
127.0.0.1:6379> LRANGE list1 0 -1
1) "1"
2) "123"
3) "666"
4) "123"
5) "3"
6) "3"
7) "6"
8) "7"
#LLEN key
#summary: Get the length of a list
#since: 1.0.0
#获取此list的长度
127.0.0.1:6379> LLEN list1
(integer) 8
 #LTRIM key start stop
 #summary: Trim a list to the specified range
 #since: 1.0.0
 #对选定范围两侧的元素进行清空
 127.0.0.1:6379> LRANGE list2 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
6) "444"
127.0.0.1:6379> LTRIM list2 2 -2
OK
127.0.0.1:6379> LRANGE list2 0 -1
1) "3"
2) "2"
3) "1"

单播队列

redis还可以完成单播队列的功能,通过它的BLPOP 或者BRPOP完成
#BLPOP key [key ...] timeout
#summary: Remove and get the first element in a list, or block until one is available
#since: 2.0.0
#阻塞等待直到一个list中的第一个元素可用时对其进行删除并获取
#BRPOP key [key ...] timeout
#summary: Remove and get the last element in a list, or block until one is available
#since: 2.0.0
#阻塞等待直到一个list中的最后一个元素可用时对其进行删除并获取

来实现这个功能:
客户端1:

127.0.0.1:6379> BLPOP list2 0

此时会一直阻塞等待。。。。

客户端2:

127.0.0.1:6379> RPUSH list2 333 444
(integer) 2

向list中推入元素

此时客户端1:

127.0.0.1:6379> BLPOP list2 0
1) "list2"
2) "333"
(57.78s)

发现,得到了客户端1推送的第一个元素

至此,我们发现一个List,其实是可以有这么多用途的:
在这里插入图片描述

Hash

如果学过java,那么一定知道java中有一个类型叫hashmap,这里通俗来说,可以把hash就当成一个hashmap。
按照之前的惯例,我们来看下hash有哪些操作:

127.0.0.1:6379> help @hash

  HDEL key field [field ...]
  summary: Delete one or more hash fields
  since: 2.0.0

  HEXISTS key field
  summary: Determine if a hash field exists
  since: 2.0.0

  HGET key field
  summary: Get the value of a hash field
  since: 2.0.0

  HGETALL key
  summary: Get all the fields and values in a hash
  since: 2.0.0

  HINCRBY key field increment
  summary: Increment the integer value of a hash field by the given number
  since: 2.0.0

  HINCRBYFLOAT key field increment
  summary: Increment the float value of a hash field by the given amount
  since: 2.6.0

  HKEYS key
  summary: Get all the fields in a hash
  since: 2.0.0

  HLEN key
  summary: Get the number of fields in a hash
  since: 2.0.0

  HMGET key field [field ...]
  summary: Get the values of all the given hash fields
  since: 2.0.0

  HMSET key field value [field value ...]
  summary: Set multiple hash fields to multiple values
  since: 2.0.0

  HSCAN key cursor [MATCH pattern] [COUNT count]
  summary: Incrementally iterate hash fields and associated values
  since: 2.8.0

  HSET key field value
  summary: Set the string value of a hash field
  since: 2.0.0

  HSETNX key field value
  summary: Set the value of a hash field, only if the field does not exist
  since: 2.0.0

  HSTRLEN key field
  summary: Get the length of the value of a hash field
  since: 3.2.0

  HVALS key
  summary: Get all the values in a hash
  since: 2.0.0

可以发现和hashmap的操作相差不多,这里我嫌麻烦,就不一一再讲应用了。

应用场景

点赞,收藏,详情页

Set

set与list比较相似,但区别是,set是无序的,且会对内部存储的元素进行去重。
来看看有哪些操作:

help @set
SADD key member [member ...]
  summary: Add one or more members to a set
  since: 1.0.0

  SCARD key
  summary: Get the number of members in a set
  since: 1.0.0

  SDIFF key [key ...]
  summary: Subtract multiple sets
  since: 1.0.0

  SDIFFSTORE destination key [key ...]
  summary: Subtract multiple sets and store the resulting set in a key
  since: 1.0.0

  SINTER key [key ...]
  summary: Intersect multiple sets
  since: 1.0.0

  SINTERSTORE destination key [key ...]
  summary: Intersect multiple sets and store the resulting set in a key
  since: 1.0.0

  SISMEMBER key member
  summary: Determine if a given value is a member of a set
  since: 1.0.0

  SMEMBERS key
  summary: Get all the members in a set
  since: 1.0.0

  SMOVE source destination member
  summary: Move a member from one set to another
  since: 1.0.0

  SPOP key [count]
  summary: Remove and return one or multiple random members from a set
  since: 1.0.0

  SRANDMEMBER key [count]
  summary: Get one or multiple random members from a set
  since: 1.0.0

  SREM key member [member ...]
  summary: Remove one or more members from a set
  since: 1.0.0

  SSCAN key cursor [MATCH pattern] [COUNT count]
  summary: Incrementally iterate Set elements
  since: 2.8.0

  SUNION key [key ...]
  summary: Add multiple sets
  since: 1.0.0

  SUNIONSTORE destination key [key ...]
  summary: Add multiple sets and store the resulting set in a key
  since: 1.0.0

从这些操作,我们大致可以总结出set的特点:
在这里插入图片描述

随机事件的应用场景

针对随机事件,可以来聊一聊。
可以使用set类型中的各种随机操作来完成日常中的一些需求
先介绍一个操作:

#SRANDMEMBER key [count]
#summary: Get one or multiple random members from a set
#since: 1.0.0
#随机获取1个元素或多个元素
#这里的count根据取值不同,选取元素策略也有所不同:
#如果count>0 会返回一个去重的结果集(但总数不超过现有总数)
#如果count=0 不返回任何内容
#如果count<0 会返回一个可能重复的结果集,但个数一定是期望的个数

这里我们可以分析一下如何合理利用在哪些场景:
1、假设一公司发放奖品,奖品个数大于人数,公司希望给每个人都可以多得到几件礼物。
这种count就可以取负数,将set里放入人名,取出的结果就很可能会一个人出现多次,符合需求。
例如一共有4个人:

127.0.0.1:6379> SMEMBERS peoples
1) "wangwu"
2) "lisi"
3) "zhanggsan"
4) "liming"

然后给这4个人分发20件礼物,可以这样做:

127.0.0.1:6379> SRANDMEMBER peoples -20
 1) "liming"
 2) "lisi"
 3) "liming"
 4) "zhanggsan"
 5) "lisi"
 6) "liming"
 7) "lisi"
 8) "wangwu"
 9) "lisi"
10) "zhanggsan"
11) "wangwu"
12) "zhanggsan"
13) "zhanggsan"
14) "liming"
15) "liming"
16) "lisi"
17) "liming"
18) "zhanggsan"
19) "wangwu"
20) "wangwu"

2、假设公司年会,一定是礼物数远小于人数的,此时count可以使用正数
假设还是之前那四个人,但礼物只有2个

127.0.0.1:6379> SRANDMEMBER peoples 2
1) "lisi"
2) "wangwu"
127.0.0.1:6379> SRANDMEMBER peoples 2
1) "lisi"
2) "liming"

3、实际上,我们在年会上,都是1人分发1个号码,一轮一轮抽奖,当中奖后,将从待选区被移除,此时这个方法可能就不太适用了,可以用另外一个指令:

SPOP key [count]
summary: Remove and return one or multiple random members from a set
since: 1.0.0
#随机移除并返回count个数的元素
127.0.0.1:6379> SPOP peoples
"liming"
127.0.0.1:6379> SPOP peoples
"zhanggsan"
127.0.0.1:6379> SPOP peoples
"wangwu"
127.0.0.1:6379> SPOP peoples
"lisi"
127.0.0.1:6379> SPOP peoples
(nil)

Zset(sorted-set)

接下来, 就是我们的最后一种类型,zset,和set比较相似,它也是会对集合内元素进行去重操作(更准确些,应该叫更新),但不同的是它是又是一种支持对集合内元素进行排序的数据结构
既然要排序,自然需要一个东西来标识一个元素是多大,因此在zset中有一个score的存在。
zset有一个特点是存储元素在物理内存中的存放是从左到右,从小到大的。且不会随命令的操作而改变元素的位置。

还是先来看下它有哪些命令:

127.0.0.1:6379> help @sorted_set
BZPOPMAX key [key ...] timeout
  summary: Remove and return the member with the highest score from one or more sorted sets, or block until one is available
  since: 5.0.0

  BZPOPMIN key [key ...] timeout
  summary: Remove and return the member with the lowest score from one or more sorted sets, or block until one is available
  since: 5.0.0

  ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
  summary: Add one or more members to a sorted set, or update its score if it already exists
  since: 1.2.0

  ZCARD key
  summary: Get the number of members in a sorted set
  since: 1.2.0

  ZCOUNT key min max
  summary: Count the members in a sorted set with scores within the given values
  since: 2.0.0

  ZINCRBY key increment member
  summary: Increment the score of a member in a sorted set
  since: 1.2.0

  ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
  summary: Intersect multiple sorted sets and store the resulting sorted set in a new key
  since: 2.0.0

  ZLEXCOUNT key min max
  summary: Count the number of members in a sorted set between a given lexicographical range
  since: 2.8.9

  ZPOPMAX key [count]
  summary: Remove and return members with the highest scores in a sorted set
  since: 5.0.0

  ZPOPMIN key [count]
  summary: Remove and return members with the lowest scores in a sorted set
  since: 5.0.0

  ZRANGE key start stop [WITHSCORES]
  summary: Return a range of members in a sorted set, by index
  since: 1.2.0

  ZRANGEBYLEX key min max [LIMIT offset count]
  summary: Return a range of members in a sorted set, by lexicographical range
  since: 2.8.9

  ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
  summary: Return a range of members in a sorted set, by score
  since: 1.0.5

  ZRANK key member
  summary: Determine the index of a member in a sorted set
  since: 2.0.0

  ZREM key member [member ...]
  summary: Remove one or more members from a sorted set
  since: 1.2.0

  ZREMRANGEBYLEX key min max
  summary: Remove all members in a sorted set between the given lexicographical range
  since: 2.8.9

  ZREMRANGEBYRANK key start stop
  summary: Remove all members in a sorted set within the given indexes
  since: 2.0.0

  ZREMRANGEBYSCORE key min max
  summary: Remove all members in a sorted set within the given scores
  since: 1.2.0

  ZREVRANGE key start stop [WITHSCORES]
  summary: Return a range of members in a sorted set, by index, with scores ordered from high to low
  since: 1.2.0

  ZREVRANGEBYLEX key max min [LIMIT offset count]
  summary: Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.
  since: 2.8.9

  ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]
  summary: Return a range of members in a sorted set, by score, with scores ordered from high to low
  since: 2.2.0

  ZREVRANK key member
  summary: Determine the index of a member in a sorted set, with scores ordered from high to low
  since: 2.0.0

  ZSCAN key cursor [MATCH pattern] [COUNT count]
  summary: Incrementally iterate sorted sets elements and associated scores
  since: 2.8.0

  ZSCORE key member
  summary: Get the score associated with the given member in a sorted set
  since: 1.2.0

  ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
  summary: Add multiple sorted sets and store the resulting sorted set in a new key
  since: 2.0.0

挑选一些常用的命令进行讲解:

  #ZADD key [NX|XX] [CH] [INCR] score member [score member ...]
  #summary: Add one or more members to a sorted set, or update its score if it already exists
  #since: 1.2.0
  #添加元素 每个元素都要带有分值
 ZADD z1 8 apple 10 orange 12 banana
 #ZRANGE key start stop [WITHSCORES]
 #summary: Return a range of members in a sorted set, by index
 #since: 1.2.0
 #按照索引范围获取由低到高排序的元素
 127.0.0.1:6379> ZRANGE z1 0 -1
1) "apple"
2) "orange"
3) "banana"
127.0.0.1:6379> ZRANGE z1 0 -1 withscores
1) "apple"
2) "8"
3) "orange"
4) "10"
5) "banana"
6) "12"
  #ZREVRANGE key start stop [WITHSCORES]
  #summary: Return a range of members in a sorted set, by index, with scores ordered from high to low
  #since: 1.2.0
  #按照索引范围获取由高到低排序的元素
  127.0.0.1:6379> ZREVRANGE z1 0 -1
1) "banana"
2) "orange"
3) "apple"
 #ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
  #summary: Return a range of members in a sorted set, by score
  #since: 1.0.5
  #按照分值范围获取元素
  127.0.0.1:6379> ZRANGEBYSCORE z1 10 12
1) "orange"
2) "banana"
  #ZSCORE key member
  #summary: Get the score associated with the given member in a sorted set
  #since: 1.2.0
  #获取元素的分数
  127.0.0.1:6379> ZSCORE z1 apple
"8"
  #ZRANK key member
  #summary: Determine the index of a member in a sorted set
  #since: 2.0.0
  #获取指定元素的排名
  127.0.0.1:6379> ZRANK z1 apple
(integer) 0
127.0.0.1:6379> ZRANK z1 orange
(integer) 1
127.0.0.1:6379> ZRANK z1 banana
(integer) 2

集合操作

zset也支持多集合的操作,但因为zset一个元素除了值本身外,还有score,那两个集合进行合并的时候留谁舍谁呢?

  ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
  summary: Add multiple sorted sets and store the resulting sorted set in a new key
  since: 2.0.0
  #对任意个数的zset进行合并操作 numkeys指定参与的zset个数
  #weights可以分别定义每个zset在参与的时候其每个元素的分值大小缩放比例  aggregate定义在遇到重复元素值时要做的操作,  不填默认为SUM操作
  
127.0.0.1:6379> ZADD z1 20 zhanggsan 30 lisi 60 wangwu
(integer) 3
127.0.0.1:6379> ZADD z2 60 zhangsan 66 liming 
(integer) 2
127.0.0.1:6379> ZRANGE z1 0 -1 withscores
1) "zhangsan"
2) "20"
3) "lisi"
4) "30"
5) "wangwu"
6) "60"
127.0.0.1:6379> ZRANGE z2 0 -1 withscores
1) "zhangsan"
2) "60"
3) "liming"
4) "66"

#接下来对两个集合进行操作
#不指定aggregate 默认对重复元素进行 SUM操作
127.0.0.1:6379> ZRANGE unkey1 0 -1 withscores
1) "lisi"
2) "30"
3) "wangwu"
4) "60"
5) "liming"
6) "66"
7) "zhangsan"
8) "80"

#指定各zset参与操作的score分值缩放比例
127.0.0.1:6379> ZUNIONSTORE unkey2 2 z1 z2 WEIGHTS 1 0.5
(integer) 4
127.0.0.1:6379> ZRANGE unkey2 0 -1 withscores
1) "lisi"
2) "30"
3) "liming"
4) "33"
5) "zhangsan"
6) "50"
7) "wangwu"
8) "60"


#指定aggregate 对重复元素进行 MAX操作 也就是留下最大的那个
127.0.0.1:6379> ZUNIONSTORE unkey4 2 z1 z2 AGGREGATE MAX
(integer) 4
127.0.0.1:6379> ZRANGE unkey4 0 -1 withscores
1) "lisi"
2) "30"
3) "wangwu"
4) "60"
5) "zhangsan"
6) "60"
7) "liming"

当然,如果仅仅只有这些,这个类型是很少会被人使用的。
zset类型是支持进行计算的

  #ZINCRBY key increment member
  #summary: Increment the score of a member in a sorted set
  #since: 1.2.0
  #将元素的分数进行增加
  127.0.0.1:6379> ZRANGE z1 0 -1 withscores
1) "apple"
2) "8"
3) "orange"
4) "10"
5) "banana"
6) "12"
127.0.0.1:6379> ZINCRBY z1 20 apple
"28"
127.0.0.1:6379> ZRANGE z1 0 -1 withscores
1) "orange"
2) "10"
3) "banana"
4) "12"
5) "apple"
6) "28"

从上可以看出,,zset会动态实时维护它们的排名,随着计算而变动。

应用场景

试想一下在线网游的游戏排行榜,杀怪越多的人排行越高,这个数值频繁会发生变动,redis作为一个内存型数据库比较合适,而此时使用zset天然支持的"排名系统"也是个不错的选择。

存储原理

由上可以看出,zset要实时维护一个集合里元素的顺序,那它是如何保证效率高效的呢?
我们首先想到的,为了保证顺序,它内部有可能会是使用一个链表结构来进行元素的存储:
在这里插入图片描述
但随着数据量增多,链表的时间复杂度O(n)会使得检索等操作变得越来越慢。
那redis怎么做的呢?
redis使用了一种数据结构:skip list(跳表)
这是一个什么结构呢?
其实简单来说,它就是在链表的基础上,多了几“层”

在这里插入图片描述
如图,在一层的链表基础上,每隔一些元素,就会有1个元素在上层中也有它的存在。
这样有什么好处呢?

一次插入

我们试想一下, 我们使用这个结构新增一个元素的过程:
例如我们要插入8。
此时会从最上层开始从左向右遍历,发现8是3和11中间的值,因此此时可以向下降层。
在第二层发现8是3-11之间的,继续降层,到达第一层
此时由11开始向左找,发现8是7-11之间的。
此时只需要在7和11之间插入8 ,然后将指针的引用切换一下,就可完成了插入。
一个元素插入完成后,会随机向上层造一个自身的数,具体造几层是随机的。

造层的细节

接着上面说的,当8完成插入后,会向左一直找,直到找到3,发现3有一个上层的指向,
顺着指向继续移动,到上层的3,此时发现层次就是2层了。
开始继续向右移动,发现7是在3-11之间的, 因此只需要将3指向7,7再指向11, 只需要改变一下指针的引用,就完成了一个元素在一层的生成。。

一次删除

从上向下,找到范围,确定元素,移除元素,将元素左右边界的元素进行指针关联修复引用就可完成了。

一次更新

一次更新其实可以理解成先把元素删除掉,修复指针引用后,然后走一遍插入的操作。

一次查询

无论增删改,都是需要先进行查询的,查找从上向下。

有没有发现这样的结构有点像树? 这样使得遍历比较少的节点就可以完成插入。
其实跳表还可以叫平衡树,它对于增删改查综合的能力算是相对最优的。

至此,redis的五种数据类型就介绍完了。
本人只对其中比较有特点的进行了讲解,如果想了解更全面的用法可以参考redis官方网站:
http://www.redis.cn/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值