如何最佳地对MySQL表进行Sharding?我能想到的方法有:
- 在应用程序层面进行Sharding?
- 在MySQL代理层面进行Sharding?
- 为Sharding设置中央查找服务器?
您是否了解该领域中的任何有趣项目或工具?
除非完全不能避免,否则不要分片MySQL表。
当您编写应用程序时,通常希望以最大化速度和开发人员速度的方式进行。只有在必要时才会优化延迟(答案准备时间)或吞吐量(单位时间内的答案数量)。
只有当所有这些分区的总和不再适合单个数据库服务器实例时,即出现写入或读取问题时,才会对其进行分区,然后将分区分配给不同的主机(=分片)。
写入的情况是要么a)写入频率永久超载了此服务器的磁盘,要么b)有太多的写入正在进行,因此该复制层次结构的复制永久滞后。
读取的情况下,如果数据大小过大,工作集就不再适合存储器,数据读取开始从磁盘而不是大部分时间从内存中提供。
只有在必须分片时才这样做。
一旦分片,您就会以多种方式为其付费:
许多SQL不再声明性。
通常,在SQL中,您告诉数据库要获取哪些数据,并由优化器将该规范转换为数据访问程序。这是件好事,因为它是灵活的,并且编写这些数据访问程序会损害速度。
在分片环境中,您可能正在节点A上连接表与节点B上的数据进行比较,或者拥有大于一个节点的表,在节点A和B上并且正在使用来自节点B和C的数据与其进行比较。因此,您需要手动编写应用程序端基于哈希的联接解决方案以解决该问题(或者是重新发明MySQL集群),因此您最终会得到许多SQL不再声明性但以过程方式表达 SQL功能(例如,您正在循环中使用SELECT语句)。
您将产生大量网络延迟。
通常,SQL查询可以在本地解决,并且优化器了解与本地磁盘访问相关的成本,并以最小化成本的方式解决查询。
在分片环境下,查询可以通过在多个节点之间运行键值访问(希望批量键访问而不是每次往返进行单个键查找)或将 WHERE 子句的部分向前推送到可以应用它们的节点上(这称为“条件推广”)来解决。但即使在最好的情况下,这也涉及比本地情况更多的网络往返,并且更加复杂。特别是由于 MySQL 优化器根本不知道网络延迟(好吧,MySQL 集群正在缓慢改善这一点,但对于集群外的普通 MySQL 仍然如此)。您正在失去 SQL 的很多表达能力。外键约束和其他用于数据完整性的 SQL 机制无法跨越多个分片。MySQL 没有允许异步查询的 API 是可以正常工作的。当同一类型的数据驻留在多个节点上时(例如用户数据驻留在 A、B 和 C 节点上),水平查询通常需要针对所有这些节点解决(“查找所有超过 90 天未登录的用户帐户”)。数据访问时间随着节点数量的增加呈线性增长,除非可以并行请求多个节点并在结果到达时聚合结果(“Map-Reduce”)。这需要一个异步通信 API,而 MySQL 没有具备良好工作状态的异步查询的 API。另一种选择是在子进程中进行大量分支和连接,这使事情变得更加困难。一旦开始分片,数据结构和网络拓扑将成为应用程序的性能点。为了表现得合理,您的应用程序需要意识到这些内容,这意味着真正只有应用程序级别的分片是有意义的。问题更多地是您是否想要自动分片(例如通过哈希主键确定哪行数据进入哪个节点)还是以手动方式功能拆分(“与 xyz 用户故事相关的表转到此主节点,而 abc 和 def 相关表则转到那个主节点”)。功能分片的优点在于,如果正确实施,它对大多数开发人员来说是不可见的,因为与他们用户故事相关的所有表格都将本地可用。这使得他们在尽可能长的时间内仍然从声明性SQL中受益,并且由于跨网络转移的数量被保持最小,也会产生更少的网络延迟。
功能分片的缺点在于,它不允许任何单个表格比一个实例更大,并且需要设计师手动关注。
功能分片的优点在于,相对容易对现有的代码库进行一些不太大的更改就可以实现。过去几年中,Booking.com 已经多次完成了这项工作,并且效果很好。
话虽如此,在看完你的问题后,我认为你提出了错误的问题,或者我完全误解了你的问题陈述。
应用程序级别分片:dbShards是我所知道的唯一一个能够实现“应用程序感知分片”的产品。网站上有几篇很好的文章。仅从定义上来说,应用程序感知分片将更加高效。如果一个应用程序知道在没有查找或被代理重定向的情况下在哪里处理事务,那么这本身就会更快。当某人考虑进行分片时,速度通常是主要问题,如果不是唯一的问题。
一些人使用代理进行“分片”,但在我看来,这违背了分片的目的。你只是使用另一台服务器告诉你的事务数据在哪里或者存储在哪里。通过应用程序感知分片,你的应用程序自己知道去哪里。更加高效。
这与#2基本相同。
你知道这个领域有什么有趣的项目或工具吗?
该领域中有几个新项目:
CREATE TABLE t1 (col1 INT, col2 CHAR(5), col3 DATETIME) PARTITION BY HASH ( YEAR(col3) );
(这只是四种分区类型之一)
一个非常重要的限制:
InnoDB外键和MySQL分区不兼容。分区的InnoDB表不能有外键引用,也不能有被外键引用的列。具有或被外键引用的InnoDB表不能进行分区。
PARTITION BY HASH(YEAR...)
将扫描_所有_分区。 真糟糕。 - Rick James当然是应用级别的。
我在这本书中发现了最好的方法。
《高性能MySQL》 http://www.amazon.com/High-Performance-MySQL-Jeremy-Zawodny/dp/0596003064
简短描述:您可以将数据分成许多部分,并在每个服务器上存储约50部分。它将帮助您避免分片的第二大问题 - 重新平衡。只需将其中一些移动到新服务器,一切都会很好:)
我强烈建议您购买并阅读“mysql扩展”部分。
Shard-Query 是一个基于 OLAP 的 MySQL 分片解决方案。它允许您定义一组分片表和非分片表。非分片表(如查找表)可以自由地与分片表连接,只要表是通过分片键连接的,分片表也可以相互连接(不能跨分片或自身连接跨越分片边界)。作为 OLAP 解决方案,Shard-Query 通常具有最小响应时间不超过 100ms,即使对于简单查询也是如此,因此它不适用于 OLTP。Shard-Query 旨在并行分析大型数据集。
MySQL也存在OLTP分片解决方案。闭源解决方案包括ScaleDB, DBShards。开源OLTP解决方案包括JetPants, Cubrid或Flock/Gizzard(Twitter基础设施)。