据库优化

对于一个以数据为中心的应用,数据库的好坏直接影响到程序的性能,因此数据库性能至关重要。一般来说,要保证数据库的效率,要做好以下四个方面的工作:

 数据库设计

 sql语句优化

 数据库参数配置

 恰当的硬件资源和操作系统

这个顺序也表现了这四个工作对性能影响的大小

SQL优化的一般步骤

通过show status命令了解各种SQL的执行频率。

定位执行效率较低的SQL语句-(重点select)通过explain分析低效率的SQL语句的执行情况,确定问题并采取相应的优化措施

u     sql语句的优化

sql语句有几类

ddl (数据定义语言) [create alter drop]

dml(数据操作语言)[insert delete upate ] select

dtl(数据事务语句) [commit rollback savepoint]

dcl(数据控制语句) [grant  revoke]

show status命令

该命令可以显示你的mysql数据库的当前状态.我们主要关心的是 “com”开头的指令

show status like ‘Com%’  <=> show session  status like ‘Com%’  //显示当前控制台的情况

show global  status like ‘Com%’ ; //显示数据库从启动到查询的次数

MySQL客户端连接成功后,通过使用show [session|global] status 命令可以提供服务器状态信息。其中的session来表示当前的连接的统计结果,global来表示自数据库上次启动至今的统计结果。默认是session级别的。
下面的例子:
show status like 
Com_%;


其中Com_XXX表示XXX语句所执行的次数。
重点注意:Com_select,Com_insert,Com_update,Com_delete通过这几个参数,可以容易地了解到当前数据库的应用是以插入更新为主还是以查询操作为主,以及各类的SQL大致的执行比例是多少。

还有几个常用的参数便于用户了解数据库的基本情况。
Connections
:试图连接MySQL服务器的次数
Uptime
:服务器工作的时间(单位秒)
Slow_queries
:慢查询的次数 (默认是10)

显示连接数据库次数

show status like  'Connections';

创建表

CREATE TABLE dept( /*部门表*/

deptno MEDIUMINT   UNSIGNED  NOT NULL  DEFAULT 0,

dname VARCHAR(20)  NOT NULL  DEFAULT "",

loc VARCHAR(13) NOT NULL DEFAULT ""

) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;

 

这里我创建了一个预处理函数

 

create function rand_string(n INT)

returns varchar(255)

begin

 declare chars_str varchar(100) default

   'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';

 declare return_str varchar(255) default '';

 declare i int default 0;

 while i < n do

   set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));

   set i = i + 1;

   end while;

  return return_str;

  end $$

delimiter ;

select rand_string(6);

随机产生部门编号

delimiter $$

drop  function rand_num $$

 

#这里我们又自定了一个函数

create function rand_num( )

returns int(5)

begin

 declare i int default 0;

 set i = floor(10+rand()*500);

return i;

  end $$

 

delimiter ;

select rand_num();

 

#emp表中插入记录(海量的数据)

delimiter $$

drop procedure insert_emp $$

 

create procedure insert_emp(in start int(10),in max_num int(10))

begin

declare i int default 0;

 set autocommit = 0; 

 repeat

 set i = i + 1;

 insert into emp values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());

  until i = max_num

 end repeat;

   commit;

 end $$

 

delimiter ;

#调用刚刚写好的函数, 1800000条记录,100001号开始

call insert_emp(100001,1800000);

 

索引

说起提高数据库性能,索引是最物美价廉的东西了。不用加内存,不用改程序,不用调sql,只要执行个正确的’create index’,查询速度就可能提高百倍千倍,这可真有诱惑力。可是天下没有免费的午餐,查询速度的提高是以插入、更新、删除的速度为代价的,这些写操作,增加了大量的I/O

目的,就是看看怎样处理,在海量表中,查询的速度很快!

select * from emp where empno=123456;

测试 ,比如我们把

select * from emp where empno=34678 

用了1.5秒,我现在优化.

快速体验emp表的 empno

alter table emp add primary key(empno);  //建立索引.

alter table emp drop primary key   //删除主键索引

然后,再查速度变快.

 

Explain

介绍一款非常重要工具 explain, 这个分析工具可以对 sql语句进行分析,可以预测你的sql执行的效率.

他的基本用法是:

