Mysql 优化

概述
互联网服务中最容易遇到瓶颈的就是数据库,在数据库调优上我们需要关注以下几个方向
1. 架构调优 2 mysql 配置调优 3 设计优化 4 sql 索引优化
架构调优
在架构调优上,我们需要关注的是提高整个mysql 的吞吐量与可用性
 
mysql 配置调优
 
per thread buffers 线程缓存调优
read_buffer_size
该参数用于表的顺序扫描,表示每个线程分配的缓冲区大小。比如,在进行全表扫描时,MySQL会按照数据的存储顺序依次读取数据块,每次读取的数据块首先会暂存在 read_buffer_size 中,当 buffer 空间被写满或者
或者全部数据读取结束后,再将buffer中的数据返回给上层调用者,以提高效率。默认为 128KB 。这个参数不要设置过大,一般在 128~256KB 即可。
 
read_rnd_buffer_size
该参数用于表的随机读取,表示每个线程分配的缓冲区大小。比如,按照一个非索引字段做orderby排序操作时,就会利用这个缓冲区来暂存读取的数据。默认为 256KB 。这个参数不要设置过大,一般在 128~256KB 即可。
 
sort_buffer_size
在表进行orderby和 groupby 排序操作时,由于排序的字段没有索引,会出现 Usingfilesort ,为了提高性能,可用此参数增加每个线程分配的缓冲区大小。默认为 2MB 。这个参数不要设置过大,一般在 128~256KB 即可。
 
thread_stack
该参数表示每个线程的堆栈大小。默认为192KB。如果是 64 位操作系统,设置为 256KB 即可,这个参数不要设置过大。
 
join_buffer_size
表进行join连接操作时,如果关联的字段没有索引,会出现 Usingjoinbuffer ,为了提高性能,可用此参数增加每个线程分配的缓冲区大小。默认为 128KB 。这个参数不要设置过大,一般在 128~256KB 即可。一般出现 Usingjoinbuffer 的时候,要通过增加索引来解决。
 
binlog_cache_size
一般来说,如果数据库中没有什么大事务,写入也不是特别频繁,将其设置为1~2MB是一个合适的选择。如果有很大的事务,可以适当增加这个缓存值,以获得更好的性能。
 
max_connections
该参数用来设置最大连接数,默认为100。一般设置为 512~1000 即可。
该参数设置过小的最明显特征是出现” Toomanyconnections ”错误
global buffers 全局缓存
innodb_buffer_pool_size
这个参数是InnoDB存储引擎的核心参数,默认为 128MB ,在 mysql 专用服务器上,这个参数建议设置为物理内存的 60%~70% 。主要作用是缓存 innodb 表的索引,数据,插入数据时的缓冲。并不是设置的越大越好。设置的过大,会导致 system swap 空间被占用,导致操作系统变慢,从而减低 sql 查询的效率。
 
innodb_additional_mem_pool_size
该参数用来存储数据字典信息和其他内部数据结构。表越多,需要在这里分配的内存越多。如果InnoDB用光了这个池内的内存, InnoDB 开始从操作系统分配内存,并且往 MySQL 错误日志中写警告信息,默认值是 8MB ,当发现错误日志中已经有相关的警告信息时,就应该适当地增加该参数的大小。一般设置为 16MB 即可。
innodb_max_dirty_pages_pct
这个百分比是,最大脏页的百分数,当系统中脏页所占百分比超过这个值,INNODB就会进行写操作以把页中的已更新数据写入到磁盘文件中。
innodb_log_buffer_size
事务日志所使用的缓冲区。InnoDB在写事务日志的时候,为了提高性能,先将信息写入 InnodbLogBuffer 中,当满足 innodb_flush_log_trx_commit 参数所设置的相应条件(或者日志缓冲区写满)时,再将日志写到文件(或者同步到磁盘)中。可以通过 innodb_log_buffer_size 参数设置其可以使用的最大内存空间。默认为 8MB ,一般设置为 16~64MB 即可。
 
key_buffer_size该参数用来缓存 MyISAM 存储引擎的索引参数。 MySQL5.5 默认为 InnoDB 存储引擎,所以这个参数不用设置过大
 
