一. MySQL优化要点

  MySQL优化是一门复杂的综合性技术,主要包括:

  1 表的设计合理化(符合 3NF,必要时允许数据冗余) 

  2.1 SQL语句优化(以查询为主)

  2.2 适当添加索引(主键索引,唯一索引,普通索引(包括联合索引),全文索引)

  3 分表技术(水平分割,垂直分割)

  4 读写分离(写包括update/delete/insert)

  5 存储过程(模块化编程,提高执行速度)

  6 MySQL配置优化

  7 数据库服务器硬件升级

  8 定时数据清理,碎片整理(MyISAM)

二. 3NF是什么

  1. 第一范式

  第一范式是最基本的范式。要求数据库表中的所有字段值都是不可分解的原子值,即要求列的原子性。

  2. 第二范式

  第二范式是建立在第一范式的基础之上的,要求数据库表中的记录(行)必须是唯一的,即要求行的唯一性。

  通常通过设计一个主键来实现(建议主键不要有具体的业务含义)。

  3. 第三范式

  满足第三范式必须要满足第二范式。要求非主键列必须直接依赖于主键,不能存在传递依赖,及表中不能有冗余数据。

  表中某字段的信息可以通过其他列推导出来,就不应该设计此列。

  反3NF:没有冗余的数据库表设计未必是最优设计,有时为了提高效率,需要降低范式标准,适当增加冗余字段。

