1. show status
可以通过该命令了解mysql的服务器运行状态参数以及各种SQL 的执行频率等
Aborted_clients:非正常关闭导致客户端终止而中断的连接数
Aborted_connects:试图连接到MySQL服务器而失败的连接数
com*:各种数据库操作的数量 ,可以查看该项数据来了解数据库何种操作的频繁程度(事务型的操作可以查看Com_commit和Com_rollback来了解事务的提交已经回滚情况,假如回滚操作频繁,可能意味着应用程序编写有问题)
slow_queries: 慢查询的次数
2. show variables like "%slow%"
定向的查看某项数据库状态参数
以慢查询为例子,首先我们以上显示关于慢查询的配置信息
log_slow_queries ON/OFF慢查询sql记录 slow_lauch_time 默认为2秒 规定查询时长超过多久算是慢查询 slow_query_log ON/OFF 开启慢查询记录日志 slow_query_log_file 慢查询记录日志文件的存放路径
开启慢查询记录
mysql> set global slow_query_log="ON";
慢查询只有在查询完成之后才会被记录到慢查询记录日志中
所以我们可以先使用show processlist命令显示mysql的线程列表,来查看线程的状态,是否锁表等状态,可以实时的查看sql执行情况,同时对一些锁表操作进行优化
3. explain
解释执行计划
当我们定位到慢查询之后,我们可以使用explain来分析sql的执行计划
mysql> explain select * from shop_detail_info s left join account a on a.mobile = s.username; #展示的查询效果 +----+-------------+-------+------------+------+---------------+--------+---------+-----------------------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-----------------------+------+----------+-------------+ | 1 | SIMPLE | s | NULL | ALL | NULL | NULL | NULL | NULL | 22 | 100.00 | NULL | | 1 | SIMPLE | a | NULL | ref | mobile | mobile | 51 | bibixiaopu.s.username | 1 | 100.00 | Using where | +----+-------------+-------+------------+------+---------------+--------+---------+-----------------------+------+----------+-------------+
select_type: 查询类型
table: 输出结果集的表
type:表示表的连接类型
当表中仅有一行并且type的值为system是最佳的连接类型
当select操作中使用索引进行表连接时type为ref
当select的表连接没有使用索引时,经常会看到type=all,表示对该表进行了全表扫描,这是需要考虑通过创建索引来提高连接的效率
possible_keys:表示查询时,可以使用的索引列
key:表示使用的索引
key_len: 当前使用的索引的长度
rows:扫描的范围
Extra:执行情况的描述与说明
4. 索引
一般的慢查询都是由于索引使用不当引起的问题
创建索引示例
mysql> create index ind_test on table1(name(5))
- 对于char或者varchar可以使用前缀索引来节省空间
- order By 和group by中索引不能生效
- mysql估计使用索引比全表查询慢,则不使用索引
例如 如果key_part1均匀分布在1和100之间,我们使用如下查询语句
mysql> select * from tableName where key_part1>1 and key_part1<90
- 查询条件不是索引列的第一部分时索引不生效
例如 like 以通配符%开始
mysql> select * from '%ssss';
- where 条件后边的的字符串一定要加引号,如果限制条件后面跟的是数字,mysql需要转化为字符串,此时将不会使用索引
5. show status like 'Handler_read%';
使用该命令查看索引命中率
如果索引正在工作,handler_read_key的值将会很高,变相的代表了索引的命中率,假如数值比较小的话,说明增加索引得到的性能改善不够明显,因为所索引的命中率并不是很高,表示此索引不经常被使用
Handler_read_rnd_next的值比较高说明查询运行效率低,此时应该建立索引来提升查询效率,此数值的含义是在数据文件中读下一行的请求数,如果数据库正在做大量的表扫描,该数值则会比较高,通常表示索引不正确或者写入的查询没有利用索引
6. 简单的优化方法
定期分析表
使用analyze table 用来分析和修复存储表中的关键字分布
比如我们可以使用show index in table_name 来查看索引的散列程度,如果大大小于数据的实际的散列程度,那么这时候索引其实就相当于失效了,这时候我们可以使用 ANALYZE TABLE table_name;命令来修复索引的散列程度,之后在使用show index操作可以看到散列程度大大提高
使用check table来查看表中是否存在错误
使用optimize table定期的清理表中的文件碎片,并且可以重新利用未使用的空间,一般当表出现了大量删除或者对于类似于变长类型字段text/ varchar的频繁更新与修改的时候我们可以定期的使用此命令
7. 常用的sql优化
7.1. 优化insert语句
大批量insert操作的时候,我们可以分批量的将一定的数据拼装成一条>insert语句,来提高执行效率
例如:mysql> insert into test values (1,2),(2,3),(3,4)
7.2. 将索引文件和数据文件分别存放在不同的磁盘上
7.3.优化group by语句
如果查询包括group by的字段,但又想要避免排序结果的消耗,我们可以>在sql语句最后添加order by null来指定禁止排序
例如:mysql> select a count(*) from test group by a order by null
7.4.优化order by语句
order by之后的字段最好保持升序或者降序一致,这样才能使用索引
7.5. 优化join 语句
将某些子查询转换成join的表关联查询
例如:我们需要将所有没有订单记录的用户取出来,我们可以使用not inmysql> SELECT * FROM customerinfo WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )
此时如果使用join连接会大幅度的提升查询效率,尤其是当salesinfo表中创建有customerid列的索引
SELECT * FROM customerinfo LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.CustomerID WHERE salesinfo.CustomerID IS NULL
join之所以能够效率更快,是因为这时候mysql不需要在内存中创建临时表
8. 拆分表
8.1. 纵向分表
纵向拆分是按照应用访问的频度,将经常访问的字段和不经常访问的字段拆分成不同的表,经常访问的字段尽量是定长的,这样可以有效的提高表的查询和更新效率
8.2:横向分表
按照应用的情况,将数据横向拆分成几个表恨着通过分区分到多个分区中
例如:
- 订单是一个实效性很强的实体,我们很少查询几年前的订单数据,我们就可以在订单的创建时间列上创建分区函数来做分区。
- 比如帖子通常情况下只有在首页推荐的最新的帖子被访问次数很多,而几年前的帖子被访问的几率较小,这时候我们可以根据帖子的主键id来做分区, id小于300w的在一个分区上,id在300到600w之间的在一个分区上。
9. 锁问题
我们可以通过检查table_lock_waited和table_lock_immediate状态变量来分析系统上的表锁定争夺,也可以通过检查Innodb_row_lock状态变量来分析系统上的行锁争夺情况
命令如下:
mysql> show status like '%Table%'
mysql> show status like 'innodb_row_lock%'
什么情况下使用表锁:
- 很多操作都是读表
- 在严格条件的索引上读取和更新,当更新或者删除可以用单独的索引来获取时
- UPDATE tbl_name SET column=value WHERE unique_key_col=key_value;
- DELETE FROM tbl_name WHERE unique_key_col=key_value;
- select和insert语句并发执行,但是只有很少的update和delete语句
- 很多的扫描表和对全表的group by操作,但是没有任何的写表操作
行级锁的优点:
- 当在许多线程中访问不同的行时只存在少量的锁定冲突
- 回滚时只有少量的更改
- 可以长时间的锁定单一行
行级锁的缺点:
- 比页级锁和表级锁占用更多的内存
- 当在表的大部分中使用时,比页级锁或表级锁多顶速度慢,因为必须获取更多的锁
- 当大部分数据上经常进行group by 操作或者必须经常扫描整个表,比其他锁定明显慢很多
insert ......select ........ 带来的问题
当使用insert ....... select..... 进行记录的插入时,如果select的表是innodb类型的,不论insert的表是什么类型的,都会对select表的记录进行锁定,而在oracle数据库中不存在这种情况,因此从oracle中迁移过来的数据可能会存在一些类似于对比较多的记录进行统计分析,然后将统计结果插入到另外一个表中,这样的操作因为非常少,所以可能并没有设置相应的索引。如果迁移到mysql数据库中之后没有做相应的调整,对需要select的表实际是进行的全表扫描导致的所有记录的锁定,这样将会对对应的其他操作造成很恶劣的影响,所以建议统计数据最好不要写入表中
10. 优化mysql server
key_buffer_size 的设置
可以将指定的表索引缓存进入指定的key_buffer,这样可以更小的降低线程之间的竞争
CACHE INDEX a.t1, a.t2, b.t3 IN hot_cache
11. 应用的优化
- 使用连接池
- 避免对同一数据的重复检索
- 使用mysql的查询缓存
查询缓存存储select查询的文本以及发送给客户端的相应结果。如果随后收到一个相同的查询,服务器从查询缓存中重新得到查询结果,而不需要再重新解析和执行查询
适用范围:不发生数据更新的表。当表更改(包括表结构和表数据)后,查询缓存值的相关条目会被清空
- 加cache层
12. 负载均衡
- 利用MySQL的主从复制可以有效的分流更新操作和查询操作
- 采用分布式数据库架构