MYSQL设计优化

  1. 云栖社区>
  2. 博客>
  3. 正文

MYSQL设计优化

技术mix呢 2017-10-20 21:22:00 浏览785

本文将从各方面介绍优化mysql设计的一些方式。

1、优化sql语句

(1)定位须要优化的sql语句

1)show status统计SQL语句频率

对Myisam和Innodb存储引擎都计数的參数:

SHOW STATUS能够依据须要显示session级别的统计结果和global级别的统计结果。

1.Com_select  运行select操作的次数,一次查询仅仅累加1;
2.Com_insert 运行insert操作的次数,对于批量插入的insert操作,仅仅累加一次。
3.Com_update 运行update操作的次数。
4.Com_delete 运行delete操作的次数;

运行如:SHOW STATUS WHERE Variable_name = 'Com_select';


对Innodb存储引擎计数的參数(计算的方式不一样):
1.Innodb_rows_read select查询返回的行数;
2.Innodb_rows_inserted 运行Insert操作插入的行数;
3.Innodb_rows_updated 运行update操作更新的行数;
4.Innodb_rows_deleted 运行delete操作删除的行数;
通过以上几个參数,能够非常easy的了解当前数据库的应用是以插入更新为主还是以查询操作为主,以及各种类型的SQL大致的运行比例是多少。
对于更新操作的计数。是对运行次数的计数,不论提交还是回滚都会累加。



对于事务型的參数
1.Com_commit 事务提交次数
2.Com_rollback 事务回滚次数
对于回滚操作很频繁的数据库,可能意味着应用编写存在问题。



数据库的基本情况的參数:
1.Connections 试图连接Mysqlserver的次数
2.Uptime server工作时间

3.Slow_queries 慢查询的次数


2)定位运行效率较低的SQL语句

两种方式定位运行效率较低的SQL语句:
(1)通过慢查询日志定位那些运行效率较低的sql语句(须要查询结束后)。用--log-slow-queries[=file_name]选项启动时,mysqld写一个包括全部运行时间超过long_query_time秒的SQL语句的日志文件.
(2)使用show processlist命令查看当前MySQL在进行的线程。包含线程的状态,是否锁表等等,能够实时的查看SQL运行情况。同一时候对一些锁表操作进行优化。

3)EXPLAIN命令分析SQL语句

通过explain或者desc 获取MySQL怎样运行SELECT语句的信息
EXPLAIN SELECT * FROM message a LEFT JOIN mytable b ON a.id = b.id WHERE a.id=1;
返回结果
+--------+---------------+-------+-------+--------------+----------------+-----------+-------+--------+--------------+
|   id   | select_type   | table | type  | possible_keys| key            | key_len   | ref   |  rows  |  Extra       |
+--------+---------------+-------+-------+--------------+----------------+-----------+-------+--------+--------------+
|   1    | SIMPLE        | a     | const | PRIMARY      | PRIMARY        | 4         | const | 1      |              |
|   1    | SIMPLE        | b     | ALL    | NULL             | NULL           | NULL      |       | 9999   |              |
+--------+---------------+-------+-------+--------------+----------------+-----------+-------+--------+--------------+
select_type:select 类型
table:      输出结果集的表
type:       表示表的连接类型
①当表中仅有一行是最佳的连接类型;
②当select操作中使用索引进行表连接时type的值为ref;
③当select的表连接没有使用索引时,常常会看到type的值为ALL,表示对该表进行了全表扫描。这时须要考虑通过创建索引来提高表连接的效率。

possible_keys:表示查询时,能够使用的索引列.
key:          表示使用的索引
key_len:      索引长度
rows:         扫描范围
Extra:运行情况的说明和描写叙述

