20.2 扩展和拆分
在互联网的架构设计中, 一个关键的衡量指标就是系统的可扩展性, 也称为伸缩性, 指的是系统不断增加其承载能力的能力.
在业务的高速发展中, IT 系统不应该成为整个公司的瓶颈. 扩展可简单地分为以下两类.
1. 向上扩展(scale up)
相对而言, 这是更简单的方式, 你应该优先使用该方式, 一般情况下向上扩展就足够了, 但如果向上扩展的代价太大了, 那么它就不可取了.
向上扩展总是有极限的, 比如, 你可以不断地增加 CPU 节点, 但由于 CPU 节点之间还需要进行通信, 因此 CPU 缓存一致性会越来越难以保证,
等到了一个极限, 即使你增加了 CPU 节 点, 系统的吞吐也不可能上升, 甚至可能还会下降.
目前 MySQL 能充分利用的资源是有限制的,
比如主机使用的是 256 GB 内存, 32 核, 一个 PCI-E Flash 卡一般就足够了, 如果超过了这个硬件成本, 即使有性能上的提升, 但成本上可能就不合算了.
随着软硬件技术的发展和硬件价格的变化, 你需要寻找一个性价比良好的方案合理搭配软硬件.
市场上的主流产品, 不仅能够 做到更大规模的生产, 而且成本也更低, 一般是更值得考虑的.
如果硬件规格过高或过新, 你可能不得不付出更昂贵的成本, 性价比反而不好.
对于一些应用类型, 比如复杂的查询, 即使再昂贵的硬件也无济于事, 因为性能更受限制于体系架构, 你的体系架构无法充分利用到你的硬件资源.
所以笔者建议, 在做扩展之前, 要先确认你已经很难进行软件架构的优化了, 比如, 你是否一定要访问这么多数据? 是否可以减少访问? 是否可以归档和清理数据.
2. 横向扩展
横向扩展也称为水平扩展(scale out), 是指通过副本, 垂直拆分, 水平拆分的方式, 把不同的数据放到不同的节点中, 我们所说的节点指的是物理部署的 MySQL 实例.
副本比较好理解, 一般是指增加数据库的从库(复制副本), 通过分担一部分读的流量到从库上, 扩展整个系统的处理能力, 也就是我们常说的读写分离, 20.3 节我们将详述读写分离.
垂直拆分指的是按功能模块划分数据, 采用这种方式的多个数据库之间的表结构不一样.
比如一个电商网站, 可能有库存管理的数据, 也有用户相关的数据, 它们属于不同的功能, 可以拆分到不同的节点.
对于更微观的层级, 数据表可以按字段划分数据: 大字段和字段访问频率相对于表中其他低很多的字段更适合从表中垂直分拆出去, 通过减少 I/O 资源消耗来达到优化性能的目的.
水平拆分 (sharding) 指的是将同一个表中的数据进行分片保存到不同的数据库中, 一般实现为数据库内的分表设计, 这些数据库中的表的结构一般都是相同的.
当内存, 磁盘空间, 磁盘 I/O, 网络, CPU 等资源受限制了, 我们就需要拆分, 以获得持续稳定的服务能力.
数据库节点的处理能力都是有限制的, 进行水平拆分主要是需要扩展写的能力.
读的能力相对比较容易扩展, 我们可以通过缓存, 副本等进行扩展.
但写的能力, 单个主机 (节点) 可能难以处理, 这个时候, 你需要进行水平拆分, 将数据按照一定的规则, 分配到不同的节点.
如果预知数据会有一个爆炸式的增长, 那么可以直接从单节点过渡到分片 (sharding) 结构, 而不是经过许多垂直拆分, 导致架构变得复杂, 难看.
市场中已经有一些数据库中间层产品, 或者一些云数据库, 宣称能够将数据透明地拆分到不同的节点中, 可完美地实现水平扩展,
现实使用中, 这样的产品可能会有各种限制, 而且效率不高.
通过自己手动地分片数据, 让应用层到具体的节点去获取数据会更高效,
因为我们要知道一个原则, 应用层才真正地了解数据, 知道数据应该如何查询和处理, 没有人比你自己更熟悉自己的应用.
进行数据分片设计的一个重要步骤是确定 sharding key, 常见的有用户 ID, 比如, 我们可以通过对用户 ID 进行散列, 把数据分布到不同的节点中.
大部分应用, 使用一些简单的算法进行分库分表的路由, 准备 10 倍到 20 倍的扩展, 1 到 2 年的扩展即可,
也就是说, 我们把分库分表的逻辑直接写在程序里, 使用 mod,crc32 等散列算法即可.
许多人还使用 key 分段的方法来做容量扩展及负载均衡, 笔者不是很推荐采用这种方式, 因为可能会导致数据不均衡和热点数据不均衡的问题, 调整起来也会很困难.
如果预见到数据和负载会有爆炸式的增长, 那么更值得推荐的方式是设计一张路由表, 里面存储数据到后端物理节点的路由信息.
这样的实现方法有一个好处, 我们可以达到一个效果, 前端有许多逻辑的 DB, 而后端是有限的一些物理节点. 这种方式更方便我们进行迁移和维护.
路由表不一定要保存在数据库里, 你也可以设计一个高可用的服务来存储这个路由信息.
如下是设计和维护分片需要注意的一些要点.
1)确保最重要最频繁的查询访问尽可能少的节点, 不仅要方便存储数据, 还要确保数据读取方便高效.
2)要避免单个节点的资源超过限制, 要有足够的监控和预警措施, 各分片的数据也可能会不均衡, 你需要及时发现这种情况, 并进行调整.
3)分片数量要合适, 要有利于负载均衡和进行维护, 比如修改表结构.
分片数量也不能过多, 过多可能会导致一些异常问题, 比如, 一个简单的查询要访问多个分片响应会变得很慢.
需要说明的一点是, 分片可以让失效的数据保持在某个范围内, 但同时分片将导致更多的硬件错误.
虽然按照分片的逻辑, 把数据拆分到多个节点上很美好, 如果可能发生故障, 每次只会有一部分服务异常, 但节点出问题的概率也大大增加了,
而实际生产环境中单台主机宕机的概率是很小的, 所以不要过多地分片, 你应该在资源可能短缺的情况下, 为了扩容考虑的目的而考虑分片.
节点的个数和分片的数量有赖于 DBA 和研发, 产品等团队一起讨论确定,
互联网应用数据的容量规划在很多情况下都不是很清晰, 这是现实, 所以应尽可能地沟通信息, 掌握足够的数据是有必要的.
4)分片之后, 表之间的连接会比较困难, 你需要尽量避免连接, 或者采用更合适的方案来组合数据,
比如, 我们可以设定多个 sharding key, 使用不同的 sharding key 冗余多份数据, 以做到减少连接, 更高效地访问数据.
我们也可以使用缓存, 统计表等手段来减少连接.
5)热点数据可能会导致性能问题, 可能需要调整 sharding key 或算法, 将数据切割为更小的分片, 如果对于复制延时要求不高, 也可以利用从节点来扩展读的能力.
6)应确保节点的快速恢复, 比如对于单点故障, 你可以自动路由到正常的节点, 或者是提供开关降级服务, 比如提供一个只读的节点, 用于保障部分功能可用.
注意:
可扩展并不是要构建一个完美的扩展架构, 而是应用程序真的需要进行扩展时, 才考虑扩展, 我们要预先规划, 即使在以后规模变得很大的时候, 系统也仍然能够提供合格的服务.
来源: http://www.bubuko.com/infodetail-3346586.html