为什么要用NoSql

随着流量的增加,链路性能会成为系统非常大的挑战,实现链路高性能有以下方案:

对于关系数据库,为解决高QPS带来的数据库压力,可以采用分库分表、读写分离。但还会存在一些挑战,比如IO压力大、索引维护成本高、数据库一致性加锁影响写性能、表结构schema难扩展、全文检索能力差。

针对于这些问题引用NoSql是一个不错的选择。

NoSql数据库可以满足弹性扩缩容、大数据高性能、灵活数据模型等需求。常见的NoSql包括解决缓存问题的Redis、解决聚合索引问题的ES、大数据量下的列式存储的HBase、以及文档存储的MongoDB。

今天我们先从常用解决缓存问题的Redis聊起。

什么是Redis

Redis是一种基于内存的key-value存储数据库,基于内存实现、采用单线程、非阻塞IO多路复用来满足大量数据读写效率要求高的场景。

优点是:查询复杂度O(1),TPS可达10W TPS。缺点是:基于内存存储,空间有限,数据淘汰可能丢失数据;不支持条件查询。

所以redis常用于缓存数量受限,对数据持久要求不高,读多写少的场景。

常见数据类型

Redis主要支持五种数据结构:

  1. string

  2. hash

  3. list

  4. set

  5. 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主从切换的过程。

哨兵结构:

如果主节点挂了,哨兵集群的主从切换过程:

  1. 判断主节点下线

  2. 选举新主

  3. 选举哨兵leader

  4. 主从切换

判断主节点下线

某个哨兵通过心跳监控到主节点下线后,会给其他哨兵发送确认命令,其他哨兵根据自己的判断,回复Y or N。根据所有哨兵的投票少数服从多数情况,判断节点下线。

看,redis的实现处处透露着简单。

选举新主

哨兵们在挂掉节点的从节点中找到心跳保活最好、复制进度最高、ID编号最小的节点成为主节点。

选举哨兵leader

每次主从切换操作的主节点下线、从节点成主,都是由哨兵leader执行的。第一个判断主节点挂掉的哨兵会申请成为哨兵leader,成为哨兵leader需满足:赞成票大于半数。

主从切换

哨兵leader执行主从切换,需要完成以下通知:

  1. 通知其他哨兵新节点的地址;

  2. 通知所有从节点新的主节点地址,从节点收到通知之后申请主从同步;

  3. 通知客户端连接新的主节点。

总的来说,哨兵就是通过过半投票方式做了很多负责的决策。

缓存常见问题

我们看下在系统架构中,如何使用redis缓存:

使用缓存时,常见的问题主要有:缓存穿透、缓存雪崩、缓存击穿。

缓存穿透

缓存穿透是key不存在缓存中,导致每次请求都打到了数据库上,不能起到缓存保护数据库的目的,会被一些不法分子利用,对数据库进行攻击。

解决思路:

  1. 对于那些在缓存和数据库中都没有的无效key缓存下来,并设置一个过期时间,可以解决请求的key变化不频繁的情况;

  2. 构建一个布隆过滤器,记录全部的数据,通过请求和滤波器的对比判断是否存在,不存在则直接返回错误信息,无需查询缓存和数据库;

  3. 接口层添加校验,比如用户鉴权和参数校验等方式。

缓存雪崩

缓存雪崩是因为大量key设置了相同的过期时间,导致同一时间大量key失效,请求打到数据库,造成压力。

解决雪崩问题:

  1. 集群部署,防止单机出现问题使整个缓存服务没法使用;

  2. 对缓存进行实时监控,当访问速度小于阈值时,通过机器或者服务替换进行恢复;

  3. 增加DB的访问读写开关,当请求变慢到超越阈值的时候,关闭读开关,通过部分或者所有的请求failfast立即返回,等待恢复访问之后再打开开关;

  4. 热点数据设置为永不失效,有更新的情况就更新缓存即可;

  5. 设置数据的不同失效时间,比如设置随机的缓存失效时间,减少大规模失效带来的问题。

缓存击穿

缓存击穿是大量请求访问某个热点key,在热点key失效后,这些请求打到db。

