MySQL数据库的schema设计优化

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 本文介绍了数据库schema常见的一些缺陷,以及一些优化方法。

MySQL schema设计中的缺陷

太多的列

MySQL的存储引擎API工作时需要在服务器层和存储引擎层之间通过行缓冲格式拷贝数据,然后在服务器层将缓冲内容解码成各个列。从行缓冲中将编码过的列转换成行数据结构的代价是十分大的。而转换的代价依赖与列的数量。当我们研究一个CPU占用非常高的案例时,发现客户使用了非常宽的表,然而只有一小部分的列会实际用到,这时候转换的代价就非常高了。

MySQL限制了每个关联操作最多只能有61个表,一个粗略的经验,如果希望查询执行的快速且并发性好,单个查询最好在12个表以内做关联。

全能的枚举

注意放置过度使用枚举

你别一个枚举,举了个数字全集出来,那就不礼貌了。

变相的枚举

枚举列允许在列中存储一组定义值中的单个值,集合set列则允许在列中存储一组定义值中的一个或多个值。

比如

create TABLE 。。。 (
is_default set('Y','N') NOT NULL default 'N'
)

这里我们需要注意到这个真假的情况是不会同时出现的,那么我们就应该毫无疑问的使用枚举而不是这个set。

非此发明的null

我们之前写了避免使用null的好处,并且建议尽可能的考虑替代方案。比如我们可以用0,或者一些特殊字符去代替null。

但是遵循这一原则也不要走极端。当确实需要表示未知值时也不要害怕使用null。

范式和反范式

​ 范式:

范式是符合某一种级别的关系模式的集合。关系数据库中的关系必须满足一定的要求,满足不同程度要求的为不同范式。

第一范式(1NF)

在任何一个关系数据库中,第一范式(1NF) [2] 是对关系模式的基本要求,不满足第一范式(1NF)的数据库就不是关系数据库。

第二范式(2NF)

是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个[实例]或行必须可以被唯一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。这个唯一属性列被称为[主关键字]或主键、主码。

范式的优点和缺点

优点:

  • 范式化的更新操作比反范式化的更新要快
  • 当数据较好的范式化,就只有很少或者较少的重复数据,所以只需要修改更是少的数据。
  • 范式化的表通常更小,可以更好的放在内存里,所以执行的操作会更快。
  • 很少的重复数据也就意味着在select时我们会更少的使用distinct或者group by 语句。

缺点:

  • 需要关联

反范式化的优点和缺点

反范式化的schema因为所有的数据都在一张表中,所以很好的避免了关联。

混用范式化和反范式化

最常见的反范式化数据的方法就是复制或者缓存,在不同的表里存储相同的特定列。我们还可以使用触发器更新缓存值,这使得实现这样的方案变得更简单。

缓存表和汇总表

有时候提升性能的最好方法是在同一张表中保存衍生的冗余数据。然而,有时也需要创建一张完全独立的汇总表或缓存表。

我们用术语缓存表来表示存储那些可以比较简单的从schema其他表获得的数据的表。而术语汇总表,则保存的是使用group by 语句聚合数据的表。

我们使用汇总表,要远比我们扫描表的全部行要有效的多。

缓存表则相反,其对优化搜索和检索查询语句很有效。这些查询语句经常需要特殊的表和索引结构。例如:可能会需要很多不同的索引组合来加速各种类型的查询。这些矛盾的需求有时候要创建一张只包含主表中部分列的缓存表。一个有用的技巧是我吗可以使用不同的存储引擎。比如说,主表使用innodb,我吗可以把myisam作为缓存表的引擎,这样会得到更小的索引占用空间,并且可以做全文搜索。

在使用缓存表和汇总表的时候,我吗必须决定到底是实时维护数据还是定期重建。那个更好依赖于应用程序,但是定期重建并不只是节省资源,也可以保持表不会有那么多的碎片,以及有完全顺序组织的索引。

当然为了安全 ,我们还会在重建这些表的时候使用一个影子表,来保证数据在操作过程也是可以使用的。

物化视图

计数器表

计数器表是一个经常会用到的东西,我们使用单独的表可以帮助避免查询缓存失效。

下面我们要展示呢一些更高级的技巧:

你比如说,我们有一个计数器表,是记录这个网站的点击次数的这样一个表,但我们每次修改的时候都会有一个全局的互斥锁,这也就导致了这些事务只能串行执行。我们要是想获得更好的性能,就可以将计数器保存在多行,每次随机选择一行进行更新。我们对这个计数表这样更新:

CREATE TABLE hit_counter(
slot tinyint unsigned not null primary key ,
cnt int unsigned not null
)ENGINE = InnoDB

我们预先在表中增加100行数据,选择一个随机的槽进行更新:

UPDATE hit_counter SET cnt = cnt +1 WHERE slot = RAND()*100;

要统计结果,我们就使用下面这样的聚合查询:

SELECT SUM(cnt) FROM hit_counter; 

我们一个常见的需求是每隔一段时间开始一个新的计数器,我们这样修改表:

CREATE TABLE daily_hit_counter(
day date not null,
slot tinyint unsigned not null,
cnt int unsigned not null,
primary key (day,slot)
)ENGINE = InnoDB;

这样的话我们就不要去预先生成行,而用on duplicate key update语句(存在就更新,不存在那就插入)

