一、Redis 是什么

Redis 是一个使用 C 语言写成的,开源的、key-value 结构的、非关系型数据库。它支持存储的 value 类型相对更多,包括 String(字符串)、List(列表)、Set(集合)、Sorted Set(有序集合) 和 Hash(哈希),而且这些操作都是原子性的。在此基础上,Redis 支持各种不同方式的排序。为了保证效率,数据都是缓存在内存中。Redis 可以周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

使用 Redis 有哪些好处?
  • 速度快,因为数据存在内存中;
  • 支持丰富数据类型,支持 string,list,set,sorted set,hash等;
  • 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行;
  • 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除;

二、单线程?

你可能听说过 Redis 是单线程的,那会不会很慢呢?为什么 Redis 是单线程的?

Redis 的数据存储在内存中,如果数据全都在内存里,单线程的去操作就是效率最高的。

为什么呢?

  • 因为多线程的本质就是CPU模拟出来多个线程的情况,这种模拟出来的情况就有一个代价,就是上下文的切换,对于一个内存的系统来说,它没有上下文的切换就是效率最高的。因为上下文切换所花费的时间远大于直接从内存中读取数据所花费的时间。
  • Redis 用单个 CPU 绑定一块内存的数据,然后针对这块内存的数据进行多次读写的时候,都是在一个CPU上完成的,所以它是单线程处理这个事。在内存的情况下,这个方案就是最佳方案。
  • 这里我们一直在强调的单线程,只是在处理我们的网络请求的时候只有一个线程来处理,一个正式的 Redis Server 运行的时候肯定是不止一个线程的,例如 Redis 进行持久化的时候会以子进程或者子线程的方式执行。

三、Redis 数据类型

1、String

String 数据结构是简单的 key-value 类型,value 其实不仅可以是String,也可以是数字。需要注意是一个键值最大存储 512MB。

> set myName "redis"
OK
> get myName
redis
> set myName "memcache"
OK
> get myName
memcache
> set count 1
OK
> get count
1
> incr count
2
2、Hash(哈希)

Hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。 比如我们可以 Hash 数据结构来存储用户信息,商品信息等等。

> hmset lilei name "LiLei" age 26 title "Senior"
OK
> hget lilei age
26
> hget lilei title
Senior
> hset lilei title "Primary"
> hget lilei title
Primary
3、List(列表)

List是 Redis 的简单的字符串列表。list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。

> lpush mylist aaa
(integer) 1
> lpush mylist bbb
(integer) 2
> rpush mylist ccc
(integer) 3
> llen mylist
(integer) 3
> lrange mylist 0 2
1) "bbb"
1) "aaa"
1) "ccc"
4、Set

Set 对外提供的功能与 list 类似是一个列表的功能,特殊之处在于 set 不允许重复元素,可以自动排重的,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。

> sadd myset 111
(integer) 1
> sadd myset 222
(integer) 1
> sadd myset 333
(integer) 1
> sadd myset 222
(integer) 0
> smembers myset
1) "111"
2) "222"
3) "333"
5、Sorted Set

与 set 相比,Sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列。

举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Sorted set 结构进行存储。

> zadd myzset 3 a
(integer) 1
> zadd myzset 1 b
(integer) 1
> zadd myzset 2 c
(integer) 1
> zadd myzset 2 c
(integer) 0
> zadd myzset 1 d
(integer) 1
> zrangebyscore myzset 0 3
1) "b"
2) "d"
3) "c"
4) "a"

四、Redis 应用

例1: Redis 中有 1 亿个 key,其中有 10 万个 key 是已知的某个固定前缀,如何将找出这些 key。

当问题是:如何找出某一前缀的 key。这里需要注意数据规模是多大,再结合实际场景去解决。

如果数据量小,可以使用 keys 命令来查询。

> keys key*
1) "key9"
2) "key3"
3) "key1"
4) "key2"
5) "key5"
6) "key8"
7) "key7"
8) "key6"
9) "key"
10) "key4"

但是,如果数据量大,而 Redis 正在线上运行,使用 keys 命令很可能会阻塞 Redis,使其不能提供服务。

这时可以使用 SCAN cursor [MATCH pattern] [COUNT count] 命令。需要注意的是条命令返回的数量是不可控的,只能大概符合 count。

> scan 0 match key* count 5
1) "12"
2) 1) "key9"
2) "key7"
3) "key4"
> scan 12 match key* count 5
1) "14"
2) 1) "key8"
2) "key6"
3) "key5"
> scan 14 match key* count 5
1) "0"
2) 1) "key3"
2) "key1"
3) "key2"
4) "key"