三. SQL语句优化

  1.定位慢查询(查找执行速度慢的SQL语句)

  ① 了解MySQL数据库运行状态

  > show status like 'uptime'  #查询数据库运行时间(单位:s)

  > show [session | global] status like '[com_select | com_insert | com_update | com_delete]' #查询SQL语句执行次数

    session | global :默认为 session ,表示当前 session 的语句执行次数;global 表示数据库运行以来所有次数。

  > show status like 'connections'  #查询当前

  > show status like 'slow_queries'  #显示慢查询次数

  ② 如何定位慢查询

  a.首先构造一张大表的数据,SQL语句如下:

  1. CREATE DATABASE temp0919;
  2. USE temp0919;
  3.  
  4. #创建表DEPT
  5. CREATE TABLE dept( /*部门表*/
  6. deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
  7. dname VARCHAR(20) NOT NULL DEFAULT "",
  8. loc VARCHAR(13) NOT NULL DEFAULT ""
  9. ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
  10.  
  11. #创建表EMP雇员
  12. CREATE TABLE emp(
  13. empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/
  14. ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
  15. job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
  16. mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/
  17. hiredate DATE NOT NULL,/*入职时间*/
  18. sal DECIMAL(7,2) NOT NULL,/*薪水*/
  19. comm DECIMAL(7,2) NOT NULL,/*红利*/
  20. deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
  21. )ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
  22.  
  23. #工资级别表
  24. CREATE TABLE salgrade
  25. (
  26. grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
  27. losal DECIMAL(17,2) NOT NULL,
  28. hisal DECIMAL(17,2) NOT NULL
  29. )ENGINE=MyISAM DEFAULT CHARSET=utf8;
  30.  
  31. INSERT INTO salgrade VALUES (1,700,1200);
  32. INSERT INTO salgrade VALUES (2,1201,1400);
  33. INSERT INTO salgrade VALUES (3,1401,2000);
  34. INSERT INTO salgrade VALUES (4,2001,3000);
  35. INSERT INTO salgrade VALUES (5,3001,9999);
  36.  
  37. #定义一个新的命令结束符合
  38. delimiter $$
  39. #删除自定的函数
  40. drop function rand_string $$
  41.  
  42. #创建一个函数(随机产生字符串)
  43. #rand_string(n INT) rand_string 是函数名 (n INT) //该函数接收一个整数
  44. create function rand_string(n INT)
  45. returns varchar(255) #该函数会返回一个字符串
  46. begin
  47. #chars_str定义一个变量 chars_str,类型是 varchar(100),默认值'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
  48. declare chars_str varchar(100) default
  49. 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
  50. declare return_str varchar(255) default '';
  51. declare i int default 0;
  52. while i < n do
  53. set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));
  54. set i = i + 1;
  55. end while;
  56. return return_str;
  57. end $$
  58.  
  59. #函数: 随机产生部门编号
  60. drop function rand_num $$
  61.  
  62. create function rand_num( )
  63. returns int(5)
  64. begin
  65. declare i int default 0;
  66. set i = floor(10+rand()*500);
  67. return i;
  68. end $$
  69.  
  70. #******************************************
  71. #向emp表中插入记录(海量的数据)
  72. drop procedure insert_emp $$
  73.  
  74. #随即添加雇员[光标] 400w
  75. create procedure insert_emp(in start int(10),in max_num int(10))
  76. begin
  77. declare i int default 0;
  78. #把autocommit设置成0,不自动提交
  79. set autocommit = 0;
  80. repeat
  81. set i = i + 1;
  82. insert into emp values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());
  83. until i = max_num
  84. end repeat;
  85. commit;
  86. end $$
  87.  
  88. delimiter ;
  89. #调用刚刚写好的函数, 4000000条记录,从100001号开始
  90. call insert_emp(100001,4000000);
  91.  
  92. #**************************************************************
  93. # 向dept表中插入记录
  94.  
  95. delimiter $$
  96. drop procedure insert_dept $$
  97.  
  98. create procedure insert_dept(in start int(10),in max_num int(10))
  99. begin
  100. declare i int default 0;
  101. set autocommit = 0;
  102. repeat
  103. set i = i + 1;
  104. insert into dept values ((start+i) ,rand_string(10),rand_string(8));
  105. until i = max_num
  106. end repeat;
  107. commit;
  108. end $$
  109.  
  110. delimiter ;
  111. call insert_dept(100,10);

  

  b. MySQL默认执行时间大于10s的语句为慢查询,此处修改为1s

  > show variables like 'long_query_time'  #显示慢查询时间标准

  > set long_query_time = 1  #修改慢查询时间,当前session有效

  c. 记录慢查询到日志文件中

  默认情况下MySQL不会记录慢查询日志,需要在启动MySQL服务时,指定相应的参数,才可以记录慢查询日志。

  两种启动方式如下:

  (1) 修改配置后启动(linux : my.conf,  windows : my.ini) 

  1. [mysqld]
  2. #慢查询日志文件位置, 此目录文件一定要有写权限
  3. log-slow-queries="/usr/local/mysql5.6/data/black-slow.log"
  4. #慢查询时间标准
  5. long_query_time = 2
  6. #没有使用到索引的查询也将被记录在日志中
  7. log-queries-not-using-indexes

    修改配置文件后重新启动MySQL服务:

    $ sudo $MYSQL_HOME/support-files/mysql.server start  # Linux/OSX

    > mysqld.exe   # Windows

  (2) MySQL客户端修改参数

    使用MySQL客户端登陆MySQL数据库

  > show variables like '%slow_query%'   #查看慢查询相关参数

   > show variables like 'long_query_time'    #查询慢查询时间标准

   > set global slow_query_log = ON;  #开启慢查询日志

   > set global long_query_time = 1;  #调整慢查询时间标准

  此时不需要重启MySQL服务,即可开启慢查询日志。

  

  2. 索引的维护

    索引维护相关讨论见下方第四节。

  3. 索引的使用

    ① 对于创建了联合索引的情况,只有当查询条件中使用了联合索引中最左边的列,索引才会被使用。  

  1. ALTER TABLE dept ADD INDEX union_idx (dname, loc);
  2.  
  3. #如下则不会使用到此索引
  4. SELECT * FROM dept WHERE loc='Beijing';

    ② 对于使用 like 的查询,左模糊查询不会使用到相应列上的索引,及 like 查询条件的左侧不能为 '%aa' 或 '_aa' 的形式。

      如果一定要针对左模糊查询条件使用索引,可以考虑使用全文索引。

    ③ 如果查询条件中带有 or ,则要求 or 中涉及到所有列都有索引,否则不会使用索引。

      建议:尽量避免使用 or 关键字。

    ④ 如果创建索引的列的类型是字符串,则查询条件中必须使用字符串,才会使用索引。    

  1. ALTER TABLE dept ADD INDEX idx_name (dname);
  2. # 可以使用索引
  3. SELECT * FROM dept WHERE dname = 'Tom';
  4. # 不会使用索引
  5. SELECT * FROM dept WHERE dname = 235;

    ⑤ 如果MySQL判断使用全表扫描比使用索引快,则不会使用索引。(出现的情况少)

  4. explain指令的用法

  explain 指令可以查看SQL语句的执行计划,效果如下:

  

  说明:

    id :查询序列号

    select_type : 查询类型

    table:查询的表名

    type:扫描方式,all 代表全表扫描

    possible_keys:可能使用到得索引

    key:实际使用的索引

    rows:SQL语句扫描的行数

    Extra:额外信息,如排序方式等

  5 查看索引使用情况

  > show status like 'Handler_read%';

  

  handler_read_key:越高越好,表示使用索引查询的次数

  handler_read_rnd_next:越低越好,此值高说明查询低效

  6 SQL 优化小技巧

  ① 在查询语句中使用 group by 分组时,MySQL会默认对分组结果进行排序,可能会降低速度。使用 order by null 可以关闭排序。

  

  ② 有些情况下,使用连接代替子查询。使用 join 不需要在内存中创建临时表。

