redis数据库结构

一 全局命令

1.1 查看所有的key

keys *

1.2key的总数

dbsize

1.3 检查key是否存在

exists username

1.4 删除key

del password

1.5key过期,超过时间自动删

expire hello 5

可以通过ttl观察剩余时间

ttl hello

1.6 查看key的数据结构类型

type key,不存在这个key就返回none

比如:type username

 

二 数据结构和内部编码

type命令实际上返回的就是当前key的数据结构类型,分别是:

string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)

但是这些只是redis对外的数据结构。

实际上每一种数据结构都有自己底层的内部编码实现,redis会在合适的场合选择合适的内部编码,比如list数据结构包含了linkedlist和ziplist两种内部编码。我们可以通过object encoding key查询内部编码

 

三 单线程架构

Redis使用的是单线程架构和I/O多路复用模型来实现高性能内存数据库服务。

单线程

优点:不会涉及到锁,也不会涉及到线程切换而消耗CPU

缺点:无法利用多核CPU性能

 

多线程:

优点:可以充分利用多核CPU的性能

缺点:会涉及到竞态问题,就会涉及到锁;另外线程太多,上下文切换频繁,会给CPU消耗带来不好的影响

为什么Redis单线程还能保持高效的性能呢?

第一:基于内存的访问,Redis将所有数据放入内存,这个速度是很快的,这也是高效最重要的原因

第二:非阻塞I/O,Redis使用事件驱动模型epoll多路复用实现

第三:单线程避免的高并发的时候,多线程的锁的问题和线程切换的CPU开销的问题

第四:虽然是单线程的,我们还可以通过多实例来弥补。

 

什么是多路复用?

多路指的是多个网络连接,而复用指的是复用同一个线程,即多个网络连接复用同一个线程。怎么实现的呢?Linux内核提供了select,poll,epoll等系统调用。

select:

# 监视的文件描述符(fd)分为三类:writefds(写事件集合)、readfds(读事件集合)、exceptfds(异常事件集合)

# 调用底层select函数阻塞等待事件发生

# 轮询所有事件描述符集合,检查是否有事件发生,如果有就进行处理

# select 能够监视的文件描述符的数量上有限制,在linux上是1024

 

poll:

本质上和select差不多。poll使用pollfd这种结构,包含了要监视的事件和发生的事件

# 调用底层poll函数

# 轮询的时候同时检查这三种事件是否发生

# 但是pollfd并没有最大数量限制

 

epoll:

# 没有最大并发连接的限制,能打开的FD的上限远大于1024

# 不会轮询所有的描述符,而只是去

# 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递

 

四 字符串

4.1 命令

4.1.1 设置值 set/setnx

set key value [ex seconds] [px milliseconds][nx|xx]

ex: 设置秒级别的过期时间

px: 设置毫秒级别的过期时间

nx: key必须不存在,才可以设置成功

xx: key必须存在才可以设置成功

192.168.3.200:6379> set name henry ex 10 nx

OK

等价于setnx namehenry

192.168.3.200:6379> set color yellow

OK

192.168.3.200:6379> set color white px 100000 xx

 

4.1.2 批量设置值 mset

mset ip 192.168.3.20 port 8080

 

4.1.3 获取值,如果获取的key不存在则返回null

get key

 

4.1.4 获取多个keymget,不能存在的key值为nil

mget key [……key]

mget ip port

 

mget 和 get区别:

批量操作命令可以有效提高开发效率,加入没有mget这样的命令,要执行n次get命令的时间:

N次get时间 = N次网络时间 + N次命令执行时间

如果使用mget N 个key呢?

N次get时间 = 1次网络时间 + N次命令执行时间

但是批量操作,虽然有助于提升效率,但是并不是无节制的,数量过多可能造成Redis阻塞

 

4.1.5 自增自减

4.1.5.1 自增incr

incr, 用于自增操作

incr key

值不是整数返回错误;值是整数,返回自增后的结果;key不存在,按照值为0自增

4.1.5.2指定数字自增incrby

incrby key 10

 

4.1.5.3 自减 decr

decr key

 

4.1.5.4 自减指定数字decrby

decrby key 1

 

4.1.6 追加值

向字符串尾部增加值:append keyvalue

 

4.1.7 求字符串长度

strlen key

 

4.1.8 设置值并返回原来的值

getset key value