解决缓存击穿的方法:

  1. 使用互斥锁:只让一个线程构建缓存,其他线程等待缓存构建完成,直接去缓存读取数据即可;

  2. 布隆过滤器:快速判断元素是否在集合中,当访问不存在的缓存时,返回信息避免数据库挂掉;

  3. 异步构建缓存:通过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(简版)相关推荐

  1. 来,一起手撸一个简版 Redis(附源码)

    点击上方 视学算法,选择 设为星标 优质文章,及时送达 作者 | 凯京技术团队 来自 | my.oschina.net/keking/blog/3037372 今天主要介绍两个开源项目,然后创建应用最 ...

  2. 快速理解高性能HTTP服务端的负载均衡技术原理

    1.前言 在一个典型的高并发.大用户量的Web互联网系统的架构设计中,对HTTP集群的负载均衡设计是作为高性能系统优化环节中必不可少的方案.HTTP负载均衡的本质上是将Web用户流量进行均衡减压,因此 ...

  3. 深入浅出理解Redis

    深入浅出理解Redis 一.概念: redis是一款高性能的NOSQL系列的非关系型数据库 1.1.什么是NOSQL NoSQL(NoSQL = Not Only SQL),意即"不仅仅是S ...

  4. cxgrid主从表 点+号展开_深入理解Redis主从复制

    一.背景 前面的文章中,我们介绍过Redis的持久化机制,它可以实现Redis实例数据的crash-safe.但是这里有一个问题,就是Redis其实还存在着单点故障问题,比如说Redis的硬盘坏掉了, ...

  5. Dockerfile 简版大全,附赠编写实例

    基础镜像可以用于创建Docker容器.镜像可以非常基础,仅仅包含操作系统:也可以非常丰富,包含灵巧的应用栈,随时可以发布.当你在使用Docker构建镜像的时候,每一个命令都会在前一个命令的基础上形成一 ...

  6. 网络编程懒人入门(一):快速理解网络通信协议(上篇)

    1.写在前面 论坛和群里常会有技术同行打算自已开发IM或者消息推送系统,很多时候连基本的网络编程理论(如网络协议等)都不了解,就贸然定方案.写代码,显得非常盲目且充满技术风险. 即时通讯网论坛里精心整 ...

  7. 快速理解Spark Dataset

    1. 前言 RDD.DataFrame.Dataset是Spark三个最重要的概念,RDD和DataFrame两个概念出现的比较早,Dataset相对出现的较晚(1.6版本开始出现),有些开发人员对此 ...

  8. 初识react(二) 实现一个简版的html+redux.js的demo

    回顾 初识react(一) 揭开jsx语法和虚拟DOM面纱 初识react(二) 实现一个简版的html+redux.js的demo 初识react(三)在 react中使用redux来实现简版计数器 ...

  9. python往redis导数_Python:教你一招,将500W+的数据快速写入redis(文内赋赠教程)...

    Python:教你一招,将500W+的数据快速写入redis(文内赋赠教程) 作者:PHPYuan 时间:2019-03-14 03:40:44 最近遇到一个问题:用python写500W+的数据到r ...

最新文章

  1. 【DB】几种ETL模式
  2. 对于多对多关系的对象,如何建表与关联查询(转载)
  3. 计算机程序设计vb课后题,《VB程序设计》课后题答案
  4. CF1137F Matches Are Not a Child‘s Play(树上数据结构问题、树链剖分+ODT)
  5. Error--解决使用Application Loader提交ipa包审核时的报错:ERROR ITMS-90168: The binary you uploaded was invalid....
  6. ElasticSearch 7 正式发布!
  7. python 计算循环次数,05.Python循环
  8. 如何做电商直播——预告篇
  9. 异常:Handler sending message to a Handler on a dead thread
  10. 手机计算机16进制,16进制计算器安装方法 16进制计算器使用技巧
  11. 头条极速版问答自动化教程
  12. MFC中有关鼠标单击双击响应的问题
  13. segmentation fault(core dump);Run-Time Check Failure #3 -The variable 'p' is being used without bein
  14. 视频收集、视频征集、视频采集、征集视频、收集视频、采集视频工具/小程序
  15. 抖音怎么测试新号|成都集光共创
  16. 银行业务用语大全(中英文)
  17. python xlrd模块_新手菜鸟Linux学习之路
  18. 不要虚荣心太强,要踏实肯干
  19. 苹果删除照片不释放内存_删除的照片怎么找回?高效找回不耽误!_
  20. 汽车与生活方式:3D打印机彩色部件系列

热门文章

  1. 文华软件登录显示请选择服务器,文华财经随身行要登录云服务器
  2. unity 摄像头跟着鼠标移动_Unity新手入门:摄像机随玩家一起移动
  3. 格灵深瞳CTO邓亚峰:AI学习的三种路线
  4. jenkins搭建cc++自动化构建
  5. Latex中设置字体颜色
  6. 利用C语言实现顺序表
  7. thinkphp_ajax分页实现_无需整理
  8. 8.8线段树和树状数组
  9. SVN详解-linux+windows
  10. 自定义配置app.config