SQL优化总结
前言
通常来说SQL优化指的都是查询优化,但是最坑的是我们的对手 遇不安套路出牌,很多挑战通常不能用SQL技巧解决。
分析影响性能的原因
影响SQL性能的原因有很多,最令人头疼的是需求侧。业务方不关心你的sql怎么写,他们只要结果,甚至有些时候技术端也会出一些奇葩的要求。
架构侧导致的问题也挺多的,典型的问题是分页、过度退化(业务 宽表)等。
其实数据侧的问题是比较容易达成共识,并解决的。
所有的SQL性能低的原因中,SQL本身的原因是最容易解决的。
1.SQL优化
SQL优化,总共可以分为三层:系统、应用、数据库。
优化范围: 存储、主机和操作系统: 主机架构稳定性 I/O规划及配置 Swap OS内核参数 网络问题 应用程序:(Index,lock,session) 应用程序稳定性和性能 SQL语句性能 串行访问资源 性能欠佳会话管理 数据库优化:(内存、数据库设计、参数) 内存 数据库结构(物理&逻辑) 实例配置参数
我按照成本由低到高顺序进行分析,分别为参数优化、查询优化、索引优化、表设计优化、架构优化、业务优化6方面。
1.1参数优化
Max_connections
(1)简介 Mysql的最大连接数,如果服务器的并发请求量比较大,可以调高这个值,如果连接数越来越多,mysql会为每个连接提供单独的缓冲区,就会开销的越多的内存,所以需要适当的调整该值,不能随便去提高设值。 (2)判断依据 show variables like 'max_connections'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | max_connections | 151 | +-----------------+-------+ show status like 'Max_used_connections'; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | Max_used_connections | 101 | +----------------------+-------+ (3)修改方式举例 vim /etc/my.cnf Max_connections=1024 补充: 1.开启数据库时,我们可以临时设置一个比较大的测试值 2.观察show status like 'Max_used_connections';变化 3.如果max_used_connections跟max_connections相同,那么就是max_connections设置过低或者超过服务器的负载上限了,低于10%则设置过大.
back_log
(1)简介 mysql能暂存的连接数量,当主要mysql线程在一个很短时间内得到非常多的连接请求时候它就会起作用,如果mysql的连接数据达到max_connections时候,新来的请求将会被存在堆栈中,等待某一连接释放资源,该推栈的数量及back_log,如果等待连接的数量超过back_log,将不被授予连接资源。 back_log值指出在mysql暂时停止回答新请求之前的短时间内有多少个请求可以被存在推栈中,只有如果期望在一个短时间内有很多连接的时候需要增加它 (2)判断依据 show full processlist; 发现大量的待连接进程时,就需要加大back_log或者加大max_connections的值 (3)修改方式举例 vim /etc/my.cnf back_log=1024
wait_timeout 和 interactive_timeout
(1)简介 wait_timeout:指的是 mysql 在关闭一个非交互的连接之前所要等待的秒数 interactive_timeout:指的是 mysql 在关闭一个交互的连接之前所需要等待的秒数,比如我们在终端上进行 mysql 管理,使用的即使交互的连接,这时候,如果没有操作的时间超过了interactive_time设置的时间就会自动的断开,默认的是28800,可调优为7200。 wait_timeout:如果设置太小,那么连接关闭的就很快,从而使一些持久的连接不起作用 (2)设置建议 如果设置太大,容易造成连接打开时间过长,在show processlist时候,能看到很多的连接 ,一般希望 wait_timeout 尽可能低 (3)修改方式举例 wait_timeout=60 interactive_timeout=1200 长连接的应用,为了不去反复的回收和分配资源,降低额外的开销。 一般我们会将 wait_timeout 设定比较小,interactive_timeout 要和应用开发人员沟通长链接的应用是否很多。如果他需要长链接,那么这个值可以不需要调整。 另外还可以使用类外的参数弥补。
key_buffer_size
(1)简介 key_buffer_size 指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度 Ⅰ 此参数与 myisam 表的索引有关 Ⅱ 临时表的创建有关(多表链接、子查询中、union) 在有以上查询语句出现的时候,需要创建临时表,用完之后会被丢弃 临时表有两种创建方式: 内存中------->key_buffer_size 磁盘上------->ibdata1(5.6) ibtmp1 (5.7) (2)设置依据 通过 key_read_requests 和 key_reads 可以直到 key_buffer_size 设置是否合理。 mysql> show variables like "key_buffer_size%"; +-----------------+---------+ | Variable_name | Value | +-----------------+---------+ | key_buffer_size | 8388608 | +-----------------+---------+ mysql> show status like "key_read%"; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | Key_read_requests | 10 | | Key_reads | 2 | +-------------------+-------+ 一共有10个索引读取请求,有2个请求在内存中没有找到直接从硬盘中读取索引 控制在 5%以内 。 注:key_buffer_size 只对 myisam 表起作用,即使不使用 myisam 表,但是内部的临时磁盘表是 myisam 表,也要使用该值。 可以使用检查状态值created_tmp_disk_tables 得知: mysql> show status like "created_tmp%"; +-------------------------+-------+ | Variable_name | Value | +-------------------------+-------+ | Created_tmp_disk_tables | 0 | | Created_tmp_files | 6 | | Created_tmp_tables | 1 | +-------------------------+-------+ 通常地,我们习惯以 Created_tmp_tables/(Created_tmp_disk_tables + Created_tmp_tables) Created_tmp_disk_tables/(Created_tmp_disk_tables + Created_tmp_tables) 或者已各自的一个时段内的差额计算,来判断基于内存的临时表利用率。所以,我们会比较关注 Created_tmp_disk_tables 是否过多,从而认定当前服务器运行状况的优劣。 Created_tmp_disk_tables/(Created_tmp_disk_tables + Created_tmp_tables) 控制在5%-10%以内 看以下例子: 在调用 mysqldump 备份数据时,大概执行步骤如下: 180322 17:39:33 7 Connect root@localhost on 7 Query /*!40100 SET @@SQL_MODE='' */ 7 Init DB guo 7 Query SHOW TABLES LIKE 'guo' 7 Query LOCK TABLES `guo` READ /*!32311 LOCAL */ 7 Query SET OPTION SQL_QUOTE_SHOW_CREATE=1 7 Query show create table `guo` 7 Query show fields from `guo` 7 Query show table status like 'guo' 7 Query SELECT /*!40001 SQL_NO_CACHE */ * FROM `guo` 7 Query UNLOCK TABLES 7 Quit 其中,有一步是:show fields from `guo`。从 slow query 记录的执行计划中,可以知道它也产生了 Tmp_table_on_disk。 所以说,以上公式并不能真正反映到 mysql 里临时表的利用率,有些情况下产生的 Tmp_table_on_disk 我们完全不用担心,因此没必要过分关注 Created_tmp_disk_tables,但如果它的值大的离谱的话,那就好好查一下,你的服务器到底都在执行什么查询了。 (3)配置方法 key_buffer_size=64M
query_cache_size
(1) 简介: 查询缓存简称QC,使用查询缓冲,mysql将查询结果存放在缓冲区中,今后对于同样的select语句(区分大小写),将直接从缓冲区中读取结果。 SQL层: select * from t1 where name=:NAME; select * from t1 where name=:NAME; 1、查询完结果之后,会对SQL语句进行hash运算,得出hash值,我们把他称之为SQL_ID 2、会将存储引擎返回的结果+SQL_ID存储到缓存中。 存储方式: 例子:select * from t1 where id=10; 100次 1、将select * from t1 where id=10; 进行hash运算计算出一串hash值,我们把它称之为“SQL_ID" 2、将存储引擎返回上来的表的内容+SQLID存储到查询缓存中 使用方式: 1、一条SQL执行时,进行hash运算,得出SQLID,去找query cache 2、如果cache中有,则直接返回数据行,如果没有,就走原有的SQL执行流程 一个sql查询如果以select开头,那么mysql服务器将尝试对其使用查询缓存。 注:两个sql语句,只要想差哪怕是一个字符(列如大小写不一样;多一个空格等),那么这两个sql将使用不同的一个cache。 (2) 判断依据 mysql> show status like "%Qcache%"; +-------------------------+---------+ | Variable_name | Value | +-------------------------+---------+ | Qcache_free_blocks | 1 | | Qcache_free_memory | 1031360 | | Qcache_hits | 0 | | Qcache_inserts | 0 | | Qcache_lowmem_prunes | 0 | | Qcache_not_cached | 2002 | | Qcache_queries_in_cache | 0 | | Qcache_total_blocks | 1 | +-------------------------+---------+ ---------------------状态说明-------------------- Qcache_free_blocks:缓存中相邻内存块的个数。 如果该值显示较大,则说明Query Cache 中的内存碎片较多了,FLUSH QUERY CACHE会对缓存中的碎片进行整理,从而得到一个空闲块。 注:当一个表被更新之后,和它相关的cache blocks将被free。但是这个block依然可能存在队列中,除非是在队列的尾部。可以用FLUSH QUERY CACHE语句来清空free blocks Qcache_free_memory:Query Cache 中目前剩余的内存大小。通过这个参数我们可以较为准确的观察出当前系统中的Query Cache 内存大小是否足够,是需要增加还是过多了。 Qcache_hits:表示有多少次命中缓存。我们主要可以通过该值来验证我们的查询缓存的效果。数字越大,缓存效果越理想。 Qcache_inserts:表示多少次未命中然后插入,意思是新来的SQL请求在缓存中未找到,不得不执行查询处理,执行查询处理后把结果insert到查询缓存中。这样的情况的次数越多,表示查询缓存应用到的比较少,效果也就不理想。当然系统刚启动后,查询缓存是空的,这很正常。 Qcache_lowmem_prunes: 多少条Query因为内存不足而被清除出QueryCache。通过“Qcache_lowmem_prunes”和“Qcache_free_memory”相互结合,能够更清楚的了解到我们系统中Query Cache 的内存大小是否真的足够,是否非常频繁的出现因为内存不足而有Query 被换出。这个数字最好长时间来看;如果这个数字在不断增长,就表示可能碎片非常严重,或者内存很少。(上面的free_blocks和free_memory可以告诉您属于哪种情况) Qcache_not_cached:不适合进行缓存的查询的数量,通常是由于这些查询不是 SELECT 语句或者用了now()之类的函数。 Qcache_queries_in_cache:当前Query Cache 中 cache 的Query 数量; Qcache_total_blocks:当前 Query Cache 中的 block 数量;。 Qcache_hits / (Qcache_inserts+Qcache_not_cached+Qcache_hits) 90/ 10000 0 90 如果出现hits比例过低,其实就可以关闭查询缓存了。使用redis专门缓存数据库 Qcache_free_blocks 来判断碎片 Qcache_free_memory + Qcache_lowmem_prunes 来判断内存够不够 Qcache_hits 多少次命中 Qcache_hits / (Qcache_inserts+Qcache_not_cached+Qcache_hits) (3) 配置示例 mysql> show variables like '%query_cache%' ; +------------------------------+---------+ | Variable_name | Value | +------------------------------+---------+ | have_query_cache | YES | | query_cache_limit | 1048576 | | query_cache_min_res_unit | 4096 | | query_cache_size | 1048576 | | query_cache_type | OFF | | query_cache_wlock_invalidate | OFF | +------------------------------+---------+ -------------------配置说明------------------------------- 以上信息可以看出query_cache_type为off表示不缓存任何查询 各字段的解释: query_cache_limit:超过此大小的查询将不缓存 query_cache_min_res_unit:缓存块的最小大小,query_cache_min_res_unit的配置是一柄”双刃剑”,默认是4KB,设置值大对大数据查询有好处,但如果你的查询都是小数据查询,就容易造成内存碎片和浪费。 query_cache_size:查询缓存大小 (注:QC存储的最小单位是1024byte,所以如果你设定了一个不是1024的倍数的值,这个值会被四舍五入到最接近当前值的等于1024的倍数的值。) query_cache_type:缓存类型,决定缓存什么样的查询,注意这个值不能随便设置,必须设置为数字,可选项目以及说明如下: 如果设置为0,那么可以说,你的缓存根本就没有用,相当于禁用了。 如果设置为1,将会缓存所有的结果,除非你的select语句使用SQL_NO_CACHE禁用了查询缓存。 如果设置为2,则只缓存在select语句中通过SQL_CACHE指定需要缓存的查询。 修改/etc/my.cnf,配置完后的部分文件如下: query_cache_size=128M query_cache_type=1
max_connect_errors
max_connect_errors是一个mysql中与安全有关的计数器值,它负责阻止过多尝试失败的客户端以防止暴力破解密码等情况,当超过指定次数,mysql服务器将禁止host的连接请求,直到mysql服务器重启或通过flush hosts命令清空此host的相关信息 max_connect_errors的值与性能并无太大关系。 修改 /etc/my.cnf 文件,在[mysqld]下面添加如下内容 max_connect_errors=2000
sort_buffer_size
(1) 简介: 每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速 ORDER BY GROUP BY distinct union (2) 配置依据 Sort_Buffer_Size并不是越大越好,由于是 connection 级的参数,过大的设置+高并发可能会耗尽系统内存资源。 列如:500个连接将会消耗 500*sort_buffer_size(2M)=1G内存 (3) 配置方法 修改/etc/my.cnf文件,在[mysqld]下面添加如下: sort_buffer_size=1M
max_allowed_packet (重要)
(1) 简介: mysql根据配置文件会限制,server接受的数据包大小。 (2) 配置依据: 有时候大的插入和更新会受 max_allowed_packet 参数限制,导致写入或者更新失败,最大值是1GB,必须设置 1024 的倍数 (3) 配置方法: max_allowed_packet=32M
join_buffer_size
select a.name,b.name from a join b on a.id=b.id where xxxx 用于表间关联缓存的大小,和sort_buffer_size一样,该参数对应的分配内存也是每个连接独享。 尽量在SQL与方面进行优化,效果较为明显。 优化的方法:在on条件列加索引,至少应当是有MUL索引
thread_cache_size (重要)
(1) 简介 服务器线程缓存,这个值表示可以重新利用保存在缓存中线程的数量,当断开连接时,那么客户端的线程将被放到缓存中以响应下一个客户而不是销毁(前提是缓存数未达上限),如果线程重新被请求,那么请求将从缓存中读取,如果缓存中是空的或者是新的请求,那么这个线程将被重新创建,如果有很多新的线程,增加这个值可以改善系统性能. (2) 配置依据 通过比较 Connections 和 Threads_created 状态的变量,可以看到这个变量的作用。 设置规则如下:1GB 内存配置为8,2GB配置为16,3GB配置为32,4GB或更高内存,可配置更大。 服务器处理此客户的线程将会缓存起来以响应下一个客户而不是销毁(前提是缓存数未达上限) 试图连接到MySQL(不管是否连接成功)的连接数 mysql> show status like 'threads_%'; +-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | Threads_cached | 8 | | Threads_connected | 2 | | Threads_created | 4783 | | Threads_running | 1 | +-------------------+-------+ Threads_cached :代表当前此时此刻线程缓存中有多少空闲线程。 Threads_connected:代表当前已建立连接的数量,因为一个连接就需要一个线程,所以也可以看成当前被使用的线程数。 Threads_created:代表从最近一次服务启动,已创建线程的数量,如果发现Threads_created值过大的话,表明MySQL服务器一直在创建线程,这也是比较耗cpu SYS资源,可以适当增加配置文件中thread_cache_size值。 Threads_running :代表当前激活的(非睡眠状态)线程数。并不是代表正在使用的线程数,有时候连接已建立,但是连接处于sleep状态。 (3)配置方法: thread_cache_size=32 整理: Threads_created :一般在架构设计阶段,会设置一个测试值,做压力测试。 结合zabbix监控,看一段时间内此状态的变化。 如果在一段时间内,Threads_created趋于平稳,说明对应参数设定是OK。 如果一直陡峭的增长,或者出现大量峰值,那么继续增加此值的大小,在系统资源够用的情况下(内存)
innodb_buffer_pool_size (重要)
(1)简介 对于InnoDB表来说,innodb_buffer_pool_size 的作用就相当于 key_buffer_size 对于 MyISAM 表的作用一样。 (2)配置依据: InnoDB使用该参数指定大小的内存来缓冲数据和索引。 对于单独的MySQL数据库服务器,最大可以把该值设置成物理内存的80%,一般我们建议不要超过物理内存的70%。 (3)配置方法 innodb_buffer_pool_size=2048M
innodb_flush_log_at_trx_commit (很重要)
(1) 简介 主要控制了 innodb 将 log buffer 中的数据写入日志文件并 flush 磁盘的时间点,取值分别为0、1、2三个。 0,表示当事务提交时,不做日志写入操作,而是每秒钟将 log buffer 中的数据写入日志文件并 flush 磁盘一次; 1,每次事务的提交都会引起 redo 日志文件写入、flush 磁盘的操作,确保了事务的ACID; 2,每次事务提交引起写入日志文件的动作,但每秒钟完成一次 flush 磁盘操作。 (2) 配置依据 实际测试发现,该值对插入数据的速度影响非常大,设置为2时插入10000条记录只需要2秒,设置为0时只需要1秒,而设置为1时则需要229秒。因此,MySQL手册也建议尽量将插入操作合并成一个事务,这样可以大幅提高速度。 根据MySQL官方文档,在允许丢失最近部分事务的危险的前提下,可以把该值设为0或2。 (3) 配置方法 innodb_flush_log_at_trx_commit=1 双1标准中的一个1
innodb_thread_concurrency
(1)简介 此参数用来设置 innodb 线程的并发数量,默认值为0表示不限制。 (2)配置依据 在官方doc上,对于 innodb_thread_concurrency 的使用,也给出了一些建议,如下: 如果一个工作负载中,并发用户线程的数量小于64,建议设置 innodb_thread_concurrency=0; 如果工作负载一直较为严重甚至偶尔达到顶峰,建议先设置 innodb_thread_concurrency=128, 并通过不断的降低这个参数,96, 80, 64等等,直到发现能够提供最佳性能的线程数, 例如,假设系统通常有40到50个用户,但定期的数量增加至60,70,甚至200。你会发现, 性能在80个并发用户设置时表现稳定,如果高于这个数,性能反而下降。在这种情况下, 建议设置 innodb_thread_concurrency 参数为80,以避免影响性能。 如果你不希望 InnoDB 使用的虚拟 CPU 数量比用户线程使用的虚拟 CPU 更多(比如20个虚拟CPU), 建议通过设置 innodb_thread_concurrency 参数为这个值(也可能更低,这取决于性能体现), 如果你的目标是将 MySQL 与其他应用隔离,你可以考虑绑定 mysqld 进程到专有的虚拟 CPU。 但是需要注意的是,这种绑定,在 myslqd 进程一直不是很忙的情况下,可能会导致非最优的硬件使用率。在这种情况下, 你可能会设置 mysqld 进程绑定的虚拟 CPU,允许其他应用程序使用虚拟 CPU 的一部分或全部。 在某些情况下,最佳的 innodb_thread_concurrency 参数设置可以比虚拟CPU的数量小。 定期检测和分析系统,负载量、用户数或者工作环境的改变可能都需要对 innodb_thread_concurrency 参数的设置进行调整。 128 -----> top cpu 设置标准: 1、当前系统cpu使用情况,均不均匀 top 2、当前的连接数,有没有达到顶峰 show status like 'threads_%'; show processlist; (3) 配置方法: innodb_thread_concurrency=8 方法: 1. 看top ,观察每个cpu的各自的负载情况 2. 发现不平均,先设置参数为cpu个数,然后不断增加(一倍)这个数值 3. 一直观察top状态,直到达到比较均匀时,说明已经到位了
innodb_log_buffer_size
此参数确定些日志文件所用的内存大小,以M为单位。缓冲区更大能提高性能,对于较大的事务,可以增大缓存大小。 innodb_log_buffer_size=128M 设定依据: 1、大事务: 存储过程调用 CALL 2、多事务
innodb_log_file_size = 100M (重要)
设置 ib_logfile0 ib_logfile1 此参数确定数据日志文件的大小,以M为单位,更大的设置可以提高性能. innodb_log_file_size = 100M
innodb_log_files_in_group = 3 (重要)
为提高性能,MySQL可以以循环方式将日志文件写到多个文件。推荐设置为3
read_buffer_size = 1M
MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql 会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁,
并且你认为频繁扫描进行得太慢,可以通过增加该变量值以及内存缓冲区大小提高其性能。和 sort_buffer_size一样,该参数对应的分配内存也是每个连接独享。
read_rnd_buffer_size = 1M
MySql的随机读(查询操作)缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySql会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。
注:顺序读是指根据索引的叶节点数据就能顺序地读取所需要的行数据。随机读是指一般需要根据辅助索引叶节点中的主键寻找实际行数据,而辅助索引和主键所在的数据段不同,因此访问方式是随机的。
bulk_insert_buffer_size = 8M
批量插入数据缓存大小,可以有效提高插入效率,默认为8M
binary log (重要)
log-bin=/data/mysql-bin binlog_cache_size = 2M //为每个session 分配的内存,在事务过程中用来存储二进制日志的缓存, 提高记录bin-log的效率。没有什么大事务,dml也不是很频繁的情况下可以设置小一点,如果事务大而且多,dml操作也频繁,则可以适当的调大一点。前者建议是 1M,后者建议是:即 2-4M max_binlog_cache_size = 8M //表示的是binlog 能够使用的最大cache 内存大小 max_binlog_size= 512M //指定binlog日志文件的大小,如果当前的日志大小达到max_binlog_size,还会自动创建新的二进制日志。你不能将该变量设置为大于1GB或小于4096字节。默认值是1GB。在导入大容量的sql文件时,建议关闭sql_log_bin,否则硬盘扛不住,而且建议定期做删除。 expire_logs_days = 7 //定义了mysql清除过期日志的时间。二进制日志自动删除的天数。默认值为0,表示“没有自动删除”。 log-bin=/data/mysql-bin binlog_format=row sync_binlog=1 双1标准(基于安全的控制): sync_binlog=1 // 什么时候刷新 binlog 到磁盘,每次事务commit innodb_flush_log_at_trx_commit=1 set sql_log_bin=0; show status like 'com_%';
安全参数 (重要)
Innodb_flush_method=(O_DIRECT, fsync) 1、fsync: (1)在数据页需要持久化时,首先将数据写入OS buffer中,然后由os决定什么时候写入磁盘 (2)在redo buffuer需要持久化时,首先将数据写入OS buffer中,然后由os决定什么时候写入磁盘 但,如果innodb_flush_log_at_trx_commit=1的话,日志还是直接每次commit直接写入磁盘 2、 Innodb_flush_method=O_DIRECT (1)在数据页需要持久化时,直接写入磁盘 (2)在redo buffuer需要持久化时,首先将数据写入OS buffer中,然后由os决定什么时候写入磁盘 但,如果innodb_flush_log_at_trx_commit=1的话,日志还是直接每次commit直接写入磁盘 最安全模式: innodb_flush_log_at_trx_commit=1 innodb_flush_method=O_DIRECT 最高性能模式: innodb_flush_log_at_trx_commit=0 innodb_flush_method=fsync 一般情况下,我们更偏向于安全。 “双一标准” innodb_flush_log_at_trx_commit=1 *************** sync_binlog=1 *************** innodb_flush_method=O_DIRECT
参数优化结果
[mysqld] basedir=/data/mysql datadir=/data/mysql/data socket=/tmp/mysql.sock log-error=/var/log/mysql.log log_bin=/data/binlog/mysql-bin binlog_format=row skip-name-resolve server-id=52 gtid-mode=on enforce-gtid-consistency=true log-slave-updates=1 relay_log_purge=0 max_connections=1024 back_log=128 wait_timeout=60 interactive_timeout=7200 key_buffer_size=16M query_cache_size=64M query_cache_type=1 query_cache_limit=50M max_connect_errors=20 sort_buffer_size=2M max_allowed_packet=32M join_buffer_size=2M thread_cache_size=200 innodb_buffer_pool_size=1024M innodb_flush_log_at_trx_commit=1 innodb_log_buffer_size=32M innodb_log_file_size=128M innodb_log_files_in_group=3 binlog_cache_size=2M max_binlog_cache_size=8M max_binlog_size=512M expire_logs_days=7 read_buffer_size=2M read_rnd_buffer_size=2M bulk_insert_buffer_size=8M [client] socket=/tmp/mysql.sock
1.2查询优化
基本思路:
1、有大炮,就别用手枪。查询要走索引
2、溺水三千只取一瓢,不要select *
3、数据库是用来存的,不是用来算的,查就查,不要算
工具(以MySQL为例): 1、用Explain查看 SQL 执行计划: 可以发现主要拖慢效率的内容,比如某个表数据量大,资源消耗多。 2、通过 Profile 查阅每一步的资源消耗: 可以发现每一步的资源消耗,涉及多少数据,消耗多少时间等。 3、通过 Optimizer Trace 表查看 SQL 优化器生成执行计划的过程,逐步优化时好用。
基本上用explain和profile就能掌握sql执行的所有消耗,然后根据以下原则,进行优化,顺便开启Optimizer Trace看看自己优化的是否对路:
1、禁止使用“%”前导的查询 2、所有表都加别名,需要加在所有字段前面match 3、用union代替or 4、null 值判断、!=或<>操作符会导致全表扫描 5、用between、exists代替in,用not exists 代替not in 6、禁止使用select * 7、小结果集join大结果集 8、使用limit 9、减少where中对字段的计算操作
1.3索引优化
上面的SQL优化技巧中,禁止的原因基本是使索引失效。索引太重要了,因此单列一章详细讲讲。
索引的设计: 1、单张表中索引数量不超过5个; 2、单个索引字段数不超过5个; 3、频繁修改的字段上不建议建索引; 4、区分度越大,索引效果越好;性别字段建索引,等于没建; 5、索引字段尽量短,尽量选择数字字段 6、在查询、排序、分组、where判定频繁的字段上建索引 7、经常删数据的表,定期清理 8、用join代替子查询
索引的使用:
禁止在索引列进行数学运算;
使用联合索引时,按顺序查询(索引的最左原则);
尽量在索引同时满足查询和排序;
字符串索引使用前缀索引,前缀长度不超过10个字符;
索引join时,使用的字段类型要一样;
1.4表设计优化
-
编码规则设计:
开发设计的树形结构设计一般只增加一个父ID,其实我们还可以增加业务code和层级标识。业务code一般会有编码规则,以中国行政地区代码为例,每2位代表一层,随便拿一个code,通过字段截取,就能直接查到任意父节点对应的内容。
数仓建设的时候,抽离三级地区维度也非常容易,限定layer层即可。
-
扩展信息表
将核心信息放在主表中,将写频繁、新增的字段放在扩展信息表中,进行部分业务的解耦。设计是一个平衡的过程,跨表则多一次关联,都放一起则造成臃肿。
-
字段选择
tips:大文本、图片建议存文件,数据库中存路径即可。
1.5架构优化
架构层面轻易不动,一动就是大工程,其核心是分开:
1、主从+读写分离
2、分库分表
数据库事务主要就是读和写,这俩分开之后读操作可以占有服务器的所有资源,自然就快了。
分库是垂直切分,按照业务领域,将关系比较紧密的表分到同一个库中,原本一个数据库垂直切分为N个业务库,每个库的压力就小了,效率就高了。
分表是水平切分,对某个表按规则横向切分成N个小表,单表数据量就变小了,查询效率也就变高了。
1.6业务优化
如同开篇所提,我们遇到的难解的问题,来自于需求侧的比较多,也更为棘手,甚至是无法解决。在这里提几个常用技巧:
1、需求转换 摸清对方真实需求,有些时候只是想为他的论点找论据而已。 2、替代方案 一个新指标的数据逻辑太复杂,可以考虑用其他已有指标代替,能表达他的意思即可。 3、降低期望 他说饿了,不一定要给一碗鲍鱼炒饭,一个馒头也是可以的。