Professional Documents
Culture Documents
1-3 Mysql优化
1-3 Mysql优化
1 要保证数据库本身的效率,要做以下几方面的工作:
1.1 数据库(表)设计要合理:
1.1.1 符合三范式
1.1.2 合理的逆范式:如果需要通过在一个海量记录的表中查询某字段(比如求和) ,
可以在另一个记录较少的表中设计该字段,即产生了冗余,但加快了查询速度。
1.2 Sql 语句性能判断命令:
1.2.1 show status like:了解各种 SQL 的执行频率。
1.2.2 慢查询日志:开启慢查询日志来定位执行效率低的 SQL 语句。
1.2.3 Explain:通过 explain 分析执行效率低的 SQL 语句。
1.3 sql 语句优化
1.3.1 索引和索引规则
1.3.2 定时清楚不需要的数据,以及定期 optimize 来清理 MyISAM 存储引擎表的空间
(碎片整理)。
1.3.3 小技巧:
1.3.3.1 大批量 INSERT 数据时的临时处理。
1.3.3.2 避免 group by 语句排序(order by null)。
1.3.3.3 尽量使用 join 替代使用子查询。
1.3.3.4 定点数比浮点数精度更高。
1.3.3.5 日期尽量使用时间戳。
1.3.3.6 字段类型尽量设置最小大小的类型。
1.3.4 表的水平分割:针对海量记录表通过取模方式来水平分割表。
1.3.5 表的垂直分割:针对的内容多却补偿查询的字段,将其分开存在单独一个表,
以加快原表的查询速度。
1.4 对 MySQL 数据库的配置进行优化(my.ini):
1.4.1 调整缓存内存大小(MyISAM:key_buffer_size;Innodb:
Innodb_additional_mem_pool_size 和 Innodb_buffer_pool_size)
1.4.2 调整最大并发数(max_connections)
1.5 读写分离(Amoeba 服务)
1.6 硬件升级:
1.6.1 64 位:如果计算机是 64 位的,内存超过 4G,那毋庸置疑应该参数 64 位操作系
统和 64 位的 MySQL,不然 MySQL 无法发挥更大的威力。
1.7 UML 架构
2 数据库(表)设计要合理:三个范式
2.1 三范式:
2.1.1 第一范式:字段具有原子性,不可分隔,只要使用关系型数据库就自动满足第
一范式。
2.1.1.1 关系型数据库:mysql/oracle/db2/informix/sysbase/sql server。
2.1.1.2 非关系型数据库:特点是面向对象或集合的。
2.1.1.3 NoSql 数据库:特点是面向文档的,执行速度很快,比如 MongoDB。
2.1.2 第二范式:符合第一范式后,表的记录是唯一的就可以满足第二范式,一般的
做法是设置主键,因为主键具有唯一性。
2.1.3 第三范式:符合第二范式后,任何字段不能由其他字段派生出来,即字段没有
冗余。一般的做法是通过外键,即如果 b 表需要 a 表的内容,就将 a 表的主键
作为 b 表的某列的值,而不用 a 表非主键的值。比如:一个学生表,有一个班
级表,一个学生表,学生表中有一字段说明学生属于哪个班,那么该字段应放
班级表的主键,而不应该放班级表班级的名称。
2.2 逆范式:比如有个相册分类表,字段包括主键序号和相册名称,下面有记录: 1-狗;
2-猫。还有另一个图片表,字段包括主键序号、图片名、相册序号、被点击次数和
图片路径,下面有记录:1-狗 1-1-5 次,2-狗 2-1-4 次。如果我们希望去了解相册分
类表中狗相册被点击了多少次,我们可以从图片表中查询所有所有相册序号为 1 的
记录中的被点击字段并求和来获得狗相册的被点击次数,但是,这样做的效率会非
常低,因为每次都要遍历庞大的图片表。而实际中我们的做法是在相册分类表中再
建立一个相册被点击数的字段,在每次图片表被点击时,也同时更新相册分类表该
字段。这样,当需要知道相册分类表中狗相册被点击了几次,只需要直接去访问相
册分类表便可,因为相册分类表的记录内容要远远少于图片表,这样性能就会大大
提高。但这样做,显然违反了第三范式,出现了不需要的冗余字段。但是,上面提
到的学生表和班级表,有的人想如果可以查看学生时同时可以查看到该学生所在的
班级名,就做了逆范式,在把学生表中的班级序号字段改成班级名称,以希望不用
再去连接到班级表,但是这样做是不合理的,因为这会使学生表的大小大大增加浪
费了很多空间,另外,如果某个班级的名称换了,那么需要修改学生表中所有该班
级的名称。所以,针对冗余,应根据实际情况进行分析是否需要逆范式。
3 sql 语句优化
3.1 优化的一般步骤:
3.1.1 通过 show status 命令了解各种 SQL 的执行频率。
3.1.2 通过慢查询日志定位执行效率低的 SQL 语句(重点定位 SELECT)。
3.1.3 通过 explain 分析执行效率低的 SQL 语句。
3.1.4 确定优化措施。
3.2 Sql 语句
3.2.1 分类:
DDL(数据定义语言):create、alter、drop
DML(数据操作语言):insert、delete、update
select:有时 select 也归在数据操作语言中。
DTL(数据事务语句):commit、rollback、savepoint
DCL(数据控制语句):grant、revoke
3.2.2 将会重点分析 select。
#随机产生字符串:
#先定义一个新的命令结束符“$$”,因为下面将定义一个存储过程,其中有很
多的“;”,所以这里临时修改一下命令结束符,等存储过程后再改回来。
delimiter $$
#创建一个函数 rand_string(n INT),会返回 n 个英文呢字母的一个随机字符串。
delimiter $$
#下面开始插入海量数据了:
delimiter $$
#往部门表汇总添加一些数据:
delimiter $$
发现执行了 5.44 秒。
3.5.2 在一个应用程序中,会有很多 php 去 select 查询数据库,如何找到那些慢查询
的语句?MySQL 数据库可以把慢查询语句记录到日志中,慢查询日志会记录所
有执行时间超过 long_query_time 所设置的 SQL 语句。但默认情况下不写日志。
3.5.3 启 用 的 方 法 为 : 进 入 到 mysql 安 装 目 录 下 的 mysql\bin\ 下 , 通 过 这 样 启 动
MySQL,mysqld.exe - -slow-query-log。这样,当一条查询语句的查询时间超过
long_query_time 所设置的时间,slow_queries 参数就会记录一次(可通过 show
status like 'slow_queries'; 来查看),同时,这条慢查询语句将会被记录到慢查
询日志中,我们就可以找到哪些语句执行速度很慢。
3.5.4 不同版本的 mysql 的数据库文件被放在不同的路径目录下,Mysql 的数据库文件
默 认 存 放 位 置 可 以 通 过 my.ini 中 的
,该慢
查询日志也被保存在该目录下。
3.6 优化方案 – 索引
3.6.1 索引:按规则添加索引,可以巨大地提高查询速度:
3.6.2 索引的四种类型:
3.6.2.1 主键索引(PRIMARY KEY):索引是通过向字段添加索引来实现的,但主
键索引添加方法不同,是通过将字段设为主键( PRIMARY KEY)来实现自
动添加主键索引的。主键索引的效率是最高的。
3.6.2.2 唯一索引(UNIQUE INDEX)
3.6.2.3 普通索引(INDEX)
3.6.2.4 全文索引(FULLTEXT INDEX):只有 MyISAM 存储引擎支持。对一个内容
很大的字段,并取字段记录中的部分内容作为查询条件,需要使用全文
索引。比如:select * from article where content like '%student%'; 这种一般
用在英文文本中。
3.6.2.5 复合索引:它只是将多个字段一起作为某种索引,而不是单独的一种索
引类型。
3.6.3 索引的命令:
3.6.4 添加索引一般规则:
3.6.4.1 索引要加在出现在 where 子句的字段上,而不是需要查询的内容上,不
会出现在 where 子句中的字段不要加索引,因为不但起不到在该字段上
加索引的效果,反而加了索引后产生的代价。
3.6.4.2 唯一性太差的字段,也不适合单独创建索引,即使会平凡的查询(作为
where 子句的字段),它的效率也不会太高。比如:select * from emp
where sex= 'male';
3.6.4.3 更新(update、delete、insert)非常频繁的字段不适合加索引,因为字
段记录一变化,就要相应的更改索引文件。索引最好加在一般不变化的
字段上,所以主键往往是比较理想的索引字段。
3.6.4.4 对于创建的多列索引,当查询条件中使用了该多列索引中最左边的列,
该多列索引就会被使用,否则该多列索引就无法使用。所以在设计多列
索引时,需要将会被 where 子句用到的字段写在最左边。
3.6.4.5 对于使用 like 的查询,如果将百分号“%”写在最左边,如:'%xxx',索引
就无法被使用;如果百分号不在最左边,如'xxx%'、'xx%x',索引就能被
使用。
3.6.4.6 如果条件中有“OR”,只要其中有一个条件的字段不是索引,该语句就无
法使用索引。所以,应该尽量少用“OR”来作为查询条件,如果必须使用
OR,则需要考虑是否为每个 OR 条件的字段建立索引。
3.6.4.7 字段类型和字段值的类型要匹配,否则无法使用索引。比如,如果字段
类型是字符串,那一定要在条件中将字段的值用引号引起来,否则索引
无法被使用。
3.6.4.8 还需要知道一种情况,就是如果 mysql 估计使用全表扫描要比使用索引
还快,则不要使用索引。比如一个表只有一条记录,有时建立索引可能
不一定会比全表扫描更快,这取决于 mysql 的自行判断。这里我们先只
需知道可能会有这种情况。
体验:
为
alter table emp add primary key(empno);
id:1 表示该语句用到的是第一个索引,因为有时会有多个索引
select_type: 表示查询的类型。
SIMPLE
table: emp 查询的表。如果是多表查询,就会显示所有查询的表。
type: const 表示表的连接类型,有三种:
ALL:表示该语句进行了全表扫描,这样就会导致效率很低。当执行
select * from emp where ename='aaa'\G; 时会进行全盘扫描,因为 ename
不是索引,就会执行全表扫描所有的记录,就算找到了记录,由于系统
不知道是否还有符合要求的记录,所以还是会继续查找,直到扫描完表
中的所有记录。而如果加了索引,系统可以直接从索引文件中定位到数
据表的位置,一下子就能找到记录,所以,下面的 rows=1。另外,如
果执行 select * from emp,显然会进行全盘扫描,type 为 ALL,但是,
实际做项目时,我们是不会出现这种情况,因为我们网页我们是需要分
页的,即使只有一页的记录,也同样要写成诸如: select * from emp
limit 0,5; 这 样 的 样 子 , 这 样 就 不 会 出 现 type: ALL 了 。 所 以 , 在 做
explain 时如果发现有一个语句的 type 是 ALL ,一般就要避免全表扫描
ALL 这种情况,而转变成 system 或 const。
system:表仅有一行(=系统表),这是 const 连接类型的一个特例。
const:表最多有一个匹配行。
possible_keys: 表示查询时,可能使用的索引。因为一个表可能有多个索引,而系统会
PRIMARY 自行选择一个认为最优的索引。
key: PRIMARY 表示实际使用的索引。
key_len: 3 表示索引字段的长度。
ref: const
rows: 1 表示扫描的行数,即扫描了多少行才找出需要的结果,比如这里用了主
键索引,这里一下子就能找到这一行,又是主键,具有唯一性,所以
rows 就是 1,显然效率很高。如果没有索引,就会执行全表扫描,rows
就会显示该表的总记录条数,就算找到了记录,由于系统不知道是否还
有,所以还是会继续查找,知道扫描完表中的所有记录。所以, rows
也可以来判断一个语句的是否有可能需要优化。
Extra: 查询的细节(主要针对排序方式)。
No tables:查询语句中使用 FROM DUAL 或不含任何 FROM 字句。
Using filesort:当查询语句中包含 ORDER BY 操作,而且无法利用索引完
成 排 序 。 比 如 : explain select * from emp order by ename\G , 由 于
ename 字段不是索引,所以系统就会用文件的方式来排序,也会进行全
表扫描。要尽量避免出现该信息。
Impossible WHERE noticed after reading const tables: MYSQL Query
Optimizer。
通过收集统计信息,不可能存在结果。
Using temporary:某些操作必须使用临时表,常见:GROUP BY、ORDER
BY。
Using where:不用读取表中所有信息,仅通过索引就可以获取所有数
据。这种信息表示是比较好的查询。
3.8 通过如下命令,可以检查所设计的索引是否合理:
3.8.1 Handler_read_key:表示使用索引查询到的次数,这个值越高越好。命令:
show status like 'handler_read_key'; 或 status like 'handler_read%';
3.8.2 Handler_read_rnd_next:表示使用索引查询到的次数,这个值越高,则查询效
率越低。命令:
show status like 'handler_read_rnd_next'; 或 status like 'handler_read%' ;
3.9 存储引擎:
3.9.1 MyISAM 存储引擎:适用于不需要事务处理,以查询 select 和插入 insert 为主的
表,在这种情况下使用该存储引擎比 Innodb 的查询和添加速度更快。比如 bbs
中的发帖表、回复表。不支持外键;不支持事务;锁机制为表锁;当 delete 记
录时,不会释放存储空间,针对有些应用,可能很快就会写满磁盘,所以,需
要使用命令 optimize table 表名; 来定时清理释放不用的空间(碎片整理)。
3.9.2 Innodb 存储引擎:支持外键(在 php 中,通常不设置外键,而是通过编写合适
的程序来保证数据的一致性);支持事务;锁机制为行锁。
3.9.3 Memory 存储引擎:适用于数据变化频繁,需要频繁查询和修改,不需要入库
(因为入库也没有太大意义,且反而会造成频繁访问数据库),我们可以考虑
使用 Memory 存储引擎。;锁机制为表锁。这种存储引擎访问速度极快,因为
它是存放在内存中的,所以,当重启 mysql 数据库服务器后,memory 存储引擎
的表中的数据就会全部丢失。有时候如果无法使用 memcached,我们可以考虑
使用 memory 来代替。
3.11.5 在精度要求高的应用中,应使用定点数(decimal)来存储数值,以保证结果的
准确性。
3.11.6 日期类型要根据实际需要选择能够满足应用的最小存储的日期类型。日期类型
建议使用数值类型(时间戳),即 int,bigint 等这类数值类型,因为数值类型
的时间可以直接加减秒数后来退出前后的日期,比较方 便,比如: time()-
3*24*60*60 既可以得到三天前的日期的数值类型(时间戳),然后通过 date()
即可方便显示出可识别的时间。比如:date('Y-m-d', time()-3*24*60*60); 但是
date()函数有范围:
4 对 MySQL 数据库的配置进行优化(my.ini)
4.1 缓存内存:
4.1.1 最重要的参数就是内存,数据库参数配置来优化 MySQL,说白了就是将缓存做
大一些:
4.1.1.1 对于 Innodb 存储引擎:我们一般主要使用 Innodb 存储引擎,所以应将下
面两个参数调的很大:
Innodb_additional_mem_pool_size,比如=64M
Innodb_buffer_pool_size,比如=1G
4.1.1.2 对于 MyISAM 存储引擎:需要调整 key_buffer_size 参数。
4.1.1.3 当然,参数能调多大,还取决于计算机硬件限制。另外,调整以上参数
之前,需要通过 show status 先看一下当前的状态,以决定调整哪些参数。
4.1.2 最大并发数:max_connections=100,MySQL 默认最大并发数是 100 个。一般网
站可以调整到 1000 左右。注意:每一个并发都会开一个进程,所以,如果调
了太大比如 2000,可能内存也支撑不了。
4.1.2.1
5 硬件升级—64 位:
5.1 如果计算机是 64 位的,内存超过 4G,那毋庸置疑应该参数 64 位操作系统和 64 位
的 MySQL,不然 MySQL 无法发挥更大的威力。
5.2 内存、cpu 等的升级。
6 硬件升级--读写分离:
6.1.1 读写分离的目的是给大型网站缓解数据库查询压力。
6.1.2 MySQL 的主从复制+读写分离技术,读写分离技目前有官方的 MySQL-proxy 和
阿里巴巴的 Amoeba。Amoeba 能在阿里巴巴这么大流量的平台投入使用而且运
行稳定,Amoeba 的性能是很优越的。Amoeba 和 MySQL-proxy 的区别主要在数
据切分方面,
UML 架构
定时发送邮件的视频(定时维护 3 和 4):http://www.icoolxue.com/play/1372