比如上面的样例。由于是对b表的全表扫描导致效率下降,则对b表的 id 字段创建索引。查询须要扫描的行数将会降低。
返回结果
+--------+---------------+-------+-------+--------------+----------------+-----------+-------+--------+--------------+
|   id   | select_type   | table | type  | possible_keys| key            | key_len   | ref   |  rows  |  Extra       |
+--------+---------------+-------+-------+--------------+----------------+-----------+-------+--------+--------------+
|   1    | SIMPLE        | a     | const | PRIMARY      | PRIMARY        | 4         | const | 1      |              |
|   1    | SIMPLE        | b     | const | PRIMARY      | PRIMARY        | 4         | const | 1      |              |
+--------+---------------+-------+-------+--------------+----------------+-----------+-------+--------+--------------+

(2)sql语句优化方式

1)大批量插入数据 

1)对于Myisam类型的表,能够通过下面步骤高速的导入大量的数据。

 
前后两个命令用来打开或者关闭Myisam表非唯一索引的更新。在导入大量的数据到一个非空的Myisam表时,通过设置这两个命令。能够提高导入的效率。
ALTER TABLE mytable DISABLE KEYS;
INSERT INTO mytable(id, username, city, age) VALUES(1, 'name1', 'city1', 10),(2, 'name2', 'city2', 20),(3, 'name3', 'city3', 30);
ALTER TABLE mytable ENABLE KEYS;
对于导入大量数据到一个空的Myisam表。默认就是先导入数据然后才创建索引的,所以不用进行设置。

2)对于Innodb类型的表。我们有下面几种方式能够提高导入的效率(对Innodb类型的表。上面的方式并不能提高导入数据的效率)
①由于Innodb类型的表是依照主键的顺序保存的,所以将导入的数据依照主键的顺序排列,能够有效的提高导入数据的效率。
假设Innodb表没有主键,那么系统会默认创建一个内部列作为主键。所以假设能够给表创建一个主键,将能够利用这个优势提高导入数据的效率。
②在导入数据前运行SET UNIQUE_CHECKS=0,关闭唯一性校验,在导入结束后运行SET UNIQUE_CHECKS=1,恢复唯一性校验,能够提高导入的效率。
SET UNIQUE_CHECKS=0;
SET UNIQUE_CHECKS=1;
③假设使用自己主动提交的方式,建议在导入前运行SET AUTOCOMMIT=0,关闭自己主动提交,导入结束后再运行SET AUTOCOMMIT=1。打开自己主动提交,也能够提高导入的效率。


SET AUTOCOMMIT=0;

SET AUTOCOMMIT=1;


2)优化insert语句

1)假设同一时候插入非常多行,请使用多个值的INSERT语句。这比使用分开INSERT语句快(在一些情况中几倍)。
Insert into test values(1,2),(1,3),(1,4)…
2)假设从不同客户插入非常多行。能通过使用INSERT DELAYED 语句得到更高的速度。 
Delayed 的含义是让insert 语句立即运行。事实上数据都被放在内存的队列中,并没有真正写入磁盘;这比每条语句分别插入要快的多。
LOW_PRIORITY 刚好相反。在全部其它用户对表的读写完后才进行插入。
3)将索引文件和数据文件分在不同的磁盘上存放(利用建表中的选项);
4)假设批量插入,能够添加bulk_insert_buffer_size变量值的方法来提快速度。可是,这仅仅能对myisam表使用。
5)当从一个文本文件装载一个表时,使用LOAD DATA INFILE。这通常比使用非常多INSERT语句快20倍;
6)依据应用情况使用 replace 语句取代 insert;
7)依据应用情况使用 ignore keyword忽略反复记录。
INSERT DELAYED INTO mytable(id, username, city, age) VALUES(4, 'name4', 'city4', 40);
INSERT LOW_PRIORITY INTO mytable(id, username, city, age) VALUES(5, 'name5', 'city5', 50);
REPLACE INTO mytable(id, username, city, age) VALUES(5, 'name5', 'city5', 50);

INSERT IGNORE INTO mytable(id, username, city, age) VALUES(5, 'name5', 'city5', 50);


3)优化group by语句