四. 索引

  对于SQL优化来说,最重要的方式之一是添加索引。索引分为四种:主键索引,唯一索引,普通索引,全文索引。

  1. 创建索引

  ① 主键索引:当创建一张表时,指定某列位主键,即在该列上创建了一个主键索引。   

  1. CREATE TABLE aaa(
  2. id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  3. name VARCHAR(50) NOT NULL DEFAULT ''
  4. );
  5.  
  6. CREATE TABLE bbb(
  7. id INT UNSIGNED,
  8. name VARCHAR(50) NOT NULL DEFAULT ''
  9. );
  10. ALTER TABLE bob ADD PRIMARY KEY(id);

  ② 普通索引:先创建表,然后再创建普通索引,普通索引可以单独一列或多列上创建,在多列上创建的叫做联合索引

  1. CREATE TABLE ddd(
  2. id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  3. name VARCHAR(50) NOT NULL,
  4. card_no VARCHAR(20) NOT NULL
  5. );
    #普通索引
  6. CREATE INDEX idx_name ON ddd(name);
  7. #联合索引
  8. CREATE INDEX idx_name_cardno ON ddd(name, card_no);

  ③ 唯一索引:当表的某列被指定为 UNIQUE 约束时,则在此列上创建了一个唯一索引。

  1. CREATE TABLE ccc(
  2. id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  3. name VARCHAR(32) UNIQUE
  4. )
  5.  
  6. #unique字段可以为NULL,并且可以有多个NULL
  7. CREATE TABLE ddd(
  8. id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
  9. name VARCHAR(32) UNIQUE
  10. );
  11.  
  12. CREATE UNIQUE INDEX name_uni ON ccc(name);
  13.  
  14. #在普通索引创建时加上unique关键字即为唯一索引,唯一联合索引

  ④ 全文索引:主要针对文件文本的检索,例如文章内容。全文索引只对MyISAM引擎起效。

  1. CREATE TABLE article(
  2. id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
  3. title VARCHAR(200) NOT NULL,
  4. body TEXT,
  5. FULLTEXT (title, body) # 全文索引
  6. ) ENGINE=MyISAM CHARSET UTF8;
  7.  
  8. #全文索引的使用
  9. SELECT * from article WHERE body LIKE '%mysql%'; #错误用法,不会使用全文索引
  10.  
  11. SELECT * FROM article WHERE MATCH(title, body) AGAINST ('mysql') # 正确用法
  12.  
  13. #是否使用索引可以通过explain命令查看

  注: a. 全文索引只对MyISAM引擎生效

     b. MySQL本身提供的全文索引只对英文生效

     c. 停止词:在文本中针对所有单词或字符创建索引是一个无穷大的数量级,因此对一些常用词和字符不会创建全文索引,这些词和字符被称作停止词

  2. 查询,删除,修改索引

  1. #查询
  2. desc table_name; #缺点:无法显示索引名称
  3. show index(es) from table_name;
  4. show keys from table_name;
  5.  
  6. #删除
  7. alter table table_name drop index index_name;
  8.  
  9. #主键索引的删除(必须是非auto_increment)
  10. alter table table_name drop primary key;
  11.  
  12. #修改:先删除,再添加
  13. alter table table_name drop index index_name;
  14. alter table table_name add index index_name table_name(col_name);

  3. 索引的代价

    a. 占用更多的磁盘空间(典型的以空间换时间)

    b. 使得DML语句变慢

  4. 创建索引的依据

    a. 创建索引的列经常在WHERE条件中使用

    b. 创建索引的表达到一定数据量,数据条数过少没必要创建索引

    c. 创建索引的列的字段值不是有限的几个值,例如性别,是否上线等

    d. 创建索引的列的字段值不是频繁变化,例如记录登录次数,在线人数等字段

 