cursor 从 0 开始,这里第一个返回值为 cursor 用于下一次迭代,当 cursor 再次为 0 时,说明迭代完成。

例2:如何通过 Redis 实现分布式锁

首先想到的方法是使用 SETNX key value,如果 key 不存在,则创建并赋值,返回 1;若 key 已存在,则创建失败,返回 0。

当某一个线程通过 SETNX key value 设置成功后占用该资源,其他线程执行该命令就会设置失败,说明已经有线程占用该资源,通过这种方式来实现分布式锁。

> setnx mylock 11
(integer) 1
> setnx mylock 22
(integer) 0
> get mylock
"11"

同时,为了防止死锁,还需要设置 key 的过期时间 EXPIRE key second,设置 key 的过期时间,经过 second 秒后 key 会被删除。

> expire mylock 11
(integer) 1

虽然 Redis 中每个命令都满足原子性,但两个命令组合起来不满足了,例如若设置锁后,程序挂了,来不及设置过期时间,那么这个锁就无法释放了。

所以我们需要把上面两个命令结合起来的命令 SET key value [EX seconds] [PX milliseconds] [NX|XX],其中 NX 是 key 不存在时设置成功,XX 是 key 存在时设置成功。例如:

> set mylock 123 ex 10 nx
OK

这里还需要注意,不要让大量的 key 在同一时间过期,因为删除大量的 key 很耗时,会出现卡顿现象。所以我们可以在设置 key 的过期时间时,加上一个随机值来避免。

例3:使用 Redis 实现消息队列

使用 List 作为队列,RPUSH 生产消息,LPOP 消费消息。

rpush testlist aaa
(integer) 1
> rpush testlist bbb
(integer) 2
> rpush testlist ccc
(integer) 3
> lpop testlist
"aaa"
> lpop testlist
"bbb"
> lpop testlist
"ccc"

但是这样有个缺点,就是只能一对一的消息通信,所以还可以使用 pub/sub 主题订阅者模式,发送者(pub)使用 PUBLISH channel message 发送消息,订阅者(sub)使用 SUBSCRIBE channel 接受消息,并且订阅者可以接收任意数量的 channel。

但是消息的发布是无状态的,无法保证可达性。

五、Redis 数据持久化存储

上面说了 Redis 是基于内存的数据库,一旦进程退出,数据就会丢失,所以我们需要它也可以把数据写到磁盘上,当 Redis 重启后,可以从磁盘中恢复数据。

Redis 提供了两种解决方案将内存中的数据保存到磁盘上。一种是 RDB 持久化,原理是将 Reids 在内存中的全部数据库记录定时 dump 到磁盘上的 RDB 持久化;另外一种是 AOF 持久化,原理是将 Reids 的操作日志以追加的方式写入文件。

1、RDB(Redis DataBase)

RDB 是 Redis 用来进行持久化的一种方式,是把当前内存中的数据集快照写入磁盘,也就是 Snapshot 快照所有数据。恢复时是将快照文件直接读到内存里。

RDB 存储的劣势:

RDB 方式数据没办法做到实时持久化,该方式是每间隔一段时间做一次备份。因为 bgsave 每次运行都要执行 fork 操作创建子进程,属于重量级操作,频繁执行成本过高。所以如果 Redis 意外挂掉,就会丢失最后一次快照后的所有修改;

RDB 是内存数据的全量同步,数据量大会由于 I/O 而严重影响性能;

RDB 文件使用特定二进制格式保存,Redis 版本演进过程中有多个格式的 RDB 版本,旧版本无法兼容新版的格式。

2、AOF(Append Only File)

AOF 持久化:记录除了查询以外,所有变更数据库状态的指令,以 append 的形式追加保存到 AOF 文件中。AOF 文件通常会比 RDF 文件体积更大。

3、RDB-AOF 混合模式

混合模式是先使用 bgsave 以 RDB 形式将内存中的全部数据写入磁盘,之后当有新的数据时,再使用 AOF 的形式追加到文件中。

