前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL优化

MySQL优化

作者头像
编程之心
发布2022-05-10 09:43:34
6070
发布2022-05-10 09:43:34
举报
文章被收录于专栏:编程之禅编程之禅

MySQL优化

MySQL 优化方案

  • 对于 **MySQL** 的性能优化,大部分情况下都是想减少查询所消耗的时间;而一个查询是由很多个环节组成,那么就需要从每个环节消耗时间进行入手。

配置优化(连接)

  • 当客户端连接到服务端有可能服务端连接数不够导致应用程序获取不到连接而报出 **Mysql: error 1040: Too many connections**** **的错误。一般情况下可以从两个方面去解决连接数不够的问题:
    • 对于服务端来说,可以增加服务端的可用连接数(**MySQL 8.0.11 **默认连接数为 **200**),当有多个应用或很多请求同时访问数据库且连接数不够时,可以修改配置参数增加可用的连接数或及时释放不活动的连接,交互式和非交互式的客户端的默认超时时间都是 **28800m (8h)**,可以把该值调小。
代码语言:javascript
复制
-- 查看最大连接数,默认为 200
show variables like 'max_connections'; 
-- 修改最大连接数
set GLOBAL max_connections = 200;
-- 查看默认的超时时间,默认为 28800 秒 / 8 小时
show global variables like 'wait_timeout';
-- 修改超时时间
set GLOBAL wait_timeout = 28800;
  • 对于客户端来说,可以减少服务端获取的连接数,当不想每一次执行 **SQL** 语句都创建一个新的连接时可以引入连接池来实现连接的重用;现在市面上有 **ORM** 层面(**MyBatis** 自带的连接池)、专用的连接池工具(阿里的 **Druid****Spring Boot 2.x** 默认自带的 **Hikari**、以及 **DBCP****C3P0**)。当客户端修改为从连接池获取连接后,在连接池中会维护一定数量的连接,其他客户端排队等待获取连接对数据库进行操作。**Druid** 的连接池默认大小是 **8****Hikari** 的连接池默认大小是 **10****PostgreSQL** 给出的数据库建议设置的连接池大小公式为 (机器核数 * 2) + 1。对于减少连接数会提高吞吐量和设置连接池的大小和 **CPU** 核数相关的原因是因为每一个连接,服务端都需要创建一个线程去处理它,连接数越多,那么服务端创建的线程数量就会越多。
image.png
image.png

架构优化(缓存)

缓存

  • 对于应用系统的并发数非常大的情况下,假如没有缓存会给数据库带来很大的压力以及从应用的层面来说,操作数据的速度也会受到影响。在这种情况下可以引入第三方的缓存相关的服务器来解决该问题(**Redis**);运行一个独立的服务器是属于架构层面的优化。
image.png
image.png

主从复制

概述
  • 当单台数据库服务满足不了访问需求时可以做数据库的集群方案;如果做集群方案会面临到不同的节点之间数据一致性的问题,当读写多台数据库节点就需要用到复制技术来保证节点数据的一致性;被复制的节点称之为 **master**,复制的节点被称为 **slave****slave** 自身也可以作为其他节点的数据来源—级联复制。
image.png
image.png
  • **MySQL** 中的主从复制在做更新操作时会记录 **binlog** ,它是一种逻辑日志。有了该日志文件后会从服务器获取主服务器的 **binlog** 文件,然后解析里面的 **SQL** 语句,在服务器上执行一遍,从而保持主从数据一致。这其中的 **I/O** 线程主要是连接到 **master** 去获取 **binlog** 并解析。在 **Master** 节点上有一个 **log dump** 线程,主要是用于发送 **binlog****slave** 的。另外 **Slave****SQL** 线程是用来读取 **relay log** 并把数据写入到数据库的。
image.png
image.png
  • 在做了主从复制后就把数据写入到 **master** 节点,而读的请求可以分摊到 **slave** 节点上—读写分离,读写分离可以在一定程度上减低数据库服务器的访问压力,但会出现主从数据的一致性问题。然后把数据写入了 **master** 后,马上到 **slave** 中查询的话数据可能还未从 **Master** 服务器同步到 **Slave** 服务器上。
image.png
image.png
  • 在早期的 **MySQL****slave****SQL** 线程是单线程的,**Master** 可以支持 **SQL** 语句的并行执行,配置了多少的最大连接数就是最多同时多少个 **SQL** 并行地执行。而 **Slave****SQL** 只能单线程地排队执行;当在主库并发量很多的情况下进行数据同步,就会出现一定的延迟。从库不能并行执行的原因是因为当用户增加一条数据并对其做修改操作,然后删除时,在从库上的顺序不能出现变化。