五. 分表技术

  当业务数据越来越多的时候,会导致某些数据表的数据量非常巨大,导致系统的性能下降。可以通过分表的方式改善性能。

  1. 水平分表

    将一张大表中的数据,或者即将产生大量数据的表,按照业务无关的属性随机均匀的存入多张结构相同的分表中。

    假设订单信息表,可以创建order_info_00,order_info_01...order_info_99,一共100张分表,订单编号是唯一的,每次存入的时候,用订单编号取hashcode后,再对100(分表数量)取模,得到的结果即为将要存储数据的分表的序号。

    Java中可如下操作: 

  1. #取哈希值时有可能结果为Integer.MIN_VALUE,导致取序号出错
  2. int index = Math.abs(orderNo.hashcode() % 100);  
  3. String idxStr = String.format("%2d", index);
  4. String tableName = "order_info_" + idxStr;

  2. 垂直分表

  在某些表中,可能会有占用空间比较大得字段,类型如text,varchar(3000),用于存储文章内容,回帖内容等,这些字段会严重影响系统的检索速度,此类字段查询的次数也相对较少,这个时候可以将其提取出来,单独建表存储,与原来的表共用主键id。这样在保证了数据的关联一致的同时,加快了原来表的检索速度。

  3. 数据库中文本视频类数据的存储

   通常不直接将文本或视频内容存储在数据库中,而只是存储文本或视频所在的路径,查询时按照路径去检索文件的真正内容。

  

六. 读写分离

  当系统的并发访问量特别大的时候,单一的MySQL服务器的负载特别大,导致数据库性能下降,升值造成服务器崩溃。这个时候可以考虑搭建MySQL集群,使用读写分离技术来改善这种状况。集群中包含一台master服务器,多台slave服务器,master服务器负责执行DML(insert/delete/update)语句,slave服务器负载执行select语句,主服务器通过日志文件将操作同步到从服务器上。

   。。。。

七. MySQL配置优化

  。。。。

八. 定时维护

 1.存储引擎的选择

  MyISAM引擎:对事物要求不高,数据以添加和查询为主

  InnoDB引擎:严格要求失误,保存的数据都是重要数据

MyISAM 和 InnoDB 区别
  事务支持 查询添加 全文索引 锁机制 外键支持
