FROM 《姜承尧的 MySQL 实战宝典》
数字类型
整型的 signed 和 unsigned
不要刻意使用 unsigned,使用 unsigned 可能返回不想要的结果。
unsigned 和 unsigned 数值相减之后依然为 unsigned,否则就会报错。
为了避免这个错误,需要对数据库参数 sql_mode 设置为 NO_UNSIGNED_SUBTRACTION,允许相减的结果为 signed。
浮点类型和高精度型
Float 和 Double 不是高精度,真实的生产环境中不推荐使用。
高精度 DECIMAL 类型可以使用,当声明该类型列时,可以(并且通常必须要)指定精度和标度。
金额字段的设计并不推荐使用 DECIMAL 类型,而更推荐使用 INT 整型类型。
整型类型与自增设计
- 自增整型做主键,一律使用 BIGINT
- 自增值并不持久化,可能会有回溯现象(MySQL 8.0 版本前)
达到自增整型类型的上限值时,再次自增插入,MySQL 数据库会报重复错误。
不建议使用整型类型做主键,更为推荐的是字符串类型。
资金字段设计
不推荐用 DECIMAL 类型,而是更推荐将 DECIMAL 转化为整型类型,推荐使用用分单位存储,而不是用元单位存储。原因:
- 所有金额相关字段都是定长字段,占用 8 个字节,存储高效
- 直接通过整型计算,效率更高
字符串类型
CHAR 和 VARCHAR
CHAR(N) 用来保存固定长度的字符,N 的范围是 0 ~ 255。
VARCHAR(N) 用来保存变长字符,N 的范围为 0 ~ 65536。
超出 65536 个字符的情况下,可以考虑使用更大的字符类型 TEXT 或 BLOB,两者最大存储长度为 4G,其区别是 BLOB 没有字符集属性,纯属二进制存储。
在 MySQL 数据库下,绝大部分场景使用类型 VARCHAR 就足够了。
字符集
在表结构设计中,除了将列定义为 CHAR 和 VARCHAR 用以存储字符以外,还需要额外定义字符对应的字符集,因为每种字符在不同字符集编码下,对应着不同的二进制值。
从底层存储内核看,在多字节字符集下,CHAR 和 VARCHAR 底层的实现完全相同,都是变长存储。
包括 MySQL 8.0 版本在内,字符集默认设置成 UTF8MB4,8.0 版本之前默认的字符集为 Latin1。
常见的字符集有 GBK、UTF8,推荐把 MySQL 的默认字符集设置为 UTF8MB4。鉴于目前默认字符集推荐设置为 UTF8MB4,所以在表结构设计时,可以把 CHAR 全部用 VARCHAR 替换,底层存储的本质实现一模一样。
排序规则
比较和排序字符串的一种规则,每个字符集都会有默认的排序规则。用命令 SHOW CHARSET 来查看。
排序规则
- _ci 结尾,表示不区分大小写(Case Insensitive)
- _cs 表示大小写敏感
- _bin 表示通过存储字符的二进制进行比较
需要注意的是,比较 MySQL 字符串,默认采用不区分大小的排序规则。 绝大部分业务的表结构设计无须设置排序规则为大小写敏感。
正确修改字符集
# 错误,对于已经存在的列,其默认字符集并不做修改
ALTER TABLE table_name CHARSET utf8mb4;
# 正确
ALTER TABLE table_name CONVERT TO CHARSET utf8mb4;
用户性别设计
在 MySQL 8.0 版本之前,可以使用 ENUM 字符串枚举类型,只允许有限的定义值插入。
自 MySQL 8.0.16 版本开始,数据库原生提供 CHECK 约束功能,可以方便地进行有限状态列类型的设计。
CONSTRAINT `user_chk_1` CHECK (((`sex` = _utf8mb4'M') or (`sex` = _utf8mb4'F')))
账户密码存储设计
从合规性角度看,所有用户隐私字段都需要加密。
动态加盐 + 非固定加密算法。
# 密码存储格式可以采取
$动态盐$加密算法$加密后的字符串
日期类型
常见的日期类型有 YEAR、DATE、TIME、DATETIME、TIMESTAMEP,常见使用的日期类型为DATETIME 和 TIMESTAMP。
DATETIME
类型 DATETIME 最终展现的形式为:YYYY-MM-DD HH:MM:SS,固定占用 8 个字节。
MySQL 5.6 版本开始,DATETIME 类型支持毫秒,DATETIME(N) 中的 N 表示毫秒的精度。
常见的函数 NOW、SYSDATE 也支持精确到毫秒。
TIMESTAMP
# 指定默认时间
DEFAULT CURRENT_TIMESTAMP(N)
# 自动记录当前时间
DEFAULT CURRENT_TIMESTAMP(N) ON UPDATE CURRENT_TIMESTAMP(N)
从 MySQL 5.6 版本开始,TIMESTAMP 占用 4 个字节,带毫秒时占用 7 个字节。
优点是可以带有时区属性,本质上是从毫秒转化而来,实际存储的内容为‘1970-01-01 00:00:00’到现在的毫秒数。
DATETIME vs TIMESTAMP
建议使用类型 DATETIME。 对于时区问题,可以由前端或者服务这里做一次转化。
TIMESTAMP 的劣势
- 性能不如 DATETIME: DATETIME 不存在时区转化问题
- 性能抖动: 海量并发时,存在性能抖动问题
- 上限值 2038 年很快就会到来
优化 TIMESTAMP 可以使用显式的时区,而不是操作系统时区。(在配置文件中显示地设置时区)
表结构设计规范:每条记录都要有一个时间字段
在做表结构设计规范时,强烈建议每张业务核心表都增加一个 DATETIME 类型的 last_modify_date 字段,并设置修改自动更新机制, 即便标识每条记录最后修改的时间。
非结构存储
用户登录设计
在数据库中,JSON 类型比较适合存储一些修改较少、相对静态的数据,比如用户登录信息的存储。
当 JSON 数据量非常大,用户希望对 JSON 数据进行有效检索时,可以利用 MySQL 的函数索引功能对 JSON 中的某个字段进行索引。
# 创建虚拟列 cellphone,数据来源于函数 loginInfo->>"$.cellphone"
# 也就是 JSON 中存储的 cellphone
ALTER TABLE UserLogin ADD COLUMN cellphone VARCHAR(255) AS (loginInfo->>"$.cellphone");
# 虚拟列的基础上创建一个唯一索引 idx_cellphone
ALTER TABLE UserLogin ADD UNIQUE INDEX idx_cellphone(cellphone);
用户画像设计
强烈推荐用 JSON 类型在数据库中存储用户画像信息,并结合 JSON 数组类型和多值索引的特点进行高效查询。
举例,将标签单独定义一个集合,记录用户画像时保存标签集合的编号,而不是使用分隔符存储字符串。
{MySQL 8.0.17 版本开始支持 Multi-Valued Indexes,用于在 JSON 数组上创建索引,并通过函数 member of、json_contains、json_overlaps 来快速检索索引数据。}
# 示例
# 创建多值索引
ALTER TABLE UserTag
ADD INDEX idx_user_tags ((cast((userTags->"$") as unsigned array)));
# 根据编号查询对应标签(1 个标签使用 MEMER OF)
EXPLAIN SELECT * FROM UserTag
WHERE 10 MEMBER OF(userTags->"$")\G
# 查询多个标签,使用 JSON_CONTAINS(命中全部)
SELECT * FROM UserTag
WHERE JSON_CONTAINS(userTags->"$", '[2,10]');
# 查询多个标签,使用 JSON_OVERLAP(命中任意个)
EXPLAIN SELECT * FROM UserTag
WHERE JSON_OVERLAPS(userTags->"$", '[2,3,10]')\G
表结构设计:忘记范式准则
在真实业务中,不必严格遵守三范式的要求。而且有时为了性能考虑,还可以进行反范式的设计,比如在数据仓库领域。
工程上的表结构设计实战
- 每张表一定要有一个主键(方法有自增主键设计、UUID 主键设计、业务自定义生成主键)
- 消除冗余数据存在的可能
一张表有主键,就已经直接满足一范式的要求了,使用 BIGINT 的自增类型作为主键的设计仅仅适合非核心业务表,比如告警表、日志表等。真正的核心业务表,一定不要用自增键做主键。原因如下:
- 自增存在回溯问题
- 自增值在服务器端产生,存在并发性能问题
- 自增值做主键,只能在当前实例中保证唯一,不能保证全局唯一
- 公开数据值,容易引发安全问题
- MGR(MySQL Group Replication) 可能引起的性能问题
- 分布式架构设计问题
自增值是在 MySQL 服务端产生的值,需要有一把自增的 AI 锁保护,若这时有大量的插入请求,就可能存在自增引起的性能瓶颈。想获得自增值的最大并发性能,把参数 innodb_autoinc_lock_mode 设置为2。由于其还存在上述 5 个问题。因此,在互联网海量并发架构实战中,更推荐 UUID 做主键或业务自定义生成主键。
UUID主键设计
# Version 1规范,MySQL中 UUID 组成,时间计数的标准为 100ns
UUID = 时间低(4字节)- 时间中高+版本(4字节)- 时钟序列 - MAC地址
在存储时间时,UUID 是根据时间位逆序存储, 低时间低位存放在最前面,高时间位在最后,即 UUID 的前 4 个字节会随着时间的变化而不断“随机”变化,并非单调递增。而随机值在插入时会产生离散 IO,从而产生性能瓶颈。这也是 UUID 对比自增值最大的弊端。
MySQL 8.0 推出了函数 UUID_TO_BIN:
- 通过参数将时间高位放在最前,解决了 UUID 插入时乱序问
- 去掉了无用的字符串”-“,精简存储空间
- 将字符串其转换为二进制值存储,空间最终从之前的 36 个字节缩短为了 16 字节
可以将主键修改为 BINARY(16),用于存储排序后的 16 字节的 UUID 值。
MySQL 8.0 也提供了函数 BIN_TO_UUID,支持将二进制值反转为 UUID 字符串。
业务自定义生成主键
分布式数据库架构,仅用 UUID 做主键依然是不够的。
分布式架构的核心业务表,推荐类似如下的设计:
PK = 时间字段 + 随机码(可选) + 业务信息1 + 业务信息2 ......
消除冗余和反范式设计
范式设计的目的就是通过依赖和传递依赖消除冗余。
随着 JSON 数据类型的普及,MySQL 在线业务也可以进行反范式的设计。
合理的冗余可以带来更高效的处理。
表压缩
页压缩
通常我们认为,如果一个页中存放的记录数越多,数据库的性能越高。这是因为数据库表空间中的页是存放在磁盘上,MySQL 数据库先要将磁盘中的页读取到内存缓冲池,然后以页为单位来读取和管理记录。一个页中存放的记录越多,内存中能存放的记录数也就越多,那么存取效率也就越高。若想将一个页中存放的记录数变多,可以启用压缩功能。
之所以不压缩单条记录或压缩表空间:
- 压缩每条记录: 每次读写都要压缩和解压,过于依赖 CPU 的计算能力,性能会明显下降;另外,因为单条记录大小不会特别大,一般小于 1K,压缩效率也并不会特别好。
- 压缩表空间: 压缩效率非常不错,但要求表空间文件静态不增长,这对基于磁盘的关系型数据库来说,很难实现。
压缩会增加 CPU 开销,同时也会降低 IO 开销。
MySQL 压缩表
COMPRESS 页压缩
MySQL 5.7 版本之前的页压缩功能。
将一个页压缩到指定比例大小,如果无法压缩到当前比例,就会多产生一个页来补充。
16K -> 8K (成功)
16K -> 2 * 8K (不成功)
# 启用 COMPRESS 页压缩
ROW_FORMAT=COMPRESS
# 压缩比例
KEY_BLOCK_SIZE
压缩前的页和压缩后的页同时存在内存缓冲中,导致内存开销增大,MySQL 性能退化。
TPC 压缩
前提 <当前操作系统支持文件空洞特性>
利用空洞压缩到文件系统的最小单位 4K
对数据库性能的侵入几乎无影响,TPC 压缩在内存中只有一个解压缩后的页。只有当这个页需要刷新到磁盘时,才会触发页压缩功能一次。
# 创建 TPC 压缩表
CREATE TABLE Transaction (
transactionId BINARY(16) PRIMARY KEY,
.....
)
COMPRESSION=ZLIB | LZ4 | NONE;
表压缩在业务上的使用
COMPRESS -> 性能不敏感的业务表:日志、监控、告警表等
TPC -> 较为核心的流水业务表
对于当前频繁使用的流水表(当日)不启用表压缩,对于历史流水表(往日)启用 TPC 压缩。
# 启用 TPC 表压缩,但只对后续新增的数据进行压缩
ALTER TABLE table COMPRESSION = ZLIB;
# 对整表进行压缩
ALTER TABLE table COMPRESSION=ZLIB;
OPTIMIZE TABLE table;
表的访问设计
通过 Memcached 协议访问
MySQL 5.6 版本开始支持通过插件 Memcached Plugin,以 KV 方式访问表,这时可以将 MySQL视作一个 Memcached KV 数据库。
对于数据的访问不再是通过 SQL 接口,而是通过 KV 数据库中常见的 get、set、incr 等请求。
开启流程:
- 开启 Memcached 插件
- 配置表与 KV 的映射关系
# 安装映射表
mysql> source MYSQL_HOME/share/innodb_memcached_config.sql
# 安装插件,默认会启动11211端口
mysql> INSTALL PLUGIN daemon_memcached soname "libmemcached.so";
执行完上述操作后,会新增一个库 innodb_memcache,里面的表 containers 就是需要配置的KV映射表,示例:
INSERT INTO containers
VALUES ('User','test','user_id','user_id|cellphone|last_login','0','0','0','PRIAMRY')
使用时:
# 读取映射表User
mc.get('@@User')
mc.get('key1')
# 读取映射表sbtest1
mc.get('@@sbtest1')
mc.get('sb1_key1','aa|bbb|ccc')
通过 X Protocol 访问表
MySQL 5.7 版本开始原生支持 JSON 二进制数据类型,同时也提供将表格映射为一个 JSON 文档。
MySQL 也提供了 X Protocol 这样的 NoSQL 访问方式。
X Protocol 通过新的 MySQL Shell 命令,需要单独安装。
# 通过新的 X Protocol 访问 MySQL 数据库
root@MBP-Windows:# mysqlsh root@localhost/test
若要通过 X Protocol 协议管理文档数据,也需要下载新的 MySQL Connector,并引入新的 X 驱动库。
索引:排序
索引是提升查询速度的一种数据结构。
B+树索引结构
B+树是目前为止排序最有效率的数据结构。像二叉树,哈希索引、红黑树、SkipList,在海量数据基于磁盘存储效率方面远不如 B+ 树索引高效。
索引是对记录进行排序
优化 B+树索引插入的性能
平衡树查询高效,而在插入时的维护开销就大,但排序的开销其实并没有想象得那么大。
真正的开销在于 B+ 树索引的维护,保证数据排序,这里存在两种不同数据类型的插入情况:
- 数据顺序(或逆序)插入:B+ 树索引的维护代价非常小,叶子节点都是从左往右进行插入,比较典型的是自增 ID 的插入、时间的插入
- 数据无序插入:B+ 树为了维护排序,需要对页进行分裂、旋转等开销较大的操作,另外,即便对于固态硬盘,随机写的性能也不如顺序写,所以磁盘性能也会收到较大影响。比较典型的是用户昵称,每个用户注册时,昵称是随意取的,若在昵称上创建索引,插入是无序的,索引维护需要的开销会比较大
所以对于 B+ 树索引,在 MySQL 数据库设计中,仅要求主键的索引设计为顺序,比如使用自增,或使用函数 UUID_TO_BIN 排序的 UUID,而不用无序值做主键。
MySQL 中 B+ 树索引的设计与管理
# 查看每个索引信息的表
mysql.innodb_index_stats
不存在一张表中索引数量不能超过多少的说法!
# 查看未被使用的索引
sys.schema_unused_indexes
搜索并删除长期未被使用的索引来优化数据库。MySQL 8.0 推出了索引不可见功能,在删除前可以设置为对优化器不可见,观察业务是否收到影响。
ALTER TABLE t1
ALTER INDEX idx_name INVISIBLE/VISIBLE;
索引组织表
数据存储有堆表和索引组织表两种方式。
- 堆表中的数据无序存放, 数据的排序完全依赖于索引(Oracle、Microsoft SQL Server、PostgreSQL 早期默认支持的数据存储都是堆表结构)。
- 索引组织表,数据根据主键排序存放在索引中,主键索引也叫聚集索引(Clustered Index)。在索引组织表中,数据即索引,索引即数据。MySQL InnoDB 存储引擎就是这样的数据组织方式。
聚集索引的叶节点存的是主键和行记录。
二级索引
除了主键索引外,其他的索引都称之为二级索引(Secondeary Index), 或非聚集索引(None Clustered Index)。
二级索引也是 B+ 树索引,但它和主键索引不同的是叶子节点存放的是索引键值、主键值。
通过二级索引只能定位主键值,需要额外再通过主键索引进行查询,才能得到最终的结果。这种“二级索引通过主键索引进行再一次查询”的操作叫作“回表”。
索引组织表这样的二级索引设计有一个非常大的好处:若记录发生了修改,则其他索引无须进行维护,除非记录的主键发生了修改。
唯一索引可以理解为额外附加了一个单独的索引(只存放索引键值,目的就是用于检测是否唯一)
二级索引可能是比较顺序插入,也可能是完全随机的插入。
- 要比较顺序,对聚集索引性能友好
- 尽可能紧凑,对二级索引的性能和存储友好
函数索引
5.7 版本开始,MySQL 开始支持创建函数索引。
- 优化业务 SQL 性能
- 结合虚拟列使用
创建虚拟列,使用函数索引直接使用虚拟列。
组合索引
组合索引的排序按照前后出现顺序会不同。
例如 组合索引 (a, b)
# 正确
# where 中 and 条件的先后顺序对如何选择索引是无关的,优化器会去分析判断选用哪个索引
SELECT * FROM table WHERE a = ?
SELECT * FROM table WHERE a = ? AND b = ?
SELECT * FROM table WHERE b = ? AND a = ?
# 错误
SELECT * FROM table WHERE b = ?
因为索引是排序,而组合索引的排序是根据出现先后顺序进行的(最左原则),因而不存在 a 直接 搜索 b 是找不到结果的(无序的,可以类比基数排序)。
基于这个特性,对于一些有先后顺序的排序场景,可以通过组合索引一次获取。
{注:MySQL 8.0 支持倒序索引}
其次,二级组合索引的叶子节点,包含索引键值和主键值。这意味这不需要回表!
通过组合索引避免回表的优化技术也称为索引覆盖(Covering Index)。
索引出错
MySQL 选择索引的原理
在关系型数据库中,B+ 树索引只是存储的一种数据结构,具体使用还要依赖数据库的优化器,优化器决定了具体某一索引的选择。优化器的选择是基于成本(cost),哪个索引的成本越低,优先使用哪个索引。
数据库 mysql 下的表 server_cost、engine_cost 则记录了对于各种成本的计算。
表 server_cost 记录了 Server 层优化器各种操作的成本,这里面包括了所有 CPU Cost:
- disk_temptable_create_cost:创建磁盘临时表的成本,默认为20。
- disk_temptable_row_cost:磁盘临时表中每条记录的成本,默认为0.5。
- key_compare_cost:索引键值比较的成本,默认为0.05,成本最小。
- memory_temptable_create_cost:创建内存临时表的成本:默认为1。
- memory_temptable_row_cost:内存临时表中每条记录的成本,默认为0.1。
- row_evaluate_cost:记录间的比较成本,默认为0.1。
表 engine_cost 记录了存储引擎层各种操作的成本,这里包含了所有的 IO Cost:
- io_block_read_cost:从磁盘读取一个页的成本,默认值为1。
- memory_block_read_cost:从内存读取一个页的成本,默认值为0.25。
未能使用创建的索引
优化器识别到了索引,但却采用全表扫描获取结果。
是因为优化器判断二级索引回表开销大于直接扫描。
索引创建在有限状态上
B+ 树索引通常要建立在高选择性的字段或字段组合上。
但在有些低选择性的列上,是有必要创建索引的,比如订单的交易状态。
通常订单状态绝大部分都是已完成,只有绝少部分因为系统故障原因,会在 15 分钟后还没有完成订单,因此订单状态是存在数据倾斜的。但 MySQL 并不知道状态存在数据倾斜,会认为是平均分布的。因此优化器会为了避免回表,采用全表扫描。
可以利用 MySQL 8.0 的直方图功能,创建一个直方图,让优化器知道数据的分布,从而更好地选择执行计划。
ANALYZE TABLE orders
UPDATE HISTOGRAM ON o_orderstatus;
在创建完直方图后,MySQL会收集到字段的数值分布。
JOIN 连接
连接算法
MySQL 8.0 版本支持两种 JOIN 算法:
- Nested Loop Join (查询数据量较小、语句相对简单,大多使用索引连接表之间的数据)
- Hash Join (查询数据量较大,关联表的数量非常多)
Nested Loop Join
假设表 R 和 S 进行连接:
对于 R 中每一个符合匹配条件的 row r:
查找 S 中索引值为 r 的索引 idx_s
如果存在
发送给 client
# LEFT JOIN 左表 R 为驱动表
SELECT ... FROM R LEFT JOIN S ON R.x = S.x WEHRE ...
# RIGHT JOIN 右表 S 为驱动表
SELECT ... FROM R RIGHT JOIN S ON R.x = S.x WEHRE ...
# INNER JOIN 谁需要查询的数据量越少,谁就是驱动表
SELECT ... FROM R INNER JOIN S ON R.x = S.x WEHRE ...
Hash Join
Hash Join,用于两张表之间连接条件没有索引的情况。
诚然可以通过创建索引变成 Nested Loop Join 的情况,但:
- 如果有些列是低选择度的索引,那么创建索引在导入数据时要对数据排序,影响导入性能
- 二级索引有回表问题,若筛选的数据量比较大,则不如全表扫描
对于 R 中每一个符合匹配条件的 row r:
以 r 为 key 创建哈希表 ht
对于 S 中每一个符合匹配条件的 row s:
在哈希表 ht 中搜索 s
如果存在
发送给 client
在驱动表的选择上,都是较小的表作为驱动表。(添加表和查表都是O(1),哈希表小则内存占用少)
OLTP 业务写 JOIN
OLTP 业务完全可以大胆放心地写 JOIN,但是要确保 JOIN 的索引都已添加。
没事别瘠薄吃饱了撑的去拆 JOIN 语句,搞清楚自己的定位,优化器比大部分开发人员聪明。
子查询
MySQL 8.0 版本中,优化器会自动地将 IN 子查询优化,优化为最佳的 JOIN 执行计划。
依赖子查询的优化
在 MySQL 8.0 版本之前,MySQL 对于子查询的优化并不充分,在子查询的执行计划中会看到 DEPENDENT SUBQUERY 的提示,这表示是一个依赖子查询,子查询需要依赖外部表的关联。
看到这样的提示,就要警惕, 因为 DEPENDENT SUBQUERY 执行速度可能非常慢,大部分时候需要手动把它转化成两张表之间的连接。若看到依赖子查询的执行计划,记得先进行 SQL 重写优化。非 8.0 版本可以通过派生表的方式降低对于子查询表的扫描。
分区表
CREATE TABLE t (
a INT,
b INT,
c DATETIME,
d VARCHAR(32),
e INT,
PRIMARY KEY (a,b,c),
KEY idx_e (e)
)
partition by range columns(c) (
PARTITION p0000 VALUES LESS THAN ('2019-01-01'),
PARTITION p2019 VALUES LESS THAN ('2020-01-01'),
PARTITION p2020 VALUES LESS THAN ('2021-01-01'),
PARTITION p9999 VALUES LESS THAN (MAXVALUE)
);
分区表就是把物理表结构相同的几张表,通过一定算法,组成一张逻辑大表,MySQL 中的分区表是把一张大表拆成了多张表,每张表有自己的索引,从逻辑上看是一张表,但物理上存储在不同文件中。
当前 MySQL 数据库支持的分区函数类型有 RANGE、LIST、HASH、KEY、COLUMNS。
无论选择哪种分区函数,都要指定相关列成为分区算法的输入条件,这些列就叫“分区列”(依照这个列和条件进行分区)。另外,在 MySQL 分区表中,主键也必须是分区列的一部分,不然创建分区表时会失败。
分区表:唯一索引
在 MySQL 数据库中,分区表的索引都是局部,而非全局。也就是说,索引在每个分区文件中都是独立的。分区表上的唯一索引必须包含分区列信息,否则创建会报错。
{理解:假设分区列为学校名,一个学号在同一个学校内(分区)唯一,但不同学校会出现相同学号的学生。因此唯一索引包含分区列是指,将(学校,学号)的组合看作唯一索引,而学号只在学校(分区)内唯一。所以最好还是通过 UUID,例如身份证来避免局部唯一问题。}
分区表的误区:性能提升
分区表技术不是用于提升 MySQL 数据库的性能,而是方便数据的管理,更多的是解决数据迁移和备份的问题。
分区表还会引入新的性能问题,比如非分区列的查询。非分区列的查询。即使分区列上已经创建了索引,但因为索引是每个分区文件对应的本地索引,所以要查询每个分区。