当前位置: 代码迷 >> MySQL >> mysql优化小结(一)
  详细解决方案

mysql优化小结(一)

热度:884   发布时间:2016-05-05 17:13:31.0
mysql优化总结(一)

一、mysql的sql_mode模式:

(1)、该模式指的是sql模式可控制服务器操作的方式,并且sql模式存在于全局与会话级别。如果想知道全局级或会话级SQL模式的当前值,可以使用的语句为:

mysql> SELECT @@GLOBAL.sql_mode,mysql> SELECT @@SESSION.sql_mode。

(2)、如果想知道SQL模式的当前值,可以使用:SELECT @@sql_mode。

 

二、有的mysql使用的默认的存储引擎为InnoDB,有的 默认的存储引擎为MyISAM。这种两种是最常用的。这两种引擎有六大区别: 

 

存储类型:MyISAMInnoDB
  构成上的区别:1.每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名指出文件类型。
2..frm文件存储表定义。
3.数据文件的扩展名为.MYD (MYData)。
4.索引文件的扩展名是.MYI (MYIndex)。
基于磁盘的资源是InnoDB表空间数据文件和它的日志文件,InnoDB 表的大小只受限于操作系统文件的大小,一般为 2GB
事务处理上方面:MyISAM类型的表强调的是性能,其执行速度比InnoDB类型更快,但是不提供事务支持InnoDB提供事务支持事务,外部键等高级数据库功能
SELECT   UPDATE,INSERT,Delete操作如果执行大量的SELECT,MyISAM是更好的选择1.如果你的数据执行大量的INSERT或UPDATE,出于性能方面的考虑,应该使用InnoDB表
2.DELETE   FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
3.LOAD   TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用
对AUTO_INCREMENT的操作1.每表一个AUTO_INCREMEN列的内部处理。
2.MyISAM为INSERT和UPDATE操作自动更新这一列。这使得AUTO_INCREMENT列更快(至少10%)。在序列顶的值被删除之后就不能再利用。(当AUTO_INCREMENT列被定义为多列索引的最后一列,可以出现重使用从序列顶部删除的值的情况)。
3.AUTO_INCREMENT值可用ALTER TABLE或myisamch来重置
4.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引
5.更好和更快的auto_increment处理
1.如果你为一个表指定AUTO_INCREMENT列,在数据词典里的InnoDB表句柄包含一个名为自动增长计数器的计数器,它被用在为该列赋新值。
2.自动增长计数器仅被存储在主内存中,而不是存在磁盘上
3.关于该计算器的算法实现,请参考
4.AUTO_INCREMENT列在InnoDB里如何工作
表的具体行数select count(*) from table,MyISAM只要简单的读出保存好的行数,注意的是,当count(*)语句包含   where条件时,两种表的操作是一样的 InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行
  表锁

提供行锁(locking on row level),提供与 Oracle 类型一致的不加锁读取(non-locking read in SELECTs),另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”

 

 

三、为什么有大量数据操作的时候要用到InnoDB数据库?
所谓事务处理,就是原子性操作。
打个比方,支持事务处理的Innodb表,建设一个中,发帖是给积分的。你发了一个帖子执行一个insert语句,来插入帖子内容,插入后就要执行一个update语句来增加你的积分。假设一种特殊情况突然发生,insert成功了,update操作却没有被执行。也就是说你发了帖子却没有增加相应的积分。这就会造成用户不满。如果使用了事务处理,insert和update都放入到事务中去执行,这个时候,只有当insert和update两条语句都执行生成的时候才会将数据更新、写入到中,如果其中任何一条语句失败,那么就会回滚为初始状态,不执行写入。这样就保证了insert和update肯定是一同执行的。
mysiam表不支持事务处理,同时mysiam表不支持外键。外键不用说了吧?不知道的话,去网上查吧。
同时,在执行数据库写入的操作(insert,update,delete)的时候,mysiam表会锁表,而innodb表会锁行。通俗点说,就是你执行了一个update语句,那么mysiam表会将整个表都锁住,其他的insert和delete、update都会被拒之门外,等到这个update语句执行完成后才会被依次执行。
而锁行,就是说,你执行update语句是,只会将这一条记录进行锁定,只有针对这条记录的其他写入、更新操作会被阻塞并等待这条update语句执行完毕后再执行,针对其他记录的写入操作不会有影响。
因此,当你的数据库有大量的写入、更新操作而查询比较少或者数据完整性要求比较高的时候就选择innodb表。当你的数据库主要以查询为主,相比较而言更新和写入比较少,并且业务方面数据完整性要求不那么严格,就选择mysiam表。因为mysiam表的查询操作效率和速度都比innodb要快

 

 四、千万级数据量的数据大表该如何优化?

