redis

一、简介

使用场景

  • 缓存
  • 排行榜系统
  • 计数器应用
  • 社交网络
  • 消息队列系统

Redis适合存贮数据规模不太大,热数据。

单线程也快

  1. 纯内存访问,内存的响应时长约为100纳秒,这是Redis达到每秒万级别访问的基础
  2. 非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多时间
  3. 单线程避免了线程切换和竞态产生的消耗

二、数据类型

常用数据类型有string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。

这些数据结构都是Redis对外的数据结构,实际上每种数据结构都有自己底层的内部编码实现,而且是多种实现,且随Redis版本迭代会有变化。这样设计好处,一是内部编码改进后对外部无影响,二是多种内部编码可以在不同场景下发挥各自的优势。


对外数据结构.PNG

内部编码实现.PNG
object encoding key

返回内部编码实现

1、字符串

优先键都是字符串类型,而且其他几种数据结构都是再字符串基础上构建的。

字符串类型的值可以是字符串(简单的字符串,复杂的字符串如JSON、XML)、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB

命令

1、设置值

set key value

对同一个键多次设置会覆盖旧值

setnx key value

只有该键不存在才能设置成功。由于Redis单线程,如果有多个客户端同时执行setnx,只有一个能成功,setnx可以作为分布式锁的一种实现方案。
2、获取值

get key

不存在返回nil
3、批量设置值

mset key value [key value ...]

4、批量获取值

mget key [key ...]

批量操作可以提高效率


多次get.PNG

批量get.PNG

Redis可以支撑每秒数万的读写操作,是指Redis服务端的处理能力,而网络可能成为性能瓶颈。
5、计数

incr key

自增1,返回自增后的值,键不存在当作0自增,返回1

decr key  // 自减
incrby key increment  // 自增指定数字
decrby key increment  // 自减指定数字
incrbyfloat key increment  // 自增浮点数

内部编码

  • int: 8个字节的长整型
  • embstr: 小于等于39个字节的字符串
  • raw: 大于39个字节的字符串
    Redis会根据当前值的类型和长度决定使用哪种内部编码实现。


    常见字符串命令.PNG

2、哈希

Redis中的哈希类型是指键值对里的值也是键值对类型,形如value={{field1, value1}, ...{fielNd, valueN}}

命令

1、设置值

hset key field value

hsetnx类似setnx,但作用域由键变成field

2、获取值

hget key value

3、批量设置获取

hmset key field value [field value ...]
hmget key field [field ...]

4、删除field

hdel key field [field ...]

5、计算field个数

hlen key

6、判断field存在

hexists key field

7、获取所有field

hkeys key

8、获取所有值

hvals key

9、field值自增

hincrby key field increment
hincrbyfloat key field increment

内部编码

  • ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个),同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,更节省内存
  • hashtable(哈希表):当哈希类型无法满足ziplist条件时,Redis会使用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)。


    常见哈希命令.PNG

3、列表

列表类型是用来存储多个有序的字符串的,一个列表最多存储232-1个元素,可以两端插入和弹出元素,故可充当栈和队列。

list操作.PNG

命令

1、添加元素

rpush key value [value ...]  // 从右边插入
lpush key value [value ...]  // 从左边插入

2、获取列表长度

llen key

3、索引查值

索引从0开始计算

lrange key start end  // start至end的索引范围内的值,包括end
lindex key index  // index处的值

4、删除元素

rpop key  // 从右边弹出
lpop key  // 从左边弹出

5、阻塞弹出

brpop key [key ...] timeout
blpop key [key ...] timeout
  • 列表为空:如果timeout=3,则会等待3秒返回nil,如果timeout=0,会一直阻塞等待下去
  • 列表非空:timeout无效,立即返回
  • 多个键时,会遍历,只要有一个键能弹出元素就返回

内部编码

  • ziplist(压缩列表):当列表类型元素个数小于hash-max-ziplist-entries配置(默认512个),同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为列表的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,更节省内存
  • linkedlist(链表):当列表类型无法满足ziplist条件时,Redis会使用linkedlist作为哈希的内部实现。
  • quicklist:结合了ziplist和linkedlist两者的优势

使用场景

  • lpush + lpop = 栈
  • lpush + rpop = 队列
  • lpush + brpop = 消息队列


    常见列表命令.PNG

4、集合

一个集合最多存储232-1个元素,集合中元素是不重复的,无序的。Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。

命令

1、添加元素

sadd key element [element ...]

返回添加成功元素个数