MyISAM 支持 表级
InnoDB 不支持 行级

  Memory 引擎:数据频繁变化,不需要入库,并且频繁查询修改,速度极快(MySQL服务重启后数据消失),可以用作缓存。

  2. MyISAM 引擎定期进行碎片整理

    使用 MyISAM 引擎的表中得数据被删除后,表数据存储所在的文件大小并不会改变,即表文件占用的空间不会释放,长此以往会严重拖慢表的查询速度。所以需要定期对使用 MyISAM 引擎的表进行碎片整理。

  1. > OPTIMIZE TABLE table_name;

  3. 定时备份

  ① 手动备份数据库

  1. #备份数据库,命令行下操作
  2. $ ./mysqldump -u root -pxxxx temp dept > ~/backend/mysql-temp-dept.bak
  3. #xxxx为 MySQL 登录密码,temp 为需要备份的数据库,dept 为需要备份的表名
  4. #表名可以有多个,[tb1, tb2, tb3,....]
  5.  
  6. #使用备份内容恢复数据,MySQL 客户端下操作
  7. > source ~/backend/mysql-temp-dept.bak

  ②使用定时器完成自动备份

  a. Windows下

    1> 将备份指令写入一个bat脚本(mybaktask.bat): 

  1. #mysqldump.exe路径包含空格,需要用""包围
  2. "C:\programe files\mysql5.6.26\bin\mysqldump.exe" -u root -pxxxx temp dept >d:\temp.dept.bak

    2> 把 mybaktask.bat 做成一个计划任务

     "计算机"-(右键)"属性"-"管理"-"系统工具"-"任务计划程序"-"创建任务"

    注:windows下希望每次备份的文件名称加上时间戳,不覆盖上一次备份的文件,可以写一个PHP脚本去执行备份命令,然后再定义一个定时任务去调用PHP脚本。  

  b. Linux / OSX 下

    1> 将备份指令写入 shell 脚本(mysql-bak-task.sh)    

  1. #!/bin/bash
  2. /usr/local/mysql/bin/mysqldump -u root -pxxxx temp dept > /home/byron/backend/mysql-temp-`date +%s`.bak

    2> 使用 contrab 创建定时任务

  1. $ sudo crontab -e  #编辑定时任务
  2.  
  3. # 添加如下内容
    * 2 * * * /Users/byron/backEnd/mysql-bak-task.sh # 每天凌晨2点执行一次备份脚本

九. 增量备份

  MySQL会以二进制的形式,自动将用户对MySQL数据库的操作 ,记录到文件中。    