192.168.3.200:6379> getset address "RoyalRoad #1204"

(nil)

192.168.3.200:6379> getset address "MusicBlock"

"Royal Road #1204"

 

 

4.1.9 获取部分字符串

getrange key start end

192.168.3.200:6379> getrange address 0 4

"Music"

 

4.2 内部编码

int: 8个字节长整型

embstr: 小于等于39个字节的字符串

raw: 大于39个字节的字符串

 

Redis会根据当前值的类型和长度来决定使用哪一种内部编码实现。

192.168.3.200:6379> set desc "这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。"

192.168.3.200:6379> object encoding desc

"raw"

192.168.3.200:6379> set num 1234

OK

192.168.3.200:6379> object encoding num

"int"

192.168.3.200:6379> object encoding ip

"embstr"

 

五 哈希类型

Redis中,哈希类型是指键值本身又是一个键值对结构,即类似于字典的形式

我们知道,如果要存储用户信息有2种方式:

第一种:用户ID为key,值为用户对象

优点:简单

缺点:需要序列化和反序列化,如果要修改某个对象属性,需要取回该对象,并且修改时需要考虑并发的问题。

第二种:通过修改标签,使得标签唯一,比如user:{id}:name,user:{id}:age诸如此类,这样的话,我们不会涉及到序列化和反序列化,也不会涉及到并发问题,但是这样的话会有很多的key,对于内存来说也是不太好的

所以哈希能够很好解决这个问题:

可以保证key只有一个,field-value保证了我可以取任意user属性,而不会进行序列化和反序列化的问题,也没有并发的问题。

 

5.1 命令

5.1.1 设置值,成功返回1,错误返回0

hset key field value

192.168.3.200:6379> hset user:1 name "nickyzhang"

(integer) 1

192.168.3.200:6379> hset user:1 age 30

(integer) 1

192.168.3.200:6379> hset user:1 address"roayl road #145"

 

5.1.2 获取值,没有返回nil

hget key field

192.168.3.200:6379> hget user:1 name

"nicky zhang"

192.168.3.200:6379> hget user:1 address

"roayl road #145"

192.168.3.200:6379> hget user:1 company

(nil)

 

5.1.3 删除field

hdel key field [field….]

hdel user:1 mail1

hdel user:1 mail1 mail2

 

5.1.4 计算field个数

hlen key

hlen user:1

 

5.1.5 批量设置或获取filed-value

hmset key field value [field value……]

hmget key field [field ……]

hmset product:1000 displayName "杨小妖进口安第斯山脉尊贵骆马绒大衣阿尔巴卡羊驼绒羊毛呢外套冬" price 2389.45 review 784

hmget product:1000 displayName review

 

5.1.6 判断field是否存在

hexists key field

hexists product:1000 review

 

5.1.7 获取所有field

hkeys key

hkeys user:1

 

5.1.8 获取所有value

hvals key

hvals product:1000

5.1.9 获取所有field-value

hgetall key

192.168.3.200:6379> hgetall user:1

1) "name"

2) "nicky zhang"

3) "age"

4) "30"

5) "address"

6) "roayl road #145"

 

最后几个命令注意了,在实际中慎用,因为redis是单线程的,所以取到全部数据然后遍历,这个比价耗时,会影响其他客户端的请求

 

5.1.10自增

hincrby hincrbyfloat

hincrby key field increment

hincrbyfloat key field increment

hincrby user:1 age 10

hincrbyfloat product:1000 price 5.5

 

5.1.11计算value字符串长度

hstrlen key field

hstrlen user:1 name

 

5.2 内部编码

5.2.1ziplist

压缩列表:当哈希表的元素个数小于hash-max-ziplist-entries配置,默认512的时候,同时所有值都小于hash-max-ziplist-value的配置,默认64字节时,Redis使用ziplist作为哈希的内部实现,所以比之hashtable更加节约内存

5.2.2hashtable

当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现,因为此时的ziplist读写效率会下降。

192.168.3.200:6379> object encoding product:1000

"hashtable"

192.168.3.200:6379> object encoding user:1

"ziplist"

 

哈希类型和关系型数据库表的比较:

# 关系型数据库可以做复杂的查询,但是Redis 的哈希比较困难

六 列表