2、删除元素

srem key element [element ...]

返回删除成功元素个数

3、计算元素个数

scard key

直接内部用于统计数量的变量的值,时间复杂度为0(1)

4、判断元素是否存在

sismember key element

存在返回1,反之0

5、随机返回指定个数元素

srandmember key [count]

count是可选参数,不写则默认为1

6、随机弹出一个元素

spop key

返回弹出的元素,集合不存在返回nil

7、获取所有元素

smembers key

元素过多可能阻塞Redis,慎用

集合间的操作

1、交集

sinter key [key ...]

2、并集

sunion key [key ...]

3、差集

sdiff key [key ...]
集合间操作.PNG

集合间的运算比较耗时,所以Redis提供了三个命令(原命令+store)将集合间的交集、并集、差集保存在destination key中。

sinterstore destination key [key ...]
sunionstore destination key [key ...]
sdiffstore destination key [key ...]

内部编码

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,Redis会选用intset来作为集合内部编码实现,节省内存
  • hashtable(哈希表):当集合元素无法满足intset条件时,Redis会使用hashtable


    常见集合命令.PNG

5、有序集合

有序集合的元素也不能重复,同时它给每个元素设置一个分数(score)作为排序的依据,分数可以有重复。有序集合提供了获取指定分数和元素范围查询、计算成员排名等功能。


有序集合.PNG

列表、集合和有序集合三者异同点


三者异同.PNG

命令

1、添加成员

zadd key score member [score member ...]

返回添加成功元素个数。有序集合相比集合提供了排序,但也产生了代价,zadd的时间复杂度为O(log(n)),sadd为O(1)

2、计算成员个数

zcard key

时间复杂度为O(1)

3、计算某个成员的分数

zscore key member

返回分数,成员不存在返回nil

4、计算成员的排名

zrank key member       // 分数从低到高
zrevrank key member   // 分数从高到低

排名从0开始计算,成员不存在返回nil

5、删除成员

zrem key member [member ...]

返回删除成功元素个数

6、增加成员的分数

zincrby key increment member

返回增加后的分数,increment可以为负数

7、返回指定排名范围的成员

zrange key start end [withscores]
zrevrange key start end [withscores]

withscores同时返回成员的分数

8、返回指定分数范围的成员

zrangebyscore key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]

limit offset count选项可以限制输出的起始位置和个数。同时min和max还支持开区间(小括号)和闭区间(中括号),-inf和+inf分别代表无限小和无限大。

9、返回指定分数范围成员个数

zcount key min max

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

zremrangebyrank key start end

11、删除指定分数范围的成员

zremrangebyscore key min max

内部编码

  • ziplist(压缩列表):当有序集合元素个数小于zset-max-ziplist-entries配置(默认128个),同时所有值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为有序集合的内部实现,ziplist更节省内存
  • skiplist(跳跃表):当ziplist无法满足条件时,Redis会使用skiplist作为内部实现


    常见有序集合命令.PNG

键管理

1、查看所有键

keys *

keys命令会遍历所有键,时间复杂度为O(n),当Redis保存了大量键时,线上环境禁止使用。

2、键总数

dbsize

dbsize命令直接获取Redis内置的键总数变量,时间复杂度为O(n)

3、检查键存在

exists key

存在返回1,反之0

4、删除键

del key [key...]

可一次删除多个键,返回成功删除键个数,删除一个不存在的键返回0

5、键过期

expire key seconds   // 键在seconds秒后过期
expireat key timestamp   // 键在秒级别时间戳后过期

persist key   // 清除过期设置

键要先存在才能设置其过期时间,如果seconds为负,则直接删除键

ttl key

ttl返回键剩余时间,返回以下情况

  • 大于等于0的整数:键剩余时间秒数
  • -1:键没设置过期时间
  • -2:键不存在

6、键所对于值的数据结构类型

type key

可返回string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合),键不存在返回none

7、键重命名

rename key newkey

如果newkey已经存在,则newkey的值会被覆盖,可以使用renamenx命令,在newkey不存在时才重命名成功。由于重命名会执行del命令删除旧键,如果键对应的值比较大,会阻塞Redis

8、随机返回一个键

randomkey

三、持久化

RDB

Redis会定期保存数据快照至一个rbd文件中,并在启动时自动加载rdb文件,恢复之前保存的数据。可以在配置文件中配置Redis进行快照保存的时机:

save [seconds] [changes]

意为在[seconds]秒内如果发生了[changes]次数据修改,则进行一次RDB快照保存,例如

