Alvin Liu

  • Home
  • About
  • Privacy Policy
Distributed System
Distributed System

Amazon Dynamo几乎实践了所有分布式理论

Dynamo是亚马逊上线多年的分布式数据库。随着不断的版本迭代,逐步满足了互联网场景的各种苛刻要求,也实践了近几十年来产生的多种经典理论。可谓集十八般武艺于一身啊。。。 重读 Amazon Dynamo 论文有感 下文转自知乎,原作者陈蔚澜:https://zhuanlan.zhihu.com/p/98640498 本文内容不仅仅局限于 Dynamo 什么是 Dynamo 亚马逊在业务发展期间面临一些问题,主要受限于关系型数据库的可扩展性和高可用性,希望研发一套新的、基于 KV 存储模型的数据库,将之命名为 Dynamo。 相较于传统的关系型数据库 MySQL,Dynamo 的功能目标与之有一些细小的差别,例如 Amazon 的业务场景多数情况并不需要支持复杂查询,却要求必要的单节点故障容错性、数据最终一致性(即牺牲数据强一致优先保障可用性)、较强的可扩展性等。 可以肯定的是,在上述功能目标的驱使下,Dynamo 需要解决以下几个关键问题: Dynamo 和 MySQL 的关系? 有的人有这种疑问,其实二者没有什么关系,Dynamo 叙述的是一种 NoSQL 数据库的设计思想和实现方案,它是一个由多节点实例组成的集群,其中一个节点称之为 Instance(或者 Node 其实无所谓),这个节点由三个模块组成,分别是请求协调器、Gossip 协议检测、本地持久化引擎,其中最后一个持久化引擎被设计为可插拔的形式,可以支持不同的存储介质,例如 BDB、MySQL 等。 数据分片 数据分片的实现方式 数据分片实在是太常见了,因为海量数据无法仅存储在单一节点上,必须要按照某种规则进行划分之后分开存储,在 MySQL 中也有分库分表方案,它本质上就是一种数据分片。 数据分片的逻辑既可以实现在客户端,也可以实现在 Proxy 层,取决于你的架构如何设计,传统的数据库中间件大多将分片逻辑实现在客户端,通过改写物理 SQL 访问不同的 MySQL 库;而 NewSQL 数据库倡导的计算存储分离架构中呢,通常将分片逻辑实现在计算层,即 Proxy 层,通过无状态的计算节点转发用户请求到正确的存储节点。 Redis 集群的数据分片 Redis 集群也是 NoSQL 数据库,它是怎么解决哈希映射问题的呢?它启动时就划分好了 16384 个桶,然后再将这些桶分配给节点占有,数据是固定地往这 16384 个桶里放,至于节点的增减操作,那就是某些桶的重新分配,缩小了数据流动的范围。 Dynamo 的数据分片 Dynamo 设计之初就考虑到要支持增量扩展,因为节点的增减必须具备很好的可扩展性,尽可能降低期间的数据流动,从而减轻集群的性能抖动。Dynamo 选择采用一致性哈希算法来处理节点的增删,一致性哈希的算法原理细节这里不再赘述,只是提一下为什么一致性哈希能解决传统哈希的问题。 我们想象一下传统哈希算法的局限是什么,一旦我给定了节点总数 h,那数据划分到哪个节点就固定了(x mod h),此时我一旦增减 h 的大小,那么全部数据的映射关系都要发生改变,解决办法只能是进行数据迁移,但是一致性哈希可以在一个圆环上优先划分好每个节点负责的数据区域。这样每次增删节点,影响的范围就被局限在一小部分数据。 下图蓝色小圆 ABCD 的代表四个实际节点,橙色的小圆代表数据,他们顺时针落在第一个碰到的节点上 一致性哈希的改进 一致性哈希是存在缺点的,如果仅仅是直接将每个节点映射到一个圆环上,可能造成节点间复杂的范围有大有小,造成数据分布和机器负载不均衡。 因此一致性哈希有个优化举措,就是引入虚拟节点,其实就是我再引入一个中间层解耦,虚拟节点平均落在圆环上,然后实际节点的映射跟某几个虚拟节点挂钩,表示我这台物理节点实际负责这些虚拟节点的数据范围,从而达到平衡负载的作用。 数据复制 数据复制是提升数据库高可用的常见手段,从实现方式上可分为同步复制、异步复制、半同步复制等,从使用场景上又可分为单向复制、双向复制、环形复制等。 Dynamo 的设计中为了保证容灾,数据被复制到 N 台主机上,N 就是数据的冗余副本数目,还记得我们说过 Dynamo 中每个节点有一个模块叫做请求协调器么,它接收到某个数据键值 K 之后会将其往圆环后的 N - 1 个节点进行复制,保证该键值 K 有 N 个副本,因此 Dynamo 中实际上每个节点既存储自己接收的数据,也存储为其他节点保留的副本数据。 Dynamo 的读写流程 Dynamo 会在数据的所有副本中选取一个作为协调者,由该副本负责转发读写请求和收集反馈结果。通常情况下,该副本是客户端从内存中维护的 数据 - 节点 映射关系中取得的,将请求直接发往该节点。 对于写请求,该副本会接收写请求,并记录该数据的更新者和时间戳,并将写请求转发给其他副本,待 W 个副本反馈写入完成后向客户端反馈写入操作成功;读取流程类似,转发读请求至所有副本,待收到 R 个副本的结果后尝试选取最新的数据版本,一旦发现数据冲突则保留冲突反馈给客户端处理。 显而易见的是,由于协调者是处理读写请求的唯一入口,因此该副本所在节点的负载肯定会飙高。 数据一致性和冲突解决 在数据存在 N 个冗余副本的情况下,想要保证强一致需要等待所有副本写入完成才能返回给客户端写入成功,但这是性能有损的,实践中通常不这么做。Dynamo 允许用户设置至少写入 W 个副本才返回,而读取的时候需要从 R 个副本上读到值才能返回,因此只要 W + R > N,就能保证一定能读到正确的值。 但是这有个问题是如何判断返回的 R 个值中哪个是最新的呢,即每个数据都应该有一个版本信息。Dynamo 为了解决这个问题引入向量时钟的概念,简单来说就是每次写入操作,写入的副本会为这条数据变更新增一个更新者和版本号的向量组 <updater, version> 作为版本信息,在后续的复制流程中也会带上这部分信息。 例如副本 A 接收到了对键值 K 的更新请求,随机为键值 K 新增版本信息 K : <A, 1>,等待之后再次更新 K 时更改为 K : <A, 2>,因此后者版本更新。 假设集群中的网络没有问题,那么对于某个键值 K 的读取一定能读取到时间戳最新的版本返回给客户端。但是遗憾的是,分布式场景下网络是一定会出问题的,各种问题。。 假设客户端在第二次更新时选择了另一个副本 B 作为协调者,那么 B 会为键值 K 保存 K : <B, 1>,这时客户端读取键值 K,协调者发现无法决定哪个版本是最新的,就类似于 Git Merge 出现冲突,只能保留这种冲突返回给客户端,由具体业务逻辑觉得采用哪个值。 Dynamo 集群成员状态监测 Dynamo 想要做到 HA(高可用),除了数据复制之外,还需要定时探测集群节点的可用性,有的业界产品依赖外部服务统一处理,例如 MySQL 的 MHA,RocketMQ 的 NS,TiDB 的 PD 等,也有的依赖于节点间自适应管理,例如 Redis 集群和 Dynamo,这二者均采用了 Gossip 协议作为集群间节点信息交换的解决方案,无需引入外部服务,是完全的去中心化的架构。 想要了解什么是 Gossip 协议,建议从 Redis 集群的架构中去学习,往往使用 Gossip 协议的集群实现都比较复杂,而且容易出错,另外 Gossip 协议本身由于数据包庞大,也极易造成性能抖动问题。 总结 最近重读了一遍 Dynamo 论文,加上之前看过几遍的 《Design Data-intensive Applications》,感觉很多分布式系统设计的概念都可以很好地衔接上,对知识梳理很有帮助,大家感兴趣也可以去看看。 参考资料