用来存储多个有序的字符串,一个列表最多可以存储2^32-1个元素。Redis中可以对列表两端插入(push)和弹出(pop),还可以获取指定范围内的元素列表,或者指定下标的元素。所以他既可以充当栈又可以充当队列的角色

 

 

 

6.1 命令

6.1.1 添加

6.1.1.1 右边添加

rpush key value

rpush animals hadoop

6.1.1.2 左边添加

lpush key value

lpush animals hive

6.1.1.3 某个元素前或者后添加

linsert key before|after pivot value

在列表key中查找value为pivot的元素的前或者后插入value

linsert animals after hadoop pig

192.168.3.200:6379> lrange animals 0 -1

1) "hive"

2) "hadoop"

3) "pig"

 

6.1.2 查找

6.1.2.1 获取指定范围内的元素列表

lrange key start end(包括start和end)

lrange animals 0 4

lrange animals 0 -1表示所有

 

6.1.2.2 获取指定索引下标的元素

lindex key index

lindex animals 2

6.1.2.3 获取列表长度

llen key

llen animals

6.1.3 删除

6.1.3.1 左侧弹出元素

lpop key

lpop animals

6.1.3.2 右侧弹出元素

rpop key

rpop animals

6.1.3.3 删除指定元素

lrem key count value

从列表中找到等于value的元素进行删除,根据count的不同分为三种情况:

# count > 0: 从左到右,删除最多count个元素

lrem animals 1 tigers

# count < 0: 从右到左 删除最多|count|个元素

lrem animals -2 tigers

# count = 0:删除所有

lrem animals 0  tigers

6.1.3.4 按照索引范围修建列表

ltrim key start end

那么就只会保留start+1 到 end+1个元素

192.168.3.200:6379> ltrim animals 2 5

OK

192.168.3.200:6379> lrange animals 0 -1

1) "panda"

2) "hive"

3) "hadoop"

4) "pig"

只会保留3-6上的元素

6.1.4 修改

lset key index newvalue

lset animals 3 pigs

6.1.5 阻塞

blpop key [key……] timeout

brpop key [key……] timeout

如果列表为空:timeout=3,那么客户端需要等待3秒才能返回,如果timeout=0,那么客户端一直等待下去,直到列表不为空

blpop fruits 0 会一直等待 直到fruits这个key中有数据才返回

brpop fruits 10 会一直等待10秒,不管有没有数据都返回,如果10秒内fruits有数据,也返回

如果有多个客户端同时在阻塞等待,由于是单线程的,那么同一时刻肯定只有一个可以返回

# 列表不为空,会立即返回

6.2 内部编码

6.2.1ziplist

当列表元素不超过list-max-ziplist-entries配的数字,默认512,同时列表,每一个元素的值都小于list-max-ziplist-value配置时,默认64字节使用

6.2.2 linkedlist

当列表类型无法满足ziplist的条件时,Redis就会使用linkedlist作为列表的内部实现

 

lpush+brpop可实现阻塞队列,或者消息队列:生产者从左侧插入元素,消费者从右侧取出数据,如果没有数据就一直阻塞

lpush+lpop: 可实现栈

lpush+rpop: 实现队列

 

七 集合

也是用来保存多个元素,和列表不一样的是,集合中不允许重复元素,而且是无序的。索引无法通过索引下标获取元素。一个集合也最多可以存储2^32-1各元素,Redis处理支持集合的增删改查,还支持集合取交集,并集,差集等

7.1 命令

7.1.1 集合内操作

7.1.1.1 添加元素

sadd key element [element……]

sadd plants narcissus cactus hollyhock orchid

7.1.1.2 删除元素

srem key element [element……]

srem plants narcissus

7.1.1.3 计算元素个数

scard key

scard plants

7.1.1.4 判断元素是否在集合中

sismemeber key element,存在返回1,否则返回0

sismember plants orchid

 

7.1.1.5 随机从集合返回指定个数元素

srandmember key [count]

srandmember plants

srandmember plants 2

 

7.1.1.6 从集合随机弹出元素,也就是删除了

spop key

spop plants

7.1.1.7 获取所有元素

smembers key

smembers plants

 

7.1.2 集合间操作

192.168.3.200:6379> sadd user:1:favorites itfootball music surfing

(integer) 4

192.168.3.200:6379> sadd user:2:favoritesbasketball music surfing reading

7.1.2.1 求交集

sinter key [key……]

192.168.3.200:6379> sinter user:1:favoritesuser:2:favorites

