高性能存储之--快速理解redis(简版)
为什么要用NoSql
随着流量的增加,链路性能会成为系统非常大的挑战,实现链路高性能有以下方案:
对于关系数据库,为解决高QPS带来的数据库压力,可以采用分库分表、读写分离。但还会存在一些挑战,比如IO压力大、索引维护成本高、数据库一致性加锁影响写性能、表结构schema难扩展、全文检索能力差。
针对于这些问题引用NoSql是一个不错的选择。
NoSql数据库可以满足弹性扩缩容、大数据高性能、灵活数据模型等需求。常见的NoSql包括解决缓存问题的Redis、解决聚合索引问题的ES、大数据量下的列式存储的HBase、以及文档存储的MongoDB。
今天我们先从常用解决缓存问题的Redis聊起。
什么是Redis
Redis是一种基于内存的key-value存储数据库,基于内存实现、采用单线程、非阻塞IO多路复用来满足大量数据读写效率要求高的场景。
优点是:查询复杂度O(1),TPS可达10W TPS。缺点是:基于内存存储,空间有限,数据淘汰可能丢失数据;不支持条件查询。
所以redis常用于缓存数量受限,对数据持久要求不高,读多写少的场景。
常见数据类型
Redis主要支持五种数据结构:
string
hash
list
set
zset
看下不同数据结构的底层支持:
string
value为字符串格式。使用场景:可以存储账号及账号是否投票。
hash
一个存储空间存储多个key-value数据,底层使用hash表和压缩列表存储。在field少时,使用数组数据结构,数据多时,通过hashmap存储。使用场景:购物车中,key为用户id,hash 的field是商品id,hash 的value是商品的数量。
list
list是顺序存储多个数据,底层是双向链表和压缩列表实现。数据以入队出队实现,可以数据分页查询。使用场景:微博粉丝关注列表。
set
set在查询上效率高,只存储key而不是value,保证key不重复,提高了效率。使用场景:用户之间的共同好友。
zset
zset是在set基础上增加了一个排序字段,实现了数据的有序存储。使用场景:微博热搜榜单。
数据持久化
为提升数据持久能力,redis支持数据持久化。有两种方式:RDB,AOF;Redis支持rdb-aof混合持久化方式,如果关闭持久化能力,redis就退化成了一个纯内存数据库和memcache没啥区别了。
RDB
备份数据文件,默认开启RDB快照;数据恢复速度更快;
利用数据快照方式记录数据键值对,在某一时刻生成数据快照文件替换原有文件;触发方式有两种:自动和手动触发;自动触发可以设置x秒内数据库修改了y次自动触发RDB的BGSAVE命令,把数据存储到磁盘中;原理是主进程fork一个子进程完成数据持久,不影响主线程工作,实现了IO最大化。RDB存储方式几乎不影响数据库性能,所以一般默认开启RDB持久化方式;redis每次宕机重启后,能恢复到上一次rdb快照记录的数据,但数据安全性低,也就出现了AOF;
AOF
追加日志文件,默认关闭;
AOF包含三种日志机制持久化命令,always、everysec、no。no:不进行数据fsync,将flush的时机交给操作系统决定,执行速度快;everysec:后代线程每秒执行一次数据存储;always:通过每写入一条日志进行一次fsync,数据持久性最好,但速度最慢;
默认策略是everysec,实现了取舍。
主进程的append写入命令追加到aof缓冲区,兼具了安全性和性能考虑,对磁盘进行文件同步sync操作。对磁盘进行文件同步sync操作,当aof文件大小超过阈值后,会进行aof重写。重写是redis会fork一个子进程,读取aof文件,将指令合并执行,生成新文件替换老文件,压缩写入一个临时文件,子进程不影响主进程,这个过程不影响文件的可用性。
由于aof是主线程将命令写入缓冲区,所以性能上是有损耗的。
内存淘汰机制
前面提到过,redis使用的是内存,所以会有总的容量限制,缓存的key需要进行清理。
在redis中有个过期字典,保存着key的过期时间,当数据到了过期时间后,会通过策略进行数据清理。
淘汰策略有两种方式:定期删除与惰性删除;
定时删除是redis默认每隔100ms随机抽取一些设置了过期时间的key,检查其是否过期,如过期,则删除,这里无需遍历所有的key检查,随机抽取即可。
为清理那些没机会遍历到的key,可以依靠惰性删除了,当查询一个key发现其过期了,就会被清理掉,保证过期的key不会被查询到。
这个设计是不是很巧妙,架构处处是取舍啊。
如果所有的key都没有过期,但是内存满了,就需要进行内存淘汰了。
redis常用的淘汰策略是allkeys-lru 和 volatile-lru。
高可用实现
单实例总是有可用性风险,redis怎么实现高可用的呢?主要依靠持久化、复制、哨兵、集群。
持久化:持久化实现高可用的底层逻辑是备份,将数据备份到磁盘,保障进程重启后数据不丢失;复制:复制是分布式存储系统常用的高可用手段,实现了数据的多机备份,通过负载均衡和故障转移与故障恢复实现了,数据的高可用。哨兵和集群都是基于它实现的;哨兵:官方提供的一种redis实例的高可用解决方案,在复制的基础之上,实现自动化故障转移与故障恢复。但是单实例存储能力有限;集群:通过集群可以解决无法负载均衡和存储能力有限问题,实现了完善的高可用;
主从切换
了解redis高可用,我们需要先了解下redis主从切换的过程。
哨兵结构:
如果主节点挂了,哨兵集群的主从切换过程:
判断主节点下线
选举新主
选举哨兵leader
主从切换
判断主节点下线
某个哨兵通过心跳监控到主节点下线后,会给其他哨兵发送确认命令,其他哨兵根据自己的判断,回复Y or N。根据所有哨兵的投票少数服从多数情况,判断节点下线。
看,redis的实现处处透露着简单。
选举新主
哨兵们在挂掉节点的从节点中找到心跳保活最好、复制进度最高、ID编号最小的节点成为主节点。
选举哨兵leader
每次主从切换操作的主节点下线、从节点成主,都是由哨兵leader执行的。第一个判断主节点挂掉的哨兵会申请成为哨兵leader,成为哨兵leader需满足:赞成票大于半数。
主从切换
哨兵leader执行主从切换,需要完成以下通知:
通知其他哨兵新节点的地址;
通知所有从节点新的主节点地址,从节点收到通知之后申请主从同步;
通知客户端连接新的主节点。
总的来说,哨兵就是通过过半投票方式做了很多负责的决策。
缓存常见问题
我们看下在系统架构中,如何使用redis缓存:
使用缓存时,常见的问题主要有:缓存穿透、缓存雪崩、缓存击穿。
缓存穿透
缓存穿透是key不存在缓存中,导致每次请求都打到了数据库上,不能起到缓存保护数据库的目的,会被一些不法分子利用,对数据库进行攻击。
解决思路:
对于那些在缓存和数据库中都没有的无效key缓存下来,并设置一个过期时间,可以解决请求的key变化不频繁的情况;
构建一个布隆过滤器,记录全部的数据,通过请求和滤波器的对比判断是否存在,不存在则直接返回错误信息,无需查询缓存和数据库;
接口层添加校验,比如用户鉴权和参数校验等方式。
缓存雪崩
缓存雪崩是因为大量key设置了相同的过期时间,导致同一时间大量key失效,请求打到数据库,造成压力。
解决雪崩问题:
集群部署,防止单机出现问题使整个缓存服务没法使用;
对缓存进行实时监控,当访问速度小于阈值时,通过机器或者服务替换进行恢复;
增加DB的访问读写开关,当请求变慢到超越阈值的时候,关闭读开关,通过部分或者所有的请求failfast立即返回,等待恢复访问之后再打开开关;
热点数据设置为永不失效,有更新的情况就更新缓存即可;
设置数据的不同失效时间,比如设置随机的缓存失效时间,减少大规模失效带来的问题。
缓存击穿
缓存击穿是大量请求访问某个热点key,在热点key失效后,这些请求打到db。
解决缓存击穿的方法:
使用互斥锁:只让一个线程构建缓存,其他线程等待缓存构建完成,直接去缓存读取数据即可;
布隆过滤器:快速判断元素是否在集合中,当访问不存在的缓存时,返回信息避免数据库挂掉;
异步构建缓存:通过Redis等数据库维护一个失效时间,当过期的时候马上获取新的数据到cache缓存。
大key治理
大key是redis缓存另一个杀手,由于大key存储空间大,访问和存储对io、cpu、内存都有比较高的压力。
查找大key 的方法:
Redis-cli --bigkeys是redis-cli自带的一个命令。它对整个redis进行扫描,寻找较大的key,并打印统计结果;
Redis-rdb-tools工具,常用命令是 rdb -c memory dump.rdb (其中dump.rdb为Redis实例的持久化文件,可通过bgsave生成);
rdb_bigkeys工具
使用方法:./rdb_bigkeys --bytes 1024 --file bigkeys.csv --sep 0 --sorted --threads 4 /home/redis/dump.rdb
/home/redis/dump.rdb 为实际的文件路径
上述命令分析dump.rdb文件中大于1024bytes的KEY, 由大到小排好序, 以CSV格式把结果输出到bigkeys.csv的文件中;
Redis 4.0引入了memory usage命令和lazyfree机制,对大key的发现和删除做了改进。
大key 问题的解决:
单个key的存储value值过大
对象分拆为几个key-value对象,使用multiGet获取值,分散操作的压力;
将数据存储在hash中,每一个field是一个具体的属性,使用hget、hmget获取value,使用hset、hmset来更新部分的属性;
hash、set、zset、list存储过多的数据
根据一定的规则进行分散存储到多个Redis实例中。对于榜单类的数据缓存可以保留前几百条,如果需要其他数据就去数据库之中去获取。
热点key问题
如果缓存设计上存在热点key,就会对缓存使用带来风险,导致流量集中处理,缓存服务可能宕机。
热key的发现:
提前预判热key的出现,比如在秒杀商城和抢票等场景下,根据已有经验去做热数据的预估,当然这种方法在有些场景下无法进行预估;
客户端的收集
在操作Redis之前加入一行代码进行统计,当然缺点是容易造成对客户端代码的入侵;
在proxy层进行收集
有些集群搭建有proxy层的入口,但没有这层设计的Redis架构就失效了;
使用Redis自带的指令
monitor 命令,统计热key的数据;分析工具redis-fania ;hotkeys参数,redis-cli -hotkeys;
抓包评估 抓包方式的开发和维护成本较高。
热key的解决:
使用二级缓存 通过ehcache,或者hashmap来处理,把数据加载到系统的二级缓存中;
备份热key 通过多个服务机器去存储热key的数据备份,在请求到来的时候,随机选取一台Redis服务器来处理访问的请求。
总结
今天我们了解了redis,看到了redis在设计上有非常多的巧妙之处,架构设计就是以最简单的方式解决复杂的问题,也体现了架构是“取舍”的哲学。
后续我们会介绍其他的nosql,比如es、hbase、mongoDB等。
高性能存储之--快速理解redis(简版)相关推荐
- 来,一起手撸一个简版 Redis(附源码)
点击上方 视学算法,选择 设为星标 优质文章,及时送达 作者 | 凯京技术团队 来自 | my.oschina.net/keking/blog/3037372 今天主要介绍两个开源项目,然后创建应用最 ...
- 快速理解高性能HTTP服务端的负载均衡技术原理
1.前言 在一个典型的高并发.大用户量的Web互联网系统的架构设计中,对HTTP集群的负载均衡设计是作为高性能系统优化环节中必不可少的方案.HTTP负载均衡的本质上是将Web用户流量进行均衡减压,因此 ...
- 深入浅出理解Redis
深入浅出理解Redis 一.概念: redis是一款高性能的NOSQL系列的非关系型数据库 1.1.什么是NOSQL NoSQL(NoSQL = Not Only SQL),意即"不仅仅是S ...
- cxgrid主从表 点+号展开_深入理解Redis主从复制
一.背景 前面的文章中,我们介绍过Redis的持久化机制,它可以实现Redis实例数据的crash-safe.但是这里有一个问题,就是Redis其实还存在着单点故障问题,比如说Redis的硬盘坏掉了, ...
- Dockerfile 简版大全,附赠编写实例
基础镜像可以用于创建Docker容器.镜像可以非常基础,仅仅包含操作系统:也可以非常丰富,包含灵巧的应用栈,随时可以发布.当你在使用Docker构建镜像的时候,每一个命令都会在前一个命令的基础上形成一 ...
- 网络编程懒人入门(一):快速理解网络通信协议(上篇)
1.写在前面 论坛和群里常会有技术同行打算自已开发IM或者消息推送系统,很多时候连基本的网络编程理论(如网络协议等)都不了解,就贸然定方案.写代码,显得非常盲目且充满技术风险. 即时通讯网论坛里精心整 ...
- 快速理解Spark Dataset
1. 前言 RDD.DataFrame.Dataset是Spark三个最重要的概念,RDD和DataFrame两个概念出现的比较早,Dataset相对出现的较晚(1.6版本开始出现),有些开发人员对此 ...
- 初识react(二) 实现一个简版的html+redux.js的demo
回顾 初识react(一) 揭开jsx语法和虚拟DOM面纱 初识react(二) 实现一个简版的html+redux.js的demo 初识react(三)在 react中使用redux来实现简版计数器 ...
- python往redis导数_Python:教你一招,将500W+的数据快速写入redis(文内赋赠教程)...
Python:教你一招,将500W+的数据快速写入redis(文内赋赠教程) 作者:PHPYuan 时间:2019-03-14 03:40:44 最近遇到一个问题:用python写500W+的数据到r ...
最新文章
- 【DB】几种ETL模式
- 对于多对多关系的对象,如何建表与关联查询(转载)
- 计算机程序设计vb课后题,《VB程序设计》课后题答案
- CF1137F Matches Are Not a Child‘s Play(树上数据结构问题、树链剖分+ODT)
- Error--解决使用Application Loader提交ipa包审核时的报错:ERROR ITMS-90168: The binary you uploaded was invalid....
- ElasticSearch 7 正式发布!
- python 计算循环次数,05.Python循环
- 如何做电商直播——预告篇
- 异常:Handler sending message to a Handler on a dead thread
- 手机计算机16进制,16进制计算器安装方法 16进制计算器使用技巧
- 头条极速版问答自动化教程
- MFC中有关鼠标单击双击响应的问题
- segmentation fault(core dump);Run-Time Check Failure #3 -The variable 'p' is being used without bein
- 视频收集、视频征集、视频采集、征集视频、收集视频、采集视频工具/小程序
- 抖音怎么测试新号|成都集光共创
- 银行业务用语大全(中英文)
- python xlrd模块_新手菜鸟Linux学习之路
- 不要虚荣心太强,要踏实肯干
- 苹果删除照片不释放内存_删除的照片怎么找回?高效找回不耽误!_
- 汽车与生活方式:3D打印机彩色部件系列