2023-02-27 0comments 1786hotness 0likes Alvin Liu Read all
High Performance

亿级流量网站架构读书笔记

感谢张开涛先生为我们分享了互联网高并发场景的经典问题和解决方案。阅读后深有感触,故整理读书笔记于此。感兴趣的小伙伴请在这里购买 https://item.jd.com/12153914.html 系统设计基本方法 高并发 高可用 服务降级 限流 切流量 业务设计 高可用 负载均衡与反向代理 负载均衡分层 负载均衡可以应用在OSI(Open System Interconnection)网络模型的不同层. 层级 层级名 实现方案 产品 二层 链路层 修改MAC地址 LVS DR 四层 传输层 修改IP地址和端口 LVS NATHaProxyNginx 七层 应用层 URL转发到IP:端口 Nginx 越底层性能越好, 因为不用拆包 越高层功能越强, 因为有更多信息可以设置转发条件 负载均衡算法 长连接 Nginx可配置和后端(上游upstream)服务器保持长连接 动态配置上游服务器 Nginx集成服务发现(Consul + etcd)自动添加新的上游服务器 对应到K8s就是Ingress OpenResty OpenResty是Nginx的一个module, 主要功能是运行Lua脚本 所有Nginx的Lua脚本都需要使用OpenResty module执行. OpenResty本身也是web服务器, 可以响应http请求 服务隔离 线程隔离 读写隔离 各机房都有只读存储, 提高性能还可以备份 静态资源隔离CDN 静态资源不论大小都要上CDN. js/css发布时应该带有版本号, 这样出问题不需要刷CDN和浏览器缓存. 直接修改网页回退js版本号就可以了. 爬虫隔离 爬虫流量可达到1/5, 一般情况给爬虫返回简单数据, 比如缓存数据 可以通过这些方法过滤爬虫请求 热点隔离 秒杀, 促销等可预测的热点访问, 应该放在独立服务器上. 读热点: 多级缓存 使用一致性哈希优化缓存命中率可能会产生热点, 进而击垮单点产生雪崩效应(连续击垮后续节点). 解决办法是通过虚拟节点把每个服务器拆分后交错插入哈希环. 写热点: 缓存+队列, 最终一致性 硬件资源隔离 服务资源隔离 独立的Redis, DB很常见, 对其它服务依赖性强也可以独立提供, 绑定部署 Hystrix线程池隔离 Hystrix使用独立的线程池来隔离失败操作. Hystrix还可以用来实现服务降级, 熔断. Servlet 3异步请求线程池 Servlet 3支持AsycContex和AsyncResponse. 可以用来自建线程池, 这样Tomcat线程池只用来把请求放到自有线程池. 请求限流 限流分类 限流算法 限流技术方案 分布式限流 核心是共享流量计数器, 原子计数CAS. 一般用Cache, 比如Redis配合Lua脚本实现原子操作. Coherence有类似功能 应用层和接入层都可以集成Redis实现根据业务逻辑的限流. 接入层限流 这里讲的是脱离业务逻辑, 根据请求特征进行限流, 比如IP rate limiter 服务降级 降级预案 降级分类 自动降级 人工开关降级 读写降级 尽早降级 在链路的前端降级可以保护更多后续服务 页面应设计降级框架, 各个部分都可以动态配置只读, 隐藏, 异步获取, 显示静态兜底数据等功能. Nginx直接降级返回请求, 处理速度比应用服务器快几十倍. Hystrix降级熔断 超时重试注意事项 回滚机制 分布式事务回滚 分布式事务回滚的错误处理 部署版本回滚 数据版本回滚 静态文件按版本发布 压力测试 应急预案 高并发 独立缓存 缓存位置 JVM对象引用 回收算法 本地缓存 4. 本地缓存的问题: 分布式缓存 多级缓存 有些缓存支持内存+磁盘分级缓存, 比如MapDB Cache-Aside 业务代码显式调用缓存, 比如读时先读缓存, 写后更新/失效缓存这种代码都写在应用程序里. Cache-Through 业务代码只读写缓存, 数据库的读取和写入由缓存代理. 一般实现缓存的读写接口. 分为直读, 直写和异步写三种. 代理写盘听起来很不靠谱. Guava支持Read-Through, Ehcache支持各种. 引用复制 缓存直接引用原始堆数据, 如果原始数据被修改, 缓存记录也被修改. Ehcache提供copier接口deep copy缓存数据防止这种错误. Guava不支持. HTTP缓存 浏览器会把服务器返回的网页缓存在本地, 下次请求时会带上上次返回的时间. 如果服务器返回304代表内容没有改变, 浏览器会直接显示本地缓存的页面. 关键的header 代理层静态缓存 Nginx Proxy Cache 清理缓存 如果需要紧急清理Nginx缓存可以使用ngx_cache_purge插件. 使用经验 多级缓存 多级缓存架构 构建缓存式网站 对高频读取的内容服务, 大量请求都是在缓存层处理的. 如果应用层能及时把变化发布到缓存,Nginx可以只从缓存读取数据(设置缓存不过期) 这就是运行在缓存上的网站. 应用要能够主动监听数据变更来更新缓存. 主要依靠订阅消息队列来实现. 如果数据量不大, 也可以考虑定期全量更新缓存. 应用场景: 商品, 订单, 用户, 分类, 价格等访问频率高, 允许一定更新延迟的页面. 缓存优化 缓存更新原子性 缓存崩溃的修复 大流量网站的读操作高度依赖缓存, 缓存崩溃对可用性影响很大. 池化 本部分包括线程池, 连接池等. 也包括通用的容器池框架apache commons-pool 2. 合理池化可以节省构建资源, 创建连接的时间. 池化的主要优势是保留长期存活的资源, 主要问题是退出程序时一定要通知远端取消对长期资源的占用. HTTP连接池 线程池 Tomcat线程池 Tomcat使用线程池来处理请求, 默认200. 可以修改配置或在自己的线程池异步处理HTTP请求. 异步/并发 在应用程序内, 同时调用多个外部服务以节省时间. 在这里使用线程池和client的方式管理和包装请求 扩容 系统演化路径 单机 -> 负载均衡 + 集群 -> 微服务 + 缓存 + 队列 扩容顺序 数据库拆分 数据库拆分的问题 Redis集群使用经验 分库分表实现方案 分布式事务错误处理 数据异构 队列 消息队列可以用来: 异步处理, 系统解耦, 数据同步, 流量削峰, 排队限流 数据同步/系统解耦 排队限流 数据库总线队列 通过订阅数据库的binlog, 对数据更改选择性同步到其它数据库, 比如异构库. 防止消息堆积 为防止消息队列被撑爆,…

2023-02-27 0comments 1921hotness 1likes Alvin Liu Read all
Post
  • General Conversion Tool convertt.top
  • SQL Developer Code Templates Autocomplete
  • Oracle Database Java Stored Procedure
  • Android 13 bypass Restricted Setting without root
  • Backup your Mac without time machine
Category
  • Architect
  • Database
  • Distributed System
  • Frontend
  • Golang
  • High Performance
  • JVM
  • Linux
  • Management
  • Misc
  • uncategorized

Android Babel Blog Cache Database Distributed System Dynamo Golang HA Heap JCS JVM Kubernetes Lens Linux Management Memory OCI Oracle Performance Planning Traefik Typescript Ubuntu Webpack WordPress

Comments
  1. martine on Use Lens to connect Kubernetes through SSH Tunnel
  2. kjstart on ES6 - ES13 新特性列表

COPYRIGHT © 2024 Alvin Liu. alvingoodliu@[email protected] ALL RIGHTS RESERVED.

Theme Made By Seaton Jiang