1) "music"

2) "surfing"

 

7.1.2.2 求并集

sunion key [key……]

192.168.3.200:6379> sunion user:1:favoritesuser:2:favorites

1) "surfing"

2) "it"

3) "football"

4) "basketball"

5) "music"

6) "reading"

 

7.1.2.3 求差集

sdiff key [key……]

192.168.3.200:6379> sdiff user:1:favoritesuser:2:favorites

1) "it"

2) "football"

 

7.1.2.4 将交集,并集,差集的结果保存为新的key

sinterstore destination key [key……]

sunionstore destination key [key……]

sdiffstore destination key [key……]

192.168.3.200:6379> sinterstore user:1_2:interuser:1:favorites user:2:favorites

(integer) 2

192.168.3.200:6379> sdiffstoreuser:1_2:diff  user:1:favoritesuser:2:favorites

 

7.2 内部编码

7.2.1 intset

当集合中元素都是整数,且都小于set-max-intset-entries配置,默认512个,redis会选用intset作为集合的内部实现,从而减少内存的使用

7.2.2hashtable

当集合类型无法满足intset的条件的时候,Redis会使用hashtable作为集合内部实现

192.168.3.200:6379> sadd nums 1 2 3 4

(integer) 4

192.168.3.200:6379> object encoding nums

"intset"

 

一般情况,比较典型的使用场景是标签,比如一个用户可能对娱乐,体育比较感兴趣,另一个用户可能对历史,新闻比较感兴趣,这些兴趣就是标签。有了这些数据就可以对得到喜欢同一个标签的人,以及用户的共同喜好的标签,对电商网站对不同类型的用户做推荐。

给用户加标签:

sadd user:1:tags tag1 tag2 tag5

sadd user:2:tags tag1 tag3 tag5

给标签添加用户

sadd tag1:users user:1 user:2

sadd tag2:users user:1 user:3

删除用户下的标签:

srem user:1:tags tag1

 

八 有序集合

集合可以排序,但是和列表排序的依据不一样的是,它给每一个元素设置一个分数作为排序依据。有序集合不能重复,但是分数可以重复

8.1 命令

8.1.1 集合内部操作

8.1.1.1 添加

zadd key score member [score member]

你也可以添加以下参数:

nx: member 不存在则添加

xx:member 存在则添加

ch: 此次操作完成后,有序集合元素元素和分数发生变化的个数

incr: 对score做增加,相当于zincrby

zadd fruits 15 apple 20 banana 45 orange

8.1.1.2 计算成员个数

zcard key

zcard fruits

8.1.1.3 获取某个成员的分数

zscore key member

zscore fruits orange

8.1.1.4 计算成员的排名

zrank key member: 从低到高

zrevrank key member 从高到低

192.168.3.200:6379> zrank fruits orange

(integer) 2

192.168.3.200:6379> zrevrank fruits orange

(integer) 0

8.1.1.5 删除成员

zrem key member [member]

 

8.1.1.6 增加成员的分数

zincrby key increment member

zincrby fruits 50 apple

 

8.1.1.7 返回指定排名范围的成员

如果加上withscores参数,表示还会返回分数

zrange key start end [withscores]

zrevrange key start end [withscores]

192.168.3.200:6379> zrange fruits 2 5 withscores

1) "orange"

2) "45"

3) "pear"

4) "56"

5) "apple"

6) "65"

7) "grape"

8) "78"

192.168.3.200:6379> zrevange fruits 4 6withscores

(error) ERR unknown command 'zrevange'

192.168.3.200:6379> zrevrange fruits 4 6withscores

1) "lemon"

2) "22"

3) "banana"

4) "20"

8.1.1.8 返回指定分数范围排名的成员

zrangebyscore key min max [withscores] [limitoffset count]

zrevrangebyscore key min max [withscores] [limitoffset count]

withscores: 返回每一个成员的分数

limit offset count:限制输出的起始位置和个数

zrangebyscore fruits 50 80 withscores

 

8.1.1.10 删除指定排名内的升序元素

zremrangebyrank key start end

8.1.1.11 删除制指定分数范围的成员

zremrangebyscore key min max

 

8.1.2 集合之间的操作

8.1.2.1 交集

zinterstore destination numkeys key [key…..][weights weight [weight….]]

[aggregate sum|max|min]

destination: 交集的结果要保存的key名字

