对 MySQL 表进行分片的最佳方法是不要这样做,除非完全不可避免。
当您编写应用程序时,您通常希望以最大化速度、开发人员速度的方式进行。仅在必要时才优化延迟(直到答案准备就绪的时间)或吞吐量(每个时间单位的答案数量)。
仅当所有这些分区的总和不再适合单个数据库服务器实例时(原因是写入或读取),您才进行分区,然后将分区分配给不同的主机(= 分片)。
写入情况是 a) 写入频率使该服务器磁盘永久超载,或者 b) 正在进行太多写入,导致复制在此复制层次结构中永久滞后。
分片的读取情况是当数据的大小太大以至于其工作集不再适合内存时,并且数据读取开始命中磁盘而不是大多数时候从内存提供服务。
只有当你have分片你就这样做。
当你进行分片时,你会以多种方式为此付出代价:
您的大部分 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 用户故事相关的表转到此master,而 abc 和 def 相关表则转到该 master”)。
功能分片的优点是,如果做得正确,大多数开发人员在大多数情况下都看不到它,因为与其用户故事相关的所有表都可以在本地使用。这使得他们仍然可以尽可能长时间地从声明式 SQL 中受益,并且由于跨网络传输的数量保持在最低限度,因此也会减少网络延迟。
功能分片的缺点是它不允许任何单个表大于一个实例,并且需要设计者的手动关注。
功能分片的优点是可以相对轻松地对现有代码库进行一些不太大的更改。http://Booking.com在过去的几年里,他们已经这样做过很多次了,而且效果很好。
话虽如此,看看你的问题,我确实相信你问了错误的问题,或者我完全误解了你的问题陈述。