---
# System prepended metadata

title: MySQL优化基本操作
tags: [MySql]

---

# MySQL优化基本操作
https://learnku.com/articles/60619

https://learnku.com/articles/61064

## 優化注意事項
https://juejin.cn/post/7208571916154847288

##  where 及 order by 涉及的列上建立索引

## limit
https://www.bilibili.com/video/BV1Kr4y1i7ru?p=93&spm_id_from=pageDriver&vd_source=f726dd30598fe01d9fbc9c5a988d6408
也可以用where
https://segmentfault.com/a/1190000021287858


如果您的需求是簡單的分頁，則使用 WHERE 和 LIMIT 的方法通常更適合。如果您需要更多靈活性和複雜性，則可以考慮使用自連接表的方法

## 尽可能不要使用 NULL 值
因为建表的时候，如果不对创建的值设置默认值，MySQL 都会设置默认为 NULL。那么为啥用 NULL 不好呢？

* NULL 使得索引维护更加复杂，强烈建议对索引列设置 NOT NULL
* NOT IN、!= 等负向条件查询在有 NULL 值的情况下返回永远为空结果，查询容易出错
* NULL 列需要一个额外字节作为判断是否为 NULL 的标志位
* 使用 NULL 时和该列其他的值可能不是同种类型，导致问题。（在不同的语言中表现不一样）
* MySQL 难以优化对可为 NULL 的列的查询
所以对于那些以前偷懒的字段，手动设置一个默认值吧，空字符串呀，0 呀补上。

虽然这种方法对于 MySQL 的性能来说没有提升多少，但是这是一个好习惯，而且以小见大，不要忽略这些细节。