explain sql语句\G

//根据返回的信息,我们可知,sql语句是否使用索引,从多少记录中取出,可以看到排序的方式.

会产生如下信息:
select_type:
表示查询的类型。
table:
输出结果集的表
type:
表示表的连接类型
possible_keys:
表示查询时,可能使用的索引
key:
表示实际使用的索引
key_len:
索引字段的长度
rows:
扫描的行数
Extra:
执行情况的描述和说明

在什么列上添加索引比较合适

  在经常查询的列上加索引.

  列的数据,内容就只有少数几个值,不太适合加索引.

  内容频繁变化,不合适加索引


create index myind on 表名 (1,2);

show indexes from 表名   //查询某表是否有索引。


l         如何检测你的索引是否有效

 

结论: Handler_read_key 越大越少

Handler_read_rnd_next 越小越好

注意

下列的表将不使用索引:
1
,如果条件中有or,即使其中有条件带索引也不会使用。
2
,对于多列索引,不是使用的第一部分,则不会使用索引。
3
like查询是以%开头
4
,如果列类型是字符串,那一定要在条件中将数据使用引号引用起来。否则不使用索引。
5
,如果mysql估计使用全表扫描要比使用索引快,则不使用索引。

常用sql优化

大批量插入数据
对于MyISAM
alter table table_name disable keys;
loading data;
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禁止排序

有些情况下,可以使用连接来替代子查询。
因为使用joinMySQL不需要在内存中创建临时表。如果想要在含有or的查询语句中利用索引,则or之间的每个条件列都必须用到索引,如果没有索引,则应该考虑增加索引   

select * from 表名 where 条件1=‘’ or 条件2=tt

在精度要求高的应用中,建议使用定点数(decimal)来存储数值,以保证结果的准确性

1000000.32 

create table sal(t1 float(10,2));

create table sal2(t1 decimal(10,2));

选择合适的存储引擎

MyISAM:默认的MySQL存储引擎。如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高。其优势是访问的速度快。

InnoDB:提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM,写的处理效率差一些并且会占用更多的磁盘空间。

选择合适的数据类型

在精度要求高的应用中,建议使用定点数来存储数值,以保证结果的准确性。

对于存储引擎是MyISAM的数据库,如果经常做删除和修改记录的操作,要定时执行optimize table table_name;功能对表进行碎片整理。


对表进行水平划分 

如果一个表的记录数太多了,比如上千万条,而且需要经常检索,那么我们就有必要化整为零了。如果我拆成100个表,那么每个表只有10万条记录。不改变表明,查询方便。

对表进行垂直划分

有些表记录数并不多,但是字段却很长,检索表时需要执行大量I/O,严重降低了性能。这个时候需要把大的字段拆分到另一个表,并且该表与原表是一对一的关系。不常用


选择适当的字段类型,特别是主键

选择字段的一般原则是保小不保大,能用占用字节小的字段就不用大字段。比如主键, 建议使用自增类型,这样省空间,空间就是效率!4个字节和按32个字节定位一条记录,谁快谁慢太明显了。

文件、图片等大文件用文件系统存储

数据库只存储路径。图片和文件存放在文件系统,甚至单独放在一台服务器(图床).

数据库参数配置

最重要的参数就是内存,我们主要用的innodb引擎,所以下面两个参数调的很大

  innodb_additional_mem_pool_size = 64M

  innodb_buffer_pool_size =1G

对于myisam,需要调整key_buffer_size

      当然调整参数还是要看状态,用show status语句可以看到当前状态,以决定改调整哪些参数

合理的硬件资源和操作系统  

如果你的机器内存超过4G,那么毋庸置疑应当采用64位操作系统和64mysql

读写分离

      如果数据库压力很大,一台机器支撑不了,那么可以用mysql复制实现多台机器同步,将数据库的压力分散。

      主库master用来写入,slave1—slave3都用来做select,每个数据库,分担的压力小了很多。

  要实现这种方式,需要程序特别设计,写都操作master,读都操作slave,给程序开发带来了额外负担。当然目前已经有中间件来实现这个代理,对程 序来读写哪些数据库是透明的。官方有个mysql-proxy,但是还是alpha版本的。新浪有个amobe for mysql,也可达到这个目的,结构如下: