架构师是互联网行业高薪又紧俏的资源. 成为架构师最基本的是设计能力. 设计与设计的区别主要体现在两方面:
1, 深度: 要解决哪些问题? 这个问题背后的根本问题是什么? 还有什么问题没有发现? 对应的能力是发现和解决问题的能力.
2, 体系: 要解决的问题的属于哪一类的问题? 这类问题能否进一步抽象, 让系统解决更大的问题? 对应的抽象归纳和体系化思维的能力.
而做架构的基本功就是研究成熟成功的系统, 并总结归纳为一种设计方法添加到自己的设计库中. 今天我们来看看 文件存储机制的通用实现及原理 - 谈 Kafka,Redis, 基于 Lucene 的搜索引擎等中间件和数据库的文件存储机制. 如果你有九年义务教育以上学历, 并且觉得看不懂这篇文章, 请给我留言~~
顺便插一句, 不知道大家有没有奇怪静儿最近都没有写高可用方面的文章. 事情是这样的, 对于高可用的很多设计, 架构, 静儿都在进行专利申请中, 为了避免对公司造成影响和损失, 暂时处于多想不说的阶段.
一个商业化中间件的性能好坏, 其文件存储机制设计是衡量一个消息队列服务技术水平和最关键指标之一. 下面先介绍一下各个中间件的存储机制. 看不懂可直接跳到最后.
各个中间件的存储机制
1 Kafka
Kafka 是最初由 Linkedin 公司开发, 是一个分布式, 分区的, 多副本的, 多订阅者, 基于 zookeeper 协议的分布式日志系统(也可以当做 MQ 系统), 常见可以用于 web/nginx 日志, 访问日志, 消息服务等等, Linkedin 于 2010 年贡献给了 Apache 基金会并成为顶级开源项目.
设计特点
Kafka 把 topic 中一个 partition(大块)大文件分成多个小文件段, 通过多个小文件段, 就容易定期清除或删除已经消费完的文件, 减少磁盘占用.
通过索引信息可以快速定位 message 和确定 response 的最大大小.
通过 index(索引)元数据全部映射到 memory(内存), 可以避免 segment file(文件片段)的 IO 磁盘操作.
通过索引文件稀疏存储, 可以大幅降低 index 文件元数据占用空间大小.
一张图解释一下刚才说的特点:
从上面的图中可以看到, 生产者和消费者并没有直接的交互. 这就达到了生产者消费者模式的第一个好处: 解耦. 作为消费者, 可以自定义消费线程数和消费者数量. 这就达到了生产者消费者模式的第二个好处: 限流. 这也是大家使用 MQ 一般想要达到的目的.
2 Redis
谈 Redis 首先要纠正一个误区: 缓存比 MySQL 快. 为了说明这个问题, 下面是静儿自己动手得到的数据.
结论: 使用了缓存后响应时间不稳定
结论: MySQL 的响应时间非常稳定
从 cat 监控中可以看到缓存的 get 时间确实较长.
顺便跑题一下, 静儿测试中还发现在 jvm 正常执行业务逻辑, 不太复杂无外部调用, 一般是 1 到 2 毫秒. 但是如果抛出了异常, 这段代码执行时间要变成 100ms+. 所以一般代码中禁止用抛出非必要异常来代替正常逻辑.
之所以给大家打这个预防针, 是因为下面要给出 Redis 的描述了.
Redis 本质上是一个 key-value 类型的内存数据库, 整个数据库加载在内存当中进行操作, 定期通过异步操作把数据库数据 flush 到硬盘上进行保存. 因为是纯内存操作, Redis 的性能非常出色, 每秒可以处理超过 10 万次读写操作, 是已经性能最快的 key-value DB.
Redis 存储机制分成两种 Snapshot 和 AOF. 无论是哪种机制, Redis 都是将数据存储在内存中.
AOF 工作原理: 是将数据先存在内存, 但是在存储的时候会使用 fsync(无阻塞进程的)来完成对本次写操作的日志记录. AOF 最关键的配置就是关于调用 fsync 追加日志文件的频率, 有两种预设频率. always: 每次记录进来都添加. everysecond 每秒添加一次.
存储模式性能和安全比较:
1. 性能
snapshot 性能是要明显高于 AOP 方式的, 原因有两点:
1 > 采用二进制方式存储, 数据文件小, 加载快速.
2 > 存储的时候是按照配置中的 save 策略来存储, 每次都是聚合很多数据批量存储, 写入的效率高. AOF 一般都是工作在实时或准实时模式下. 相对存储频率高, 效率低.
2. 数据安全
AOF 数据安全性高于 Snapshot, 原因:
snapshot 存储是基于累积批量的思想, 累积的数据越多写入效率越高, 但是如果长时间数据不写入 RDB(Redis 的数据库),Redis 遇到了崩溃, 没写入的数据就无法恢复.
Redis 中有 rewrite 功能, AOF 的存储是按照记录日志的方式去工作的, 成千上万的数据插入必然导致日志文件的扩大, Redis 这个时候会根据配置合理触发 rewrite 操作. 这个操作是将最终保留值记录到日志文件中, 从而缩小日志文件的大小. 这个类似于 lucene 的 optimize 操作.
3 MySQL/MariaDB
MySQL 与目前流行的 nosql 数据库最大的不同是规定了严格的数据类型. 数据类型因为在创建表时在内存中严格划定了地址空间, 所以能限定字段的数据存储长度.
数据类型限定范围的方式有两种: 1 是严格限定空间, 划分了多少空间就只能存储多少数据, 超出的数据将被切断; 2 是使用额外的字节的 bit 位来标记某个数据, 存储了就进行标记, 不存储就不标记. 下面介绍 3 种类型的存储方式.
1. 整形的存储
它严格限定空间, 每个已划分的字节上的 bit 位上的 0 和 1 直接可以计算出数值, 所以它的范围是根据 bit 位的数量值来计算的. 一个字节有 8 个 bit 位, 一个 bit 位可以构成 2 的 8 次方 = 256 个数值. 同理 2 字节共 2 的 16 次方 = 65526 个数值. 也就是说, 在 0-255 之间的数字都只占用 1 个字节, 256-65535 之间的数字占用 2 个字节.
2.char 的存储
char 类型是常被成为定长字符串类型, 它严格限定空间长度, 但它限定的是字符数不是字节数. 它有 "短了就使用空格补足" 的能力.
3.varchar 常被称为 "变长字符串类型". 它存储数据时使用额外的字节的 bit 位来标记某个字节是否存储了数据. 每存储 1 个字节占用一个 bit 位进行标记. 一个额外的字节可以标记 256 个字节, 2 个额外的字节可以标记 65536 个字节. MySQL/mariadb 限制了最大能存储 65536 个字节. 这表示, 如果是单字节的字符, 它最多能存储 65536 个字符, 如果是多字节字符, 如 UTF8 的每个字节占用 3 个字节, 它最多能存储 65536/3=21845 个字符.
4 基于 Lucene 的搜索引擎
提到 基于 Lucene 的搜索引擎, 大家可能更熟悉 Elasticsearch(ES). 静儿有段时间专门负责公司的搜索, 没有现在这么忙, 本应该是非常难得和宝贵的机会. 有大把的时间研究源码, 可惜当时整个心都不在工作上. 当时用的还是 solr4.X.X(Solr 是基于 Lucene 的搜索引擎系统, lucene 和 Solr 版本同步更新, 目前最新是 7.6.0), 当时 Solr 每月发布一个新版本. 版本有很多 bug, 我当时是在源码上做了很多修复补丁的. 但是当时各种意识都很薄弱. 如果当初给 apache 提 patch, 有些应该是能通过的. 这就能形成了一个良好的反馈循环, 可能现在的状况截然不同.
想起 dubbo 有段时间已经不更新版本, 后来捐给了 apache, 给这个项目又重新注入了生机. 其中的一个教训就是大家一定要有很强的开源意识. 这种意识还包括在自己做功能方案和开发实现的时候要有业界调研和对标的意识.
静儿的梦想是实现自己的搜索引擎中间件, 所以目前从事的是和这个原理有很深渊源的工作: 容器调度. 什么? 看不出来联系? 那这个以后再慢慢聊.
Lucene 的数据都存在一个目录下, 一个目录构成了一个索引. Lucene 经多年演进优化, 现在的一个索引文件可以分为 4 个部分: 词典, 倒排表, 正向文件, 列式存储. 一张表解释 lucene 的存储原理:
通用原理
1. 高频读取操作放于内存.
2. 文件分段减少磁盘 IO 操作.
3. 磁盘存储防止数据丢失.
4. 更好的结构化可以提升存储和读取效率.
以上です. 不解释.
总结
之前和同事聊天, 同事说他们架构师基于原来的版本设计了一个非常完美的 2.0 方案. 但是这个方案并不解决现有的任何问题. 这也是静儿想做架构并且可以很轻松的找到一个架构师职位, 但一直都是在项目组内自己动手写代码的原因: 一个旁观者想了解内部的痛点很困难.
前段时间大家纷纷剖析拼多多优惠券事件背后的技术问题. 不可否认, 技术是有点菜. 但是拼多多目前整体看是成功的. 成功在哪里呢? 它成功的解决了商家 (其他平台门槛高, 入住不了) 和消费者 (以更低的价格买到商品) 的痛点. 所以这点状况并未伤及元气.
在做设计的时候, 我会首先问自己一个问题: 现在哪里最痛? 架构讲究深度和体系. 深度解决的是痛点根本性的问题. 体系解决的是未来的痛点问题. 不以解决问题为目的设计的系统都是 "成功之母".
来源: http://www.tuicool.com/articles/NnmaAje