查詢注意欄位 如果是不等於 記得她不會找null 的
所以要 or
![](https://i.imgur.com/S5erJO1.png)

## 添加索引
对于经常查询的字段，请加上索引，有索引和没有索引的查询速度相差十倍甚至更多。

* 一般来说，每张表都需要有一个主键 id 字段
* 常用于查询的字段应该设置索引
* varchar 类型的字段，在建立索引的时候，最好指定长度
* 查询有多个条件时，优先使用具有索引的条件
* 像 LIKE 条件这样的模糊搜索对于字段索引是无效的，需要另外建立关键词索引来解决

**请尽量不要在数据库层面约束表和表之间的关系**，这些表之间的依赖应该在代码层面去解决
当表和表之间有约束时，虽然增删查的 SQL 语句变简单了，但是带来的负面效果是插入等操作数据库都会去检查约束（虽然可以手动设置忽略约束），这样相当于把一些业务逻辑写到了数据库层，不便于维护。



## 数据库中那些可以用整形表示的数据就不要使用字符串类型，到底是用 varchar 还是 char 要看字段的可能值。

这种优化往往在数据库中有大量数据以后是不可行的，最好在数据库设计之前就设计好。

* 对于那些可能值很有限的列，使用 tinyint 代替 VARCHAR，
    * 比如记录移动设备平台，只有两个值：android，ios，那么就可以使用 0 表示 android，1 表示 ios，这种列一定要写好注释
    * 为什么不用 ENUM 呢？ENUM 扩展困难，比如后来移动平台又增加了一个 ipad，那岂不是懵逼了，而 tinyint 加个 2 就行，而且 ENUM 在代码里面处理起来特别奇怪，是当成整形呢还是字符串，各个语言不一样。
    * 这种方式，一定要在数据库注释或者代码里面写明各个值的含义
* 对于那些定长字符串，可以使用 char，比如邮编，总是 5 位
* 对于那些长度未知的字符串，使用 varchar
* 不要滥用 bigint，比如记录文章数目的表 id 字段，用 int 就行了，21 亿篇文章上限够了
* 适当打破数据库范式添加冗余字段，避免查询时的表连接
查询的时候，肯定 int 类型比 varchar 快，因为整数的比较直接调用底层运算器就可以实现，而字符串比较要逐个字符比较。

定长数据比变长数据查询快，因为比较定长数据与数据之间的偏移是固定的，很容易计算下一个数据的偏移。而变长数据则还需要多一步去查询下一个数据的偏移量。不过。定长数据可能会浪费更多的存储空间


## 大表拆分
对于那些数据量可能近期会超过 500W 或者增长很快的表，一定要提前做好垂直分表或者水平分表，当数据量超过百万以后，查询速度会明显下降。

分库分表尽量在数据库设计初期敲定方案，否则后期会极大增加代码复杂性而且不易更改。

垂直分表是按照日期等外部变量进行分表，水平分表是按照表中的某些字段关系，使用 hash 映射等分表。

分库分表的前提条件是在执行查询语句之前，已经知道需要查询的数据可能会落在哪一个分库和哪一个分表中。


## 优化查询语句
这个才是很多系统数据库瓶颈的始作俑者。

* 请尽量使用简单的查询，避免使用表链接
* 请尽量避免全表扫描，会造成全表扫描的语句包括但不限于：
    *  where 子句条件恒真或为空
    *  使用 LIKE
    *  使用不等操作符（<>、!=）
     * 查询含有 is null 的列
     * 在非索引列上使用 or
* 多条件查询时，请把简单查询条件或者索引列查询置于前面
* 请尽量指定需要查询的列，不要偷懒使用 select *
    * 如果不指定，一方面会返回多余的数据，占用带宽等
    * 另一方面 MySQL 执行查询的时候，没有字段时会先去查询表结构有哪些字段
* 大写的查询关键字比小写快一点点
* 使用子查询会创建临时表，会比链接（JOIN）和联合（UNION）稍慢
* 在索引字段上查询尽量不要使用数据库函数，不便于缓存查询结果
* 当只要一行数据时，请使用 LIMIT 1，如果数据过多，请适当设定 LIMIT，分页查询
* 千万不要 ORDER BY RAND ()，性能极低

隐式类型转换，会导致索引失效，例如 age 字段类型是 int，我们 where age = “1”，这样就会触发隐式类型转换。

## 日常工作中你是怎么优化 SQL 的
可以从这几个维度回答这个问题：

* 加索引
* 避免返回不必要的数据
* 适当分批量进行
* 优化 sql 结构
* 主从架构，提升读性能
* 分库分表

## 数据库索引的原理，为什么要用 B + 树，为什么不用二叉树？
可以从几个维度去看这个问题，查询是否够快，效率是否稳定，存储数据多少，以及查找磁盘次数，为什么不是二叉树，为什么不是平衡二叉树，为什么不是 B 树，而偏偏是 B + 树呢？

## 为什么不是一般二叉树？
1）当数据量大时，树的高度会比较高（树的高度决定着它的 IO 操作次数，IO 操作耗时大），查询会比较慢。 2）每个磁盘块（节点 / 页）保存的数据太小（IO 本来是耗时操作，每次 IO 只能读取到一个关键字，显然不合适），没有很好的利用操作磁盘 IO 的数据交换特性，也没有利用好磁盘 IO 的预读能力（空间局部性原理），从而带来频繁的 IO 操作。

## 为什么不是平衡二叉树呢？
我们知道，在内存比在磁盘的数据，查询效率快得多。如果树这种数据结构作为索引，那我们每查找一次数据就需要从磁盘中读取一个节点，也就是我们说的一个磁盘块，但是平衡二叉树可是每个节点只存储一个键值和数据的，如果是 B 树，可以存储更多的节点数据，树的高度也会降低，因此读取磁盘的次数就降下来啦，查询效率就快啦。

## 那为什么不是 B 树而是 B + 树呢？
1）B+Tree 范围查找，定位 min 与 max 之后，中间叶子节点，就是结果集，不用中序回溯 2）B+Tree 磁盘读写能力更强（叶子节点不保存真实数据，因此一个磁盘块能保存的关键字更多，因此每次加载的关键字越多） 3）B+

## . 如何选择合适的分布式主键方案呢？
* 数据库自增长序列或字段。
* UUID
* 雪花算法
* Redis 生成 ID
* 利用 zookeeper 生成唯一 ID

###### tags: `MySql`