MySQL数据库的优化技术: 对数据库的优化是一个综合性的技术,主要包括: 1.表的设计是否合理(符合三范式,3NF) 2.添加适当索引(常见索引有四种:普通索引,主键索引,唯一索引,全文索引,(空间索引,复合索引) 3.分表技术(水平分割,垂直分割) 4.读写分离 5.存储过程(SQL语句每次执行都需要编译,存储过程只编译一次,模块化编程) 6.对MySQL的配置优化(配置最大并发数 my.ini,调整缓存大小) 7.MySQL服务器的硬件升级 8.定时清除不需要的数据,定时进行碎片整理 9.SQL语句优化 *************************************************************************************** 数据库表的设计: 什么样的表才是符合3NF--首先复合1NF,然后复合2NF,进一步满足3NF 三范式: 1.表的列具有原子性,不可再分割,即列的信息不能分解,只要数据库是关系型数据库,就自动满足1NF。 2.表中的记录的主键唯一。 3.表中不要有冗余数据(如果能被推导出来,就不应该单独设计一个字段来表示)。 反三范式(适当冗余): 但是,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余(在表的1对N的情况下,为了提高效率,可能在1方的表中设计字段,提高效率)。 举例说明什么是适当冗余? 一个年级有多个班,一个班有多个学生,可以在年级表中增加一个字段来存放年级的全体学生数量。 *************************************************************************************** SQL语句优化: 如何从一个大项目中迅速的定位执行速度比较慢的语句(定位慢查询)? 1.MySQL数据库的一些运行状态如何查询,如查询当前MySQL的运行时间/一共执行了多少次select/update/delete/有多少连接数? show status; 已经运行的时间:show status like ‘uptime‘; 已经执行的select/update/delete/insert的次数:show status like ‘com_select‘;show status like ‘com_update‘;show status like ‘com_delete‘;show status like ‘com_insert‘; show [session|globe] status like ... //默认为session级别(当前窗口),globe为整个MySQL数据库 试图连接MySQL服务器的次数:show status like ‘connections‘; 显示慢查询的次数:show status like ‘slow_queries‘; //实际包括查询、添加、删除等操作 2.如何定位慢查询(默认情况下MySQL认为10秒钟才是一个慢查询,可以修改): 显示慢查询的时间:show variabkles like ‘long_query_time‘; 修改MySQL的慢查询的时间:set long _query_time=1; delimiter $$ 可以定义一个新的命令结束符 若出现一条语句指向时间超过慢查询设定的时间,就会统计; 默认情况下,MySQL不会记录慢查询,需要在启动时指定: bin\mysqld.exe --sqfe-mode--slow-query-log //MySQL5.5版本 bin\mysqld.exe-log-slow-queries=d:/abc.log //MySQL5.0版本 需要关闭MySQL服务,再重新启动(不是关闭当前窗口而是关闭整个服务)。 日志文件放置的路径可以查看my.ini中的datadir。 此时再出现慢查询将记录到日志文件中。
常用SQL优化:
大批量插入数据的情况:
对于MyISAM:
1.alter table table_name disable keys;
2.loading data; //insert语句;
3.alter table table_name enable keys;
对于Innodb:
1.将要导入的数据按照主键排序
2.set unique_checks=0,关闭唯一性校验。
3.set autocommit=0,关闭自动提交。
优化group by 语句
默认情况,MySQL对所有的group by col1,col2进行排序。
这与在查询中指定order by col1, col2类似。
如果查询中包括group by但用户想要避免排序结果的消耗,则可以使用order by null禁止排序。
有些情况下,可以使用连接来替代子查询。
因为使用join,MySQL不需要在内存中创建临时表。
*************************************************************************************** 增加索引: 提高数据库性能的方法中索引是最物美价廉的。不用加内存,不用改程序,不用调整SQL语句,只要执行个正确的‘create index ... ‘,查询速度就可能提高百倍千倍,但查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的I/O。
索引是对数据库表中一列或多列的值进行排序的一种结构。索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。 当表中有大量记录时,若要对表进行查询,有两种方法: 第一种搜索信息方式是全表搜索,是将所有记录一一取出,和查询条件进行一一对比,然后返回满足条件的记录,这样做会消耗大量数据库系统时间,并造成大量磁盘I/O操作; 第二种就是在表中建立索引,然后在索引中找到符合查询条件的索引值,最后通过保存在索引中的ROWID(相当于页码)快速找到表中对应的记录。
主要的四种索引:(普通索引,主键索引,唯一索引,全文索引) 1.索引的添加: 1.1.主键索引的添加: 当一张表,把某个列设为主键的时候,则该列就是主键索引。 create table aaa(id int unsigned primary key auto_increment,name varchar(32) not null default ‘‘); 主键索引也可以在创建表之后再添加: alter table [表名] add primary key [列名]; 1.2.普通索引添加: 一般来说,普通索引的创建,是先创建表,然后创建普通索引。 create table ccc(int unsigned,name varchar(32)); create index [索引名] on [表明/列名] 1.3.全文索引的添加: 全文索引主要是针对文本的检索,如:文章。 全文索引针对MyISAM(一种存储引擎)有效,只针对英文有效(中文用sphinx(coreseek)技术)。 停止词的概念:因为在一个文本中,创建索引是一个无穷大的数,因此,对一些常用词和字符,就不会创建,这些词,称为停止词。 CREATE TABLE articles ( id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY, title VARCHAR(200), body TEXT, FULLTEXT (title,body) )engine=myisam charset utf8; INSERT INTO articles (title,body) VALUES (‘MySQL Tutorial‘,‘DBMS stands for DataBase ...‘), (‘How To Use MySQL Well‘,‘After you went through a ...‘), (‘Optimizing MySQL‘,‘In this tutorial we will show ...‘), (‘1001 MySQL Tricks‘,‘1. Never run mysqld as root. 2. ...‘), (‘MySQL vs. YourSQL‘,‘In the following database comparison ...‘), (‘MySQL Security‘,‘When configured properly, MySQL ...‘); 如何使用全文索引: select *from articles where body like ‘%mysql%‘; //错误用法 select *from articles where match(title,body) against(‘database‘); //正确用法 通过explain语句可以分析,MySQL如何执行你的SQL语句(explain select * from articles where body like ‘%mysql%‘ /G //解释执行过程)。 explain告诉我们MySQL将使用怎么样的执行计划来优化Query(这个语句还没有被真正执行)。 1.4.唯一索引的添加: 当表的某一列被指定为unique约束时,这个列就是一个唯一索引。 create table ddd(id int primary key auto_increment,name varchar(32) unique); 这是name列就是一个唯一索引。 唯一索引也可以在创建表之后在创建唯一索引: create table eee(id int primary key auto_increment,name varchar(32)); create unique index [索引名] on [表明/列名] unique字段可以为null(不是‘‘),并且可以有多个,有具体内容(包括‘‘)则不能重复。主键字段不能为null也不能重复。 2.索引的查询: desc [表名] //但是不能显示索引名 show index(es) from [表名] show keys from [表名] 3.索引的删除: alter table [表名] drop index [索引名]; ?alter table [表名] drop primary key; //删除主键索引 4.索引的修改: 先删除,再重新创建。 为什么创建索引后,查找速度会变快? 利用二叉树算法(BinaryTree)构建索引文件,时间复杂度log2N。(第六集) 不同的存储引擎,会使用不同的算法来构建索引文件。 MySQL的常用存储引擎: +--------------+----------------+ | 存储引擎 | 允许的索引类型 | +--------------+----------------+ | MyISAM | BTREE | +--------------+----------------+ | InnoDB | BTREE | +--------------+----------------+ | MENORY/HEAP | BTREE/HASH | +--------------+----------------+ 索引的代价: 1.磁盘占用--索引文件会占用磁盘空间 2.对dml(update delete insert)语句的效率影响--需要同时维护索引文件 在哪些列上适合添加索引: 较频繁的作为查询条件字段应该创建索引 select * from emp where empno = 1 唯一性太差的字段不适合单独创建索引,即使频繁作为查询条件 select * from emp where sex = ‘男‘ 更新非常频繁的字段不适合创建索引 select * from emp where logincount = 1 不会出现在WHERE子句中字段不该创建索 总结:(满足以下条件的字段,才应该创建索引) a: 肯定在where条经常使用 b: 该字段的内容不是唯一的几个值(比如性别) c: 字段内容不是频繁变化 索引的使用: 查询时使用索引最重要的条件是查询条件中需要使用索引。 下列几种情况下有可能使用到索引: 1.对于创建的多列索引,只要查询条件使用了最左边的列,索引一般就会被使用。 alter table dept add index my_ind (dname,loc); //dname左边的列,loc就是右边的列 explain select * from dept where loc=‘aaa‘\G //这样则不会使用索引 2.对于使用like的查询,查询如果是‘%aaa‘不会使用到索引,而‘aaa%‘会使用到索引。 (在模糊查询时,关键字最前面不能使用%或者_这样的字符,如果一定要前面有变化的值,则考虑使用全文索引->sphinx.) explain select * from dept where dname like ‘%aaa‘\G 下列的表将不使用索引: 1.如果条件中有or,即使其中有条件带索引也不会使用(即要求使用的所有字段,都必须建立索引, 我们建议大家尽量避免使用or关键字)。 2.对于多列索引,不是使用的第一部分,则不会使用索引。 3.like查询是以%开头。 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。(添加时,字符串必须‘‘) 5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引。 查看索引的使用情况: show status like‘Handler_read%‘; handler_read_key:这个值越高越好,越高表示使用索引查询到的次数。 handler_read_rnd_next:这个值越高,说明查询低效。 *************************************************************************************** 选择合适的存储引擎: myisam:表对事务的要求不高,同时操作是以查询和添加为主(如bbs的发帖表和回复表)。 innoda:对事务要求高,保存的数据都是重要数据(如订单表、账号表)。 memory:数据变化非常频繁,不需要入库,同时频繁的查询和修改。 MyISAM、InnoDB的区别: 主要从事务管理、查询和添加速度、支持全文索引、锁机制、是否支持外键几个方面来说。 如果存储引擎是myisam,必须定时进行碎片整理。 create table test100(id int unsigned ,name varchar(32))engine=myisam; //利用myisam存储引擎创建表 我们应该定义对myisam进行整理 optimize table [表名]; //对表进行碎片整理 选择合适的数据类型: 在精度要求高的应用中,建议使用定点数来存储数值,以保证结果的准确性。 deciaml不要用float *************************************************************************************** 定时完成数据库备份: 1.手动备份数据库/表的方法: mysqldump -u [用户名] -p[密码] [数据库] > [文件路径] //在cmd控制台输入,备份的是数据库 如:mysqldump -u root -pabc123 temp > d:\temp.bak //文件名和扩展名可以随便写(-p和abc123是没有空格的) mysqldump -u [用户名] -p [密码] [数据库] [表名1] [表名2] [表名3] ... > [文件路径] //在cmd控制台输入,备份的是数据库中的某几张表 2.使用备份文件恢复数据: source d:dept.bak //在MySQL控制台输入 3.使用定时器自动完成备份(将备份数据的指令写入到bat文件--批处理文件,通过任务调度器定时执行): ----mytask.bat---- %mysql安装目录的bin的绝对路径(如果有空格需要用双引号将路径括起来)%/mysqldump -u root -pabc123 temp > d:\temp.bak 4.利用任务管理器定时运行批处理文件: 控制面板--任务管理 默认是以覆盖文件的方式,考虑如何以日期为文件名保存? *************************************************************************************** 表的分割:
当一个表很大,拥有海量的数据,可以将一个大表分割成多个小表。 水平表的分割(表的结构不发生变化): 比如将一张用户表分成三张表的时候,可以利用主键id%3的值来确定用户在哪张表上。 1.首先我创建三张表 user0 / user1 /user2 , 2.然后我再创建 uuid表,该表的作用就是提供自增的id。 create table user0( id int unsigned primary key , name varchar(32) not null default ‘‘, pwd varchar(32) not null default ‘‘) engine=myisam charset utf8; create table user1( id int unsigned primary key , name varchar(32) not null default ‘‘, pwd varchar(32) not null default ‘‘) engine=myisam charset utf8; create table user2( id int unsigned primary key , name varchar(32) not null default ‘‘, pwd varchar(32) not null default ‘‘) engine=myisam charset utf8; create table uuid( id int unsigned primary key auto_increment)engine=myisam charset utf8; 3.添加用户的时候根据id判断应该加入哪张表。 垂直表的分割(表的结构发生变化): 把某些表的某些字段(这些字段的查询量很大,而又不需要经常查询该字段),单独放到另外一张表,从而提高效率。 选择字段的一般原则是保小不保大,能用占用字节小的字段就不用大字段。 比如主键, 建议使用自增类型,这样省空间,空间就是效率。按4个字节和按32个字节定位一条记录,谁快谁慢太明显了。 涉及到几个表做join时,效果就更明显了。 ***特别注意:建议使用一个不含业务逻辑的id做主键。 *************************************************************************************** 对MySQL的配置优化: 最重要的参数就是内存,我们主要用的innodb引擎,所以下面两个参数调的很大。 innodb_additional_mem_pool_size = 64M innodb_buffer_pool_size =1G 对于myisam,需要调整key_buffer_size; 当然调整参数还是要看状态,用show status语句可以看到当前状态,以决定改调整哪些参数。 在my.ini修改端口3306,默认存储引擎和最大连接数。 *************************************************************************************** 读写分离: 如果数据库压力很大,一台机器支撑不了,那么可以用mysql复制实现多台机器同步,将数据库的压力分散(负载均衡)。 例如: 主库master用来写入,slave1—slave3都用来做select,每个数据库分担的压力小了很多; 要实现这种方式,需要程序特别设计,写都操作master,读都操作slave,给程序开发带来了额外负担; 当然目前已经有中间件来实现这个代理,对程序来读写哪些数据库是透明的,官方有个mysql-proxy,但是还是alpha版本的。 *************************************************************************************** 增量备份(5.0无法增量备份,5.1后可以): MySQL数据库会以二进制的形式,自动把用户对MySQL数据库的操作,记录到文件,当用户希望恢复的时候可以使用备份文件,进行恢复。 增量备份会记录增、删、改、建表操作,不会记录查询操作(查询不改变数据库)。 记录的内容有操作的sql语句、操作的时间、position。 步骤: 1.配置my.ini文件或者my.cof,启用二进制备份(即增量备份) #The TCP/IP Port the MySQL Server will listen on port=3307 #指定备份文件的路径 log-bin=d:/binlog/mylog 2.启动MySQL得到文件 d:/binlog/mylog/mylog.index //索引文件,存放有哪些备份文件 d:/binlog/mylog/mylog.000001 //真正的备份文件 可以使用mysqlbinlog程序来查看备份文件的内容。 mysqlbinlog [备份文件路径] //在cmd中进入mysql目录下的bin中再执行 通过备份文件恢复数据库: 通过时间点来恢复: mysqlbinlog --stop-datetime="2013-01-14 18:20:21" d:/binlog/shunping.000001 | mysql -uroot -p 通过位置来恢复: mysqlbinlog --stop-position="110" d:/binlog/shunping.000001 | mysql -uroot -p 取出时间段操作: mysqlbinlog --start-datetime="2013-01-14 18:20:21" --stop-datetime="2013-01-14 18:40:21" d:/binlog/shunping.000001 | mysql -uroot -p 重新执行位置段的操作: mysqlbinlog --start-position="110" --stop-position="2345" d:/binlog/shunping.000001 | mysql -uroot -p 随着时间的推移,二进制文件里面的数据越来越大,所以要定期的做一些清理工作。 1.reset master 可以删除列于索引文件中的所有二进制日志,把二进制日志索引文件重新设置为空,并创建一个新的二进制日志文件 2.PURGE {MASTER | BINARY} LOGS TO ‘log_name‘ PURGE {MASTER | BINARY} LOGS BEFORE ‘date‘ 用于删除列于在指定的日志或日期之前的日志索引中的所有二进制日志。这些日志也会从记录在日志索引文件中的清单中被删除,这样被给定的日志成为第一个。 3.设置my.ini中的参数[mysqld]下的-EXPIRE_LOGS_DAYS。 此参数是设置日志的过期天数,过期的日志将会被自动删除, 如何在工作中将全备份和增量备份配合使用: 方案:每周一做一次全备份(mysqldump),启用增量备份,把过期时间设置为大于等于全备份的周期时间。 如果数据库全奔溃了,先全恢复,在增量恢复;如果误操作可以看增量日志进行增量恢复。(需要查看日志文件的详细内容) ***************************************************************************************