异步与全同步
  • 减少主从复制的延迟:在主从复制的过程中,**MySQL** 默认是异步复制;对于 **Master** 写入到 **binlog** 且事务结束,就返回给客户端了。对于 **Slave** 接收到 **binlog** 就结束了;**Master** 并不关心 **Slave** 的数据是否写入成功。如果要减少延迟就可以等待从库中的全部事务执行完毕才返回客户端,这样的方式称之为全同步复制;只有在从库写完数据时,主库才会返回给客户端。这种方式可以保证读之前数据已经同步成功,但它的问题是事务执行的时间会变长后,导致 **Master** 节点的性能下降。
image.png
image.png
半同步复制
  • 在介于异步和全同步复制之间有一种叫半同步复制的方式:当主库执行完客户端提交的事务后不是立刻返回给客户端的,而是等待至少一个从库接收到 **binlog** 并写到 **relay log** 中才返回给客户端。**Master** 不会等待很长的时间,但返回给客户端时,数据即将写入成功,因为它只是剩下最后读取 **relay log** 并写入从库。
image.png
image.png
  • 如果想在数据库里使用半同步复制,就需要安装 **mysql/plugin** 目录下的一个插件并启动。对于异步复制,半步同步复制提高了数据的安全性,但也造成了一定程度的延迟;它需要等待一个 **Slave** 写入 **relay log**  在这里多了一个网络交互的过程,所以半同步复制最好在低延时的网络中使用。当要减少主从同步的延迟,减少 **SQL** 执行造成的等待时间,就可以使用 **MySQL 5.6** 版本中支持的多库并行复制,该方式是在 **Slave** 上让多个 **SQL** 语句可以并行执行,而不是排队执行。
代码语言:javascript
复制
-- Master 执行
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
-- 启动
SET GLOBAL rpl_semi_sync_master_enabled = 1;
-- 查看是否启动成功
SHOW variables LIKE '%semi_sync%';

-- Slave 执行
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
-- 启动
SET global rpl_semi_sync_master_enabled = 1;
-- 查看是否启动成功
SHOW global variables LIKE '%semi_sync%';
多库并行复制
  • 多库并行复制指在多个数据库上执行 **SQL** 语句且各自操作自己的数据库,这样就不会产生并发的问题,并且对执行的顺序也没有要求。当使用该方式时,数据库的从库中的 **SQL** 线程就可以并发执行。但在大部分情况下都是单库多表的情况,在一个数据库中可以实现并行复制主要是因为数据库本身就是支持多个事务同时操作的情况。它们之间是互不干扰的,即便操作不同的表、行的情况下,也不会存在资源的竞争和数据的干扰。那在主库上能并行执行的事务,是否在从库上也能并行执行操作呢?所以就引入了异步复制之 **GTID** 复制。
image.png
image.png
异步复制之 GTID 复制
  • 异步复制之 **GTID** 复制指把那些在主库上并行执行的事务分为一个组并给他们设定编号,这组事务在从库上也可以并行地执行,这个号就称为 **GTID(Global Transaction Identifiers)**。如果要使用 **GTID** 复制就可以通过修改配置参数打开它,默认是关闭的。所以说无论对 **master | slave** 的连接方式进行优化,还是让从库可以并行执行 **SQL**,都是从数据库的层面去解决主从复制延迟的问题。
代码语言:javascript
复制
-- 查看 GTID 是否开启
show global variables like 'gtid_mode';
image.png
image.png

分库分表

  • 除了数据库本身层面外,还可以在应用层中减少主从同步延迟问题。当做了主从复制后,在单个 **Master** 节点或单张表存储的数据过大时(亿级别数据),单表的查询性能还是会下降,这时就需要进一步对单台数据库节点的数据分型拆分,这就是分库分表。在分库分表中存在两种:
    • 垂直分库:主要解决减少并发压力问题,把一个数据库按照业务拆分成不同的数据库;
    • 水平分库:主要解决存储瓶颈问题,把单张表的数据按照一定的规则分布在多个数据库上。
image.png
image.png
image.png
image.png
image.png
image.png

高可用方案

