Redis流行的原因(redis为什么用)
nanshan 2024-10-20 07:36 14 浏览 0 评论
1. Redis是什么
Redis官方这样解释
Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams. Redis has built-in replication, Lua scripting, LRU eviction, transactions and different levels of on-disk persistence, and provides high availability via Redis Sentinel and automatic partitioning with Redis Cluster.
简言之Redis(全称:Remote Dictionary Server 远程字典服务)是一个使用ANSI C编写的开源、支持网络、基于内存,并提供多种语言API的可持久化的键值对存储数据库。
2. Redis由来
2008年,意大利的一家创业公司Merzia推出了一款基于MySQL的网站实时统计系统LLOOGG,然而没过多久该公司的创始人Salvatore Sanfilippo(网名Antirez)便开始对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身定做一个数据库,并于2009年开发完成,这个数据库就是Redis。
不过Salvatore Sanfilippo并不满足只将Redis用于LLOOGG这一款产品,而是希望让更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Noordhuis一起继续着Redis的开发,直到今天。
Antirez今年已经四十多岁,依旧在孜孜不倦地写代码,为Redis的开源事业持续贡献力量。
Redis端口为什么是6379?
6379 是 "MERZ " 九宫格输入法对应的数字。
Alessia Merz 是一位意大利舞女、女演员。Redis 作者 Antirez 早年看电视节目,觉得 Merz 在节目中的一些话愚蠢可笑,Antirez 喜欢造“梗”用于平时和朋友们交流,于是造了一个词 “MERZ”,形容愚蠢,与 “stupid” 含义相同。
后来 Antirez 重新定义了 “MERZ” ,形容”具有很高的技术价值,包含技艺、耐心和劳动,但仍然保持简单本质“。
到了给 Redis 选择一个数字作为默认端口号时,Antirez 没有多想,把 “MERZ” 在手机键盘上对应的数字 6379 拿来用了。
3. Redis为什么快?
- 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速
- 数据结构简单(数据结构是专门设计的),对数据操作也简单
- 采用单线程(6.x之前),避免了不必要的上下文切换和竞争条件- 不存在多进程/线程切换消耗 CPU- 不存在加锁/释放锁操作,没有因为可能出现死锁而导致的性能消耗
- 非阻塞I/O多路复用模型
4. RESP协议
RESP全称:REdis Serialization Protocol
- 实现简单
- 快速解析
- 可读性强
Redis协议将传输的结构数据分为5种最小单元类型,单元结束时统一加上回车换行符号\r\n。
- 单行字符串 以 + 符号开头。
- 多行字符串 以 $ 符号开头,后跟字符串长度。
- 整数值 以 : 符号开头,后跟整数的字符串形式。
- 错误消息 以 - 符号开头。
- 数组 以 * 号开头,后跟数组的长度。
- 单行字符串 hello world
+hello world\r\n - 多行字符串 hello world
$11\r\nhello world\r\n
多行字符串当然也可以表示单行字符串。 - 整数 1024
:1024\r\n - 错误 参数类型错误
-WRONGTYPE Operation against a key holding the wrong kind of value\r\n - 数组 [1,2,3]
*3\r\n:1\r\n:2\r\n:3\r\n - NULL 用多行字符串表示,不过长度要写成-1。
$-1\r\n - 空串 用多行字符串表示,长度填 0。
$0\r\n\r\n
注意这里有两个\r\n。为什么是两个?因为两个\r\n之间,隔的是空串.
5. 丰富的数据类型
常用的几种数据类型
- string
- list
- set
- zset
- hash
- HyperLogLog
- bitmap
- Geo
6. 数据过期淘汰策略
懒性删除
- 触发机制
当访问redis中键值对时会判断这个键值对是否过期,如果过期的话就会删除这个键值对并返回nil
优点:对CPU友好,不用执行与当前命令无关的操作
缺点:对内存不友好,当大量过期的键值对不被访问时会浪费大量内存空间
定期删除
为了弥补惰性删除对于内存的不友好,redis中还有一种过期策略即定期删除。
- 触发机制
当一个键值对设置expire后,redis中会维护一个过期字典。这个过期字典在redis中会使用serverCron时间事件轮询,轮询过期键值对进行释放。(redis.conf配置文件中hz配置项配置,serverCron每秒执行次数, 默认10表示每100ms执行一次serverCron)
redis中限制每次过期key清理时间不超过CPU时间的25%,这段时间内会执行如下步骤操作:
- 随机选取过期字典中的100个key
- 淘汰所有的过期key
- 如果过期key超过25个则重复步骤1
主动删除
物理机的内存空间是有限的,当所有内存被占满以后redis接收到写操作命令应该怎么处理?此时就会触发主动删除
- 触发机制
redis.conf配置文件中maxmemory参数设置redis占用内存的大小,当超过这个值限定以后将会根据maxmemory-policy设置清理redis内存对象
有关这个maxmemory提醒一点:集群环境下适当调低maxmemory配置,给output buffer预留空间。因为output buffer空间并不包括在maxmemory中 - 清理策略
清理策略划分可以分为两个维度与三个方面
两个维度分别是过期键中筛选、所有键中筛选
三个方面分别是 lru、ttl、random - volatile-lru:过期键中最长时间未调用的键值对
- volatile-ttl:过期键中即将过期的键值对
- volatile-random:过期键中随机删除
- allkeys-lru:所有键中最长时间未调用的键值对
- allkeys-random:所有键中随机删除
- noevication:不清理,返回异常
7. 持久化
目前,分为3种持久化方式
- RDB
- AOF
- 混合模式(RDB+AOF)
RDB(Redis DataBase)
- 开启方式
redis.conf配置save
# save "" # 关闭RDB
save 900 1 # after 900 sec (15 min) if at least 1 key changed
save 300 10 # after 300 sec (5 min) if at least 10 keys changed
save 60 10000 # after 60 sec if at least 10000 keys changed
功能核心函数:rdbSave() / rdbLoad()
- RDB触发场景:
- 执行 SHUTDOWN 命令(未配置AOF)
2. 执行 SAVE/BGSAVE 命令
127.0.0.1:6379> BGSAVE
21499:M 08 Jun 2020 17:53:18.596 * Background saving started by pid 21528
Background saving started
127.0.0.1:6379> 21528:C 08 Jun 2020 17:53:18.601 * DB saved on disk
21528:C 08 Jun 2020 17:53:18.601 * RDB: 0 MB of memory used by copy-on-write
21499:M 08 Jun 2020 17:53:18.681 * Background saving terminated with success
SAVE/BGSAVE区别
SAVE :使用主进程进行RDB快照数据的持久化(会阻塞Redis其他操作)
BGSAVE :fork()出来子进程进行RDB快照数据的持久化(推荐)
优点:
1.RDB是一个非常紧凑的文件,保存了Redis在某个时间点上的数据集,非常适合用于进行备份
2.RDB在恢复大数据集时的速度比 AOF 的恢复速度要快
缺点:
1.数据集比较庞大时,fork()可能会非常耗时,造成服务器在某段时间内停止处理客户端
2.每隔一段时间才保存一次RDB文件,在这种情况下,一旦发生故障停机,你就可能会丢失好这段时间的数据
AOF(Append-only file)
- 开启方式
redis.conf配置
appendonly yes
# appendfsync always
# appendfsync no
appendfsync everysec
核心函数:flushAppendOnlyFile()
每当执行服务器(定时)任务或者函数时flushAppendOnlyFile函数都会被调用,这个函数执行以下两个工作
WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件
SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中
优点:
1.如不小心执行flushall命令, 只要AOF文件未被重写,停止服务器, 移除AOF文件末尾的FLUSHALL命令并重启Redis,就可以将数据集恢复到flushall执行之前的状态
2.可读性高
3.默认为每秒钟fsync一次,也最多只会丢失一秒钟的数据
缺点:
1.AOF文件比较大
2.加载入内存,耗时比RDB慢
混合模式
- 开启方式
# When rewriting the AOF file, Redis is able to use an RDB preamble in the
# AOF file for faster rewrites and recoveries. When this option is turned
# on the rewritten AOF file is composed of two different stanzas:
#
# [RDB file][AOF tail]
#
# When loading Redis recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# tail.
aof-use-rdb-preamble yes
- 显式的查看混合模式的数据
- 执行BGREWRITEAOF命令
? software redis-cli
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set name ivansli
OK
127.0.0.1:6379> set today 20200608
OK
127.0.0.1:6379> keys *
1) "today"
2) "name"
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
127.0.0.1:6379> set time 19:15:55
OK
2. 查看appendonly.aof文件
REDIS0009? redis-ver5.0.7?
redis-bits?@?ctime?
?^used-mem??
aof-preamble???today <4nameivansli??4?wq???*2
$6
SELECT
$1
0
*3
$3
set
$4
time
$8
19:15:55
混合模式的AOF文件数据,相当于:
某刻的RDB格式全量数据 + 此刻之后的RESP格式增量数据
为什么使用混合模式(RDB优点+AOF优点):
- RDB格式数据加载快速
- AOF追加的RESP数据,可以减少数据的丢失
- 既能保证Redis重启时的速度,又能降低数据丢失的风险
重启之后数据加载
源码(V5.08)追踪
server.c
main() -> loadDataFromDisk()
/* Function called at startup to load RDB or AOF file in memory. */
void loadDataFromDisk(void) {
long long start = ustime();
if (server.aof_state == AOF_ON) {
if (loadAppendOnlyFile(server.aof_filename) == C_OK)
serverLog(LL_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
} else {
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
if (rdbLoad(server.rdb_filename,&rsi) == C_OK) {
serverLog(LL_NOTICE,"DB loaded from disk: %.3f seconds",
(float)(ustime()-start)/1000000);
/* Restore the replication ID / offset from the RDB file. */
if ((server.masterhost ||
(server.cluster_enabled &&
nodeIsSlave(server.cluster->myself))) &&
rsi.repl_id_is_set &&
rsi.repl_offset != -1 &&
/* Note that older implementations may save a repl_stream_db
* of -1 inside the RDB file in a wrong way, see more
* information in function rdbPopulateSaveInfo. */
rsi.repl_stream_db != -1)
{
memcpy(server.replid,rsi.repl_id,sizeof(server.replid));
server.master_repl_offset = rsi.repl_offset;
/* If we are a slave, create a cached master from this
* information, in order to allow partial resynchronizations
* with masters. */
replicationCacheMasterUsingMyself();
selectDb(server.cached_master,rsi.repl_stream_db);
}
} else if (errno != ENOENT) {
serverLog(LL_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
exit(1);
}
}
}
判断是否开启了AOF
- 是:则通过loadAppendOnlyFile() 加载AOF文件
- 否:通过rdbLoad() 加载RDB文件
loadAppendOnlyFile()
/* Replay the append log file. On success C_OK is returned. On non fatal
* error (the append only file is zero-length) C_ERR is returned. On
* fatal error an error message is logged and the program exists. */
int loadAppendOnlyFile(char *filename) {
struct client *fakeClient;
FILE *fp = fopen(filename,"r");
......
/* Check if this AOF file has an RDB preamble. In that case we need to
* load the RDB file and later continue loading the AOF tail. */
char sig[5]; /* "REDIS" */
if (fread(sig,1,5,fp) != 5 || memcmp(sig,"REDIS",5) != 0) {
/* No RDB preamble, seek back at 0 offset. */
if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
} else {
/* RDB preamble. Pass loading the RDB functions. */
rio rdb;
serverLog(LL_NOTICE,"Reading RDB preamble from AOF file...");
if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
rioInitWithFile(&rdb,fp);
if (rdbLoadRio(&rdb,NULL,1) != C_OK) {
serverLog(LL_WARNING,"Error reading the RDB preamble of the AOF file, AOF loading aborted");
goto readerr;
} else {
serverLog(LL_NOTICE,"Reading the remaining AOF tail...");
}
}
/* Read the actual AOF file, in REPL format, command by command. */
while(1) {
int argc, j;
unsigned long len;
robj **argv;
char buf[128];
sds argsds;
struct redisCommand *cmd;
/* Serve the clients from time to time */
if (!(loops++ % 1000)) {
loadingProgress(ftello(fp));
processEventsWhileBlocked();
}
if (fgets(buf,sizeof(buf),fp) == NULL) {
if (feof(fp))
break;
else
goto readerr;
}
......
}
......
}
通过读取AOF文件的前5个字符来判断是否是RDB+AOF混合模式
- 是:则先加载RDB数据(二进制数据),再一条一条的加载AOF数据(RESP协议格式数据)
- 否:一条一条的加载AOF数据(RESP协议格式数据)
大致的重启数据加载流程为:
小结
至于Redis为什么是最流行的键值对存储数据库,仁者见仁智者见智。
个人认为,总结几点:
- 是开源的(节约企业自研成本)
- 数据类型丰富(不仅仅是单纯的k-v)
- 处理速度快(单机读写10W+左右)
- 支持数据持久化
- 多种语言API支持
附:
曾有一同事在追溯Redis源码之后,评价道“这是我看过最舒服的源码”。
源码注释详细不说,各种变量、函数名称也是十分标准。
由此可见一斑
相关推荐
- 服务器数据恢复—Raid5数据灾难不用愁,Raid5数据恢复原理了解下
-
Raid5数据恢复算法原理:分布式奇偶校验的独立磁盘结构(被称之为raid5)的数据恢复有一个“奇偶校验”的概念。可以简单的理解为二进制运算中的“异或运算”,通常使用的标识是xor。运算规则:若二者值...
- 服务器数据恢复—多次异常断电导致服务器raid不可用的数据恢复
-
服务器数据恢复环境&故障:由于机房多次断电导致一台服务器中raid阵列信息丢失。该阵列中存放的是文档,上层安装的是Windowsserver操作系统,没有配置ups。因为服务器异常断电重启后,rai...
- 服务器数据恢复-V7000存储更换磁盘数据同步失败的数据恢复案例
-
服务器数据恢复环境:P740+AIX+Sybase+V7000存储,存储阵列柜上共12块SAS机械硬盘(其中一块为热备盘)。服务器故障:存储阵列柜中有磁盘出现故障,工作人员发现后更换磁盘,新更换的磁盘...
- 「服务器数据恢复」重装系统导致XFS文件系统分区丢失的数据恢复
-
服务器数据恢复环境:DellPowerVault系列磁盘柜;用RAID卡创建的一组RAID5;分配一个LUN。服务器故障:在Linux系统层面对LUN进行分区,划分sdc1和sdc2两个分区。将sd...
- 服务器数据恢复-ESXi虚拟机被误删的数据恢复案例
-
服务器数据恢复环境:一台服务器安装的ESXi虚拟化系统,该虚拟化系统连接了多个LUN,其中一个LUN上运行了数台虚拟机,虚拟机安装WindowsServer操作系统。服务器故障&分析:管理员因误操作...
- 「服务器数据恢复」Raid5阵列两块硬盘亮黄灯掉线的数据恢复案例
-
服务器数据恢复环境:HPStorageWorks某型号存储;虚拟化平台为vmwareexsi;10块磁盘组成raid5(有1块热备盘)。服务器故障:raid5阵列中两块硬盘指示灯变黄掉线,无法读取...
- 服务器数据恢复—基于oracle数据库的SAP数据恢复案例
-
服务器存储数据恢复环境:某品牌服务器存储中有一组由6块SAS硬盘组建的RAID5阵列,其中有1块硬盘作为热备盘使用。上层划分若干lun,存放Oracle数据库数据。服务器存储故障&分析:该RAID5阵...
- 「服务器虚拟化数据恢复」Xen Server环境下数据库数据恢复案例
-
服务器虚拟化数据恢复环境:Dell某型号服务器;数块STAT硬盘通过raid卡组建的RAID10;XenServer服务器虚拟化系统;故障虚拟机操作系统:WindowsServer,部署Web服务...
- 服务器数据恢复—RAID故障导致oracle无法启动的数据恢复案例
-
服务器数据恢复环境:某品牌服务器中有一组由4块SAS磁盘做的RAID5磁盘阵列。该服务器操作系统为windowsserver,运行了一个单节点Oracle,数据存储为文件系统,无归档。该oracle...
- 服务器数据恢复—服务器磁盘阵列常见故障表现&解决方案
-
RAID(磁盘阵列)是一种将多块物理硬盘整合成一个虚拟存储的技术,raid模块相当于一个存储管理的中间层,上层接收并执行操作系统及文件系统的数据读写指令,下层管理数据在各个物理硬盘上的存储及读写。相对...
- 「服务器数据恢复」IBM某型号服务器RAID5磁盘阵列数据恢复案例
-
服务器数据恢复环境:IBM某型号服务器;5块SAS硬盘组成RAID5磁盘阵列;存储划分为1个LUN和3个分区:第一个分区存放windowsserver系统,第二个分区存放SQLServer数据库,...
- 服务器数据恢复—Zfs文件系统下误删除文件如何恢复数据?
-
服务器故障:一台zfs文件系统服务器,管理员误操作删除服务器上的数据。服务器数据恢复过程:1、将故障服务器所有磁盘编号后取出,硬件工程师检测所有硬盘后没有发现有磁盘存在硬件故障。以只读方式将全部磁盘做...
- 服务器数据恢复—Linux+raid5服务器数据恢复案例
-
服务器数据恢复环境:某品牌linux操作系统服务器,服务器中有4块SAS接口硬盘组建一组raid5阵列。服务器中存放的数据有数据库、办公文档、代码文件等。服务器故障&检测:服务器在运行过程中突然瘫痪,...
- 服务器数据恢复—Sql Server数据库数据恢复案例
-
服务器数据恢复环境:一台安装windowsserver操作系统的服务器。一组由8块硬盘组建的RAID5,划分LUN供这台服务器使用。在windows服务器内装有SqlServer数据库。存储空间LU...
- 服务器数据恢复—阿里云ECS网站服务器数据恢复案例
-
云服务器数据恢复环境:阿里云ECS网站服务器,linux操作系统+mysql数据库。云服务器故障:在执行数据库版本更新测试时,在生产库误执行了本来应该在测试库执行的sql脚本,导致生产库部分表被tru...
你 发表评论:
欢迎- 一周热门
-
-
爱折腾的特斯拉车主必看!手把手教你TESLAMATE的备份和恢复
-
如何在安装前及安装后修改黑群晖的Mac地址和Sn系列号
-
[常用工具] OpenCV_contrib库在windows下编译使用指南
-
WindowsServer2022|配置NTP服务器的命令
-
Ubuntu系统Daphne + Nginx + supervisor部署Django项目
-
WIN11 安装配置 linux 子系统 Ubuntu 图形界面 桌面系统
-
解决Linux终端中“-bash: nano: command not found”问题
-
NBA 2K25虚拟内存不足/爆内存/内存占用100% 一文速解
-
Linux 中的文件描述符是什么?(linux 打开文件表 文件描述符)
-
K3s禁用Service Load Balancer,解决获取浏览器IP不正确问题
-
- 最近发表
-
- 服务器数据恢复—Raid5数据灾难不用愁,Raid5数据恢复原理了解下
- 服务器数据恢复—多次异常断电导致服务器raid不可用的数据恢复
- 服务器数据恢复-V7000存储更换磁盘数据同步失败的数据恢复案例
- 「服务器数据恢复」重装系统导致XFS文件系统分区丢失的数据恢复
- 服务器数据恢复-ESXi虚拟机被误删的数据恢复案例
- 「服务器数据恢复」Raid5阵列两块硬盘亮黄灯掉线的数据恢复案例
- 服务器数据恢复—基于oracle数据库的SAP数据恢复案例
- 「服务器虚拟化数据恢复」Xen Server环境下数据库数据恢复案例
- 服务器数据恢复—RAID故障导致oracle无法启动的数据恢复案例
- 服务器数据恢复—服务器磁盘阵列常见故障表现&解决方案
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)