默认情况下,MySQL排序全部GROUP BY col1。col2,....(如同指定了ORDER BY  col1,col2。...)
假设查询包含GROUP BY但想避免排序结果的消耗,能够指定 ORDER BY NULL禁止排序。


比如:

SELECT * FROM mytable GROUP BY username ORDER BY NULL;


4)优化order by语句

下面情况能够使用索引:
SELECT * FROM t1 ORDER BY key_part1,key_part2,... ;  --order by字段都为同一组合索引的一部分
SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC, --key_part2 DESC;--where条件和order by使用同样的索引字段 
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;--order by的的全部字段顺序同样
下面情况不使用索引:
1)SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC。--order by的字段混合ASC和DESC
2)SELECT * FROM t1 WHERE key2=constant ORDER BY key1;--用于查询行的keyword与ORDER BY中所使用的不同样

3)SELECT * FROM t1 ORDER BY key1, key2;--对不同的keyword使用ORDER BY


5)优化join语句 

Mysql4.1開始支持SQL的子查询。这个技术能够使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在还有一个查询中。
使用子查询能够一次性的完毕非常多逻辑上须要多个步骤才干完毕的SQL操作,同一时候也能够避免事务或者表锁死,而且写起来也非常easy。
可是,有些情况下。子查询能够被更有效率的连接(JOIN).. 替代。

如果我们要将全部没有订单记录的用户取出来,能够用以下这个查询完毕:
SELECT * FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )
假设使用连接(JOIN).. 来完毕这个查询工作,速度将会快非常多。

尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好,查询例如以下:
SELECT * FROM customerinfo
LEFT JOIN salesinfo ON customerinfo.CustomerID=salesinfo.CustomerID
WHERE salesinfo.CustomerID IS NULL 

连接(JOIN).. 之所以更有效率一些。是由于 MySQL不须要在内存中创建暂时表来完毕这个逻辑上的须要两个步骤的查询工作。


(3)调度优先级

MySQL提供语句调节复来改变语句(insert update delete)调度的优先级。能够依据应用是以查询还是更新为主,来协调自多个client的操作。
(调度策略的改动主要是针对Myisam存储引擎的;对于Innodb存储引擎,语句的运行是由获得行锁的顺序来决定的)
默认调度策略
1)写入操作优先于读取操作。
2)对某张数据表的写入操作某一时刻仅仅能发生一次,写入请求依据它们到达的次序来处理。
3)对某张数据表的多个读取操作能够同一时候地进行。
查询为主的调度策略
1)LOW_PRIORITY keyword应用于 DELETE 、 INSERT 、 LOAD DATA 、 REPLACE和UPDATE 。
2)HIGH_PRIORITYkeyword应用于SELECT。


3)DELAYEDkeyword应用于INSERT和REPLACE语句。
更新等操作被设置为LOW_PRIORITY(低优先级)请求。那么读取操作优先级会高于写操作。 

SELECT操作被设置为HIGH_PRIORITY(高优先级),则也会调整SELECT到正在等待的写入操作之前。

假设写入者在等待的时候。第二个读取者到达了,那么就同意第二个读取者插到写入者之前。仅仅有在没有其他的读取者的时候,才同意写入者開始操作。

设置方式:

1)启动方式

假设使用--low-priority-updates选项来启动server,则全部支持LOW_PRIORITY选项的语句都默认地依照低优先级来处理。

2)sql方式

通过使用INSERT HIGH_PRIORITY来把INSERT语句提高到正常的写入优先级,能够消除该选项对单个INSERT语句的影响。

如INSERT HIGH_PRIORITY INTO mytable(id, username, city, age) VALUES(7, 'name7', 'city7', 70);

注意:

可能存在LOW_PRIORITY的写入操作永远被堵塞的情况。


2、优化数据表

(1)优化表的数据类型

函数PROCEDURE ANALYSE()能够对数据表中的列的数据类型提出优化建议。(比方冗余字段的建议)
语法:
SELECT * FROM tbl_name PROCEDURE ANALYSE(); --输出的对数据表中的每一列的数据类型提出优化建议