高可用 **HA** 方案需要解决的问题是当一个 **Master** 节点宕机时提升一个数据最新的 **Slave** 成为 **Master** 节点;如果同时运行多个 **Master** 节点又必须要解决 **Master** 之间数据复制和对客户端连接路由的问题就有不同的解决方案(难度不同、运维管理成本不同)。

  • 主从复制:传统的 **HAProxy + keepalived** 的方案是基于主从复制的;
  • **NDB Cluster**:基于 **NDB Cluster** 存储引擎的 **MySQL Cluster**
cluster-components-1.png
cluster-components-1.png
image.png
image.png
  • **MHA/MMM****MMM [Master-Master replication manager for MySQL]** 是一种多主的高可用架构,它和    **MHA [MySQL Master High Available]** 都是对外提供一个虚拟 **IP** 并监控 **Master & Slave**。当主节点发送故障时,就需要把一个从节点升级为主节点,并把从节点里面比主节点缺少的数据补上后就把虚拟 **IP** 指向一个新的主节点。
image.png
image.png
  • **MGR**:在 **MySQL 5.7.17** 版本中推出了 **InnoDB Cluster -- MySQL Group Replication -- MSG**,该套件包括了 **mysql shell & mysql-route**
image.png
image.png

SQL 语句优化(优化器)

慢查询日志

  • 在服务层每天执行了很多的 **SQL** 语句,那么就需要记录执行比较慢的 **SQL** 语句;而优化器就是对执行的 **SQL** 语句进行分析,生成执行计划。当需要查看执行比较慢的 **SQL** 语句时就需要打开慢日志开关,它默认是关闭的,因为开启慢查询日志和 **bin log & optimizer-trace** 都是有代价的。
代码语言:javascript
复制
-- 查看慢日志信息,默认是 OFF
SHOW VARIABLES LIKE '%slow_query%'
-- 控制执行多长时间的 SQL 记录到慢日志中,默认 10s
SHOW VARIABLES LIKE '%long_query%';
-- 动态开启慢查询日志 1 开启,0 关闭(重启后失效)
SET @@global.slow_query_log = 1;
-- mysql 默认记录到慢日志时间是 10s,修改时间
SET @@global.long_query_time = 3;
  • 在开启了慢日志查询后,执行下面语句后并使用 **MySQL** 提供的工具 **mysqldumpslow** 进行分析;
代码语言:javascript
复制
-- 模拟慢查询
select sleep(10);
SELECT * FROM `user` WHERE username = 'John_687134';

-- 使用 mysqldumpslow 工具进行分析查询时最慢的 5 条 SQL 语句
mysqldumpslow -s t -t 5 -g 'select' /var/lib/mysql/localhost-slow.log
image.png
image.png
image.png
image.png
  • Count:代表这条 **SQL** 语句执行了多少次;
  • **Time**:代表执行的时间(括号中代表累计时间);
  • **Lock** 代表锁定时间(括号中代表累计时间);
  • **Rows** 代表返回的记录数(括号中代表累计记录数)。

SHOW PROFILE

  • **SHOW PROFILE** 可以查看 **SQL** 语句执行时使用的资源(**CPU、I/O** 等情况);
代码语言:javascript
复制
-- 查看是否开启,在 MySQL 8.0.19 中默认是开启的
select @@profiling; 
-- 如果没有开启就通过下面语句进行开启
set @@profiling=1;
-- 查看 profile 统计
show profiles;
-- 查看最后一个 SQL 的执行详细信息,找出耗时比较多的环节
show profile;
-- 根据 id 查看执行详细信息
show profile for query 1;
  • 当然除了查看慢日志和 **show profile** 外要对当前数据库中执行的慢的 SQL 进行分析,可以通过查看运行线程状态和服务器运行信息、存储引擎信息来分析。使用 **show processlist** 命令显示运行线程,可以根据 **id****kill** 线程,也可以查表。
代码语言:javascript
复制
select * from information_schema.processlist;

含义

ID

线程的唯一标志,可以根据它 **kill** 线程

**USER**

启动这个线程的用户,普通用户只能看到自己的线程

HOST

哪个 **IP** 端口发起的连接

**DB**

操作的数据库

**COMMAND**

线程的命令

**TIME**

操作持续时间

**STATE**

线程状态

**INFO**

**SQL** 语句的前 **100** 个字符

