面试官:水平分表如何选择 sharding key?分表和分区有什么区别?从 Innodb 底层存储说明为什么需要做垂直分表?

数据库
现实情况中能不水平分表就尽量不要水平分表,而是在经过增加索引,读写分离后仍无法解决查询慢的情况下才进行分表。

面试官:什么情况下选择分库?什么情况下选择分表?

分库和分表是数据库层面的两种常见解决方案,它们分别解决不同的问题。

1. 分库解决的问题

分库主要是为了解决高并发的问题。在数据库系统中,当对数据库的访问量(QPS,Queries Per Second)非常高时,会对数据库实例造成很大的压力,降低数据查询效率,甚至导致连接失败。这是因为每个数据库实例都有连接数的限制,当访问连接数过多时,就会超出这个限制。

此时,将一个数据库实例的操作拆分成对多个数据库实例的操作,即进行分库,可以有效地降低单个数据库实例的压力,提高系统的并发处理能力。此外,分库还可以实现业务数据的隔离,便于管理和维护。

2. 分表解决的问题

(水平)分表主要是为了解决数据量大的问题。当一张表中的数据量过大时,对该表的读写操作都会变得效率低下。这是因为数据库需要遍历的记录过多导致可能得IO次数增多,如果表中的记录数目过多,这个过程将会非常耗时。

通过分表,可以将数据分散到多个表中,每个表的数据量都相对较小,这样在执行查询操作时,数据库只需要在相对较小的表中查找数据,从而大大提高查询效率。此外,分表还可以避免单表数据过大引发的一系列问题,如备份和恢复的速度慢等。

现实情况中能不水平分表就尽量不要水平分表,而是在经过增加索引,读写分离后仍无法解决查询慢的情况下才进行分表。原因主要基于以下几点考虑:

  • 复杂性增加:水平分表将原始表的数据按某种策略(如哈希、范围等)拆分成多个子表,每个子表包含原始表的一部分数据。这增加了系统的复杂性,因为需要管理多个表,并且在查询、更新和删除数据时需要考虑数据分布和路由。
  • 数据一致性难以保证:在水平分表的情况下,某些操作(如跨分表事务)可能难以保证数据的一致性。分布式事务问题是一个挑战,因为当使用分片技术进行水平分表时,可能会涉及到多个数据库节点的事务处理。这增加了系统的复杂性和开发成本,同时也可能影响数据的可靠性和完整性。
  • 维护和迁移成本:水平分表后,数据的维护和迁移成本也会增加。例如,当需要添加新的字段或修改现有字段时,需要在所有相关的子表上执行相应的操作。此外,如果需要进行数据迁移或备份,也需要考虑如何高效地处理多个分片的数据。

面试官:水平分表该如何选择sharding key,应该遵循哪些原则?

Sharding Key(分片键)是用于将数据进行分片的属性或字段。选择Sharding Key 最重要的原则是:我们的业务最频繁的使用哪个字段访问数据的。

以电商系统的订单表分片为例。假设把 OrderlD 作为 Sharding Key行不行?

在电商系统中,买家和商家都有查看订单列表的需求,但最能为电商带来利益的是买家用户,而买家用户对于订单最高频的访问是 app 中"我的订单"页面,此时的查询条件是用户ID。但是如果我们的分片依据是 OrderID,强行查询的只能查询所有的分片,并合并查询结果,效率很低,且没法分页。

而如果把 UserID 作为Sharding Key,此时用户在app中"我的订单"页面正好可以使用到分片键,一个用户对应的订单信息都在一个分片中,因为分片是使用的UserID,此时效率最高。直接去对应的分片去查询就可以了。

问题来了,有使用OrderlD进行查询的场景怎么办,怎么通过OrderID找到对应的数据分片?

管理员或者商家可能不使用UserID而只使用OrderID作为查询条件查看单条订单。为了保证用户在主场景下的查询效率,还是以UserID作为分片键。但是在生成订单ID的时候,可以将用户ID的后几位作为订单ID的一部分。比如18位的订单号,它的第15-18位是用户ID的后四位。此时按照订单ID查询的时候,可以根据订单ID中的用户ID找到分片。

假设我们已经确定了把 UserID 作为Sharding Key ,那么商家想查询自己的订单列表怎么办?

一般的方案是把订单数据同步到其他的存储中间件中,用其他存储中间件解决复杂查询的问题。例如可以构建一套以商家ID作为Sharding Key的只读订单库,专门供商家使用。或者可以将数据同步到HDFS中,用一些大数据技术生成对应的订单报表。

一个选择分片键的一些通用依据如下所示:

(1) 数据分布均匀性:

  • 分片键的数据基数要足够大,也就是分片键的value尽可能不同,以确保数据能够均匀分布到各个分片节点上,避免数据倾斜。
  • 增长趋势可预测,便于进行容量规划和分片管理。
  • 避免选择可能导致热点数据的字段,如使用时间戳作为分片键时,需要注意追加写入可能导致特定分片成为热点。