SELECT * FROM tbl_name PROCEDURE ANALYSE(16,256);--不要为那些包括的值多于16个和256字节的ENUM类型提出建议。(假设没有这种限制。输出信息可能非常长;且ENUM定义通常非常难阅读)

使用如:mysql> DESC user_account;

+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| USERID    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| USERNAME  | varchar(10)      | NO   |     | NULL    |                |
| PASSSWORD | varchar(30)      | NO   |     | NULL    |                |
| GROUPNAME | varchar(10)      | YES  |     | NULL    |                |
+-----------+------------------+------+-----+---------+----------------+

mysql> select * from user_account PROCEDURE ANALYSE(1)\G;

*************************** 1. row ***************************
             Field_name: ibatis.user_account.USERID
              Min_value: 1
              Max_value: 103
             Min_length: 1
             Max_length: 3
       Empties_or_zeros: 0
                  Nulls: 0
Avg_value_or_avg_length: 51.7500
                    Std: 50.2562
Optimal_fieldtype: TINYINT(3) UNSIGNED NOT NULL

......

第一行输出分析出ibatis.user_account.USERID列最小值1,最大值103,最小长度1。最大长度3...,字段的优化建议:将该字段的数据类型改成TINYINT(3) UNSIGNED NOT NULL

(2)拆分表提高訪问效率

主要是针对Myisam类型的表,拆分有两种方式:
纵向拆分:
纵向拆分是仅仅依照顾用訪问的频度,将表中常常訪问的字段和不常常訪问的字段拆分成两个表,常常訪问的字段尽量是定长的。这样能够有效的提高表的查询和更新的效率。


横向拆分:
横向拆分是指依照顾用的情况,有目的的将数据横向拆分成几个表或者通过分区分到多个分区中,这样能够有效的避免Myisam表的读取和更新导致的锁问题。

(3)规范化和逆规范化

依据实际情况考虑下面两个需求:
规范化的需求:
规范化设计强调数据的独立性。数据应该尽可能少地冗余,由于存在过多的冗余数据,意味着要占用了很多其它的物理空间。同一时候也对数据的维护和一致性检查带来了麻烦。


逆规范化的需求:
对于查询操作非常多的应用,一次查询可能须要訪问多表进行,假设通过冗余同样数据纪录在一个表中,更新的代价添加不多,可是查询操作效率能够有明显提高。

(4)内存暂时表

使用create temporary table语法创建的暂时表,是基于连接的表。数据保存在内存里面,当连接断开后。表会被删除。
比方,当须要做统计分析时。假设统计的数据量不是非常大,使用insert和select把数据移到暂时表中,比直接在表上做统计要效率更高。

(5)选择合适的表类型

1)假设应用出现比較严重的锁冲突。请考虑是否更改存储引擎到innodb。行锁机制能够降低锁冲突。


2)假设应用查询操作非常多。且不须要事务型业务,则能够考虑使用Myisam存储引擎。

3、优化client应用

(1)使用连接池 

对于訪问数据库来说,建立连接的代价比較昂贵,因此,我们有必要建立"连接池"以提高訪问的性能。
我们能够把连接当作对象或者设备,池中又有很多已经建立的连接,訪问本来须要与数据库的连接的地方。都改为和池相连,池暂时分配连接供訪问使用,结果返回后,訪问将连接交还。


(2)避免反复检索

理清訪问逻辑,须要对同样表的訪问,尽量集中在同样sql訪问。一次提取结果,降低对数据库的反复訪问。

4、优化数据库server

(1)使用mysql查询缓存

作用:
查询缓存会保存SELECT查询的结果。假设随后收到一个同样的查询 请求,server从查询缓存中又一次得到查询结果,而不须要又一次解析和运行查询。
适用场景:
不发生数据更新的表。当表更改(包含表结构和表数据)后。查询缓存值的相关条目会被清空。