1).数据的容量:1-3年内会大概多少条数据,每条数据大概多少字节;
2).数据项:是否有大字段,那些字段的值是否经常被更新;
3).数据查询SQL条件:哪些数据项的列名称经常出现在WHERE、GROUP BY、ORDER BY子句中等;
4).数据更新类SQL条件:有多少列经常出现UPDATE或DELETE 的WHERE子句中;
5).SQL量的统计比,如:SELECT:UPDATE+DELETE:INSERT=多少?
6).预计大表及相关联的SQL,每天总的执行量在何数量级?
7).表中的数据:更新为主的业务 还是 查询为主的业务
8).打算采用什么数据库物理服务器,以及数据库服务器架构?
9).并发如何?
10).存储引擎选择InnoDB还是MyISAM?
大致明白以上10个问题,至于如何设计此类的大表,应该什么都清楚了!
至于优化若是指创建好的表,不能变动表结构的话,那建议InnoDB引擎,多利用点内存,减轻磁盘IO负载,因为IO往往是数据库服务器的瓶颈,另外对优化索引结构去解决性能问题的话,建议优先考虑修改类SQL语句,使他们更快些,不得已只靠索引组织结构的方式,当然此话前提是,索引已经创建的非常好,若是读为主,可以考虑打query_cache,以及调整一些参数值:sort_buffer_size,read_buffer_size,read_rnd_buffer_size,join_buffer_size

 

 五、MYSQL IN 和 EXISTS的优化规则:

当B表的数据集小于A表的数据集时,用in优于exists,当A表的数据集系小于B表的数据集时,用exists优于in。

优化原则:in小表驱动大表,即小的数据集驱动大的数据集。

示例select * from A where id in(select if from B)等价于for select * from A where B 和for select * from A where A.id = B.id。

当B表的数据集必须小于A表的数据集时,用in优于exists。

select * from A where exists (select 1 from B where B.id = A.id)

当A表的数据集系小于B表的数据集时,用in优于exists。

 

六、mysql order by 语句用法与优化详解:

order by keyword 是用来给记录中的数据根据关键字进行分类的。

SELECT _name(s) FROM table_name ORDER BY column_name。

1).ORDER BY的索引优化。

SELECT [column1],[column2],...FROM[TABLE] ORDER BY [sort];

2).WHERE+ORDER BY的索引优化。

SELECT [column1],[column2],...FROM[TABLE] WHERE [CcolumnX] = [value] ORDER BY [sort];

建立一个联合索引(columnX,sort)来实现order by优化。如果columnX对应多个值,就无法用上面的索引来实现order by的优化。

3).WHERE+多个字段ORDER BY

SELECT * FROM [TABLE] WHERE uid = 1 ORDER BY x,y LIMIT 0,10;

建立索引(uid,x,y)实现order by的优化,比建立(x,y,uid)索引效果要要得多。

在有些情况下,mysql可以使用一个索引来满足order by子句,而不需要额外的排序。where 条件和order by使用相同的索引,并且order by的顺序和索引顺序相同,并且order by的字段都是升序或者都是降序。

select * from t1 order by key_part1,keypart2,...;

select * from t1 where key_part1 = 1order by key_part1 DESC,key_part2 DESC;

select * from t1 where order by key_part1 DESC,key_part2 DESC;

但是一下情况不适用索引:

①SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
--order by的字段混合ASC和DESC
②SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
--用于查询行的关键字与ORDER BY中所使用的不相同
③SELECT * FROM t1 ORDER BY key1, key2;
--对不同的关键字使用ORDER BY

 

七、InnoDB基本优化配置

InnoDB设置