(2) 查询模式适配性:

  • 与业务最频繁的查询模式匹配,以便在查询时能够高效地定位到数据所在的分片节点。
  • 支持就近路由,即查询时能够直接定位到包含所需数据的分片节点,提升查询效率。

(3) 字段的更新频率:

    选择低更新频率的字段作为Sharding Key,以减少数据迁移和重新分片的频率。

面试官:Mysql的水平分表和分区有什么区别,什么时候用水平分表什么时候用分区?介绍一下Mysql中常见的分区算法?

1. 水平分表(Sharding)

原理:水平分表是将一个大型表的数据按某种规则拆分到多个独立的表中。这些表通常具有相同的结构,但存储不同的数据。

应用场景:水平分表适用于数据量特别大、需要分布式存储和高并发访问的场景,如大型电商平台、社交网络等。同时分表可以分散到不同的数据库实例,当单个数据库实例无法承载所有数据或处理所有请求时,水平分表成为了一种有效的解决方案。

优点:

  • 可以突破单节点数据库服务器的I/O能力限制,提高系统的可扩展性。
  • 可以将数据分散到多个存储单元中,以减轻单表的数据量和访问压力,从而提高数据库的性能。

缺点:

  • 实现和维护相对复杂,需要手动管理各个分表,包括表的创建、数据迁移和备份恢复等操作。
  • 跨表查询需要应用程序处理或使用中间件支持,增加了开发难度和成本。

2. 分区(Partitioning)

原理:分区是将一个表的数据按某种规则划从逻辑上分成多个分区,每个分区存储一部分数据。但这些分区仍然属于同一个表和同一个数据库实例。

应用场景:分区适用于中等规模的数据优化,通过分区,可以优化查询性能和管理效率。

优点:

  • 数据库系统自动管理分区,支持自动分区裁剪和优化,提高了查询性能。
  • 管理和维护相对简单,减少了开发和运维成本。

缺点:扩展性相对较弱,受限于单个数据库实例的资源。

当需要增加新的分区或调整分区范围时,可能需要重新定义分区规则并迁移数据,增加了系统停机时间和数据不一致性风险。

3. 分区和分表对比

对比指标

分区

分表

查询性能

可以通过只扫描相关分区来提高查询效率,减少I/O操作量。

查询性能通常优于分区,因为每个小表都是独立的,可以充分利用数据库索引和缓存机制。

数据管理

便于进行局部备份、恢复和数据清理操作,但整体表结构仍然保持一致。

数据管理相对复杂,需要对多个表进行协调操作。

并发性能

可以提高并发性能,因为不同分区可以独立操作,减少锁冲突。

并发性能通常优于分区,因为每个小表都是独立的,可以充分利用数据库并发处理能力。

扩展性

扩展性有限,因为分区仍然属于同一个表,受到数据库表大小等限制。

扩展性较好,可以通过增加小表数量来实现水平扩展。

维护成本

维护成本相对较低,因为表结构仍然保持一致,只需关注分区策略的优化。

维护成本较高,需要对多个表进行协调操作和维护。

适用场景

适用于数据量大但查询条件较为集中的场景,如按时间范围查询的日志表。

适用于数据量大且查询条件较为分散的场景,如用户信息表、订单表等。

以下是MySQL中常见的分区算法及其使用场景:

(1) Range分区(范围分区)

定义:基于属于一个给定连续区间的列值,把多行分配给分区。

使用场景:适用于那些可以基于某个范围进行划分的数据。例如,可以按年份、月份或日期范围对表进行分区,以便查询特定时间段内的数据时,能够只扫描包含所需数据的分区,从而提高查询效率。

示例:

CREATE TABLE orders (
    id INT NOT NULL AUTO_INCREMENT,
    order_date DATE,
    customer_id INT,
    amount DECIMAL(10,2),
    PRIMARY KEY (id, order_date)
)
PARTITION BY RANGE (YEAR(order_date)) (
    PARTITION p0 VALUES LESS THAN (2010),
    PARTITION p1 VALUES LESS THAN (2015),
    PARTITION p2 VALUES LESS THAN (2020),
    PARTITION p3 VALUES LESS THAN (2025)
);

(2) List分区(列表分区)

定义:基于预定义的值列表进行划分。

使用场景:适用于那些值具有离散特性的列。例如,当某个字段的值只有有限的几个选项时,可以使用List分区。

示例:

CREATE TABLE employees (
    id INT NOT NULL,
    department_id INT NOT NULL,
    name VARCHAR(50)
)
PARTITION BY LIST (department_id) (
    PARTITION p_hr VALUES IN (1, 2),
    PARTITION p_finance VALUES IN (3, 4),
    PARTITION p_it VALUES IN (5, 6)
);

(3) Hash分区(哈希分区)

定义:使用哈希函数对列值进行计算,然后根据结果分配到不同的分区。

使用场景:适用于数据分布较为均匀的场景。通过哈希分区,可以将数据均匀地分布到不同的分区中,从而提高查询性能。

示例:

CREATE TABLE users (
    id INT NOT NULL,
    email VARCHAR(100) NOT NULL,
    signup_date DATE NOT NULL
)
PARTITION BY HASH(id)
PARTITIONS 4;

