朱晔的互联网架构实践心得S1E10:数据的权衡和折腾【系列完】算法
【下载本文PDF进行阅读】数据库
本文站在数据的维度谈一下在架构设计中的一些方案对数据的权衡以及数据流转过程当中的折腾这两个事情。最后进行系列文章的总结和以后系列文章写做计划的一些展望。设计模式
数据的权衡
正所谓鱼和熊掌不能兼得,舍了才能得。架构或技术设计方案中针对数据这个事情,有太多体现了权衡思想的地方。缓存
空间和时间
咱们来想一想有哪些广义上空间换时间(性能)的例子,也就是经过使用更多的存储或内存空间加快了任务的单次执行速度或整体吞吐量:安全
- 让数据在更快的地方:也就是缓存。速度和价格原本就是矛盾的,咱们不可能10万买到百千米加速在4秒内的高性能跑车。存储虽便宜可是速度慢,内存虽然贵可是速度快,使用级联的缓存存储方案咱们能够在这当中作一个平衡。不只仅是架构设计上咱们几乎都会用到缓存,CPU会有多级缓存,OS也有页面缓存机制。
- 让数据一次性提交:也就是缓冲。在进行IO操做的时候,真正和磁盘和网络交互以前,咱们每每都会创建缓冲区。在大多数的时候进行IO操做对于10字节和100字节的数据须要的IO时间是同样的,咱们能够在缓冲区进行短期的数据累积后一次性进行操做,这种作法不必定能提升单次执行性能可是能够增长吞吐(对于繁忙的系统,吞吐达到瓶颈后单次的执行会排队,因此反过来也能够认为提升单次性能)。
- 让数据更靠近用户:CDN就是一个典型应用。让数据离用户更近意味数据不须要通过太多的机房和链路交换就能够到达用户终端,显然能够提升访问性能。其实说白了就是让数据在离用户更近的地方缓存一份,在客户端缓存也算。
- 让数据面向查询存储:相对于面向存储优化。常见的存储数据结构上,咱们知道写入性能最好的是追加文件的日志存储,而后是LSM树而后是B+树,读取性能则反过来。为了性能咱们一般会在保存数据时候进行必定的排序分类而后按必定的数据结构保存,而不只仅是把原始信息存下来,这样在查询搜索的时候避免了数据全扫。这些特殊的(甚至有的时候是额外的)数据结构的维护也体现了空间换时间。
- 让数据面向输出优化:以前说的物化视图就是一个例子,在保存数据的时候直接保存咱们最终须要查询的数据,这样查询的时候就不须要作各类关联。在微博架构设计的时候咱们每每会更极端,为每一个人维护一个信息流队列,在发微博的时候直接为全部的粉丝的队列追加数据,这样就避免了首页查询时候很是夸张的Join操做。
- 让数据复制多份:复制多份数据意味着咱们能够有多个相同的数据源来为读取作服务。以前也提到过,虽然这是为伸缩性考虑,可是必定程度上其实也能够提升性能。
- 让数据计算默默进行:搞两份数据,一份数据是用户如今就在看的,另外一份数据是新版本的用户将要看到的数据,经过在后台另外一块空间单独处理这份新数据,处理完成后再展示给用户就至关于用户不须要花费时间等待数据的处理加工了。
- 浪费一半的空间倒腾:这里说的是相似于JVM的新生代的复制算法。始终有两块大小一致的幸存区,在须要回收的时候能够将一个幸存区和伊甸园中的有用对象一次性搬到另外一块区域中。虽然浪费了空间,可是这种每次都给我一张新的纸来画图的方式解决了碎片化的问题。
随着存储和带宽的优化,互联网架构更多考虑空间换时间,在有明显带宽和存储瓶颈的视频图片等资源的传输上,咱们会采起压缩的方式用时间来换空间。网络
一致性和可用性
对于分布式存储,数据复制多份(分片)保存。在出现网络故障的时候,节点之间的数据没法一致,这个时候若是咱们追求数据一致性那么就只能等待系统恢复再去使用这个系统,这个时候系统就不可用,固然咱们也能够放弃一致性的追求先凑合用系统,这个过程当中节点之间的数据没法确保一致。分布式的状况下受到外部客观因素的限制,不可能二者都保证,咱们只能根据业务需求来决定放弃哪一个。有关CAP有不少讨论,对于各类分布式存储也有按照CP(偏向于一致性)和AP(偏向于可用性)进行了分类,目前其实大多数的系统都把这个选择权交给了客户端交给了用户,在使用数据的时候咱们能够选择是CP仍是AP,因此不能简单认定某个存储就是CP或AP的。数据结构
数据查询的专有DSL(领域专用语言)和通用查询语言
相似Mongodb、ElasticSearch、HBase等存储系统都有本身的查询DSL,关系型数据库大多支持以SQL这种通用查询语言进行数据查询。ElasticSearch现已支持了SQL查询,基于HBase也有不少组件提供SQL查询(好比Phoenix)。咱们知道每一种存储引擎都有其特色,专有DSL能够作到让你以引擎最合适的方式来作查询,支持了通用的SQL后每每会产生滥用致使的性能问题,可是能够带来无比的便利性(语言和语言之间的翻译是很痛苦的,并且这个翻译每每是有损的)。在咱们本身作服务设计对外API,设计业务逻辑处理引擎的时候,其实也会遇到这样的设计层次问题。咱们是提供专门的API来容许外部操做咱们的数据呢,仍是开发出一套DSL容许外部使用这套语言来作复合查询,甚至直接支持相似SQL这种通用语言(直通数据库)。好比在作风控规则引擎的时候,咱们能够把每一条规则做为写死的代码逻辑来写,也能够开发出一套DSL让风控专家能够配置数据字段和规则,甚至能够直接使用SQL进行配置,开发报表系统、监控系统也是相似的道理。架构
数据的折腾
从数据的角度咱们来看看在互联网系统中,数据到底经历了什么,体如今数据的存储和流转两方面。就来讲说用户的注册这一过程吧:框架
- 用户填写了注册表单,产生了用户名、密码这一注册数据
- Web网站服务端从HTTP(S)请求的Body中收到了Key=Value形式的数据
- 数据经过Thrift二进制协议编码传输给了用户服务(RPC调用),在这个过程当中其实只有 Value编码了进去参与传输
- 在用户服务服务端收到数据后根据接口IDL和数据拼在一块儿反序列化让不一样的数据字段又有了含义
- 用户服务把这个信息保存到了关系型数据库MySQL(以MySQL二进制协议在网络传输),数据成为了一份RedoLog后以B树形式数据结构在磁盘上存储
- MySQL主库又把数据以SQL语句(或数据行)的形式把数据复制到从库
- 用户服务随后又把得到的用户Id以及用户的基本Profile信息以Key为字符串Value为Json文本的形式在Redis中进行缓存
- 用户服务而后把有新用户注册这个事件做为一个消息(Json序列化)推送到MQ Broker上
- (异步)红包服务监听了这个消息,在收到消息后从新把字节流转换为事件Json对象为新用户派发红包
- (异步)风控服务也监听了消息,从事件中提取出IP地址等信息,使用CEP复琐事件处理技术对消息进行处理而后触发报警
- (异步)大数据仓库服务也监听了消息,从数据库查询用户完整的信息而且把数据保存到了HBase中以LSM树存储
- 在用户服务完成处理后,Web网站也就知道了用户注册成功了告知用户注册成功的消息,而后用户尝试登录,因为主从同步的延迟,数据库从库没有查询到新注册的用户,用户迷茫了,我注册成功了但我是谁呢(这里开个玩笑,这是一个设计错误,通常而言对于本身产生的数据的查询原则是只能在主库查询)
因此咱们看到,互联网分布式架构的复杂性在于一份数据可能会以不一样的形式作转换不一样的通讯结构走传输,不用的数据结构作存储,在每个环节,数据可能都以不一样的形式体现,数据自己和数据表明的意义可能头尾分离。若是咱们对程序内存作一份Dump,对各个环节的TCP包作几份Dump,对数据最终落地的存储作几份Dump而后在这几份二进制的Dump中找到而且看一下咱们的数据长啥样,看看是否还能理解数据的意义,这是否是会是一件有趣的事情呢?运维
就这么简单的一份数据,为何咱们要这么折腾呢?缘由在于咱们不得不引入某个技术来解决处理某一方面问题的时候又引入了更多的复杂度,而后咱们又须要引入更多的技术来处理,循环往复:
- 在单体架构中,咱们依靠数据库提供的索引+事务能够解决大部分的性能和一致性问题
- 单体架构在性能、可扩展性、可靠性等方面不能知足需求,咱们引入了分布式架构
- 在分布式架构中,咱们作了数据复制的分片,不但须要解决因网络、时钟、资源等问题致使的节点的协调,并且须要在空间分布和时间错位两个层面去考虑以前已经解决的处理的很好的事务性问题
- 咱们使用不一样的数据库做为复合数据源来取长补短,不一样的数据库在分布式架构实现上各不相同,咱们须要花大量时间来作高可用的调研,同时它们在存储模型上大不相同,咱们须要进行深度研究了解存储模型对于数据增量的性能衰减以及故障后的恢复等方面(诸如Mongodb、和ElasticSearch这种数据库愈来愈有趋势把本身搞成大而全面的数据存储解决方案了,官方永远宣传的都是本身能干啥而不是不能干啥,至于不能干啥只能靠本身去研究和总结)
- 不一样的网络中间件都有各自不一样的协议和通信方式,协议在处理先后兼容性方面各不相同,不一样的语言对于基础数据类型都有不一样的处理方式,在语言和协议的交互转化中也会有一些兼容性问题
- 咱们使用各类方式来提升性能,数据会以不一样的形态在多处保存,随着时间的推移数据必然会产生不一致性,对于不一致的数据咱们如何发现,发现后去容忍仍是补偿同步
- 随着组件的增多,咱们不但愿组件的不稳定形成木桶效应,咱们会引入各类弹性的方案来容许组件的暂时不稳定,咱们不但愿出了问题找不到来源,会引入各类监控手段来作全链路全组件监控
- 数据每每不能彻底掌握在本身手里,愈来愈多的外部三方服务会提供数据源以及垂直领域的解决方案,和外部系统进行交互保留了分布式系统全部的困难以外,还须要考虑安全、隔离等问题
架构的复杂性在于数据,数据的复杂性在于多变的结构、数据的共享同步以及数据的增加。单机单用户的软件演变为B/S形式的软件演变为SAAS形式的软件,数据从单机到对内分布式到在整个互联网上造成分布式愈来愈复杂。架构设计中可能一大半的时间在考虑数据的处理存储问题。
系列文章总结
系列文章到这里算是一个结束了,在本系列文章中咱们没有过多涉及具体的技术和算法,咱们从高层架构的角度阐述了个人一些实用性经验,力求在广度上都有涉及:
- 第一篇文章,我谈了对All-In-One架构起步的见解,谈了不一样语言的选择和技术团队中业务和架构团队的特性。
- 第二篇文章,我谈了我认为互联网架构中最重要的三要素,微服务+消息队列+定时任务,消息队列和定时任务其实体现的是数据实时流式处理和非实时批次处理的两大流派。
- 第三篇文章,我谈了如何发挥不一样类型数据库(关系型数据库MySQL、缓存型数据库Redis、文档型数据库Mongodb、搜索型数据库ElasticSearch、时间型数据库InfluxDb)所长结合以前说的三要素作复合型数据源,固然还有更多类型的数据库,好比图数据库等等在须要的时候也能够引入作合适的业务。
- 第四篇文章,我谈了如何以开源的一些项目(ElasticSearch+Logstash+Kibana、Telegraf+InfluxDb+Grafana)快速搭建起简单的监控、日志和数据分析平台以及阐述了对打点和异常两个事情的见解。
- 第五篇文章,我谈了随着项目的发展提炼打造合适的中间件的必要性,以及介绍了一些常见中间件(配置管理、服务管理、全链路监控、数据访问、分布式缓存、任务管理、发布管理等)的需求功能以及设计上的注意点。
- 第六篇文章,我谈了架构升级迁移的步骤和注意点,以及开发人员比较容易忽略的安全方面的问题,我总结了我认为比较重要的安全意识的十个原则(木桶效应、不信任客户端、数据和代码分清楚、用户看不到不等于黑客看不到、最小化接口权限设计和复用的矛盾、一开始就要考虑安全、作好防刷防暴破控制、产品逻辑注意一体性、作好异常数据监控报警、对内的数据注意权限控制和审计)。
- 第七篇和第八篇文章,我针对微软分享的三十种云架构设计模式(涉及监控、性能、可扩展、数据管理、设计实现、消息、弹性、安全等方面)给出了本身的见解。
- 第九篇文章,我谈了架构设计评审时候咱们针对组件选型、性能、可伸缩性、灵活性、可扩展性、可靠性、安全性、兼容性、弹性处理、事务性、可测试下、可运维性、监控等方面会一块儿讨论和提出的一些点,以及写好一篇概要的技术文档最主要的五个手段(需求脑图、系统架构图、对外API脑图、交互时序图和数据库ER图)。
- 本文最后从架构中最重要的数据角度展开讨论了架构中作的妥协权衡以及分布式架构设计的无限复杂性。
我想了一下,有几方面的内容能够造成其它的系列文章和你们一块儿探讨,尽请期待:
- Spring框架愈来愈完善了,庞大的产品体系和插件式的架构让Java开发愈来愈快速和灵活了,在《你不知道的Spring》系列文章中咱们或许能够聊一下Spring那些咱们不知道的点滴以及如何以Spring为核心作一些扩展。
- 对于分布式架构中的数据复制和分片、高可用、一致性处理,每个产品都有其算法和思路,也有很是多的争论,在《分布式架构细节设计》系列文章中咱们或许能够详细针对微服务和分布式架构具体的一些处理方式挖掘一些细节的点一一展开讨论。
- JVM和JDK通过这么多年的发展,造成了一些设计模式和技巧,理解这些更多的意义在于咱们能够在咱们的软件设计和架构中借鉴,在《软件内部设计模式》系列文章中能够聊聊这部分的一些所见。