数据库 -- 大表优化

性能状态关键指标
QPS,Queries Per Second:每秒查询数,一台数据库每秒能够处理的查询次数
TPS,Transactions Per Second:每秒处理事务数

1. 什么是成本?

  • I/O成本
    表使用的MyISAM、InnoDB存储引擎都是将数据和索引都存储到磁盘上的,当查询表中的记录时,需先把数据或者索引加载到内存中然后再操作。从磁盘到内存这个加载的过程损耗的时间称之为I/O成本。
  • CPU成本
    读取以及检测记录是否满足搜索条件、对结果集进行排序等操作损耗的时间称之为CPU成本。

2 . 字段优化

  • 尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT。
  • VARCHAR的长度只分配真正需要的空间
  • 使用枚举或整数代替字符串类型
  • 尽量使用TIMESTAMP而非DATETIME
  • 单表不要有太多字段,建议在20以内
  • 避免使用NULL字段,很难查询优化且占用额外索引空间
  • 用整型来存IP

3. order by优化
using filesort:有两种算法:双路排序,单路排序(根据IO次数)
数据库4.1版本之前:默认使用双路排序:扫描两次磁盘

  • 从磁盘读取排序字段,在buffer缓冲区进行排序操作
  • 扫描其它字段

数据库4.1之后:默认单路排序:一次性读取全部字段,在buffer区挑出排序字段进行排序。
如果数据量很大,无法将数据一次性读取,会分片处理。
如果buffer太小(小于排序的列的总数大小),MySQL会自动由单路—>双路

a. 根据情况选择使用单路、双路;调整buffer大小;
b. 尽量保持排序字段排序的一致性:在group by分组时,默认对分组字段排序,所以在order by中尽量对分组字段排序,避免二次排序。

4. 索引优化

  • 索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY后涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描
  • 应尽量避免在WHERE子句中对字段进行NULL值判断
  • 值分布很稀少的字段不适合建索引,例如"性别"这种只有两三个值的字段
  • 字符字段只建前缀索引、且最好不要做主键
  • 不用外键,由程序保证约束
  • 尽量不用UNIQUE,由程序保证约束
  • 使用多列索引时注意顺序和查询条件保持一致,同时删除不必要的单列索引

5. 查询SQL优化

  • 可通过开启慢查询日志来找出较慢的SQL
  • 不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,查询时要尽可能将操作移至等号右边.
  • sql语句尽量简单:一条sql只能在一个cpu运算;大语句拆小语句,一条大sql可堵死整个库。
  • OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内
  • 避免%xxx式查询,可以使用索引覆盖挽救一部分
  • 使用同类型进行比较,若name类型为varchar, name=123索引失效
  • 尽量避免在WHERE子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描
  • 列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大

注意:在使用 OR 时,可能会导致 索引无法使用。
在为某个索引确定范围区间时,只需要把用不到相关索引的搜索条件替换为TRUE,该条件在回表后才会用到。

SELECT * FROM single_table WHERE key2 > 100 OR common_field = 'abc';

把使用不到idx_key2索引的搜索条件替换为TRUE:

SELECT * FROM single_table WHERE key2 > 100 OR TRUE;
即:SELECT * FROM single_table WHERE TRUE;

强制使用idx_key2执行查询的话,对应的范围区间就是(-∞, +∞),即需要将全部二级索引的记录进行回表,这个代定比全表扫描都大。一个使用到索引的搜索条件和没有使用该索引的搜索条件使用OR连接起来后是无法使用该索引的。

6. SQL性能检测
6.1 explain:见另一篇博客
6.2 慢查询日志:
记录响应时间超过参数long_query_time 设置值并且扫描记录数不小于min_exanined_row_limit 的SQL语句。默认该日志是关闭的。

  • show variables like ‘%slow_query_log%’:检查是否开启了慢查询日志
    临时开启:set global slow_query_log = 1; (在内存中开启)
    永久开启:/etc/my.cnf 配置

  • 查询超过阈值的SQL个数:
    show global status like ‘%slow_queries%’

  • 查看具体的慢SQL:(通过 mysqldumpslow )设置一些过滤条件,快速查找
    s:排序方式
    r:逆序
    l:锁定时间
    g:正则匹配模式

举例如下:按照时间排序,前10条包含left join 查询语句的SQL
mysqldumpslow -s t -t 10 -g “left join” /var/lib/mysql/localhost-slow.log(日志位置)

6.3 全局查询日志:show profiles / show profile: 默认关闭
前序准备:
select @@have_profiling: 查看数据库是否支持
show @@profiling: 查看是否开启
set profiling = 1; 打开,会记录profiling打开之后,所有sql语句所花费的时间。

查询SQL语句执行的耗时:show profiles

7. 主从复制和读写分离
因为数据库大多都是读操作,所以部署一主多从架构,主数据库负责写操作,并做双击热备,多台从数据库做负载均衡,负责读操作。
常通过代理程序实现读写分离,常见代理程序有MySQL Proxy。可大大增加数据库高并发能力,解决单台性能瓶颈问题,数据库横向扩展性也很容易。

8. 缓存池和连接缓存

缓存有本地缓存和分布式缓存,本地缓存是将数据缓存到本地服务器内存中或者文件中。分布式缓存系统redis可以缓存海量数据,扩展性好,性能稳定,速度很快。

InnoDB存储引擎在处理客户端的请求时,当访问某个页的数据时,就会把完整的页的数据全部加载到内存中,即使我们只需要访问一个页的一条记录,那也需要先把整个页的数据加载到内存中。因此我们可以在读写之后把页缓存起来,将来有请求再次访问该页面时,就可以省去磁盘IO的开销。

在MySQL服务器启动的时候向操作系统申请了一片连续的内存,叫做Buffer Pool,并为每一个缓存页都创建了控制信息。把每个页对应的控制信息占用的一块内存称为一个控制块,控制块和缓存页是一一对应的。
在这里插入图片描述

注:在Buffer Pool中被修改的页称为脏页,脏页并不是立即刷新,而是被加入到flush链表中,待之后的某个时刻同步到磁盘上。

join buffer: 关于连接时部分优化

扫描一个表的过程其实是先把这个表从磁盘上加载到内存中,每次访问被驱动表,都会把该表加载到内存,在内存中只会和驱动表的一条记录做匹配,之后从内存中清除掉。即驱动表中有多少条记录,就得把被驱动表从磁盘上加载到内存多少次。

为此,join buffer就是执行连接查询前申请的一块固定大小的内存,先把若干条驱动表结果集中的记录装在这个join buffer中,然后开始扫描被驱动表,被驱动表的记录一次性和join buffer中的多条驱动表记录做匹配,显著减少被驱动表的I/O代价。
在这里插入图片描述

注:驱动表的记录并不是所有列都会被放到join buffer中,只有查询列表中的列和过滤条件中的列才会被放到join buffer中。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值