高性能NoSQL与高性能缓存架构

第71篇

极客时间《从0开始学架构》课程笔记。

一、高性能NoSQL

关系数据库的缺点

  • 关系数据库存储的是行记录,无法存储数据结构
  • 关系数据库的 schema 扩展很不方便
  • 关系数据库在大数据场景下 I/O 较高
  • 关系数据库的全文搜索功能比较弱

NoSQL 技术是为了弥补关系型数据库缺陷而产生的,NoSQL 不是银弹,而应该将 NoSQL 作为 SQL 的一个有力补充。
NoSQL != No SQL,而是 NoSQL = Not Only SQL。

常见的 NoSQL 方案分为 4 类

  • K-V 存储:解决关系数据库无法存储数据结构的问题,以 Redis 为代表。
  • 文档数据库:解决关系数据库强 schema 约束的问题,以 MongoDB 为代表。
  • 列式数据库:解决关系数据库大数据场景下的 I/O 问题,以 HBase 为代表。
  • 全文搜索引擎:解决关系数据库的全文搜索性能问题,以 Elasticsearch 为代表。

各种高性能 NoSQL 方案的典型特征和应用场景

K-V 存储

定义:K-V 存储的全称是 Key-Value 存储,其中 Key 是数据的标识,和关系数据库中的主键含义一样,Value 就是具体的数据。

Redis 是 K-V 存储的典型代表。Redis 的 Value 是具体的数据结构,包括 string、hash、list、set、sorted set、bitmap 和 hyperloglog,所以常常被称为数据结构服务器。

缺点:不支持完整的 ACID 事务,Redis 的事务只能保证隔离性和一致性(I 和 C),无法保证原子性和持久性(A 和 D)。

文档数据库

定义:为了解决关系数据库schema不易修改问题而产生的新型数据库。绝大部分文档数据库存储的数据格式是 JSON(或者 BSON),因为 JSON 数据是自描述的,能够描述复杂的数据结构。适合电商和游戏这类的业务场景。
优势: no-schema,可以存储和读取任意的数据。

  1. 新增字段简单
  2. 历史数据不会出错
  3. 可以很容易存储复杂数据

缺点:不支持事务,无法实现关系数据库的 join 操作。注:最新版MongoDB已经支持join操作。

列式数据库

定义:按照列来存储数据的数据库,与之对应的传统关系数据库被称为“行式数据库”,因为关系数据库是按照行来存储数据的。
一般将列式存储应用在离线的大数据分析和统计场景中,因为这种场景主要是针对部分列单列进行操作,且数据写入后就无须再更新删除
优势:节省 I/O,并且列式存储还具备更高的存储压缩比,能够节省更多的存储空间。典型的场景就是海量数据进行统计。

缺点:列式存储的随机写效率要远远低于行式存储的写效率,另外列式存储高压缩率在更新场景下也会成为劣势,因为更新时需要将存储数据解压后更新,然后再压缩,最后写入磁盘。

全文搜索引擎

定义:全文搜索引擎的技术原理被称为“倒排索引”(Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,其基本原理是建立单词到文档的索引。全文搜索引擎的索引对象是单词和文档,而关系数据库的索引对象是键和行。
全文搜索的业务场景下,通过索引也无法达到快速查询的目标时,使用全文搜索引擎。因为全文搜索的条件可以随意排列组合,而且全文搜索的模糊匹配通过like查询方式效率很低。
优势:全文搜索引擎能够基于 JSON 文档建立全文索引,然后快速进行全文搜索。

缺点:为了让全文搜索引擎支持关系型数据的全文搜索,需要做一些转换操作,即将关系型数据转换为文档数据。目前常用的转换方式是将关系型数据按照对象的形式转换为 JSON 文档,然后将 JSON 文档输入全文搜索引擎进行索引。

二、高性能缓存架构

适用场景

  1. 经过复杂运算后得出数据,如同时在线人数
  2. 读多写少的数据,如微博

缓存基本原理

将可能重复使用的数据放到内存中,一次生成、多次使用,避免每次使用都去访问存储系统,减轻存储系统压力。

架构设计要点

缓存穿透

定义:是指缓存没有发挥作用,业务系统虽然去缓存查询数据,但缓存中没有数据,业务系统需要再次去存储系统查询数据。
情形1:存储数据不存在。
解决方案:如果查询存储系统的数据没有找到,则直接设置一个默认值(可以是空值,也可以是具体的值)存到缓存中,这样第二次读取缓存时就会获取到默认值,而不会继续访问存储系统。
情形2:缓存数据生成耗费大量时间或者资源
解决方案:识别爬虫然后禁止访问,要么就是做好监控,发现问题后及时处理。

缓存雪崩

定义:是指当缓存失效(过期)后引起系统性能急剧下降的情况。即当旧的缓存已经被清除,新的缓存还未生成,并且处理这些请求的线程都不知道另外有一个线程正在生成缓存,因此所有的请求都会去重新生成缓存,都会去访问存储系统,从而对存储系统造成巨大性能压力。
常见解决方法:更新锁机制和后台更新机制。
1、更新锁
对缓存更新操作进行加锁保护,保证只有一个线程能够进行缓存更新,未能获取更新锁的线程要么等待锁释放后重新读取缓存,要么就返回空值或者默认值。分布式集群的业务系统要实现更新锁机制,需要用到分布式锁,如 ZooKeeper。

2、后台更新
由后台线程来更新缓存,而不是由业务线程来更新缓存,缓存本身的有效期设置为永久,后台线程定时更新缓存。后台更新既适应单机多线程的场景,也适合分布式集群的场景,相比更新锁机制要简单一些。

缓存热点

定义:对于一些特别热点的数据,如果大部分甚至所有的业务请求都命中同一份缓存数据,则这份数据所在的缓存服务器的压力也很大。
解决方案:复制多份缓存副本,将请求分散到多个缓存服务器上,减轻缓存热点导致的单台缓存服务器压力。并且设定一个过期时间范围,不同的缓存副本的过期时间是指定范围内的随机值。

猜你喜欢

转载自blog.csdn.net/weixin_34191734/article/details/86780007