4. 选择分区算法的建议

  • Range分区:当需要基于某个范围进行划分时,如按年份、月份等,可以选择Range分区。
  • List分区:当某个字段的值只有有限的几个选项时,可以选择List分区。
  • Hash分区:当数据分布较为均匀,且希望将数据均匀地分布到不同的分区中时,可以选择Hash分区。

面试官:能不能说说看什么是垂直分表以及垂直分表的场景?从Innodb底层存储说明为什么需要做垂直分表?

1. 定义

垂直分表是将一个宽表(即包含多个字段的表)按照字段进行拆分,形成多个子表,每个子表仅包含原表中的部分字段。这些子表通过主键或唯一索引进行关联,以保持数据的完整性。

2. 原理及作用

优化查询性能:通过拆分表中的字段,可以减少单个表的宽度,从而提高查询效率。尤其是当查询只涉及部分字段时,可以避免加载不必要的字段,减少I/O开销。

分离冷热数据:将使用频率较高的热数据与使用频率较低的冷数据分离到不同的表中,可以优化数据库性能,减少对冷数据的访问开销。

提高缓存命中率:表宽度减小后,缓存的效果会更好,因为缓存通常只能存储有限数量的数据行或数据页。当表宽度较小时,更多的数据行或数据页可以被缓存,从而提高缓存命中率。

3. 适用场景

垂直分表通常适用于以下几种场景:

  • 宽表拆分:当一个表中包含了大量的字段,且部分字段的使用频率较低时,可以通过垂直分表将常用字段和不常用字段分开。
  • 冷热数据分离:在大数据应用中,部分数据的访问频率很高(热数据),而部分数据的访问频率很低(冷数据)。通过垂直分表可以将冷热数据分开,以优化数据库性能。
  • 功能模块独立:如果某些功能模块只需要访问表中的部分字段,那么可以将这些字段独立出来,以减少查询时的I/O负担。
  • 大字段处理:表中包含大字段(如BLOB、TEXT等)时,这些字段会占用大量存储空间和I/O资源。通过垂直分表,可以将这些大字段独立存储,以减少对其他查询的影响。

从InnoDB存储结构的角度来看,实现垂直分表的原因主要基于以下几点:

4. InnoDB存储结构概述

InnoDB是MySQL的默认存储引擎,它管理数据的方式是通过行(Row)组成页(Page),页再组成区(Extent),区再组成段(Segment),最后段组成表空间(Tablespace)。其中,页是InnoDB存储的基本单位,其大小通常为16KB。

5. 垂直分表与InnoDB存储结构的关联

减少跨页检索:

  • 在InnoDB存储结构中,数据是按页存储的。如果表中的字段过多,每行数据占用的空间就会增大,这可能导致一行数据跨越多个页存储。当进行查询时,如果需要跨页检索数据,就会增加I/O操作的次数,降低查询效率。
  • 通过垂直分表,将不常用或大字段拆分到单独的表中,可以减少主表的宽度,使每行数据占用的空间减小,从而减少跨页检索的可能性,提高查询效率。

优化页内数据行存储:

  • InnoDB存储引擎在压缩和解压缩时会花费额外的时间。如果表中的数据行跨页存储,那么在解压缩时需要处理更多的数据,这会增加I/O负担。
  • 通过垂直分表,可以优化页内数据行的存储,使更多的数据行能够在同一个页内存储,从而减少跨页检索和磁盘扫描的范围,达到提高查询效率的目的。

提升缓存命中率:

  • 数据库缓存通常只能存储有限的数据量。当表宽度较大时,缓存中可能只能存储少量的数据行,这降低了缓存的命中率。
  • 通过垂直分表,将表拆分成更小的表,可以使更多的数据行能够被缓存,从而提高缓存命中率,减少磁盘I/O操作,提高数据库性能。
责任编辑:赵宁宁 来源: 程序员阿沛
相关推荐

2020-11-18 09:39:02

MySQL数据库SQL

2020-07-30 17:59:34

分库分表SQL数据库

2022-06-22 07:32:53

Sharding分库数据源

2021-01-26 05:37:08

分库分表内存

2019-03-06 14:42:01

数据库分库分表

2024-01-17 14:42:24

分库分表数据库数据分片

2024-07-25 18:20:03

2024-09-19 08:42:43

2024-04-03 15:33:04

JWTSession传输信息

2023-02-17 08:10:24

2021-04-19 08:16:38

Hive数据类型大数据技术

2022-02-19 21:36:05

Hive数据,节点

2021-10-27 20:54:24

分库分表高并发

2019-09-09 08:28:48

互联网数据磁盘

2020-10-29 09:10:06

MySQL

2021-11-30 07:44:50

FinalFinallyFinalize

2021-12-10 12:01:37

finalfinallyfinalize

2021-12-13 06:56:45

Comparable元素排序

2024-03-20 15:12:59

KafkaES中间件

2021-12-23 07:11:31

开发
点赞
收藏

51CTO技术栈公众号