Merge branch 'master' of gitee.com:Eyestorm/notes
BIN
数据库/MySQL/image/2021-09-02-00-50-45.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
数据库/MySQL/image/2021-09-02-00-51-00.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
数据库/MySQL/image/2021-09-02-00-54-06.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
数据库/MySQL/image/2021-09-02-00-54-18.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
数据库/MySQL/image/2021-09-02-10-11-38.png
Normal file
|
After Width: | Height: | Size: 390 KiB |
BIN
数据库/MySQL/image/2021-09-02-10-14-13.png
Normal file
|
After Width: | Height: | Size: 437 KiB |
BIN
数据库/MySQL/image/2021-09-02-10-15-31.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
数据库/MySQL/image/2021-09-02-10-16-47.png
Normal file
|
After Width: | Height: | Size: 133 KiB |
BIN
数据库/MySQL/image/2021-09-02-10-17-53.png
Normal file
|
After Width: | Height: | Size: 128 KiB |
BIN
数据库/MySQL/image/2021-09-02-10-19-31.png
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
数据库/MySQL/image/2021-09-02-12-05-03.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
@@ -51,9 +51,8 @@ frm和MYI可以存放在不同的目录下。MYI文件用来存储索引,但
|
||||
3. 压缩表。上面说到支持数据压缩,说明肯定也支持这个格式。在数据文件发生错误时候,可以使用check table工具来检查,而且还可以使用repair table工具来恢复。
|
||||
|
||||
### 存储引擎的特点
|
||||
有一个重要的特点那就是不支持事务,但是这也意味着他的存储速度更快,如果你的读写操作允许有错误数据的话,只是追求速度,可以选择这个存储引擎。
|
||||
有一个重要的特点那就是**不支持事务**,但是这也意味着他的存储速度更快,如果你的读写操作允许有错误数据的话,只是追求速度,可以选择这个存储引擎。
|
||||
|
||||
MyISAM存储引擎
|
||||
|
||||
MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事物。MyISAM主要特性有:
|
||||
|
||||
@@ -65,7 +64,7 @@ MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓
|
||||
|
||||
4. 最大的键长度是1000字节,这也可以通过编译来改变,对于键长度超过250字节的情况,一个超过1024字节的键将被用上
|
||||
|
||||
5. BLOB和TEXT列可以被索引
|
||||
5. **BLOB和TEXT列**可以被索引
|
||||
|
||||
6. NULL被允许在索引的列中,这个值占每个键的0~1个字节
|
||||
|
||||
@@ -81,18 +80,34 @@ MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓
|
||||
|
||||
12. VARCHAR和CHAR列可以多达64KB
|
||||
|
||||
|
||||
### 存储引擎的特点2
|
||||
|
||||
设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用它。
|
||||
|
||||
提供了大量的特性,包括压缩表、空间数据索引等。
|
||||
|
||||
不支持事务。
|
||||
|
||||
不支持行级锁,只能对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。
|
||||
|
||||
可以手工或者自动执行检查和修复操作,但是和事务恢复以及崩溃恢复不同,可能导致一些数据丢失,而且修复操作是非常慢的。
|
||||
|
||||
如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。
|
||||
|
||||
|
||||
## 2.2 InnoDB
|
||||
|
||||
### 存储引擎的特点
|
||||
InnoDB是默认的数据库存储引擎,他的主要特点有:
|
||||
> 对于InnoDB来说,最大的特点在于支持事务。但是这是以损失效率来换取的。
|
||||
> 对于InnoDB来说,最大的特点在于**支持事务**。但是这是以损失效率来换取的。
|
||||
1. 可以通过自动增长列,方法是auto_increment。
|
||||
2. 支持事务。默认的事务隔离级别为可重复度,通过MVCC(并发版本控制)来实现的。
|
||||
3. 使用的锁粒度为行级锁,可以支持更高的并发;
|
||||
4. 支持外键约束;外键约束其实降低了表的查询速度,但是增加了表之间的耦合度。
|
||||
5. 配合一些热备工具可以支持在线热备份;
|
||||
6. 在InnoDB中存在着缓冲管理,通过缓冲池,将索引和数据全部缓存起来,加快查询的速度;
|
||||
7. 对于InnoDB类型的表,其数据的物理组织形式是聚簇表。所有的数据按照主键来组织。数据和索引放在一块,都位于B+数的叶子节点上;
|
||||
6. 在InnoDB中存在着缓冲管理,通过**缓冲池**,将索引和数据全部缓存起来,加快查询的速度;
|
||||
7. 对于InnoDB类型的表,其数据的物理组织形式是**聚簇表**。所有的数据按照主键来组织。数据和索引放在一块,都位于B+数的叶子节点上;
|
||||
|
||||
### 存储引擎的实现
|
||||
当然InnoDB的存储表和索引也有下面两种形式:
|
||||
@@ -120,6 +135,18 @@ InnoDB是默认的数据库存储引擎,他的主要特点有:
|
||||
|
||||
5. InnoDB被用在众多需要高性能的大型数据库站点上。InnoDB不创建目录,使用InnoDB时,MySQL将在MySQL数据目录下创建一个名为ibdata1的10MB大小的自动扩展数据文件,以及两个名为ib_logfile0和ib_logfile1的5MB大小的日志文件
|
||||
|
||||
|
||||
### 存储引擎的特点3
|
||||
|
||||
1. 是 MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎。
|
||||
|
||||
2. 实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ Next-Key Locking 防止幻影读。
|
||||
|
||||
3. 主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。
|
||||
4. 内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。
|
||||
5. 支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。
|
||||
|
||||
|
||||
## 2.3 Memory
|
||||
|
||||
### 存储引擎的实现
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 10个MySQL性能调优的方法
|
||||
# 性能调优
|
||||
> 参考文献
|
||||
> * [https://www.jb51.net/article/70111.htm](https://www.jb51.net/article/70111.htm)
|
||||
> * [https://www.cnblogs.com/jiekzou/p/5371085.html](https://www.cnblogs.com/jiekzou/p/5371085.html)
|
||||
@@ -8,86 +8,156 @@
|
||||
1. 数据库设计优化:
|
||||
1. 选择合适的存储引擎
|
||||
2. 设计合理的表结构(符合3NF)
|
||||
3. 添加适当索引(index) [四种: 普通索引、主键索引、唯一索引unique、全文索引]
|
||||
3. 添加适当索引(index) 普通索引、主键索引、唯一索引、全文索引、组合索引、覆盖索引。
|
||||
2. 查询语句优化:
|
||||
1. 通过show status命令了解各种SQL的执行频率。
|
||||
2. 定位执行效率较低的SQL语句-(重点select,记录慢查询)
|
||||
3. 通过explain分析低效率的SQL
|
||||
1. 遵守查询规范。
|
||||
2. 分析日志:通过show status命令了解各种SQL的执行频率。定位执行效率较低的SQL语句(重点select,记录慢查询)
|
||||
3. explain分析低效率的SQL语句
|
||||
3. 查询过程优化
|
||||
1. 从内存中读取数据
|
||||
2. 减少磁盘写入操作(更大的写缓存)
|
||||
3. 提高磁盘读取速度
|
||||
## 1 选择合适的存储引擎: InnoDB
|
||||
3. 提高磁盘读取速度(硬件设备改善)
|
||||
|
||||
* 除非你的数据表使用来做只读或者全文检索 (相信现在提到全文检索,没人会用 MYSQL 了),你应该默认选择 InnoDB 。
|
||||
|
||||
* 你自己在测试的时候可能会发现 MyISAM 比 InnoDB 速度快,这是因为: MyISAM 只缓存索引,而 InnoDB 缓存数据和索引,MyISAM 不支持事务。但是 如果你使用 innodb_flush_log_at_trx_commit = 2 可以获得接近的读取性能 (相差百倍) 。
|
||||
## 1.1 存储引擎
|
||||
* 支持全文索引:MyISAM
|
||||
* 支持外键:Innodb
|
||||
* 支持缓存:Innodb
|
||||
* 支持事务:Innodb
|
||||
* 支持并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。
|
||||
* 支持备份:InnoDB 支持在线热备份。
|
||||
* 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。
|
||||
* 其它特性:MyISAM 支持压缩表和空间数据索引。
|
||||
|
||||
### 1.1 如何将现有的 MyISAM 数据库转换为 InnoDB:
|
||||
## 1.2 表结构
|
||||
|
||||
符合3BNF/EBNF范式
|
||||
* 属性不可分
|
||||
* 非主属性依赖完全的主属性
|
||||
* 非主属性不依赖其他的非主属性
|
||||
* 消除多值依赖(多对多关系)
|
||||
## 1.3 添加索引
|
||||
索引类型选择:
|
||||
1. **前缀索引**。对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。前缀长度的选取需要根据索引选择性来确定。字符串列加索引最好加短索引,即对前该列的前xx个字符,例如:key 'ind_user_info_addr' (addr(10)) USINGBTREE代表对addr列的前10个字符家索引。
|
||||
2. **复合索引**。在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。如果创建了一个(username,age,addr)的复合索引,那么相当于创建了(username,age,addr),(username,age),(username)三个索引,所以在创建复合索引的时候应该将最常用的限制条件的列放在最左边,依次递减。
|
||||
|
||||
索引使用规范:
|
||||
1. 不能再索引字段上计算。会导致索引无效,成为全表扫描。
|
||||
2. 将非”索引”数据分离,比如将大篇文章分离存储,不影响其他自动查询。
|
||||
3. **覆盖索引**。能够直接通过索引查询到所需要的字段,不需要通过回表查询,找到数据。
|
||||
4. **关联查询**。保证关联的字段都建立索引,并且字段类型一致,这样才能两个表都使用索引。如果字段类型不一样,至少一个表不能使用索引。保证连接的索引是相同类型。即a.age = b.age,a表的和b表的age字段类型保证一样,并且都建立了索引。
|
||||
5. **最左匹配原则**:索引列如果使用like条件进行查询,那么 like 'xxx%' 可以使用索引,like '%xxx' 不能使用索引。
|
||||
6. 索引列的值最好不要有null值。列中只要包含null,则不会被包含到索引中,复合索引中只要有一列为null值,则这个复合索引失效的。所以创建索引列不要设置默认值null
|
||||
7. 如果where条件中使用了一个列的索引,那么后面order by 在用这个列进行排序时,便不会再使用索引。where条件用到复合索引中的字段时,最好把该字段放在复合索引的左端,这样才能使用索引提高查询。
|
||||
8. 排序索引。尽量不要使用包含多个列的排序,如果需要则给这些列加上复合索引。
|
||||
|
||||
|
||||
|
||||
## 2.1 查询规范
|
||||
|
||||
1. **减少请求的数据量**。单条查询最后增加 LIMIT,停止全表扫描。
|
||||
- 只返回必要的列:最好不要使用 SELECT * 语句。
|
||||
- 只返回必要的行:使用 LIMIT 语句来限制返回的数据。
|
||||
3. 不用 MYSQL 内置的函数,因为内置函数不会建立查询缓存。
|
||||
4. inner join 内连接也叫做等值连接,left/right join 是外连接。能用inner join连接的尽量用inner join连接
|
||||
5. 尽量使用外连接来替换子查询。在使用on和where的时候,先用on,在用where。使用join时,用小的结果驱动大的结果(left join左表结果尽量小,如果有条件,先放左边处理,right join同理反向)。多表联合查询尽量拆分多个简单的sql语句进行查询。
|
||||
6. 尽量不要使用BY RAND()命令。减少排序order by。少用OR。尽量不要使用not in 和<> 操作
|
||||
7. 避免类型转换,也就是转入的参数类型要和字段类型一致。
|
||||
8. 不要在列上进行运算。
|
||||
9. 使用批量插入操作代替一个一个插入
|
||||
10. 对于多表查询可以建立视图。
|
||||
|
||||
|
||||
|
||||
## 2.2 分析日志
|
||||
修改mysql的慢查询.
|
||||
```
|
||||
mysql -u [USER_NAME] -p -e "SHOW TABLES IN [DATABASE_NAME];" | tail -n +2 | xargs -I '{}' echo "ALTER TABLE {} ENGINE=InnoDB;" > alter_table.sql
|
||||
|
||||
perl -p -i -e 's/(search_[a-z_]+ ENGINE=)InnoDB//1MyISAM/g' alter_table.sql
|
||||
|
||||
mysql -u [USER_NAME] -p [DATABASE_NAME] < alter_table.sql
|
||||
|
||||
mysql -u [USER_NAME] -p -e "SHOW TABLES IN [DATABASE_NAME];" | tail -n +2 | xargs -I '{}' echo "ALTER TABLE {} ENGINE=InnoDB;" > alter_table.sql
|
||||
|
||||
perl -p -i -e 's/(search_[a-z_]+ ENGINE=)InnoDB//1MyISAM/g' alter_table.sql
|
||||
|
||||
mysql -u [USER_NAME] -p [DATABASE_NAME] < alter_table.sql
|
||||
show variables like 'long_query_time' ; //可以显示当前慢查询时间
|
||||
set long_query_time=1 ;//可以修改慢查询时间
|
||||
```
|
||||
### 1.2 为每个表分别创建 InnoDB FILE:
|
||||
|
||||
记录所有查询,这在用 ORM 系统或者生成查询语句的系统很有用。
|
||||
```
|
||||
innodb_file_per_table=1
|
||||
log=/var/log/mysql.log
|
||||
```
|
||||
这样可以保证 ibdata1 文件不会过大,失去控制。尤其是在执行 mysqlcheck -o –all-databases 的时候。
|
||||
注意不要在生产环境用,否则会占满你的磁盘空间。
|
||||
|
||||
|
||||
|
||||
## 2. 保证从内存中读取数据,讲数据保存在内存中
|
||||
|
||||
### 2.1 足够大的 innodb_buffer_pool_size
|
||||
|
||||
推荐将数据完全保存在 innodb_buffer_pool_size ,即按存储量规划 innodb_buffer_pool_size 的容量。这样你可以完全从内存中读取数据,最大限度减少磁盘操作。
|
||||
|
||||
第一步:如何确定 innodb_buffer_pool_size 足够大,数据是从内存读取而不是硬盘?
|
||||
|
||||
查询buffer size
|
||||
记录执行时间超过 1 秒的查询:
|
||||
```
|
||||
mysql> SHOW GLOBAL STATUS LIKE 'innodb_buffer_pool_pages_%';
|
||||
+----------------------------------+--------+
|
||||
| Variable_name | Value |
|
||||
+----------------------------------+--------+
|
||||
| Innodb_buffer_pool_pages_data | 129037 |
|
||||
| Innodb_buffer_pool_pages_dirty | 362 |
|
||||
| Innodb_buffer_pool_pages_flushed | 9998 |
|
||||
| Innodb_buffer_pool_pages_free | 0 | !!!!!!!!
|
||||
| Innodb_buffer_pool_pages_misc | 2035 |
|
||||
| Innodb_buffer_pool_pages_total | 131072 |
|
||||
+----------------------------------+--------+
|
||||
6 rows in set (0.00 sec)
|
||||
|
||||
innodb_additional_mem_pool_size = 1/200 of buffer_pool
|
||||
innodb_max_dirty_pages_pct 80%
|
||||
long_query_time=1
|
||||
log-slow-queries=/var/log/mysql/log-slow-queries.log
|
||||
```
|
||||
发现 Innodb_buffer_pool_pages_free 为 0,则说明 buffer pool 已经被用光,需要增大 innodb_buffer_pool_size
|
||||
|
||||
或者用iostat -d -x -k 1 命令,查看硬盘的操作。
|
||||
|
||||
第二步:服务器上是否有足够内存用来规划。执行 echo 1 > /proc/sys/vm/drop_caches 清除操作系统的文件缓存,可以看到真正的内存使用量。
|
||||
|
||||
### 2.2 数据预热
|
||||
|
||||
默认情况,只有某条数据被读取一次,才会缓存在 innodb_buffer_pool。所以,数据库刚刚启动,需要进行数据预热,将磁盘上的所有数据缓存到内存中。数据预热可以提高读取速度。
|
||||
|
||||
对于 InnoDB 数据库,可以用以下方法,进行数据预热:
|
||||
|
||||
1. 将以下脚本保存为 MakeSelectQueriesToLoad.sql
|
||||
## 2.3 explain分析查询
|
||||
|
||||
### 分析方法
|
||||
```
|
||||
Explain select * from emp where ename=“wsrcla”
|
||||
```
|
||||
会产生如下信息:
|
||||
|
||||
* select_type:表示查询的类型。
|
||||
* table:输出结果集的表
|
||||
* type:表示表的连接类型
|
||||
* possible_keys:表示查询时,可能使用的索引
|
||||
* key:表示实际使用的索引
|
||||
* key_len:索引字段的长度
|
||||
* rows:扫描出的行数(估算的行数)
|
||||
* Extra:执行情况的描述和说明
|
||||
|
||||
### 重构查询方式
|
||||
|
||||
#### 1. 切分大查询
|
||||
|
||||
一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。
|
||||
|
||||
```sql
|
||||
DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH);
|
||||
```
|
||||
|
||||
```sql
|
||||
rows_affected = 0
|
||||
do {
|
||||
rows_affected = do_query(
|
||||
"DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000")
|
||||
} while rows_affected > 0
|
||||
```
|
||||
|
||||
#### 2. 分解大连接查询
|
||||
|
||||
将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联,这样做的好处有:
|
||||
|
||||
- 让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
|
||||
- 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
|
||||
- 减少锁竞争;
|
||||
- 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可伸缩。
|
||||
- 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。
|
||||
|
||||
```sql
|
||||
SELECT * FROM tag
|
||||
JOIN tag_post ON tag_post.tag_id=tag.id
|
||||
JOIN post ON tag_post.post_id=post.id
|
||||
WHERE tag.tag='mysql';
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT * FROM tag WHERE tag='mysql';
|
||||
SELECT * FROM tag_post WHERE tag_id=1234;
|
||||
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 3.1 内存读取
|
||||
|
||||
### 增大缓存
|
||||
* 将更新操作先记录在change buffer,减少读磁盘,语句的执行速度会得到明显的提升。而且,数据读入内存是需要占用buffer pool的,所以这种方式还能够避免占用内存,提高内存利用率
|
||||
* innodb_buffer_pool_size。将数据完全保存在 innodb_buffer_pool中。可以完全从内存中读取数据,最大限度减少磁盘操作。
|
||||
|
||||
### 数据预热
|
||||
|
||||
默认情况,只有某条数据被读取一次,才会缓存在 innodb_buffer_pool。数据库启动后,需要进行数据预热,将磁盘上的所有数据缓存到内存中。数据预热可以提高读取速度。
|
||||
|
||||
```sql
|
||||
SELECT DISTINCT
|
||||
CONCAT('SELECT ',ndxcollist,' FROM ',db,'.',tb,
|
||||
' ORDER BY ',ndxcollist,';') SelectQueryToLoadCache
|
||||
@@ -115,157 +185,34 @@ SELECT DISTINCT
|
||||
) AA
|
||||
ORDER BY db,tb;
|
||||
```
|
||||
2. 执行
|
||||
```
|
||||
mysql -uroot -AN < /root/MakeSelectQueriesToLoad.sql > /root/SelectQueriesToLoad.sql
|
||||
```
|
||||
3. 每次重启数据库,或者整库备份前需要预热的时候执行:
|
||||
```
|
||||
mysql -uroot < /root/SelectQueriesToLoad.sql > /dev/null 2>&1
|
||||
```
|
||||
|
||||
### 2.3 不要让数据存到 SWAP 中
|
||||
|
||||
如果是专用 MYSQL 服务器,可以禁用 SWAP,如果是共享服务器,确定 innodb_buffer_pool_size 足够大。或者使用固定的内存空间做缓存,使用 memlock 指令。
|
||||
|
||||
|
||||
## 3.2 减少磁盘读写
|
||||
|
||||
## 3 定期优化重建数据库
|
||||
### 使用足够大的写入缓存innodb_log_file_size
|
||||
|
||||
mysqlcheck -o –all-databases 会让 ibdata1 不断增大,真正的优化只有重建数据表结构:
|
||||
```
|
||||
CREATE TABLE mydb.mytablenew LIKE mydb.mytable;
|
||||
INSERT INTO mydb.mytablenew SELECT * FROM mydb.mytable;
|
||||
ALTER TABLE mydb.mytable RENAME mydb.mytablezap;
|
||||
ALTER TABLE mydb.mytablenew RENAME mydb.mytable;
|
||||
DROP TABLE mydb.mytablezap;
|
||||
```
|
||||
* 如果用 1G 的 innodb_log_file_size ,假如服务器当机,需要 10 分钟来恢复。
|
||||
* 推荐 innodb_log_file_size 设置为 0.25 * innodb_buffer_pool_size
|
||||
|
||||
## 4 减少磁盘写入操作
|
||||
|
||||
### 4.1 使用足够大的写入缓存 innodb_log_file_size
|
||||
|
||||
但是需要注意如果用 1G 的 innodb_log_file_size ,假如服务器当机,需要 10 分钟来恢复。
|
||||
|
||||
推荐 innodb_log_file_size 设置为 0.25 * innodb_buffer_pool_size
|
||||
|
||||
### 4.2 innodb_flush_log_at_trx_commit
|
||||
### innodb_flush_log_at_trx_commit
|
||||
|
||||
这个选项和写磁盘操作密切相关:
|
||||
|
||||
```sql
|
||||
innodb_flush_log_at_trx_commit = 1 则每次修改写入磁盘
|
||||
innodb_flush_log_at_trx_commit = 0/2 每秒写入磁盘
|
||||
|
||||
如果你的应用不涉及很高的安全性 (金融系统),或者基础架构足够安全,或者 事务都很小,都可以用 0 或者 2 来降低磁盘操作。
|
||||
|
||||
### 4.3 避免双写入缓冲
|
||||
```
|
||||
innodb_flush_method=O_DIRECT
|
||||
```
|
||||
如果你的应用不涉及很高的安全性 (金融系统),或者基础架构足够安全,或者事务都很小,都可以用 0 或者 2 来降低磁盘操作。
|
||||
|
||||
## 5 提高磁盘读写速度
|
||||
|
||||
RAID0 尤其是在使用 EC2 这种虚拟磁盘 (EBS) 的时候,使用软 RAID0 非常重要。
|
||||
|
||||
|
||||
|
||||
## 6 充分使用索引
|
||||
|
||||
### 6.1 查看现有表结构和索引
|
||||
```
|
||||
SHOW CREATE TABLE db1.tb1/G
|
||||
```
|
||||
### 6.2 添加必要的索引
|
||||
|
||||
索引是提高查询速度的唯一方法,比如搜索引擎用的倒排索引是一样的原理。
|
||||
|
||||
索引的添加需要根据查询来确定,比如通过慢查询日志或者查询日志,或者通过 EXPLAIN 命令分析查询。
|
||||
|
||||
```
|
||||
ADD UNIQUE INDEX
|
||||
ADD INDEX
|
||||
```
|
||||
|
||||
### 6.3 实例:优化用户验证表:
|
||||
添加索引
|
||||
|
||||
```
|
||||
ALTER TABLE users ADD UNIQUE INDEX username_ndx (username);
|
||||
ALTER TABLE users ADD UNIQUE INDEX username_password_ndx (username,password);
|
||||
```
|
||||
每次重启服务器进行数据预热
|
||||
```
|
||||
echo “select username,password from users;” > /var/lib/mysql/upcache.sql
|
||||
```
|
||||
添加启动脚本到 my.cnf
|
||||
```
|
||||
[mysqld]
|
||||
init-file=/var/lib/mysql/upcache.sql
|
||||
```
|
||||
### 6.4 实例:使用自动加索引的框架或者自动拆分表结构的框架
|
||||
比如,Rails 这样的框架,会自动添加索引,Drupal 这样的框架会自动拆分表结构。会在你开发的初期指明正确的方向。所以,经验不太丰富的人一开始就追求从 0 开始构建,实际是不好的做法。
|
||||
|
||||
## 7 分析查询日志和慢查询日志
|
||||
修改mysql的慢查询.
|
||||
```
|
||||
show variables like 'long_query_time' ; //可以显示当前慢查询时间
|
||||
set long_query_time=1 ;//可以修改慢查询时间
|
||||
```
|
||||
记录所有查询,这在用 ORM 系统或者生成查询语句的系统很有用。
|
||||
```
|
||||
log=/var/log/mysql.log
|
||||
```
|
||||
注意不要在生产环境用,否则会占满你的磁盘空间。
|
||||
|
||||
记录执行时间超过 1 秒的查询:
|
||||
```
|
||||
long_query_time=1
|
||||
log-slow-queries=/var/log/mysql/log-slow-queries.log
|
||||
```
|
||||
|
||||
### explain查询语句
|
||||
|
||||
Explain select * from emp where ename=“wsrcla”
|
||||
会产生如下信息:
|
||||
select_type:表示查询的类型。
|
||||
table:输出结果集的表
|
||||
type:表示表的连接类型
|
||||
possible_keys:表示查询时,可能使用的索引
|
||||
key:表示实际使用的索引
|
||||
key_len:索引字段的长度
|
||||
rows:扫描出的行数(估算的行数)
|
||||
Extra:执行情况的描述和说明
|
||||
|
||||
## 8 激进的方法,使用内存磁盘
|
||||
|
||||
现在基础设施的可靠性已经非常高了,比如 EC2 几乎不用担心服务器硬件当机。而且内存实在是便宜,很容易买到几十G内存的服务器,可以用内存磁盘,定期备份到磁盘。
|
||||
|
||||
将 MYSQL 目录迁移到 4G 的内存磁盘
|
||||
```
|
||||
mkdir -p /mnt/ramdisk
|
||||
sudo mount -t tmpfs -o size=4000M tmpfs /mnt/ramdisk/
|
||||
mv /var/lib/mysql /mnt/ramdisk/mysql
|
||||
ln -s /tmp/ramdisk/mysql /var/lib/mysql
|
||||
chown mysql:mysql mysql
|
||||
```
|
||||
## 9 用 NOSQL 的方式使用 MYSQL
|
||||
|
||||
B-TREE 仍然是最高效的索引之一,所有 MYSQL 仍然不会过时。
|
||||
|
||||
用 HandlerSocket 跳过 MYSQL 的 SQL 解析层,MYSQL 就真正变成了 NOSQL。
|
||||
|
||||
## 10 其他
|
||||
|
||||
1. 单条查询最后增加 LIMIT 1,停止全表扫描。
|
||||
2. 将非”索引”数据分离,比如将大篇文章分离存储,不影响其他自动查询。
|
||||
3. 不用 MYSQL 内置的函数,因为内置函数不会建立查询缓存。
|
||||
4. PHP 的建立连接速度非常快,所有可以不用连接池,否则可能会造成超过连接数。当然不用连接池 PHP 程序也可能将
|
||||
5. 连接数占满比如用了 @ignore_user_abort(TRUE);
|
||||
6. 使用 IP 而不是域名做数据库路径,避免 DNS 解析问题
|
||||
## 3.3 提高磁盘读取速度
|
||||
|
||||
|
||||
|
||||
## 4 其他原则
|
||||
|
||||
”建立合理索引”(什么样的索引合理?) “
|
||||
分表分库”(用什么策略分表分库?)
|
||||
“主从分离”(用什么中间件?)
|
||||
1. 不在数据库做运算:cpu计算务必移至业务层
|
||||
1. 控制单表数据量:单表记录控制在1000w
|
||||
1. 控制列数量:字段数控制在20以内
|
||||
1. 平衡范式与冗余:为提高效率牺牲范式设计,冗余数据
|
||||
1. 拒绝3B:拒绝大sql,大事物,大批量
|
||||
@@ -125,9 +125,27 @@ CREATE FULLTEXT INDEX index_content ON article(content)
|
||||
```
|
||||
|
||||
### 覆盖索引
|
||||
索引包含所有需要查询的字段的值。
|
||||
|
||||
* 覆盖索引,select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖。
|
||||
* 这个时候,可以不用访问指定的数据。也就不需要回表查询具体的数据,通过索引即可得到想要的数据。
|
||||
|
||||
|
||||
|
||||
具有以下优点:
|
||||
|
||||
- 索引通常远小于数据行的大小,只读取索引能大大减少数据访问量。
|
||||
- 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
|
||||
- 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。
|
||||
|
||||
|
||||
### 索引优点
|
||||
|
||||
- 大大减少了服务器需要扫描的数据行数。
|
||||
|
||||
- 帮助服务器避免进行排序和分组,以及避免创建临时表(B+Tree 索引是有序的,可以用于 ORDER BY 和 GROUP BY 操作。临时表主要是在排序和分组过程中创建,不需要排序和分组,也就不需要创建临时表)。
|
||||
|
||||
- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,会将相邻的数据都存储在一起)。
|
||||
### 索引缺点
|
||||
|
||||
1. 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行insert、update和delete。因为更新表时,不仅要保存数据,还要保存一下索引文件。
|
||||
@@ -183,4 +201,9 @@ select username, score from t1 where username = '小明'
|
||||
### 前缀索引
|
||||
* tire索引/前缀索引:用来索引字符串。
|
||||
|
||||
> 特点:索引能够加快数据库查询,需要占用一定的存储空间。基本表更新后,索引也需要更新。用户不必显示地选择索引,关系数据库管理系统在执行查询时,会自动选择合适的索引作为存储路径。
|
||||
|
||||
> 特点:索引能够加快数据库查询,需要占用一定的存储空间。基本表更新后,索引也需要更新。用户不必显示地选择索引,关系数据库管理系统在执行查询时,会自动选择合适的索引作为存储路径。
|
||||
|
||||
|
||||
> MySQL中常用的索引结构有:B+树索引和哈希索引两种。目前建表用的B+树索引就是BTREE索引。
|
||||
> 在MySQL中,MyISAM和InnoDB两种存储引擎都不支持哈希索引。只有HEAP/MEMORY引擎才能显示支持哈希索引。
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# JOIN连接查询
|
||||
# 5 JOIN连接查询
|
||||
|
||||
> 参考文献
|
||||
> * [MySQL的JOIN用法](https://www.cnblogs.com/hcfinal/p/10231694.html)
|
||||
|
||||
@@ -1,360 +1,41 @@
|
||||
# MySQL
|
||||
<!-- GFM-TOC -->
|
||||
* [MySQL](#mysql)
|
||||
* [一、索引](#一索引)
|
||||
* [B+ Tree 原理](#b-tree-原理)
|
||||
* [MySQL 索引](#mysql-索引)
|
||||
* [索引优化](#索引优化)
|
||||
* [索引的优点](#索引的优点)
|
||||
* [索引的使用条件](#索引的使用条件)
|
||||
* [二、查询性能优化](#二查询性能优化)
|
||||
* [使用 Explain 进行分析](#使用-explain-进行分析)
|
||||
* [优化数据访问](#优化数据访问)
|
||||
* [重构查询方式](#重构查询方式)
|
||||
* [三、存储引擎](#三存储引擎)
|
||||
* [InnoDB](#innodb)
|
||||
* [MyISAM](#myisam)
|
||||
* [比较](#比较)
|
||||
* [四、数据类型](#四数据类型)
|
||||
* [整型](#整型)
|
||||
* [浮点数](#浮点数)
|
||||
* [字符串](#字符串)
|
||||
* [时间和日期](#时间和日期)
|
||||
* [五、切分](#五切分)
|
||||
* [水平切分](#水平切分)
|
||||
* [垂直切分](#垂直切分)
|
||||
* [Sharding 策略](#sharding-策略)
|
||||
* [Sharding 存在的问题](#sharding-存在的问题)
|
||||
* [六、复制](#六复制)
|
||||
* [主从复制](#主从复制)
|
||||
* [读写分离](#读写分离)
|
||||
* [参考资料](#参考资料)
|
||||
<!-- GFM-TOC -->
|
||||
# MySQL分库分表和主从分离
|
||||
|
||||
## 1 概述
|
||||
|
||||
## 一、索引
|
||||
### 分库分表的原因
|
||||
|
||||
### B+ Tree 原理
|
||||
当一张表的数据达到几千万时,查询一次所花的时间会变多,如果有联合查询的话,有可能会死在那儿了。分表的目的就在于此,减小数据库的负担,缩短查询时间。
|
||||
* 用户请求量太大。因为单服务器TPS,内存,IO都是有限的。解决方法:分散请求到多个服务器上; 其实用户请求和执行一个sql查询是本质是一样的,都是请求一个资源,只是用户请求还会经过网关,路由,http服务器等。
|
||||
|
||||
#### 1. 数据结构
|
||||
* 单库太大。单个数据库处理能力有限;单库所在服务器上磁盘空间不足;单库上操作的IO瓶颈 解决方法:切分成更多更小的库
|
||||
|
||||
B Tree 指的是 Balance Tree,也就是平衡树。平衡树是一颗查找树,并且所有叶子节点位于同一层。
|
||||
* 单表太大。CRUD都成问题;索引膨胀,查询超时。解决方法:切分成多个数据集更小的表。
|
||||
|
||||
B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具有 B Tree 的平衡性,并且通过顺序访问指针来提高区间查询的性能。
|
||||
### 分库分表的形式
|
||||
|
||||
在 B+ Tree 中,一个节点中的 key 从左到右非递减排列,如果某个指针的左右相邻 key 分别是 key<sub>i</sub> 和 key<sub>i+1</sub>,且不为 null,则该指针指向节点的所有 key 大于等于 key<sub>i</sub> 且小于等于 key<sub>i+1</sub>。
|
||||
* 单库单表。单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/33576849-9275-47bb-ada7-8ded5f5e7c73.png" width="350px"> </div><br>
|
||||
* 单库多表。随着用户数量的增加,user表的数据量会越来越大,当数据量达到一定程度的时候对user表的查询会渐渐的变慢,从而影响整个DB的性能。如果使用mysql, 还有一个更严重的问题是,当需要添加一列的时候,mysql会锁表,期间所有的读写操作只能等待。可以通过某种方式将user进行水平的切分,产生两个表结构完全一样的user_0000,user_0001等表,user_0000 + user_0001 + …的数据刚好是一份完整的数据。
|
||||
|
||||
#### 2. 操作
|
||||
* 多库多表。随着数据量增加也许单台DB的存储空间不够,随着查询量的增加单台数据库服务器已经没办法支撑。这个时候可以再对数据库进行水平区分。
|
||||
|
||||
进行查找操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。
|
||||
|
||||
插入删除操作会破坏平衡树的平衡性,因此在进行插入删除操作之后,需要对树进行分裂、合并、旋转等操作来维护平衡性。
|
||||
|
||||
#### 3. 与红黑树的比较
|
||||
|
||||
红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+ Tree 作为索引结构,这是因为使用 B+ 树访问磁盘数据有更高的性能。
|
||||
|
||||
(一)B+ 树有更低的树高
|
||||
|
||||
平衡树的树高 O(h)=O(log<sub>d</sub>N),其中 d 为每个节点的出度。红黑树的出度为 2,而 B+ Tree 的出度一般都非常大,所以红黑树的树高 h 很明显比 B+ Tree 大非常多。
|
||||
|
||||
(二)磁盘访问原理
|
||||
|
||||
操作系统一般将内存和磁盘分割成固定大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点。
|
||||
|
||||
如果数据不在同一个磁盘块上,那么通常需要移动制动手臂进行寻道,而制动手臂因为其物理结构导致了移动效率低下,从而增加磁盘数据读取时间。B+ 树相对于红黑树有更低的树高,进行寻道的次数与树高成正比,在同一个磁盘块上进行访问只需要很短的磁盘旋转时间,所以 B+ 树更适合磁盘数据的读取。
|
||||
|
||||
(三)磁盘预读特性
|
||||
|
||||
为了减少磁盘 I/O 操作,磁盘往往不是严格按需读取,而是每次都会预读。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的磁盘旋转时间,速度会非常快。并且可以利用预读特性,相邻的节点也能够被预先载入。
|
||||
|
||||
### MySQL 索引
|
||||
|
||||
索引是在存储引擎层实现的,而不是在服务器层实现的,所以不同存储引擎具有不同的索引类型和实现。
|
||||
|
||||
#### 1. B+Tree 索引
|
||||
|
||||
是大多数 MySQL 存储引擎的默认索引类型。
|
||||
|
||||
因为不再需要进行全表扫描,只需要对树进行搜索即可,所以查找速度快很多。
|
||||
|
||||
因为 B+ Tree 的有序性,所以除了用于查找,还可以用于排序和分组。
|
||||
|
||||
可以指定多个列作为索引列,多个索引列共同组成键。
|
||||
|
||||
适用于全键值、键值范围和键前缀查找,其中键前缀查找只适用于最左前缀查找。如果不是按照索引列的顺序进行查找,则无法使用索引。
|
||||
|
||||
InnoDB 的 B+Tree 索引分为主索引和辅助索引。主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引。因为无法把数据行存放在两个不同的地方,所以一个表只能有一个聚簇索引。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/45016e98-6879-4709-8569-262b2d6d60b9.png" width="350px"> </div><br>
|
||||
|
||||
辅助索引的叶子节点的 data 域记录着主键的值,因此在使用辅助索引进行查找时,需要先查找到主键值,然后再到主索引中进行查找。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/7c349b91-050b-4d72-a7f8-ec86320307ea.png" width="350px"> </div><br>
|
||||
|
||||
#### 2. 哈希索引
|
||||
|
||||
哈希索引能以 O(1) 时间进行查找,但是失去了有序性:
|
||||
|
||||
- 无法用于排序与分组;
|
||||
- 只支持精确查找,无法用于部分查找和范围查找。
|
||||
|
||||
InnoDB 存储引擎有一个特殊的功能叫“自适应哈希索引”,当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引,这样就让 B+Tree 索引具有哈希索引的一些优点,比如快速的哈希查找。
|
||||
|
||||
#### 3. 全文索引
|
||||
|
||||
MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较是否相等。
|
||||
|
||||
查找条件使用 MATCH AGAINST,而不是普通的 WHERE。
|
||||
|
||||
全文索引使用倒排索引实现,它记录着关键词到其所在文档的映射。
|
||||
|
||||
InnoDB 存储引擎在 MySQL 5.6.4 版本中也开始支持全文索引。
|
||||
|
||||
#### 4. 空间数据索引
|
||||
|
||||
MyISAM 存储引擎支持空间数据索引(R-Tree),可以用于地理数据存储。空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。
|
||||
|
||||
必须使用 GIS 相关的函数来维护数据。
|
||||
|
||||
### 索引优化
|
||||
|
||||
#### 1. 独立的列
|
||||
|
||||
在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。
|
||||
|
||||
例如下面的查询不能使用 actor_id 列的索引:
|
||||
|
||||
```sql
|
||||
SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;
|
||||
```
|
||||
|
||||
#### 2. 多列索引
|
||||
|
||||
在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。
|
||||
|
||||
```sql
|
||||
SELECT film_id, actor_ id FROM sakila.film_actor
|
||||
WHERE actor_id = 1 AND film_id = 1;
|
||||
```
|
||||
|
||||
#### 3. 索引列的顺序
|
||||
|
||||
让选择性最强的索引列放在前面。
|
||||
|
||||
索引的选择性是指:不重复的索引值和记录总数的比值。最大值为 1,此时每个记录都有唯一的索引与其对应。选择性越高,每个记录的区分度越高,查询效率也越高。
|
||||
|
||||
例如下面显示的结果中 customer_id 的选择性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。
|
||||
|
||||
```sql
|
||||
SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
|
||||
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
|
||||
COUNT(*)
|
||||
FROM payment;
|
||||
```
|
||||
|
||||
```html
|
||||
staff_id_selectivity: 0.0001
|
||||
customer_id_selectivity: 0.0373
|
||||
COUNT(*): 16049
|
||||
```
|
||||
|
||||
#### 4. 前缀索引
|
||||
|
||||
对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符。
|
||||
|
||||
前缀长度的选取需要根据索引选择性来确定。
|
||||
|
||||
#### 5. 覆盖索引
|
||||
|
||||
索引包含所有需要查询的字段的值。
|
||||
|
||||
具有以下优点:
|
||||
|
||||
- 索引通常远小于数据行的大小,只读取索引能大大减少数据访问量。
|
||||
- 一些存储引擎(例如 MyISAM)在内存中只缓存索引,而数据依赖于操作系统来缓存。因此,只访问索引可以不使用系统调用(通常比较费时)。
|
||||
- 对于 InnoDB 引擎,若辅助索引能够覆盖查询,则无需访问主索引。
|
||||
|
||||
### 索引的优点
|
||||
|
||||
- 大大减少了服务器需要扫描的数据行数。
|
||||
|
||||
- 帮助服务器避免进行排序和分组,以及避免创建临时表(B+Tree 索引是有序的,可以用于 ORDER BY 和 GROUP BY 操作。临时表主要是在排序和分组过程中创建,不需要排序和分组,也就不需要创建临时表)。
|
||||
|
||||
- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,会将相邻的数据都存储在一起)。
|
||||
|
||||
### 索引的使用条件
|
||||
|
||||
- 对于非常小的表、大部分情况下简单的全表扫描比建立索引更高效;
|
||||
|
||||
- 对于中到大型的表,索引就非常有效;
|
||||
|
||||
- 但是对于特大型的表,建立和维护索引的代价将会随之增长。这种情况下,需要用到一种技术可以直接区分出需要查询的一组数据,而不是一条记录一条记录地匹配,例如可以使用分区技术。
|
||||
|
||||
## 二、查询性能优化
|
||||
|
||||
### 使用 Explain 进行分析
|
||||
|
||||
Explain 用来分析 SELECT 查询语句,开发人员可以通过分析 Explain 结果来优化查询语句。
|
||||
|
||||
比较重要的字段有:
|
||||
|
||||
- select_type : 查询类型,有简单查询、联合查询、子查询等
|
||||
- key : 使用的索引
|
||||
- rows : 扫描的行数
|
||||
|
||||
### 优化数据访问
|
||||
|
||||
#### 1. 减少请求的数据量
|
||||
|
||||
- 只返回必要的列:最好不要使用 SELECT * 语句。
|
||||
- 只返回必要的行:使用 LIMIT 语句来限制返回的数据。
|
||||
- 缓存重复查询的数据:使用缓存可以避免在数据库中进行查询,特别在要查询的数据经常被重复查询时,缓存带来的查询性能提升将会是非常明显的。
|
||||
|
||||
#### 2. 减少服务器端扫描的行数
|
||||
|
||||
最有效的方式是使用索引来覆盖查询。
|
||||
|
||||
### 重构查询方式
|
||||
|
||||
#### 1. 切分大查询
|
||||
|
||||
一个大查询如果一次性执行的话,可能一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小的但重要的查询。
|
||||
|
||||
```sql
|
||||
DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH);
|
||||
```
|
||||
|
||||
```sql
|
||||
rows_affected = 0
|
||||
do {
|
||||
rows_affected = do_query(
|
||||
"DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000")
|
||||
} while rows_affected > 0
|
||||
```
|
||||
|
||||
#### 2. 分解大连接查询
|
||||
|
||||
将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联,这样做的好处有:
|
||||
|
||||
- 让缓存更高效。对于连接查询,如果其中一个表发生变化,那么整个查询缓存就无法使用。而分解后的多个查询,即使其中一个表发生变化,对其它表的查询缓存依然可以使用。
|
||||
- 分解成多个单表查询,这些单表查询的缓存结果更可能被其它查询使用到,从而减少冗余记录的查询。
|
||||
- 减少锁竞争;
|
||||
- 在应用层进行连接,可以更容易对数据库进行拆分,从而更容易做到高性能和可伸缩。
|
||||
- 查询本身效率也可能会有所提升。例如下面的例子中,使用 IN() 代替连接查询,可以让 MySQL 按照 ID 顺序进行查询,这可能比随机的连接要更高效。
|
||||
|
||||
```sql
|
||||
SELECT * FROM tag
|
||||
JOIN tag_post ON tag_post.tag_id=tag.id
|
||||
JOIN post ON tag_post.post_id=post.id
|
||||
WHERE tag.tag='mysql';
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT * FROM tag WHERE tag='mysql';
|
||||
SELECT * FROM tag_post WHERE tag_id=1234;
|
||||
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);
|
||||
```
|
||||
|
||||
## 三、存储引擎
|
||||
|
||||
### InnoDB
|
||||
|
||||
是 MySQL 默认的事务型存储引擎,只有在需要它不支持的特性时,才考虑使用其它存储引擎。
|
||||
|
||||
实现了四个标准的隔离级别,默认级别是可重复读(REPEATABLE READ)。在可重复读隔离级别下,通过多版本并发控制(MVCC)+ Next-Key Locking 防止幻影读。
|
||||
|
||||
主索引是聚簇索引,在索引中保存了数据,从而避免直接读取磁盘,因此对查询性能有很大的提升。
|
||||
|
||||
内部做了很多优化,包括从磁盘读取数据时采用的可预测性读、能够加快读操作并且自动创建的自适应哈希索引、能够加速插入操作的插入缓冲区等。
|
||||
|
||||
支持真正的在线热备份。其它存储引擎不支持在线热备份,要获取一致性视图需要停止对所有表的写入,而在读写混合场景中,停止写入可能也意味着停止读取。
|
||||
|
||||
### MyISAM
|
||||
|
||||
设计简单,数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,则依然可以使用它。
|
||||
|
||||
提供了大量的特性,包括压缩表、空间数据索引等。
|
||||
|
||||
不支持事务。
|
||||
|
||||
不支持行级锁,只能对整张表加锁,读取时会对需要读到的所有表加共享锁,写入时则对表加排它锁。但在表有读取操作的同时,也可以往表中插入新的记录,这被称为并发插入(CONCURRENT INSERT)。
|
||||
|
||||
可以手工或者自动执行检查和修复操作,但是和事务恢复以及崩溃恢复不同,可能导致一些数据丢失,而且修复操作是非常慢的。
|
||||
|
||||
如果指定了 DELAY_KEY_WRITE 选项,在每次修改执行完成时,不会立即将修改的索引数据写入磁盘,而是会写到内存中的键缓冲区,只有在清理键缓冲区或者关闭表的时候才会将对应的索引块写入磁盘。这种方式可以极大的提升写入性能,但是在数据库或者主机崩溃时会造成索引损坏,需要执行修复操作。
|
||||
|
||||
### 比较
|
||||
|
||||
- 事务:InnoDB 是事务型的,可以使用 Commit 和 Rollback 语句。
|
||||
|
||||
- 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。
|
||||
|
||||
- 外键:InnoDB 支持外键。
|
||||
|
||||
- 备份:InnoDB 支持在线热备份。
|
||||
|
||||
- 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。
|
||||
|
||||
- 其它特性:MyISAM 支持压缩表和空间数据索引。
|
||||
|
||||
## 四、数据类型
|
||||
|
||||
### 整型
|
||||
|
||||
TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,一般情况下越小的列越好。
|
||||
|
||||
INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。
|
||||
|
||||
### 浮点数
|
||||
|
||||
FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。
|
||||
|
||||
FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。
|
||||
|
||||
### 字符串
|
||||
|
||||
主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。
|
||||
|
||||
VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内容。但是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作。MyISAM 会将行拆成不同的片段存储,而 InnoDB 则需要分裂页来使行放进页内。
|
||||
|
||||
在进行存储和检索时,会保留 VARCHAR 末尾的空格,而会删除 CHAR 末尾的空格。
|
||||
|
||||
### 时间和日期
|
||||
|
||||
MySQL 提供了两种相似的日期时间类型:DATETIME 和 TIMESTAMP。
|
||||
|
||||
#### 1. DATETIME
|
||||
|
||||
能够保存从 1000 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。
|
||||
|
||||
它与时区无关。
|
||||
|
||||
默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATETIME 值,例如“2008-01-16 22\<span\>:\</span\>37\<span\>:\</span\>08”,这是 ANSI 标准定义的日期和时间表示方法。
|
||||
|
||||
#### 2. TIMESTAMP
|
||||
|
||||
和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年到 2038 年。
|
||||
|
||||
它和时区有关,也就是说一个时间戳在不同的时区所代表的具体时间是不同的。
|
||||
|
||||
MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。
|
||||
|
||||
默认情况下,如果插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。
|
||||
|
||||
应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。
|
||||
|
||||
## 五、切分
|
||||
## 2 分库分表
|
||||
|
||||
### 水平切分
|
||||
|
||||
水平切分又称为 Sharding,它是将同一个表中的记录拆分到多个结构相同的表中。
|
||||
* 水平分库:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库。
|
||||
* 水平分表:系统绝对并发量并没有上来,只是单表的数据量太多,影响了SQL效率,加重了CPU负担,以至于成为瓶颈。
|
||||
|
||||
|
||||
当一个表的数据不断增多时,Sharding 是必然的选择,它可以将数据分布到集群的不同节点上,从而缓存单个数据库的压力。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/63c2909f-0c5f-496f-9fe5-ee9176b31aba.jpg" width=""> </div><br>
|
||||

|
||||
|
||||
* 主要特点
|
||||
* 每个库的结构都一样;
|
||||
* 每个库的数据都不一样,没有交集;
|
||||
* 所有库的并集是全量数据;
|
||||
|
||||
### 垂直切分
|
||||
|
||||
@@ -362,66 +43,104 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提
|
||||
|
||||
在数据库的层面使用垂直切分将按数据库中表的密集程度部署到不同的库中,例如将原来的电商数据库垂直切分成商品数据库、用户数据库等。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/e130e5b8-b19a-4f1e-b860-223040525cf6.jpg" width=""> </div><br>
|
||||
如果把业务切割得足够独立,那把不同业务的数据放到不同的数据库服务器将是一个不错的方案,而且万一其中一个业务崩溃了也不会影响其他业务的正常进行,并且也起到了负载分流的作用,大大提升了数据库的吞吐能力。
|
||||
* 垂直分库:系统绝对并发量上来了,并且可以抽象出单独的业务模块。
|
||||
* 垂直分表:系统绝对并发量并没有上来,表的记录并不多,但是字段多,并且热点数据和非热点数据在一起,单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,查询时会去读磁盘数据产生大量的随机读IO,产生IO瓶颈。
|
||||

|
||||
|
||||
* 主要特点
|
||||
* 每个库的结构都不一样;
|
||||
* 每个库的数据也不一样,没有交集;
|
||||
* 所有库的并集是全量数据;
|
||||
|
||||
### Sharding 策略
|
||||
|
||||
- 哈希取模:hash(key) % N;
|
||||
- 范围:可以是 ID 范围也可以是时间范围;
|
||||
- 映射法:哈希取模hash(key) % N。Snowflake算法。
|
||||
- HASH取模方法优点: 能保证数据较均匀的分散落在不同的库、表中,减轻了数据库压力
|
||||
- HASH取模方法缺点: 扩容麻烦、迁移数据时每次都需要重新计算hash值分配到不同的库和表
|
||||
|
||||
- 范围法:可以是 ID 范围、时间范围、地理范围。
|
||||
- RANGE方法优点: 扩容简单,提前建好库、表就好
|
||||
- RANGE方法缺点: 大部分读和写都访会问新的数据,有IO瓶颈,这样子造成新库压力过大,不建议采用。
|
||||
|
||||
- 映射表:使用单独的一个数据库来存储映射关系。
|
||||
|
||||
### Sharding 工具
|
||||
|
||||
* sharding-sphere:jar,前身是sharding-jdbc;
|
||||
* TDDL:jar,Taobao Distribute Data Layer;
|
||||
* Mycat:中间件。
|
||||
|
||||
|
||||
### Sharding 存在的问题
|
||||
|
||||
#### 1. 事务问题
|
||||
1. 事务问题。使用分布式事务来解决,比如 XA 接口。
|
||||
|
||||
使用分布式事务来解决,比如 XA 接口。
|
||||
2. 连接。可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。
|
||||
|
||||
#### 2. 连接
|
||||
3. ID 唯一性
|
||||
- 使用全局唯一 ID(GUID)
|
||||
- 为每个分片指定一个 ID 范围。
|
||||
- 分布式 ID 生成器能够计算出sharding ID
|
||||
|
||||
可以将原来的连接分解成多个单表查询,然后在用户程序中进行连接。
|
||||
|
||||
#### 3. ID 唯一性
|
||||
4. 维度的问题。假如用户购买了商品,需要将交易记录保存取来,如果按照用户的纬度分表,则每个用户的交易记录都保存在同一表中,所以很快很方便的查找到某用户的 购买情况,但是某商品被购买的情况则很有可能分布在多张表中,查找起来比较麻烦。反之,按照商品维度分表,可以很方便的查找到此商品的购买情况,但要查找 到买人的交易记录比较麻烦。所以常见的解决方式有:
|
||||
* 通过扫表的方式解决,此方法基本不可能,效率太低了。
|
||||
* 记录两份数据,一份按照用户纬度分表,一份按照商品维度分表。
|
||||
* 通过搜索引擎解决,但如果实时性要求很高,又得关系到实时搜索。
|
||||
|
||||
- 使用全局唯一 ID(GUID)
|
||||
- 为每个分片指定一个 ID 范围
|
||||
- 分布式 ID 生成器 (如 Twitter 的 Snowflake 算法)
|
||||
### 分库分表时机
|
||||
|
||||
## 六、复制
|
||||
单表1000万条记录一下,写入读取性能是比较好的. 这样在留点buffer,那么单表全是数据字型的保持在800万条记录以下, 有字符型的单表保持在500万以下。
|
||||
|
||||
### 主从复制
|
||||
|
||||
主要涉及三个线程:binlog 线程、I/O 线程和 SQL 线程。
|
||||
不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值。在业务Service来看就是,可用数据库连接少甚至无连接可用。
|
||||
|
||||
- **binlog 线程** :负责将主服务器上的数据更改写入二进制日志(Binary log)中。
|
||||
- **I/O 线程** :负责从主服务器上读取二进制日志,并写入从服务器的中继日志(Relay log)。
|
||||
- **SQL 线程** :负责读取中继日志,解析出主服务器已经执行的数据更改并在从服务器中重放(Replay)。
|
||||
* IO 瓶颈
|
||||
* 第一种:磁盘读IO瓶颈,热点数据太多,数据库缓存放不下,每次查询时会产生大量的IO,降低查询速度 -> 分库和垂直分表。
|
||||
* 第二种:网络IO瓶颈,请求的数据太多,网络带宽不够 -> 分库。
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/master-slave.png" width=""> </div><br>
|
||||
* CPU瓶颈
|
||||
* 第一种:SQL问题,如SQL中包含join,group by,order by,非索引字段条件查询等,增加CPU运算的操作 -> SQL优化,建立合适的索引,在业务Service层进行业务计算。
|
||||
* 第二种:单表数据量太大,查询时扫描的行太多,SQL效率低,CPU率先出现瓶颈 -> 水平分表。
|
||||
|
||||
### 读写分离
|
||||
|
||||
主服务器处理写操作以及实时性要求比较高的读操作,而从服务器处理读操作。
|
||||
## 3 主从同步
|
||||
|
||||
读写分离能提高性能的原因在于:
|
||||
### 主从分离
|
||||
将数据库的写操作和读操作进行分离, 使用多个从库副本(Slaver Replication)负责读,使用主库(Master)负责写, 从库从主库同步更新数据,保持数据一致。架构上就是数据库主从同步。 从库可以水平扩展,所以更多的读请求不成问题。通过此方式可以有效的提高DB集群的 QPS.
|
||||
|
||||
- 主从服务器负责各自的读和写,极大程度缓解了锁的争用;
|
||||
- 从服务器可以使用 MyISAM,提升查询性能以及节约系统开销;
|
||||
- 增加冗余,提高可用性。
|
||||
1. **读写分离**:在从服务器可以执行查询工作,降低主服务器压力;(主库写,从库读,降压)读写分离
|
||||
2. **备份容灾**:在从主服务器进行备份,避免备份期间影响主服务器服务;容灾。可以作为一种备份机制,相当于热备份(在从备份,避免备份期间影响主服务器服务)
|
||||
3. **提高可用性**:当主服务器出现问题时,可以切换到从服务器。提高可用性
|
||||
|
||||
读写分离常用代理方式来实现,代理服务器接收应用层传来的读写请求,然后决定转发到哪个服务器。
|
||||
### 主从结构
|
||||
|
||||
<div align="center"> <img src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/master-slave-proxy.png" width=""> </div><br>
|
||||
1. 一主多从
|
||||
2. 一主一从
|
||||
3. 主主结构(互为主从,相互可写)
|
||||
|
||||
## 参考资料
|
||||
### 主从同步过程
|
||||
Mysql服务器之间的主从同步是基于二进制日志BINLOG机制,主服务器使用二进制日志来记录数据库的变动情况,从服务器通过读取和执行该日志文件来保持和主服务器的数据一致。
|
||||

|
||||

|
||||
|
||||
- BaronScbwartz, PeterZaitsev, VadimTkacbenko, 等. 高性能 MySQL[M]. 电子工业出版社, 2013.
|
||||
- 姜承尧. MySQL 技术内幕: InnoDB 存储引擎 [M]. 机械工业出版社, 2011.
|
||||
- [20+ 条 MySQL 性能优化的最佳经验](https://www.jfox.info/20-tiao-mysql-xing-nen-you-hua-de-zui-jia-jing-yan.html)
|
||||
- [服务端指南 数据存储篇 | MySQL(09) 分库与分表带来的分布式困境与应对之策](http://blog.720ui.com/2017/mysql_core_09_multi_db_table2/ "服务端指南 数据存储篇 | MySQL(09) 分库与分表带来的分布式困境与应对之策")
|
||||
- [How to create unique row ID in sharded databases?](https://stackoverflow.com/questions/788829/how-to-create-unique-row-id-in-sharded-databases)
|
||||
- [SQL Azure Federation – Introduction](http://geekswithblogs.net/shaunxu/archive/2012/01/07/sql-azure-federation-ndash-introduction.aspx "Title of this entry.")
|
||||
- [MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html)
|
||||
- [MySQL 性能优化神器 Explain 使用分析](https://segmentfault.com/a/1190000008131735)
|
||||
- [How Sharding Works](https://medium.com/@jeeyoungk/how-sharding-works-b4dec46b3f6)
|
||||
- [大众点评订单系统分库分表实践](https://tech.meituan.com/dianping_order_db_sharding.html)
|
||||
- [B + 树](https://zh.wikipedia.org/wiki/B%2B%E6%A0%91)
|
||||
1. SQL语句操作变化存入BinLog日志中
|
||||
2. slave上线,连接到master服务器,进行数据同步
|
||||
3. dump thread线程把Binlog数据发送到slave中
|
||||
4. slave启动之后,创建一个I/O线程,读取master传过来的Binlog内容并写入到Relay log.
|
||||
5. slave还会创建一个SQL线程,从Relay log里面读取内容,从Exec_Master_Log_Pos位置开始执行读取到的更新事件,将更新内容写入到slave的db.
|
||||
|
||||
|
||||
### 主从同步方法
|
||||
|
||||

|
||||
|
||||
1. 异步复制。MySQL 默认的复制策略,Master处理事务过程中,将其写入Binlog就会通知Dump thread线程处理,然后完成事务的提交,不会关心是否成功发送到任意一个slave中
|
||||

|
||||
2. 半同步复制。Master处理事务过程中,提交完事务后,必须等至少一个Slave将收到的binlog写入relay log返回ack才能继续执行处理用户的事务。
|
||||

|
||||
3. 增强半同步复制。半同步的问题是因为等待ACK的点是Commit之后,此时Master已经完成数据变更,用户已经可以看到最新数据,当Binlog还未同步到Slave时,发生主从切换,那么此时从库是没有这个最新数据的,用户又看到老数据。增强半同步将等待ACK的点放在提交Commit之前,此时数据还未被提交,外界看不到数据变更,此时如果发送主从切换,新库依然还是老数据,不存在数据不一致的问题。
|
||||

|
||||
4. 组复制。MySQL在引擎层完成Prepare操作写Redo日志之后,会被MySQL的预设Hook拦截进入MGR层。MGR层将事务信息打包通过Paxos协议发送到全部节点上,只要集群中过半节点回复ACK,那么将告诉所有节点数据包同步成功,然后每个节点开始自己认证(certify)通过就开始写Binlog,提交事务或者写relay log,数据同步,如果认证不通过则rollback。总结:MGR内部实现了分布式数据一致性协议,paxos通过其来保证数据一致性。
|
||||

|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# MySQL数据类型
|
||||
|
||||
### 整型
|
||||
|
||||
TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间,一般情况下越小的列越好。
|
||||
|
||||
INT(11) 中的数字只是规定了交互工具显示字符的个数,对于存储和计算来说是没有意义的。
|
||||
|
||||
### 浮点数
|
||||
|
||||
FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型。CPU 原生支持浮点运算,但是不支持 DECIMAl 类型的计算,因此 DECIMAL 的计算比浮点类型需要更高的代价。
|
||||
|
||||
FLOAT、DOUBLE 和 DECIMAL 都可以指定列宽,例如 DECIMAL(18, 9) 表示总共 18 位,取 9 位存储小数部分,剩下 9 位存储整数部分。
|
||||
|
||||
### 字符串
|
||||
|
||||
主要有 CHAR 和 VARCHAR 两种类型,一种是定长的,一种是变长的。
|
||||
|
||||
VARCHAR 这种变长类型能够节省空间,因为只需要存储必要的内容。但是在执行 UPDATE 时可能会使行变得比原来长,当超出一个页所能容纳的大小时,就要执行额外的操作。MyISAM 会将行拆成不同的片段存储,而 InnoDB 则需要分裂页来使行放进页内。
|
||||
|
||||
在进行存储和检索时,会保留 VARCHAR 末尾的空格,而会删除 CHAR 末尾的空格。
|
||||
|
||||
### 时间和日期
|
||||
|
||||
MySQL 提供了两种相似的日期时间类型:DATETIME 和 TIMESTAMP。
|
||||
|
||||
#### 1. DATETIME
|
||||
|
||||
能够保存从 1000 年到 9999 年的日期和时间,精度为秒,使用 8 字节的存储空间。
|
||||
|
||||
它与时区无关。
|
||||
|
||||
默认情况下,MySQL 以一种可排序的、无歧义的格式显示 DATETIME 值,例如“2008-01-16 22\<span\>:\</span\>37\<span\>:\</span\>08”,这是 ANSI 标准定义的日期和时间表示方法。
|
||||
|
||||
#### 2. TIMESTAMP
|
||||
|
||||
和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年到 2038 年。
|
||||
|
||||
它和时区有关,也就是说一个时间戳在不同的时区所代表的具体时间是不同的。
|
||||
|
||||
MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。
|
||||
|
||||
默认情况下,如果插入时没有指定 TIMESTAMP 列的值,会将这个值设置为当前时间。
|
||||
|
||||
应该尽量使用 TIMESTAMP,因为它比 DATETIME 空间效率更高。
|
||||