1.innodb_buffer_pool_size  —— 默认值为 128M. 这是最主要的优化选项,因为它指定 InnoDB 使用多少内存来加载数据和索引(data+indexes). 针对专用MySQL服务器,建议指定为物理内存的 50-80%这个范围. 例如,拥有64GB物理内存的机器,缓存池应该设置为50GB左右.
如果将该值设置得更大可能会存在风险,比如没有足够的空闲内存留给操作系统和依赖文件系统缓存的某些MySQL子系统(subsystem),包括二进制日志(binary logs),InnoDB事务日志(transaction logs)等.

2.innodb_log_file_size —— 默认值为 48M. 有很高写入吞吐量的系统需要增加该值以允许后台检查点活动在更长的时间周期内平滑写入,得以改进性能. 将此值设置为4G以下是很安全的. 过去的实践表明,日志文件太大的缺点是增加了崩溃时所需的修复时间,但这在5.5和5.6中已得到重大改进.

3.innodb_flush_method  —— 默认值为 fdatasync. 如果使用 硬件RAID磁盘控制器, 可能需要设置为 O_DIRECT. 这在读取InnoDB缓冲池时可防止“双缓冲(double buffering)”效应,否则会在文件系统缓存与InnoDB缓存间形成2个副本(copy).
如果不使用硬件RAID控制器,或者使用SAN存储时, O_DIRECT 可能会导致性能下降.MySQL用户手册 和 Bug #54306  详细地说明了这一点.

4.innodb_flush_neighbors —— 默认值为 1. 在SSD存储上应设置为0(禁用) ,因为使用顺序IO没有任何性能收益. 在使用RAID的某些硬件上也应该禁用此设置,因为逻辑上连续的块在物理磁盘上并不能保证也是连续的.

5.innodb_io_capacity and innodb_io_capacity_max —— 这些设置会影响InnoDB每秒在后台执行多少操作. 如果你深度了解硬件性能(如每秒可以执行多少次IO操作),则使用这些功能是很可取的,而不是让它闲着.


有一个很好的类比示例:  假如某次航班一张票也没有卖出去 —— 那么让稍后航班的一些人乘坐该次航班,有可能是很好的策略,以防后面遇到恶劣的天气. 即有机会就将后台操作顺便处理了,以减少同稍后可能的实时操作产生竞争.

 有一个很简单的计算:  如果每个磁盘每秒读写(IOPS)可以达到 200次, 则拥有10个磁盘的 RAID10 磁盘阵列IOPS理论上 =(10/2)* 200 = 1000. 我说它“很简单”,是因为RAID控制器通常能够提供额外的合并,并有效提高IOPS能力. 对于SSD磁盘,IOPS可以轻松达到好几千.

 将这两个值设置得太大可能会存在某些风险,你肯定不希望后台操作妨碍了前台任务IO操作的性能. 过去的经验表明,将这两个值设置的太高,InnoDB持有的内部锁会导致性能降低(按我了解到的信息,在MySQL5.6中这得到了很大的改进).

innodb_lru_scan_depth - 默认值为 1024. 这是mysql 5.6中引入的一个新选项. Mark Callaghan  提供了 一些配置建议. 简单来说,如果增大了 innodb_io_capacity 值, 应该同时增加 innodb_lru_scan_depth.


复制(Replication)

假如服务器要支持主从复制,或按时间点恢复,在这种情况下,我们需要:

1.log-bin —— 启用二进制日志. 默认情况下二进制日志不是事故安全的(not crash safe),但如同我 以前的文章所说, 我建议大多数用户应该以稳定性为目标. 在这种情况下,你还需要启用: sync_binlog=1, sync_relay_log=1, relay-log-info-repository=TABLE and master-info-repository=TABLE.

2.expire-logs-days —— 默认旧日志会一直保留. 我推荐设置为 1-10 天. 保存更长的时间并没有太多用处,因为从备份中恢复会快得多.

3.server-id —— 在一个主从复制体系(replication topology )中的所有服务器都必须设置唯一的 server-id.

4.binlog_format=ROW  —— 修改为基于行的复制. 我最近写的另一篇 基于行的复制 ,里面叙述了我真的很喜欢它的原因,因为它可以通过减少资源锁定提高性能. 此外还需要启用两个附加设置:  transaction-isolation=READ-COMMITTED and  innodb_autoinc_lock_mode = 2.