INSERT INTO daily_hit_counter(day,slot,cnt)
VALUES (CURRENT_DATE,RAND()*100,1)
ON DUPLICATE KEY UPDATE cnt = cnt + 1;

如果希望减少表的行数,避免表变得太大,可以写一个周期执行的任务,合并所有结果到0号槽,并且删除所有其他的槽:

UPDATE daily_hit_counter as c
    INNER JOIN (
    SELECT day,SUM(cnt)AS cnt,MIN(slot)AS mslot
    FROM daily_hit_counter
    GROUP BY day
    )AS x USING(day)
SET  c.cnt = IF(c.slot = x.mslot,x.slot,0),
    c.slot = IF (c.slot = x.mslot,0,c.slot);
DELETE FROM daily_hit_counter WHERE slot <>0 AND cnt = 0;

加快alter TABLE操作的速度

MySQL对于大表的alter TABLE一直是一个大问题。mysql执行大部分的修改表的结构操作的方法是用新的结构创建一个空表,然后把旧表里的数据插入到新表。

对于常见的场景,能使用的场景只有两种:

  • 先在一台不提供服务的机器上执行ALTER TABLE 操作,然后和提供服务的主库进行切换
  • 影子拷贝:用要求的表结构创建一张和源表无关的新表,然后通过重命名和删表操作交换两张表。

不是所有的alter TABLE操作都会引起表重建。例如,有两个方法可以改变或者删除一个列的默认值(一种方法很快,一种很慢)。

慢的方式:

ALTER TABLE sakila.film 
MODIFY COLUMN rental_duration TINYINT(3) NOT NULL DEFAULT 5;

这种方式是比较慢的,因为modify这种方式是要导致表的重建的。

ALTER TABLE sakila.film 
ALTER  COLUMN rental_duration  SET DEFAULT 5;

这种alter的方式就很快,因为他是直接修改.firm文件而不涉及表数据。所以这个操作是特别快的。

只修改.frm文件

快速创建索引

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
6天前
|
数据库 索引
如何优化数据库索引?
【8月更文挑战第14天】如何优化数据库索引?
18 4
|
4天前
|
存储 SQL 数据库
数据库模式(Schema)
数据库模式,即逻辑模式,为全体数据的逻辑结构和特性的描述,构成所有用户的公共视图,基于某种数据模型,定义数据结构及关联关系,并确保完整性和安全性。每个数据库仅有一个模式,通过DDL定义。外模式为用户视图,允许多视图共存,保障数据安全,使用DML操作数据。内模式定义数据的物理存储结构,如索引组织、压缩与加密等细节。
|
9天前
|
存储 SQL 数据库
数据库模式(Schema)
数据库模式,即逻辑模式,唯一地定义了数据库数据的逻辑结构与特性,作为所有用户的共享视图。基于特定数据模型,不仅描述数据结构,还包括安全性和完整性约束。外模式为用户视图,允许一个数据库拥有多个针对不同应用的视图,增强数据安全性。内模式描述数据的实际存储方式和物理结构,如存储类型、索引组织及是否采用压缩或加密技术。
|
5天前
|
存储 SQL 缓存
优化数据库
【8月更文挑战第15天】优化数据库
9 1
|
6天前
|
SQL 存储 数据库
OceanBase数据库优化
【8月更文挑战第14天】OceanBase数据库优化
9 2
|
6天前
|
数据库连接 数据库
实现加载驱动、得到数据库对象、关闭资源的代码复用,将代码提取到相应的工具包里边。优化程序
该博客文章展示了如何通过创建工具类`Connectiontools`实现数据库连接、语句执行以及资源关闭的代码复用,以优化程序并提高数据库操作的效率和安全性。
|
9天前
|
SQL 关系型数据库 MySQL
"告别蜗牛速度!解锁批量插入数据新姿势,15秒狂插35万条,数据库优化就该这么玩!"
【8月更文挑战第11天】在数据密集型应用中,高效的批量插入是性能优化的关键。传统单条记录插入方式在网络开销、数据库I/O及事务处理上存在明显瓶颈。批量插入则通过减少网络请求次数和数据库I/O操作,显著提升效率。以Python+pymysql为例,通过`executemany`方法,可实现在15秒内将35万条数据快速入库,相较于传统方法,性能提升显著,是处理大规模数据的理想选择。
26 5
|
8天前
|
存储 缓存 运维
优化高并发环境下的数据库查询性能:实战经验与技巧
在高并发环境下,数据库性能往往成为系统瓶颈。本文将深入探讨在高并发场景下优化数据库查询性能的策略与实践,包括索引优化、查询优化、数据库架构设计以及缓存机制的应用。通过对具体案例的分析,读者将能够掌握提升数据库性能的关键技术,从而在面对大规模用户请求时提高系统的响应速度和稳定性。
|
9天前
|
存储 关系型数据库 MySQL
MySQL 上亿大表,如何深度优化?
【8月更文挑战第11天】随着大数据时代的到来,MySQL 作为广泛使用的关系型数据库管理系统,经常需要处理上亿级别的数据。当数据量如此庞大时,如何确保数据库的查询效率、稳定性和可扩展性,成为了一个亟待解决的问题。本文将围绕 MySQL 上亿大表的深度优化,分享一系列实用的技术干货,帮助你在工作和学习中应对挑战。
25 1