一、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. for Qbert sometimes we stay in lives == 0 condtion for a few frames —— baselines中环境包装器EpisodicLifeEnv的分析

    相关: baselines中环境包装器EpisodicLifeEnv的分析 一直不是很理解在reinforcement leanrning算法在atari游戏的observation的交互过程中对li ...

  2. MNN框架在Win10上的部署,支持OpenGL和Vulkan

    上篇记录了之前在win10上部署的流程,不过在camke的时候没有选择支持OpenGL和Vulkan.这里重新按照官方的语雀文档重新进行支持OpenGL和Vulkan的编译,简单做个记录.如果有其他的 ...

  3. 2.TP6的入门-分页类的改写

    看了看推荐的分页类的使用,还是很简单的,可是自己去尝试改写生成的分页类结构就会很麻烦,总是不成功,后来发现手册里面还有这个 就说你想重写分页类,就需要这样做 赶紧实践了一下,先改这里的provider ...

  4. 【昌哥IT课堂】MySQL8.0新特性之不可见主键

    一.概述作为 MySQL DBA ,相信大家都经历过在复制模式下,如果没有主键,遇到 load data ,大事务,ddl 等有大量表数据行扫描的行为时,会带来严重的主从延迟,给数据库稳定性和数据一致 ...

  5. python之DataClass

    Python 在版本 3.7 (PEP 557) 中引入了dataclass.dataclass允许你用更少的代码和更多的开箱即用功能来定义类. 下面定义了一个具有两个实例属性 name 和 age ...

  6. Docker之磁盘清理

    Docker 很占用空间,每当我们运行容器.拉取镜像.部署应用.构建自己的镜像时,我们的磁盘空间会被大量占用. 如果你也被这个问题所困扰,咱们就一起看一下 Docker 是如何使用磁盘空间的,以及如何 ...

  7. Blazor 组件库 BootstrapBlazor 中Markdown组件介绍

    组件介绍 Markdown组件是tui.editor的封装,所以所有内容均基于tui.editor. 默认状态下样子如下所示: 其代码如下: <Markdown Language="@ ...

  8. Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Faile

    解决方法: 1.新建一个安卓应用,复制下面路径红色框的代码  去替换  导入应用中的代码,就是修改gradle版本: 2.在导入的应用中如下路径添加信息 代码: android.overridePat ...

  9. Nuxt.js 应用中的 error 事件钩子

    title: Nuxt.js 应用中的 error 事件钩子 date: 2024/12/3 updated: 2024/12/3 author: cmdragon excerpt: 在任何 Web ...

  10. C# Linq 的三种去重方式(Distinct)

    前言 关于C#中默认的Distinct方法在什么情况下才能去重,这个就不用我再多讲,针对集合对象去重默认实现将不再满足,于是乎我们需要自定义实现来解决这个问题,接下来我们详细讲解几种常见去重方案,孰好 ...