一、单机 MySQL 的美好时代1.1 90年代的简单架构在互联网早期一个网站的访问量通常不大单个 MySQL 数据库完全可以轻松应付。那时的网站以静态页面为主动态交互类型的应用不多。DALData Access Layer数据访问层如 Hibernate、MyBatis 等 ORM 框架负责应用程序与数据库之间的数据交互。1.2 数据存储的三大瓶颈随着业务发展单机 MySQL 逐渐面临以下瓶颈瓶颈表现触发条件数据量过大数据总大小超过单台机器磁盘容量业务数据持续增长索引内存不足B Tree 索引无法全部加载到内存数据量达到千万级访问量过载读写混合请求超出单实例处理能力并发用户激增当满足上述 1 个或多个条件时就必须对数据库架构进行重构。二、MySQL 架构演进之路2.1 阶段一Memcached MySQL 垂直拆分随着访问量上升MySQL 数据库开始出现性能问题。程序员们开始大量使用缓存技术来缓解数据库压力同时优化数据库结构和索引。文件缓存的局限动态 Web 机器之间无法共享文件缓存大量小文件缓存带来高 IO 压力Memcached 登场Memcached 作为一个独立的分布式缓存服务器为多个 Web 服务器提供了共享的高性能缓存服务。后续又发展了基于 Hash 算法的多 Memcached 扩展方案以及一致性 Hash来解决增删缓存服务器导致大量缓存失效的问题。垂直拆分按业务模块将数据拆分到不同的数据库实例如用户库、订单库、商品库分离。2.2 阶段二MySQL 主从读写分离Memcached 只能缓解数据库的读取压力但写入压力依然集中在一个数据库上。于是主从复制技术成为标配。读写分离的价值读请求分散到多个从库提升读性能从库可作为备份主库故障时快速切换成为当时网站架构的标准配置2.3 阶段三分库分表 水平拆分 MySQL 集群在 Memcached 缓存、MySQL 主从复制、读写分离的基础上主库的写压力开始出现瓶颈。同时数据量持续猛增MyISAM vs InnoDB 的抉择MyISAM 在写数据时会使用表锁高并发写场景下会出现严重的锁问题。而 InnoDB 支持行锁发生冲突的几率低并发性能高。这也是 MySQL 5.6 之后使用 InnoDB 作为默认存储引擎的原因。分库分表拆分方式维度说明垂直拆分按业务/字段将不同业务表放到不同库或将大字段拆分到扩展表水平拆分按数据行将同一表的数据按规则如用户ID取模分散到多个库MySQL Cluster 的局限虽然 MySQL 推出了 Cluster 集群但性能仍不能满足互联网高并发场景的要求仅在高可靠性上提供了较大保证。2.4 MySQL 的扩展性瓶颈总结瓶颈类型具体表现根本原因存储瓶颈大文本字段导致表体积庞大恢复缓慢关系模型要求数据集中存储IO 压力随机 IO 频繁磁盘寻道耗时B Tree 索引结构导致锁竞争高并发写入时锁冲突严重表锁/行锁机制扩展困难分库分表技术复杂维护成本高关系型数据库非为分布式设计Schema 僵化表结构变更困难影响线上服务强 Schema 约束1000 万条 4KB 大小的文本数据就接近 40GB如果能把这些数据从 MySQL 中剥离数据库将变得轻量许多。三、今天的互联网架构全貌现代互联网架构已经演变为一个复杂的分布式系统架构层次接入层防火墙 负载均衡软负载 Nginx / 硬负载 F5应用层Web 服务器集群Tomcat 等数据层缓存Redis 数据库MySQL/Oracle 大数据Hadoop服务层移动信息、实时通信等专项服务四、为什么需要 NoSQL4.1 数据爆炸的时代背景今天我们可以通过 Google、Facebook 等平台轻松访问和抓取海量数据。用户的个人信息、社交网络、地理位置、用户生成内容UGC和操作日志已经呈指数级增长。数据特征传统 SQL 应对能力NoSQL 优势超大规模分库分表复杂且有限原生水平扩展多数据种类严格 Schema 难以适应灵活 Schema/无 Schema高并发读写磁盘 IO 成为瓶颈内存操作 顺序写实时处理复杂查询性能下降针对特定模型优化4.2 NoSQL 的核心定义NoSQLNot Only SQL意即不仅仅是 SQL泛指非关系型数据库。随着互联网 Web 2.0 网站的兴起传统关系数据库在应付超大规模和高并发的 SNS 类型纯动态网站时已显得力不从心。NoSQL 数据库的产生就是为了解决大规模数据集合、多重数据种类带来的挑战尤其是大数据应用难题。例如 Google 或 Facebook 每天为用户收集万亿比特的数据。这些类型的数据存储不需要固定的模式无需多余操作就可以横向扩展。4.3 NoSQL 代表产品产品类型核心特点MongoDB文档型灵活 Schema、JSON 存储、内置分片Redis键值存储内存操作、超高性能、数据结构丰富Memcached键值存储纯缓存、简单高效HBase列存储海量数据、强一致性Cassandra列存储高可用、最终一致性Neo4j图数据库深度关系查询五、关系型数据库 vs NoSQL 深度对比5.1 RDBMS 特性特性说明结构化数据高度组织化的结构化数据严格的表结构SQL 查询标准化的结构化查询语言表存储数据和关系都存储在单独的表中DDL/DML完善的数据定义语言和数据操作语言严格一致性强一致性保证ACID 事务原子性、一致性、隔离性、持久性ACID 详解特性全称含义AAtomicity原子性事务中的所有操作要么全部完成要么全部不做。如银行转账A 扣款和 B 收款必须同时成功或同时失败CConsistency一致性事务执行前后数据库始终处于一致状态不会破坏完整性约束IIsolation隔离性并发事务之间互不干扰未提交事务的数据对其他事务不可见DDurability持久性事务提交后修改永久保存即使宕机也不会丢失5.2 NoSQL 特性特性说明无声明查询语言没有统一的 SQL各数据库有特定 API无预定义模式无需预先定义表结构灵活适应变化多样存储模型键值对、列存储、文档存储、图形数据库最终一致性非 ACID 属性追求 BASE 理论非结构化数据支持半结构化和不可预知的数据格式高性能高可用针对高并发和分布式场景优化5.3 CAP 定理再回顾维度含义C - Consistency一致性数据一致更新所有数据变动同步A - Availability可用性好的响应性能系统持续服务P - Partition Tolerance分区容错性分布式环境下的可靠性定理任何分布式系统最多只能同时满足 CAP 中的两点无法三者兼顾。由于网络硬件必然存在延迟和丢包分区容错性P是必须实现的。因此我们只能在**一致性C和可用性A**之间做权衡类型满足特性代表特点CA一致性 可用性Oracle 单机可扩展性不强CP一致性 分区容错Redis、MongoDB性能适中保证数据正确AP可用性 分区容错Cassandra、大多数网站架构允许短暂不一致现实选择大多数 Web 应用并不需要强一致性牺牲 C 换取 P是目前分布式数据库产品的主流方向架构设计时必须做出取舍在一致性和可用性之间找到平衡六、NoSQL 如何与关系型数据库互补NoSQL 并非要取代关系型数据库而是在特定场景下提供更好的解决方案。6.1 互补一提升写入性能MySQL 的写入瓶颈数据库系统大都使用传统机械硬盘磁盘访问方式有两种随机 IO需要磁盘寻道读写效率比顺序 IO 小2-3 个数量级顺序 IO连续读写性能优异MySQL InnoDB 的写入过程顺序 IO更新 binlog、redolog、undolog随机 IO更新 datafile 和索引文件虽然 InnoDB 通过写入内存再批量刷新到磁盘来优化但随机 IO 仍然不可避免。特别是主键作为聚簇索引数据插入时需要找到特定位置写入产生大量随机 IO。一旦发生页分裂数据移动更会极大损耗写入性能。NoSQL 的解决方案LSM 树NoSQL 数据库如 HBase、Cassandra、LevelDB普遍使用 **LSM 树Log-Structured Merge Tree**作为存储引擎。LSM 树核心思想将随机 IO 转换为顺序 IO提升写入性能。写入流程数据首先写入 MemTable内存有序结构同时通过 Write Ahead LogWAL备份到磁盘顺序写MemTable 达到一定规模后刷新为 SSTableSorted String Table多个 SSTable 积累后后台合并Compaction合并后的 SSTable 仍然有序可被高效查询读取流程先从 MemTable 查询未找到则按时间顺序查询 SSTable利用 Bloom Filter 快速判断 SSTable 是否包含目标 KeyLSM 树的权衡写入性能极高全是顺序写读取性能略低需要查询多个 SSTable空间放大存在多版本数据需定期合并类似算法还有 TokuDB 使用的Fractal Tree核心思想一致将随机 IO 变为顺序 IO。6.2 互补二提升扩展性NoSQL 数据库在设计之初就考虑了分布式和大数据存储场景。以 MongoDB 为例其扩展性体现在三个层面副本集Replica┌─────────┐ ┌─────────┐ ┌─────────┐ │ Primary │ ───▶ │ Secondary│ ───▶ │ Secondary│ │ (主节点) │ 复制 │ (从节点) │ 复制 │ (从节点) │ │ 写请求 │ │ 读请求 │ │ 读请求 │ └─────────┘ └─────────┘ └─────────┘主从分离主节点承担写请求从节点分担读请求数据同步主节点将数据变动记录到 oplog类似 MySQL binlog从节点应用 oplog 保持数据一致自动故障转移主节点宕机时自动选举新主节点继续提供写服务分片ShardMongoDB 分片需要三个角色Shard Server实际存储数据的节点独立 Mongod 进程Config Server存储元信息哪些分片存储哪些数据一组 Mongod 进程Route Servermongos不存储数据仅作为路由从 Config Server 获取元信息后路由到正确 Shard负载均衡Balancer当 Shard 之间数据分布不均匀时Balancer 进程自动重新分配数据扩容时新 Shard Server 自动接收迁移数据无需手动数据迁移和验证价值NoSQL 内置的扩展性特性让我们不再需要手动做分库分表和主从分离大幅降低运维复杂度。6.3 互补三场景补充——全文搜索问题场景按商品名称模糊搜索SELECT * FROM product WHERE name LIKE %电冰箱%问题前模糊匹配%xxx无法使用索引导致全表扫描后模糊匹配xxx%可以使用索引但功能受限全表扫描在性能上无法接受NoSQL 解决方案Elasticsearch 倒排索引倒排索引将记录中的某些列做分词形成分词与记录 ID 之间的映射关系。示例商品 ID商品名称1西门子电冰箱2小米电视机3美的电冰箱分词后建立倒排索引关键词商品 ID西门子1电冰箱1, 3小米2电视机2美的3搜索过程用户搜索电冰箱 → 查倒排索引 → 返回商品 ID 1 和 3时间复杂度 O(1)与数据总量无关Elasticsearch 基于倒排索引实现是关系型数据库在全文搜索场景下的完美补充。七、NoSQL 与关系型数据库的协作模式7.1 去 IOE 化IOEIIBM 小型机昂贵数万元一台OOracle 数据库昂贵数万元一套EEMC 存储设备昂贵去 IOE 化是中国互联网企业的典型技术演进路径用开源软件 分布式架构替代昂贵的商业软硬件。7.2 现代架构中的数据库协作各司其职MySQL核心交易数据强一致性保障Redis热点数据缓存超高并发读取MongoDB灵活文档存储快速迭代开发Elasticsearch全文搜索复杂查询HBase海量时序数据离线分析Neo4j关系图谱推荐引擎八、总结本文沿着互联网架构演进的脉络揭示了 NoSQL 诞生的必然性单机 MySQL → 缓存 垂直拆分 → 读写分离 → 分库分表每一步都是对上一阶段瓶颈的突破MySQL 的扩展性瓶颈随机 IO、锁竞争、Schema 僵化推动了 NoSQL 的发展NoSQL 通过 LSM 树将随机写转为顺序写大幅提升写入性能NoSQL 内置分布式特性副本集、分片、负载均衡让扩展变得简单NoSQL 在特定场景全文搜索、图关系、海量日志下是关系型数据库的完美补充现代架构趋向混合使用SQL NoSQL 各司其职优势互补欢迎在评论区交流讨论