查询缓存:
SHOW VARIABLES LIKE '%query_cache%'; (或者 SHOW VARIABLES WHERE Variable_name LIKE '%query_cache%';) :
have_query_cache  表示server在安装时已经配置了快速缓存
query_cache_size  表示缓存区大小,单位为字节(1024字节为1KB)
query_cache_type  值从0到2,含义分别为 
            0或者off(缓存关闭)
                  1或者on(缓存打开,使用sql_no_cache的select除外)
                  2或者demand(仅仅有带sql_cache的select语句提供快速缓存)

设置查询缓存:

SET GLOBAL query_cache_size=1024*50; 单位字节。1024字节为 1KB,query_cache_size大小的设置必须大于40KB
实时监视查询缓存:
SHOW STATUS LIKE '%Qcache%';
Qcache_queries_in_cache  在缓存中已注冊的查询数目
Qcache_inserts           被增加到缓存中的查询数目
Qcache_hits              缓存採样数数目
Qcache_lowmem_prunes     由于缺少内存而被从缓存中删除的查询数目
Qcache_not_cached        没有被缓存的查询数目 (不能被缓存的,或因为 QUERY_CACHE_TYPE)
Qcache_free_memory       查询缓存的空暇内存总数
Qcache_free_blocks       查询缓存中的空暇内存块的数目
Qcache_total_blocks      查询缓存中的块的总数目

(2)使用机器快速缓存

Cache(快速缓存)、Memory(内存)、Hard disk(硬盘)都是数据存取单元,但存取速度却有非常大差异。呈依次递减的顺序。
对于CPU来说,它能够从距离自己近期的Cache快速地存取数据。而不是从内存和硬盘以低几个数量级的速度来存取数据。


而Cache中所存储的数据,往往是CPU要重复存取的数据,有特定的机制(或程序)来保证Cache内数据的命中率(Hit Rate)。
因此。CPU存取数据的速度在应用快速缓存后得到了巨大的提高。



由于将数据写入快速缓存的任务由Cache Manager负责,所以对用户来说快速缓存的内容肯定是仅仅读的。
须要你做的工作非常少。程序中的SQL语句和直接訪问DBMS时没有分别,返回的结果也看不出有什么区别。

而数据库厂商往往会在DB Server的配置文件里提供与Cache相关的參数,通过改动它们,可针对我们的应用优化Cache的管理。


(3)均衡负载

1)读写分流(主从复制)
利用mysql的主从复制能够有效的分流更新操作和查询操作。
详细的实现是一个主server,承担更新操作(为了数据的一致性),多台从server,承担查询操作(多台从server一方面用来确保可用性。一方面能够创建不同的索引满足不同查询的须要),主从之间通过复制实现数据的同步。
主从复制优化:
对于主从之间不须要复制所有表的情况,能够通过在主的server上搭建一个虚拟的从server。将须要拷贝到从server的表设置成blackhole引擎,然后定义replicate-do-table參数仅仅复制这些表。这样就过滤出须要复制的binlog。降低了传输binlog的带宽。由于搭建的虚拟的从server仅仅起到过滤binlog的作用,并没有实际纪录不论什么数据。所以对主数据库server的性能影响也很的有限。
注意:
通过复制分流查询的存在的问题是主数据库上更新频繁或者网络出现故障的时候,主从之间的数据可能存在差异,造成查询结果的异议,应用在设计的时候须要有所考虑。


2)分布式的数据库
分布式的数据库设计适合大数据量,负载高的情况,可平均多台server的负载,有良好的扩展性和高效性(读写效率)。
分布式事务:
mysql从5.0.3開始支持分布式事务,眼下分布式事务仅仅对Innodb存储引擎支持。
也能够使用mysql的Cluster功能(NDB引擎)或者使用自己用mysql api来实现全局事务。




本文转自mfrbuaa博客园博客,原文链接:http://www.cnblogs.com/mfrbuaa/p/5175003.html,如需转载请自行联系原作者