numkeys: 要进行交集的key的个数

key:具体的key

weights weight[ weight]: 为每一个键,在做交集计算的时候,会将自己分数乘以这个权重

aggregate min|max|sum: 计算成员交集后,分之按照sum,min,max进行聚合

 

 

8.1.2.2 并集

zunionstore destination numkeys key [key ...][WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

 

8.2 内部编码

8.2.1ziplist

当有序集合的元素个数小于zset-max-ziplist-entries,默认512的时候,同时每一个元素的值都小于zset-max-ziplist-value配置的值,就使用ziplist作为内部编码

8.2.2skiplist

当ziplist不满足条件的时候,就使用skiplist

 

九  key的管理

9.1 单个key管理

9.1.1key类型

192.168.3.200:6379> type fruits

zset

9.1.2 删除key

del key

9.1.3 查看内部编码

object encoding key

9.1.4 是否存在某个key

exists key

9.1.5 设置key到期时间

expire key 5(秒级)

pexpire key 5000(毫秒级)

ttl key检查到期时间(秒级)

pttl key 检查到期时间(毫秒级)

9.1.6 重命名key

rename key newkey

重命名期间,会删除旧的键,如果键对应的值比较大,会存在阻塞Redis的可能性

9.1.7 随机返回一个key

randomkey

9.1.8 迁移key,从一个Redis迁移到另一个Redis

# Redis内部key迁移:move key db

192.168.3.200:6379> move animals 1

(integer) 1

192.168.3.200:6379> select 1

OK

192.168.3.200:6379[1]> keys *

1) "animals"

192.168.3.200:6379[1]>

# 不同Redis实例key迁移:

 

dump key 会将键值序列化,格式采用rdb

restore key ttl value # 将序列化的值复原,其中ttl参数代表过期时间,ttl=0表示没有过期时间

# migrate 也适用于Redis实例间进行数据迁移

migrate host port key| destination-db timeout[COPY] [REPLACE] [KEYS key]

host:目标数据库的IP地址

port: 目标数据库端口

key|””: 如果迁移一个key,则用key,如果迁移多个则使用“”

destination-db: 目标数据库

timeout: 迁移的超时时间

copy: 如果添加此选项,并不删除原来key

replace: 不管是否目标数据库存在这个key,给替换掉

keys: 表示迁移多个key

migrate 192.168.3.201 6379 "" 1 5 copyKEYS fruits plants

192.168.3.201:6379[2]> select 1

OK

192.168.3.201:6379[1]> keys *

1) "fruits"

2) "plants"

migrate 192.168.3.201 6379 plants 2 5 replace

 

9.2 遍历key

9.2.1 全量遍历key

keys *

192.168.3.200:6379> keys fr[u|a]its

1) "fruits"

192.168.3.200:6379> keys ?ruits

1) "fruits"

9.2.2 渐进式遍历

scan cursor [MATCH pattern] [COUNT count]

cursor: 游标,第一次遍历是从0开始,每次scan遍历完都会返回当前游标的值,直到游标变为0,表示遍历结束、

MATCH pattern:模式匹配

COUNT count:每一次要遍历key的个数

192.168.3.200:6379> scan 0

1) "0"

2) 1) "user:1:favorites"

   2)"counts"

   3)"user:2:favorites"

   4)"user:1_2:diff"

   5)"user:1_2:inter"

   6)"fruits"

192.168.3.200:6379> scan 0 COUNT 2

1) "10"

2) 1) "user:1:favorites"

   2)"counts"

192.168.3.200:6379> scan 10 COUNT 2

1) "9"

2) 1) "user:2:favorites"

   2)"user:1_2:diff"

192.168.3.200:6379> scan 9 COUNT 2

1) "3"

2) 1) "user:1_2:inter"

   2)"fruits"

192.168.3.200:6379> scan 3 COUNT 2

9.3 数据库管理

9.3.1 切换数据库

select dbindex

入股破默认的话,我们知道默认有16个,那么索引就是0-15,Redis3.x版本已经弱化这个功能了

 

9.3.2 清除数据库

flushdb/flushall: 用于清除数据库,区别是flushdb只清除当前数据库,flushall会清除所有数据库,所以一旦误操作之后,后果不堪设想。

猜你喜欢

转载自blog.csdn.net/wojiushiwo577785/article/details/78998039
今日推荐