query_cache_size
缓存select语句和结果集大小的参数。查询缓存的空间不要设置的太大。
因为查询缓存是靠一个全局锁操作保护的,如果查询缓存配置的内存比较大且里面存放了大量的查询结果,当查询缓存失效的时候,会长时间的持有这个全局锁。因为查询缓存的命中检测操作以及缓存失效检测也都依赖这个全局锁,所以可能会导致系统僵死的情况
query_cache_limit
query_cache_limit指定单个查询能够使用的缓冲区大小,缺省为 1M
query_cache_type
指定是否使用查询缓冲,可以设置为0、 1 2
query_cache_min_res_unit
是在4.1版本以后引入的,它指定分配缓冲区空间的最小单位,缺省为 4K
 
tmp_table_sizemax_heap_table_size
 
它规定了内部内存临时表的最大值,每个线程都要分配。(实际起限制作用的是tmp_table_size和 max_heap_table_size 的最小值。
优化查询语句的时候,要避免使用临时表,如果实在避免不了的话,要保证这些临时表是存在内存中的。如果需要的话并且你有很多groupby语句,并且你有很多内存,增大 tmp_table_size( max_heap_table_size)
 
thread_cache_size
Thread_Cache中存放的最大连接线程数 . 在短连接的应用中 Thread_Cache 的功效非常明显 , 因为在应用中数据库的连接和创建是非常频繁的 , 如果不使用 Thread_Cache 那么消耗的资源是非常可观的 ! 在长连接中虽然带来的改善没有短连接的那么明显 , 但是好处是显而易见的 . 但并不是越大越好大了反而浪费资源这个的确定一般认为和物理内存有一定关系
table_cache表缓存
table_cache指示表高速缓存的大小。当 Mysql 访问一个表时,如果在 Mysql 表缓冲区中还有空间,那么这个表就被打开并放入表缓冲区,这样做的好处是可以更快速地访问表中的内容。一般来说,可以通过查看数据库运行峰值时间的状态值 Open_tables Opened_tables ,用以判断是否需要增加 table_cache 的值,即如果 open_tables 接近 table_cache 的时候,并且 Opened_tables 这个值在逐步增加,那就要考虑增加这个值的大小了。
在mysql默认安装情况下, table_cache 的值在 2G 内存以下的机器中的值默认时 256 512 ,如果机器有 4G 内存 , 则默认这个值是 2048 ,但这决意味着机器内存越大,这个值应该越大,因为 table_cache 加大后,使得 mysql SQL 响应的速度更快了,不可避免的会产生更多的死锁( deadlock ),这样反而使得数据库整个一套操作慢了下来,严重影响性能。所以平时维护中还是要根据库的实际情况去作出判断,找到最适合你维护的库的 table_cache
 
Innodb事务日志参数
innodb_log_file_size
日志组里每个日志文件的大小。早期的版本中,在32位的计算机上,日志文件的合并大小必须小于 4GB 。默认是 5MB 。不太好去确定 innodb_log_file_size 这个参数的大小,早期 MySQL 版本的配置文件里建议的 InnoDB 缓冲大小的 25% 是没有什么道理的,首先 InnoDB 缓冲不一定就设置对了,而且 InnoDB 事务日志一般和你的日志写入量、写入频率有关系,和你的缓冲池大小不存在必然的关系。我的经验值是 256~512MB
 
innodb_flush_log_at_trx_commit
 
当innodb_flush_log_at_trx_commit被设置为 0 时,日志缓冲将每秒一次被写到日志文件中,并且对日志文件进行磁盘操作的刷新,但是在事务提交时不进行任何操作。当这个值为 1 (默认值)时,在每个事务进行提交时,日志缓冲将被写到日志文件,且把对日志文件的变更刷新到磁盘中。当设置为 2 时,在每个事务进行提交时,日志缓冲将被写到文件,但不会对日志文件进行到磁盘操作的刷新,对日志文件的刷新每秒发生一次。我们的生产环境一般推荐设置为 innodb_flush_log_at_trx_commit=2 ,因为它可以兼顾效率和一定的安全性,理想情况下,最多可能丢失 1 秒的事务。如果设置为 1 ,则对于性能的影响会很大,因为每次提交事务,都会伴随着磁盘 I/O 的操作,需要把数据刷新到磁盘, I/O 可能会成为瓶颈,对于高安全性的数据,在能够满足 I/O 性能的前提下,可以考虑将其设置为 1
 
sync_binlog
这个参数是设置,每当写了sync_binlog次二进制日志后,把日志实际刷新到磁盘中,默认值是 0 ,不与硬盘同步。生产环境的推荐设置是 8~20 ,这样可以兼顾效率和安全,如果设置为 1 ,你可能会碰到 I/O 瓶颈,你需要选用更好的 SSD 设备,或者使用带电池的 RAID 卡来缓解 I/O 瓶颈,优化文件系统也是一个选项, ext4 xfs 就比 ext3 的表现要好得多。
 
innodb_thread_concurrency
InnoDB试着在 InnoDB 内部保持操作系统线程的数量少于或等于这个参数给出的限制。官方建议是将其设置为处理器数目加磁盘数之和,对于高并发事务,也许你应该把这个值设置得更大一些。对于一些资源等待异常的情况,后来的事务会被已经在等待队列中的事务卡住,你可以通过临时增大这个值,让更多的事务并发执行。
 
innodb_log_files_in_group
该变量控制日志文件数。默认值为2。日志是以顺序的方式写入
 
innodb_autoextend_increment
这个参数的作用是控制innodb共享表空间文件自动扩展的大小,在 mysql5.6.5 版本之前默认值是 8Mb ,从 5.6.6 版本之后默认为 64Mb ,最小值为 1Mb 最大值为 1000Mb
有大批量插入数据时可适当调大
innodb_fush_method
Windows和非 Windows 的操作系统对这个选项的值是互斥的: async_unbuffered unbuffered normal 只能在 Windows 下使用,并且 Windows 下不能使用其他的值。
其他参数
skip_name_resolve
必须设置此项,因MySQL的 DNS 解析可能会导致严重的性能问题。注意设置了此项之后, MySQL 权限表将使用 IP 来统一标识主机,而不能使用主机名来标识了。
设计优化
范式与反范式
第一范式是指数据库表的每一列(属性)都是不可分割的基本数据项,这就要求数据库的每一列都只能存放单一值,即实体中的某个属性不能有多个值或不能有重复的属性。
 
第二范式一个数据表符合第二范式的前提是该数据表符合第一范式。它的规则是要求数据表里的所有数据都要和该数据表的主键有完全相依的关系;如果有哪些数据只和主键的一部分有关的话,就得把它们独立出来变成另一个数据表。如果一个数据表的主键只有单一一个字段的话,那么它就一定符合第二范式。
第三范式
要求一个数据库表中不包含已在其它表中已包含的非主关键字信息
反范式
反范式是试图通过增加冗余数据或通过分组数据来优化数据库读取性能的过程。在某些情况下,反范式是解决数据库性能和可伸缩性的极佳策略。
 
 
引擎选择
·如果没有特殊的情况,建议选择 InnoDB 引擎。
字段类型选择
保小不保大
选择字段的一般原则是保小不保大,能用占用字节少的字段就不用大字段。比如,主键,强烈建议用int整型,不用 guid ,来省空间。空间就是效率!按 4 个字节和按 32 个字节定位一条记录,谁快谁慢太明显了。涉及几个表做 join 时,效果就更明显了。更小的字段类型占用的内存就更少,占用的磁盘空间和磁盘 I/O 也会更少,而且还会占用更少的带宽。因此,在日常选择字段时必须要遵守这一规则。
数值类型
 
在MySQL中支持的 5 个主要整数类型是 TINYINT SMALLINT MEDIUMINT INT BIGINT
手机号使用bigint(20),bigint(20)宽度为 20 ,只占用 8 字节, chart(11) 占用 33 字节
IP地址也可采用 int 整型
MySQL里提供了一个很好用的函数: INET_ATON() ,它负责把 IP 地址转为数字,而另一个函数 INET_NTOA() 负责把数字转换为 IP
 
用户在线状态,0表示离线、 1 表示在线、 2 表示离开、 3 表示忙碌、 4 表示隐身等,其实类似这样的情况,用 int 都是没有必要的,浪费空间,采用 tinyint 完全可以满足需要,不推荐使用枚举类型,存在扩展性问题
字符类型
char和 varchar 是日常使用最多的字符类型。 char(N) 用于保存固定长度的字符串,长度最大为 255 ,比指定长度大的值将被截短,而比指定长度小的值将会用空格进行填补。 varchar(N) 用于保存可变长度的字符串,长度最大为 65535 ,只存储字符串实际需要的长度(它会增加一个额外字节来存储字符串本身的长度), varchar 使用额外的 1~2 字节来存储值的长度,如果列的最大长度小于或等于 255 ,则使用 1 字节,否则就是用 2 字节。 char varchar 跟字符编码也有密切联系, latin1 占用 1 个字节, gbk 占用 2 个字节, utf8 占用 3 个字节。经常变化的值,如家庭住址,由于每个人地址都不同,有的地址很长有的很短,那么用 varchar 相对比较合适,因为它只存储字符串实际的长度。而对于固定长度的值,比如 uuid 函数,是数字和字母组成的 36 位长度的字符类型,长度是固定不变的,或者是 md5 加密后的 32 位长度的字符类型,那么可以设置为 char(36) char(32) ,这样相比于 varchar ,还节省空间,因为 varchar 还要用 1 字节存储值的长度。,不可使用大于其实际长度的 varchar 长度, MySQL 需要先在内存中分配固定的空间来保存值,这无形中就浪费了内存,而且对表的排序或使用临时表尤其不好,所以只分配真正需要的那部分空间即可。
时间类型
 
在MySQL中支持的 5 个时间类型是 DATE TIME DATETIME TIMESTAMP YEAR ,类型, datetime timestamp 都可以精确到秒,但 datetime 占用 8 字节,而 timestamp 只占用 4 字节,在日常建表时应优先选择 timestamp 类型。 timestamp 还具有自动更新时间功能
 
浮点型
·存储精确浮点数时必须使用 DECIMAL 替代 FLOAT DOUBLE
 
 
默认值
MySQL字段建议设置为非 NULL ,默认值,方便程序处理,减少 insert 时数据的插入量,避免 count 计数时忽略 null 的情况, NULL 值的存储需要额外的空间,且会导致比较运算更为复杂,这会使优化器更难以优化 SQL
 
Sql优化
定位慢查询
慢查询带来的直接性能损耗就是数据库系统中最为昂贵的I/O资源。对于 MySQL 数据库来说,来说, I/O 出现瓶颈,会导致连接数增大、锁表,更有可能导致业务访问失败,尤其是在高并发场合下。开启慢查询记录功能带来的好处是可以通过分析慢 SQL 来优化 SQL 语句,从而解决因慢 SQL 引起的各种问题
explain 分析
项目中已经实现的explain 分析拦截器,只需要在开发时开启即可
Explain 介绍
 
id
  SELECT识别符。这是 SELECT 查询序列号。这个不重要 , 查询序号即为 sql 语句执行的顺序
select_type
  select类型,它有以下几种值
  simple 它表示简单的 select, 没有 union 和子查询
  primary 最外面的 select, 在有子查询的语句中,最外面的 select 查询就是 primary
  union union语句的第二个或者说是后面那一个 . 现执行一条语句
  dependent union    UNION中的第二个或后面的 SELECT 语句,取决于外面的查询
  union result        UNION的结果
table
  输出的行所用的表,这个参数显而易见,容易理解
type
  连接类型。有多个参数,先从最佳类型到最差类型介绍
  system
  表仅有一行,这是const类型的特列,平时不会出现,这个也可以忽略不计
  const
  表最多有一个匹配行,const用于比较 primary key 或者 unique 索引。因为只匹配一行数据,所以很快,一定是用到 primary key 或者 unique ,并且只检索出两条数据的 情况下才会是 const
  eq_ref
  对于eq_ref的解释, mysql 手册是这样说的 :" 对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了 const 类型。它用在一个索引的所有部分被联接使用并且索引是 UNIQUE PRIMARY KEY eq_ref 可以用于使用 = 比较带索引的列
  ref
  对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或 PRIMARY KEY
  ref_or_null
  该联接类型如同ref,但是添加了 MySQL 可以专门搜索包含 NULL 值的行。在解决子查询中经常使用该联接类型的优化
  index_merge
  该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单, key_len 包含了使用的索引的最长的关键元素
  range
  给定范围内的检索,使用一个索引来检查行
  index     
  该联接类型与ALL相同,除了只有索引树被扫描。这通常比 ALL 快,因为索引文件通常比数据文件小
  ALL  对于每个来自于先前的表的行组合,进行完整的表扫描。如果表是第一个没标记 const 的表,这通常不好,并且通常在它情况下很差
possible_keys
  提示使用哪个索引会在该表中找到行,不太重要
keys
  MYSQL使用的索引,简单且重要
key_len
MYSQL使用的索引长度
ref   
  ref列显示使用哪个列或常数与 key 一起从表中选择行。
rows
  显示MYSQL执行查询的行数,简单且重要,数值越大越不好,说明没有用好索引
Extra  
  该列包含MySQL解决查询的详细信息
  Distinct    MySQL发现第 1 个匹配行后,停止为当前的行组合搜索更多的行。一直没见过这个值
  Not exists  
  range checked for each record 没有找到合适的索引
  using filesort    
  这意味着MySQL会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。 MySQL 有两种文件排序算法,两种方式都可以在内存或磁盘上完成。 EXPLAIN 不会告诉你 MySQL 将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成。
  using index
  只使用索引树中的信息而不需要进一步搜索读取实际的行来检索表中的信息。这个比较容易理解,就是说明是否使用了索引
  using temporary
  为了解决查询,MySQL需要创建一个临时表来容纳结果。典型情况如查询包含可以按不同情况列出列的 GROUP BY ORDER BY 子句时。
  出现using temporary就说明语句需要优
  using where
  WHERE子句用于限制哪一个行匹配下一个表或发送到客户。除非你专门从表中索取或检查所有行,如果 Extra 值不为 Using where 并且表联接类型为 ALL index ,查询可能会有一些错误化了
query cost
在MySQL 5.7 中,提供了 json 形式的 explain ,只要在 explain 后加上 FORMAT=JSON ,就可以输出,应该查看的一个组件是“ query cost ”。 query cost 是指 MySQL 根据查询执行的总开销来考虑这个特定查询的代价,并且基于许多不同的因素。
简单查询的查询开销通常小于1, 000 。开销在 1 000 100 000 之间的查询被认为是中等开销的查询,而且如果每秒只运行数百个这样的查询 ( 而不是数万个 ) ,通常会比较快。
开销超过100, 000 的查询可以当作是昂贵的。通常,当您是系统上的单个用户时,这些查询仍会快速运行,但您应该仔细考虑在交互式应用程序中使用此类查询的频率
where 条件
Where 条件的字段在符号前方使用函数,计算,正则表达式不走索引 , 字段类型不匹配不走索引,使用 or 不走索引,当如 like %% 这样的条件存在时,可以考虑使用延迟关联来提高查询效率
关联查询
尽可能少使用关联查询,如遇到关联查询的情况应尽可能拆分查询,或增加冗余字段的方式来解决,如果要使用,请确保 on 上的字段有索引
子查询
子查询尽可能使用关联查询或拆分查询代替
limit 分页
优化大表的limit 查询时,可加上相应区间缩小查找范围,也可通过内联方式来提高查询速度
or
使用union all 来代替 or 查询
 
In
Mysql 会使用二分查找去定位 in 中的数据, in 不易过长,最好不要超过 200 ,对于高并发业务,小于几十为佳
 
not in
使用 join 来代替 not in
having
避免使用having,用 where 代替
 
Order by group by
优化order by group by 关键是要避免 文件排序和临时表,主要策略是对 order by group by 的字段建立索引,另外要避免对多个字段排序和分组的情况, group by 默认进行了排序,如果不需要排序,请加上 order by null
Union
如果使用union 务必要使用 union all
 
Count
在使用count 统计数量时,如果只需要得到非精确数量,可以使用 explain 来进行统计数量
 
INSERT
· INSERT 语句 如果 使用 批量 提交( 如 INSERT INTO table VALUES(),(),() ……), 那么 VALUES 的 个数 不应 过多。
 
索引建立
1.对 where 条件中的字段建立单列或联合索引,联合索引遵守最左匹配原则
 
2.取出数据量超过表中数据的 20% mysql 优化器不会使用索引
 
3.唯一性太差的字段不建立索引
 
4.一个表中索引不超过 5
 
5.建议索引中的字段不超过 5
 
6.唯一主键和索引不要重复,优先使用主键查询
 
7.ORDERBY、 GROUPBY DISTINCT 的字段需要放在复合索引的后面,也就是说,复合索引的前面部分用于等值查询,后面的部分用于排序。
 
8.UPDATE、 DELETE 语句需要根据 WHERE 条件添加索引。
 
9·对长度过长的 VARCHAR 字段(比如网页地址)建立索引时,需要增加散列字段,对 VARCHAR 使用散列算法时,散列后的字段最好是整型,然后对该字段建立索引。
 
10.把范围条件放到复合索引的最后, WHERE 条件中的范围条件( BETWEEN < <= > >= )会导致后面的条件使用不了索引。
 
11.当查询 where 没有用到索引时,可以利用覆盖索引提高查询效率,即建立索引的字段正好是覆盖查询语句 [select 子句 ] 与查询条件 [Where 子句 ] 中所涉及的字段,也即,索引包含了查询正在查找的所有数据

你可能感兴趣的:(存储整理)