代码语言:javascript
复制
-- 查看 select 次数
SHOW GLOBAL STATUS LIKE 'com_select'; 
  • 使用 **[SHOW ENGIN](https://dev.mysql.com/doc/refman/5.7/en/show-engine.html)** 命令用于查看 **MySQL** 存储引擎的当前运行信息(事务持有的表锁、行锁、事务的锁等待状况、线程信号量等待、文件 **I/O** 请求以及 **buffer pool** 统计信息)
代码语言:javascript
复制
show engine innodb status;
-- 查看存储引擎输出是否开启,默认关闭
show variables like 'innodb_status_output%'; 
-- 开启输出
SET GLOBAL innodb_status_output=ON; 
SET GLOBAL innodb_status_output_locks=ON;

EXPLAIN 执行计划

  • MySQL 中提供了一个执行计划工具,通过该工具可以模拟优化执行 **SQL** 查询语句的过程得到 **MySQL**  是怎么对一条 **SQL** 语句的过程并且可以对其进行分析。在 **MySQL 5.6.3** 之前只能分析 **SELECT** 语句,之后就全部都能分析。
代码语言:javascript
复制
-- 创建课程表
CREATE TABLE `course` ( 
	`cid` int(3) DEFAULT NULL, 
	`cname` varchar(20) DEFAULT NULL, 
	`tid` int(3) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 

-- 插入课程信息
INSERT INTO `course` VALUES ('1', 'java', '1'); 
INSERT INTO `course` VALUES ('2', 'c++', '1'); 
INSERT INTO `course` VALUES ('3', 'python', '2'); 
INSERT INTO `course` VALUES ('4', 'go', '3'); 

-- 创建老师表
CREATE TABLE `teacher` ( 
	`tid` int(3) DEFAULT NULL, 
	`tname` varchar(20) DEFAULT NULL, 
	`tcid` int(3) DEFAULT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 

-- 插入老师基本信息
INSERT INTO `teacher` VALUES ('1', 'Sky', '1'); 
INSERT INTO `teacher` VALUES ('2', 'Sara', '2'); 
INSERT INTO `teacher` VALUES ('3', 'John', '3'); 

-- 创建老师联系信息
CREATE TABLE `teacher_contact` ( 
	`tcid` int(3) DEFAULT NULL, 
	`phone` varchar(200) DEFAULT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 

-- 插入老师联系信息
INSERT INTO `teacher_contact` VALUES ('1', '13688888888'); 
INSERT INTO `teacher_contact` VALUES ('2', '18166669999'); 
INSERT INTO `teacher_contact` VALUES ('3', '17722225555');
id
id 值不相同的情况
  • 查询顺序是先查询 **id** 值最大的,以从下往上进行查询 course → teacher → teacher_contact,子查询就是只要拿到内层的结果后才能进行外层查询。
代码语言:javascript
复制
-- 查询 java 课程的老师手机号
EXPLAIN SELECT
	tc.phone 
FROM
	teacher_contact tc 
WHERE
	tcid = (
	SELECT
		tcid 
	FROM
		teacher t 
WHERE
	t.tid = ( SELECT c.tid FROM course c WHERE c.cname = 'java' ));
image.png
image.png
id 值相同的情况
  • 表的查询顺序是从上往下顺序执行 teacher → course → teacher_contact
代码语言:javascript
复制
-- 查询课程 ID 为 2,或者联系表 ID 为 3 的老师
EXPLAIN SELECT
	t.tname,
	c.cname,
	tc.phone 
FROM
	teacher t,
	course c,
	teacher_contact tc 
WHERE
	t.tid = c.tid 
	AND t.tcid = tc.tcid 
	AND ( c.cid = 2 OR tc.tcid = 3 );
image.png
image.png
  • 在往表中插入了三条数据之后再查询的顺序为   teacher_contact → teacher → course
代码语言:javascript
复制
INSERT INTO `teacher` VALUES (4, 'Ken', 4); 
INSERT INTO `teacher` VALUES (5, 'Tim', 5); 
INSERT INTO `teacher` VALUES (6, 'Mellisa', 6);
image.png
image.png
  • 在表中数量不同的顺序发生改变是因为笛卡尔积决定的,假如有 **a、b、c** 三张表,分别有 **2、3、4** 条数据;当三张表做联合查询时且查询顺序为 **a → b → c**,那么其笛卡尔积是 **2 * 3 * 4 = 6 * 4 = 24**。如果查询顺序是 **c → b → a**,它的笛卡尔积为 **4 * 3 * 2 = 12 * 2 = 24**。因为 **MySQL** 需要把查询的结果(中间结果和最终结果都保存到内存中),所以 **MySQL** 会优先选择中间结果数据量比较小的顺序进行查询,所以最终联表查询的顺序是 **a → b → c**,所以这是为什么 **teacher** 表插入数据之后查询顺序会发生变化。
当 id 值既相同又不同的情况
  • 在这种情况下,**id** 不同时先大后小,否则从上往下。
select type
SIMPLE
  • 简单查询,不包含子查询和关联查询
代码语言:javascript
复制
EXPLAIN SELECT * FROM teacher;
image.png
image.png
PRIMARY
  • 在子查询 **SQL** 语句中最外面的一层查询就是主查询
代码语言:javascript
复制
-- 查询 java 课程的老师手机号
EXPLAIN SELECT
	tc.phone 
FROM
	teacher_contact tc 
WHERE
	tcid = (
	SELECT
		tcid 
	FROM
		teacher t 
WHERE
	t.tid = ( SELECT c.tid FROM course c WHERE c.cname = 'java' ));
image.png
image.png
SUBQUERY
  • 在子查询中所有的内层查询都是子查询类型
image.png
image.png
DERIVED
  • 衍生查询表示在得到最终结果之前都会用到临时表,对于关联查询,会先执行右边的 **table(UNION)**,然后再执行左边的 **table**,类型是 **DERIVED**
代码语言:javascript
复制
-- 查询 ID 为 1 或 2 的老师的课程
EXPLAIN SELECT
	cr.cname 
FROM
	( SELECT * FROM course WHERE tid = 1 UNION SELECT * FROM course WHERE tid = 2 ) cr;
image.png
image.png
UNION
  • 使用到了关联查询
image.png
image.png
UNION RESULT
  • 主要是显示哪些表之间在联合查询,**<union2, 3>** 表示 **id = 2 & id = 3** 存在联合查询
image.png
image.png
type
  • 常见的类型:**system > const > eq_ref > ref > range > index > all**,其他类型 **fulltext 、ref_or_null 、index_merger 、unique_subquery、index_subquery**;在所有的连接类型中越往下越差,除了常见的类型中的 **all** 都能访问到索引。

** system

  • **system****const** 中的一种特例,只有一行满足条件**

** const

  • 主键索引或唯一索引,只能查到一条数据。
代码语言:javascript
复制
-- 创建表
CREATE TABLE test ( 
	id INT ( 3 ) PRIMARY KEY, 
	content VARCHAR ( 20 ) 
);
-- 插入一条数据
INSERT INTO test VALUES ( 1, 'a' );
-- 根据 id 查询查看执行计划
EXPLAIN SELECT * FROM test WHERE id = 1;
image.png
image.png

** eq_ref

  • 一般情况下出现在多表的 **join** 查询,表示对于前表的每一个结果都只能够匹配到后表的一行结果(唯一索引的查询)
代码语言:javascript
复制
-- 为 teacher_contact 的 tcid 创建一个主键
ALTER TABLE teacher_contact ADD PRIMARY KEY(tcid);
-- 为 teacher 中的 tcid 创建一个普通主键
ALTER TABLE teacher ADD INDEX idx_tcid (tcid);

EXPLAIN SELECT
	t.tcid 
FROM
	teacher t,
	teacher_contact tc 
WHERE
	t.tcid = tc.tcid;
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png
image.png

ref **

  • 在查询时使用到了非唯一性索引或关联操作时只用到了索引的最左前缀
代码语言:javascript
复制
-- 查看 teacher 表中的普通索引 tcid 查询的执行计划
EXPLAIN SELECT * FROM teacher WHERE tcid = 3;

range **

  • 索引范围扫描,当 **where** 后面是 **between and | < | > | >= | <= | in**  时,type**range**。不走索引
代码语言:javascript
复制
-- 对 tid 加索引,否则为全表扫描 all
ALTER TABLE teacher ADD INDEX idx_tid (tid);

EXPLAIN SELECT * FROM teacher t WHERE tid BETWEEN 1 AND 2;
image.png
image.png

** **index **

  • 查询全部索引中的数据
代码语言:javascript
复制
EXPLAIN SELECT tid FROM teacher;
image.png
image.png

all

  • 当没有索引或没有用到索引就全表扫描
代码语言:javascript
复制
EXPLAIN SELECT * FROM teacher
image.png
image.png

NULL

  • 不用访问表或索引就能得到结果
代码语言:javascript
复制
EXPLAIN select 1 from dual where 1=1;
image.png
image.png
possible_keys & key
  • 可能用到的索引和实际用到的索引,如果是 **NULL** 就表示没有使用到索引,**possible_keys** 可能有一个或多个,可能会用到索引;当 possible_key = NULL**ken** 有可能有值。
key_len
  • 索引的长度(**byte**),与索引的类型和长度相关。
rows
  • **MySQL** 中认为能扫描多少行才能返回数据的一个预估值。
filtered
  • 表示存储引擎返回的数据在 **Server** 层过滤后剩下多少满足查询的记录数量的一个百分比。
ref
  • 使用哪个列或常数和索引一起从表中筛选数据
Extra
  • 执行计划给出额外的信息说明
  • **using index**:用到了覆盖索引,不用回表
代码语言:javascript
复制
EXPLAIN SELECT username FROM `user` WHERE username = 'John_21';
image.png
image.png
  • **using where**:使用了 **where** 过滤,表示存储引擎返回的记录并不是所有满足查询条件,需要在 **Server** 层进行过滤
代码语言:javascript
复制
EXPLAIN SELECT * FROM `user` WHERE password = 'b9aae9537ca945ae9382525efd73bed0';
image.png
image.png
  • **using index condition**索引下推
  • **using filesort**:不能使用索引来排序,用到了额外的排序,复合索引的前提下需要优化
代码语言:javascript
复制
EXPLAIN select * from user where name ='John' order by id;
image.png
image.png
  • **using temporary**:用到了临时表
代码语言:javascript
复制
-- distinct 非索引列
EXPLAIN SELECT DISTINCT(tid) FROM teacher;
-- group by 非索引列
EXPLAIN SELECT tname FROM teacher GROUP BY tname;
-- 使用 join 时 group 任意列
EXPLAIN SELECT t.tid FROM teacher t JOIN course c ON t.tid = c.tid GROUP BY t.tid;
image.png
image.png
优化器总结
  • 通过模拟优化器执行 **SQL** 查询语句的过程得到 **MySQL** 是怎么处理一条 **SQL** 语句的并可以对其进行语句或表的性能瓶颈。

SQL 与索引优化

  • **SQL** 语句有多个关联和子查询且比较复杂时,就需要分析 **SQL** 语句是否有其他方式编写对其优化
代码语言:javascript
复制
-- 大偏移量的 limit, 预估扫描行数 995354
EXPLAIN select * FROM user LIMIT 900000, 10;
image.png
image.png
代码语言:javascript
复制
-- 改成先过滤 ID,再 limit 10
EXPLAIN SELECT * FROM user WHERE id >= 900000 AND id < 900010 LIMIT 10;
image.png
image.png

存储引擎

存储引擎的选择

  • 可以为不同的业务表选择不同的存储引擎
    • **MyISAM**:对查询和插入操作比较多业务表;
    • **Memory**:临时数据;
    • **InnoDB**:常规并发更新多的表

字段的定义

  • 原则是使用可以正确存储数据的最小数据类型,为每列都选择合适的字段类型;

字符类型

  • 在可变长的情况下使用 **varchar** 更节省空间,它需要一个字节来记录长度;但是固定长度就使用 **char**

非空

  • 对于非空字段尽量定义成 **NOT NULL** 并提供默认值或使用特殊值、空串代替 **NULL****NULL** 类型的存储、优化、使用会存在问题;

不要使用外键、触发器、视图

  • 不仅降低了可读性,而且还影响数据库性能,对于计算的操作交给程序,数据库只做存储;对于数据的完整性应该在程序中做检查。

文件存储

  • 在数据库中尽量不要存储图片或文件等,可以把这些资源放在 **NAS | SSO | fastDFS** 上,在数据库中只是存储它的路径,在应用中配置其服务器地址。

表拆分

  • 将不常用的字段拆分出去,避免列数过多和数据量过大
image.png
image.png
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-02-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • MySQL优化
  • MySQL 优化方案
    • 配置优化(连接)
      • 架构优化(缓存)
        • 缓存
        • 主从复制
        • 分库分表
        • 高可用方案
      • SQL 语句优化(优化器)
        • 慢查询日志
        • SHOW PROFILE
        • EXPLAIN 执行计划
        • SQL 与索引优化
      • 存储引擎
        • 存储引擎的选择
        • 字段的定义
        • 字符类型
        • 非空
        • 不要使用外键、触发器、视图
        • 文件存储
        • 表拆分
    相关产品与服务
    云数据库 SQL Server
    腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档