其他配置(Misc)

1.timezone=GMT  将时区设置为格林尼治时间. 越来越多的系统管理员建议将所有服务器都设置为 格林尼治时间(GMT). 我个人非常喜欢这点,因为现在几乎所有的业务都是全球化的. 设置为你本地的时区似乎是有点武断的.

2.character-set-server=utf8mb4 and collation-server=utf8mb4_general_ci  如之前的 文章所讲述的 ,utf8 编码对新应用来说是更好的默认选项. 您还可以设置 skip-character-set-client-handshake 以忽略应用程序想要设置的其他字符集(character-set).

3.sql-mode —— MySQL默认对不规范的数据很宽容,并且会静默地截断数据. 在我 之前的一篇文章中, 我提到新应用程序最好设置为:  

STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,
 NO_AUTO_CREATE_USER,NO_AUTO_VALUE_ON_ZERO,
 NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,
 NO_ZERO_IN_DATE,ONLY_FULL_GROUP_BY.

4.skip-name-resolve —— 禁用反向域名解析. DNS解析在某些系统上可能有点慢/不稳定,所以如果不需要基于主机名的授权,我建议避免这种解析.

5.max_connect_errors —— Todd Farmer 写道 :“[这个功能]提供了没有实际意义的暴力访问攻击保护”. 事实上当设置skip-name-resolve 时, max_connect_errors 甚至不起作用(见上一段所述).

 防火墙是更合适的解决方案,通常我将3306端口屏蔽,不管是公网的还是内网的端口,只有特定的应用程序可以访问和连接到MySQL.
 我通常会设置 max_connect_errors=100000, 这样我可以避免任何“双重配置”,保证它不会碍事.

6.max-connections ——默认值是151. 我看到很多用户将他设置得比较大,大多在 300 ~ 1000之间.
 通常不可避免地这个值会被设置得更大,但让我有点紧张的是, 16核的机器在IO阻塞的情况下也只有大约 2x~10x 的连接执行能力.
 你可能希望,许多打开的连接都是空闲并休眠的. 但如果他们都处于活跃状态的话,可能会创建大量新的线程(thread-thrash).
 如果条件允许,可以为应用程序配置优化数据库连接池(connection-pools)来解决这个问题,而不是打开并保持大量连接;
 当然那些不使用连接池(non-pooled ), 迅速打开,执行任务后又尽可能快地关闭连接的应用也是可行的.
 从5.5开始的另一种解决方案(在MySQL社区版和企业版之间有一些差异) 是使用 线程池插件.


总结(Conclusion)

假设MySQL服务器的配置为:
1.64GB物理内存
2.硬件RAID控制器(假设每秒IO可达 2000 IOPS)
3.需要主从复制(Replication)
4.新的应用(eg. 非遗留系统)
5.有防火墙保护
6.不需要基于域名(hostnames,主机名)的授权
7.全球化应用,并不想固定在某一时区.
8.想要程序可靠稳定(durable).

则配置可能如下所示:


# InnoDB settings
innodb_buffer_pool_size=50G
innodb_log_file_size=2G
innodb_flush_method=O_DIRECT
innodb_io_capacity=2000
innodb_io_capacity_max=6000
innodb_lru_scan_depth=2000
# Binary log/replication
log-bin
sync_binlog=1
sync_relay_log=1
relay-log-info-repository=TABLE
master-info-repository=TABLE
expire_logs_days=10
binlog_format=ROW
transaction-isolation=READ-COMMITTED
innodb_autoinc_lock_mode = 2
# Other
timezone=GMT
character-set-server=utf8
collation-server=utf8_general_ci
sql-mode="STRICT_TRANS_TABLES,
 ERROR_FOR_DIVISION_BY_ZERO,
 NO_AUTO_CREATE_USER,
 NO_AUTO_VALUE_ON_ZERO,
 NO_ENGINE_SUBSTITUTION,
 NO_ZERO_DATE,
 NO_ZERO_IN_DATE,
 ONLY_FULL_GROUP_BY"
skip-name_resolve
max-connect-errors=100000
max-connections=500
# Unique to this machine
server-id=123

 

 

 

 

 

 

 

  相关解决方案