Redis 学习笔记之基础的更多相关文章

  1. Redis学习笔记之基础篇

    Redis是一款开源的日志型key-value数据库,目前主要用作缓存服务器使用. Redis官方并没有提供windows版本的服务器,不过微软官方开发了基于Windows的Redis服务器Micro ...

  2. Redis学习笔记~目录

    回到占占推荐博客索引 百度百科 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合). ...

  3. Redis学习笔记4-Redis配置详解

    在Redis中直接启动redis-server服务时, 采用的是默认的配置文件.采用redis-server   xxx.conf 这样的方式可以按照指定的配置文件来运行Redis服务.按照本Redi ...

  4. Redis学习笔记(三)Redis支持的5种数据类型的总结

    继续Redis学习笔记(二)来说说剩余的三种数据类型. 三.列表类型(List) 1.介绍 列表类型可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的一段片段.列表类型内部是 ...

  5. Redis学习笔记(二)Redis支持的5种数据类型的总结之String和Hash

    引言 在Redis学习笔记(一)中我们已经会安装并且简单使用Redis了,接下来我们一起来学习下Redis支持的5大数据类型. 简介 Redis是REmote DIctionary Server(远程 ...

  6. Redis学习笔记4-Redis配置具体解释

    在Redis中直接启动redis-server服务时, 採用的是默认的配置文件.採用redis-server   xxx.conf 这种方式能够依照指定的配置文件来执行Redis服务. 依照本Redi ...

  7. redis 学习笔记(6)-cluster集群搭建

    上次写redis的学习笔记还是2014年,一转眼已经快2年过去了,在段时间里,redis最大的变化之一就是cluster功能的正式发布,以前要搞redis集群,得借助一致性hash来自己搞shardi ...

  8. Redis学习笔记7--Redis管道(pipeline)

    redis是一个cs模式的tcp server,使用和http类似的请求响应协议.一个client可以通过一个socket连接发起多个请求命令.每个请求命令发出后client通常会阻塞并等待redis ...

  9. Redis学习笔记一:数据结构与对象

    1. String(SDS) Redis使用自定义的一种字符串结构SDS来作为字符串的表示. 127.0.0.1:6379> set name liushijie OK 在如上操作中,name( ...

  10. Redis学习笔记之ABC

    Redis学习笔记之ABC Redis命令速查 官方帮助文档 中文版本1 中文版本2(反应速度比较慢) 基本操作 字符串操作 set key value get key 哈希 HMSET user:1 ...

随机推荐

  1. 深入解析C#异步编程:await 关键字背后的实现原理

    C# 异步编程中 await 实现原理详解 在C#中,async 和 await 关键字用于编写异步代码.本文将详细介绍 await 的实现原理,包括状态机的生成.回调函数的注册和触发等关键步骤. 1 ...

  2. AI年代,谁还用难用的Keil?快快换CLion!!! 破解+环境替换 [上]

    安装Clion 由于这个地方碰壁比较多,所以大家严格按照我下面的安装节奏就可以了! 记得CLion的版本等,和我所说的不匹配也会导致破解失败. 包能破解安装上的. 1.下载 链接:https://pa ...

  3. 使用flask进行Mock Server模拟接口操作及问题解决

    1.flask介绍 flask是一个轻量级的python web 微框架 2.Mock Server介绍 Mock Server是一个开源的模拟服务器,它可以定义和记录API交互,支持各种http方法 ...

  4. Windows 使用 Intel(R) Arc(TM) GPU 推理ONNX 模型

    这不刚换了一个笔记本电脑,Thinkpad T14P,带有Intel ARC GPU,今天我们来尝试用这个GPU来推理ONNX模型. 环境安装 查阅了相关文档,最好使用py310环境,其他版本可能存在 ...

  5. 利用 Java 实现组合式解析器

    Ward Cunningham 曾经说过,干净的代码清晰地表达了代码编写者所想要表达的东西,而优美的代码则更进一步,优美的代码看起来就像是专门为了要解决的问题而存在的.在本文中,我们将展示一个组合式解 ...

  6. Go操作数据库之MySQL

    安装mysql驱动: go get -u github.com/go-sql-driver/mysql 初始化模块 go mod init m 执行 go mod tidy 导入包: package ...

  7. 【ElementPlus】el-form使用技巧:动态切换校验规则的最佳实践

    喵~ 今天分享一篇在 ElementPlus 中使用 el-form 动态切换校验规则 的实用方法. 一.问题概述 作为前端开发人员,在开发项目中,特别是后台管理系统,表单的使用是必不可少的.当业务需 ...

  8. 使用 spring stream 发送消息

    为什么使用spring stream ? spring stream 是用来做消息队列发送消息使用的.他隔离了各种消息队列的区别,使用统一的编程模型来发送消息. 目前支持: rabbitmq kafk ...

  9. HTML5 新的语义标签

    1.常用的语义标签 意义:语义标签便于阅读代码,也便于搜索引擎解析文档结构与内容 标签 含义 article 定义页面独立的内容区域 aside 定义侧边栏内容 header 定义了文档的头部区域 s ...

  10. Git clone报错“Connection was reset, errno 10054”

    前情 最近在使用一个UI库的时候,发现其中一个BUG,于是想尝试提一个PR. 坑位 我平时习惯用https的方式拉取代码,发现在clone代码的时候一直失败,错误提示:OpenSSL SSL_read ...