save 60 100

会让Redis每60秒检查一次数据变更情况,如果发生了100次或以上的数据变更,则进行RDB快照保存。可以配置多条save指令,让Redis执行多级的快照保存策略。Redis默认开启RDB快照。

也可以通过BGSAVE命令手动触发RDB快照保存。

RDB优点

  • 对性能影响最小。如前文所述,Redis在保存RDB快照时会fork出子进程进行,几乎不影响Redis处理客户端请求的效率。
  • 每次快照会生成一个完整的数据快照文件,所以可以辅以其他手段保存多个时间点的快照(例如把每天0点的快照备份至其他存储媒介中),作为非常可靠的灾难恢复手段。
  • 使用RDB文件进行数据恢复比使用AOF要快很多。

RDB缺点

  • 快照是定期生成的,所以在Redis crash时或多或少会丢失一部分数据。
  • 如果数据集非常大且CPU不够强(比如单核CPU),Redis在fork子进程时可能会消耗相对较长的时间,影响Redis对外提供服务的能力。

AOF

采用AOF持久方式时,Redis会把每一个写请求都记录在一个日志文件里。在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。AOF默认是关闭的,如要开启,进行如下配置:

appendonly yes

AOF提供了三种fsync配置,always/everysec/no,通过配置项[appendfsync]指定:

  • appendfsync no:不进行fsync,将flush文件的时机交给OS决定,速度最快
  • appendfsync always:每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢
  • appendfsync everysec:折中的做法,交由后台线程每秒fsync一次

随着AOF不断地记录写操作日志,因为所有的操作都会记录,所以必定会出现一些无用的日志。大量无用的日志会让AOF文件过大,也会让数据恢复的时间过长。不过Redis提供了AOF rewrite功能,可以重写AOF文件,只保留能够把数据恢复到最新状态的最小写操作集。

AOF rewrite可以通过BGREWRITEAOF命令触发,也可以配置Redis定期自动进行:

auto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb

上面两行配置的含义是,Redis在每次AOF rewrite时,会记录完成rewrite后的AOF日志大小,当AOF日志大小在该基础上增长了100%后,自动进行AOF rewrite。同时如果增长的大小没有达到64mb,则不会进行rewrite。

AOF优点

  • 最安全,在启用appendfsync always时,任何已写入的数据都不会丢失,使用在启用appendfsync everysec也至多只会丢失1秒的数据。
  • AOF文件在发生断电等问题时也不会损坏,即使出现了某条日志只写入了一半的情况,也可以使用redis-check-aof工具轻松修复。
  • AOF文件易读,可修改,在进行了某些错误的数据清除操作后,只要AOF文件没有rewrite,就可以把AOF文件备份出来,把错误的命令删除,然后恢复数据。

AOF缺点

  • AOF文件通常比RDB文件更大
  • 性能消耗比RDB高
  • 数据恢复速度比RDB慢

建议策略

Redis的数据持久化工作本身就会带来延迟,需要根据数据的安全级别和性能要求制定合理的持久化策略:

  • AOF + fsync always的设置虽然能够绝对确保数据安全,但每个操作都会触发一次fsync,会对Redis的性能有比较明显的影响
  • AOF + fsync every second是比较好的折中方案,每秒fsync一次
  • AOF + fsync never会提供AOF持久化方案下的最优性能
  • 使用RDB持久化通常会提供比使用AOF更高的性能,但需要注意RDB的策略配置
  • 每一次RDB快照和AOF Rewrite都需要Redis主进程进行fork操作。fork操作本身可能会产生较高的耗时,与CPU和Redis占用的内存大小有关。根据具体的情况合理配置RDB快照和AOF Rewrite时机,避免过于频繁的fork带来的延迟

Redis在fork子进程时需要将内存分页表拷贝至子进程,以占用了24GB内存的Redis实例为例,共需要拷贝24GB / 4kB * 8 = 48MB的数据。在使用单Xeon 2.27Ghz的物理机上,这一fork操作耗时216ms。

可以通过INFO命令返回的latest_fork_usec字段查看上一次fork操作的耗时(微秒)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • 《Redis 入门指南》(第二版) 第一章 Redis 是什么 Redis (REmote Dictionary ...
    EdenPP阅读 67,296评论 3 10
  • 五种数据结构简介 Redis是使用C编写的,内部实现了一个struct结构体redisObject对象,通过结构体...
    彦帧阅读 6,934评论 0 14