MySQL优化概述的更多相关文章

  1. mysql优化概述4

    一.分区 1.分区概念 将某张表数据,分别存储到不同的区域中. 每个分区,都是独立的表,都要存储该分区的数据,索引信息. 2.创建分区 创建表并指定分区的选项 create table 表名 ( 定义 ...

  2. mysql优化概述3

    1.前缀索引 建立索引关键字一种方案. 通常会使用字段的整体作为索引关键字. 有时,使用字段前部分数据,也可以去识别某些记录. 语法: index `索引名` (`字段`(N)); 使用字段前N个字符 ...

  3. mysql优化概述2

    一.索引的概念 利用关键字,就是记录的部分数据(某个字段,某些字段,某个字段的一部份),建立与记录位置的对应关系,就是索引.索引的关键字一定是排序的. 二.索引的类型 mysql支持四种索引: 1.主 ...

  4. Mysql优化-概述

    摘抄并用于自查笔记 为什么要优化 一个应用吞吐量瓶颈往往出现在数据库的处理速度上. 随着应用程序的使用,数据库数据逐渐增多,数据库处理压力逐渐增大. 关系型数据库的数据是存放在磁盘上,读写速度慢(与内 ...

  5. Mysql优化概述及其压力测试工具

    衡量指标 TPS:Transactions Per Second (每秒传输的事物处理个数) ,这是指服务器每秒处理的事物数,支持事物的存储引擎如Innodb等特有的一个性能指标; QPS:Queri ...

  6. mysql优化一 之 优化内容概述及开启慢查日志的相关配置

    1-1数据库优化的目的 首先是为了避免出现页面访问错误(基本有三种) (1)由于数据库连接timeout产生的页面5XX错误 (2)由于慢查询造成页面无法加载 (3)由于阻塞造成的数据无法提交 其次: ...

  7. 深入MySQL(四):MySQL的SQL查询语句性能优化概述

    关于SQL查询语句的优化,有一些一般的优化步骤,本节就介绍一下通用的优化步骤. 一条查询语句是如何执行的 首先,我们如果要明白一条查询语句所运行的过程,这样我们才能针对过程去进行优化. 参考我之前画的 ...

  8. 0104探究MySQL优化器对索引和JOIN顺序的选择

    转自http://www.jb51.net/article/67007.htm,感谢博主 本文通过一个案例来看看MySQL优化器如何选择索引和JOIN顺序.表结构和数据准备参考本文最后部分" ...

  9. MySQL优化技巧

    目录 MySQL的特点 数据类型优化 整型类型 小数类型 字符串类型 时间类型 主键类型的选择 特殊类型的数据 索引优化 一个使用Hash值创建索引的技巧 前缀索引 多列索引 聚簇索引 覆盖索引 重复 ...

随机推荐

  1. 【集合框架】JDK1.8源码分析之HashMap & LinkedHashMap迭代器(三)

    一.前言 在遍历HashMap与LinkedHashMap时,我们通常都会使用到迭代器,而HashMap的迭代器与LinkedHashMap迭代器是如何工作的呢?下面我们来一起分析分析. 二.迭代器继 ...

  2. SQL常见的系统存储过程

    1.sp_datebases 列出服务器上的所有数据库信息,包括数据库名称和数据库大小 例:exec sp_datebases 2.sp_helpdb 报告有关指定数据库或所有数据库的信息 例:exe ...

  3. 工厂模式 - Factory

    简单工厂模式 SimpleFactory Pattern,将一个具体类的实例化交给一个静态工厂方法来执行. 特点: 增加功能需要修改工厂类,扩展性较差: 参考: 设计模式学习笔记 - 简单工厂模式: ...

  4. Hadoop JAVA 开发说明

    作为Hadoop程序员,他要做的事情就是: 1.定义Mapper,处理输入的Key-Value对,输出中间结果.2.定义Reducer,可选,对中间结果进行规约,输出最终结果.3.定义InputFor ...

  5. MySQL中索引和优化的用法总结

    1.什么是数据库中的索引?索引有什么作用? 引入索引的目的是为了加快查询速度.如果数据量很大,大的查询要从硬盘加载数据到内存当中. 2.InnoDB中的索引原理是怎么样的? InnoDB是Mysql的 ...

  6. Apache Lucene(全文检索引擎)—搜索

    目录 返回目录:http://www.cnblogs.com/hanyinglong/p/5464604.html 本项目Demo已上传GitHub,欢迎大家fork下载学习:https://gith ...

  7. jQuery Ion.Calendar 日期/日历

    在线实例 实例演示 默认 实例演示 每周第一天 实例演示 输入框插件 实例演示 HTML data 属性 实例演示 回调函数1 实例演示 回调函数2 使用方法 <div id="cal ...

  8. MySQL中进行模糊搜索的一些问题

    在搜索数据库中的数据时,SQL 通配符可以替代一个或多个字符.SQL 通配符必须与 LIKE 运算符一起使用.在 SQL 中,可使用以下通配符:通配符 描述       % 替代一个或多个字符     ...

  9. SharePoint 2013 版本功能对比

    前言:在SharePoint使用中,经常纠结于版本问题,SharePoint 2013主要有免费的Foundation和收费的标准版.企业版三个版本,他们之间的功能上是不一样的,找了一些资料才发现下面 ...

  10. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q102-Q104)

    Question  102   You are designing a Windows application that accesses information stored on a ShareP ...