简单一点,上来就说说数据库优化。本文都是基于mysql数据库的优化建议。分为四个方面:索引优化、sql优化、建表优化和参数优化。
索引优化
首先介绍下索引。索引是数据库中一个排序的数据结构,用于快速查询和更新数据。根据结构形式可分为B树索引(B-tree)、散列索引(hash)、空间索引(R-tree)和全文索引(full-text)。
mysql有两大数据库引擎,分别为MYISAM和innodb,其中innodb支持事务。下面说说innodb的索引结构:索引键值的逻辑顺序和索引锁服务的表相应的物理顺序相同(即聚集索引,Cluster index),也就是说,数据和索引(B+树)在一起,记录被真实保存在索引叶子中。如下图所示:
需要注意的是,mysql索引仅支持最左前缀原则(比如 LIKE 'xx%'),同时不支持函数索引和使用IN范围查找。
索引使用还有如下原则:
1.索引字段不要超过5个,单表索引尽量不超过5个;
2.ORDER BY、GROUP BY和DISTINCT字段放在复合索引的后面,即复合索引前面字段用于等值查询,后面字段用于判断;
3.UPDATE语句根据WHERE条件增加索引;
4.把范围条件放在符合索引的最后,因为BETWEEN、<、<=、>、>=会导致后面条件用不了索引;
5.数据区分度不大的字段不宜使用索引;
sql优化
ORDER BY、GROUP BY优化
1.尽量对较少的行排序;
2.如果连接多张表。ORDER BY的列应该属于连接顺序第一张表;
3.ORDER BY、GROUP BY的列尽量都在第一张表中;
4.保证索引列和ORDER BY列相同,且顺序一致;
5.增加read_rnd_buffer_size(MySQL的随机读缓冲区大小)
分页语句优化
mysql的分页语句为:
select * from table where col = xxx limit M , N;
越往后翻页速度越慢,因为mysql会读取M+N条数据,M越大性能越差。可考虑改为如下方式:
select t1.* from table t1 ,
(select col from table where col = xxx limit M , N) t2
where t1.col = t2.col ;
或当条件是主键id时:
select * from table where id>=2345 limit 11;
select * from table where id>=(select id from table limit 10000,1) limit 10;
select * from table inner join (select id from table limit 10000,10) using (id);
子查询优化
当有如下sql:
select first_name from employee where emp_no IN (
select emp_no from salary where salary = 5000);
这样会遍历employee表每条记录带入子查询中,可改为:
select first_name from emplyee emp ,
(select emp_no from salary where salary = 5000) sal
where emp.emp_no = sal.emp_no;
其他一些sql建议
尽量不用select *
1.减少表变化时的影响;
2.可以使用到covering index;
3.select/join减少硬盘临时表生成
OR条件改为IN
OR时间复杂度:O(n);IN时间复杂度:O(Log n)。[n最好小于200条]
避免负向查询NOT、!和<>
1.不能使用索引
2.很可能全表扫描
数值比对
1.数字对数字,字符对字符;
2.数值和字符比较先转换为双精度进行比对;
3.字符和数字比较:字符转数值,不会使用索引;
建表优化
建表前首先对数据量预估,一年内的单表数据量纯int不超1000W,含char不超500W。如果会超过,建议考虑分表,可基于userid、date或area合理实现。
建议单库不超过300-400个表,表的字段少而精,好处IO高效,遍历快,修复快,提高并发,alter快。单表字段数控制在20-50个,不超50个纯INT,20个CHAR(10)。
避免使用null字段:1.很难查询优化;2.null加索引,需要额外空间;3.含null复合索引无效。尽量不用TEXT/BLOB数据类型:强制生成硬盘临时表;浪费空间,可想办法拆分字段到单独的表。
尽量不用外键:1.额外开销;2.逐行操作;3.高并发容易死锁。
数字型的索引比字符串型快:更高效,查询更快,空间更小。
参数优化
innodb_buffer_pool_size
innodb引擎缓冲池大小,主要缓存了索引和数据。如果sql查询的数据在缓存中已缓存,则不需要从磁盘中读取,建议设置为主机内存的70-80%。
Innodb缓冲池 缓存了行数据、索引、插入缓冲和锁等。innodb还使用缓冲池帮助延迟写入,这样就可以合并多个写入操作一起顺序写回。
innodb_log_file_size
日志组里日志文件大小。建议256M或更大,默认5M。
innodb_flush_log_at_trx_commit
默认1。设为2时,每个事务提交时日志缓冲被写到文件上,但不对日志文件做刷盘操作。刷盘每秒发生一次。建议设2。
sysc_binlog
默认0。不实时将二进制日志中的语句刷盘。设置大于1时实时。建议设0。
tmp_table_size
决定内部内存临时表最大值,每个线程都会分配,如果内存临时表超过限制,mysql就会把它转为基于磁盘的MYISAM表。GROUP BY和DISTINCT等不能走索引的sql会总临时表,适当加大tmp_table_size和max_heap_table_size的值。
table_open_cache
指定表高速缓存大小。每当mysql访问一张表,如果表缓冲区还有空间,该表就被打开放入其中,这样可以加快访问表内容。
query_cache_size
控制mysql query cache的大小。如果mysql开启query cache,在执行每个query时会先锁住query cache,然后判断是否存在query cache,如果存在则返回;不存在则进行引擎查询。同时insert、update和delete会将query cache失效掉,所以适合写少读多的情况。