避免使用NULL字段
很难进行查询优化
NULL列加索引,需要额外空间
含NULL复合索引无效
举例
`a` char(32) DEFAULT NULL--错误
`b` int(10) NOT NULL--错误
`c` int(10) NOT NULL DEFAULT 0--正确
少用并拆分TEXT/BLO ##B
TEXT类型处理性能远低亍VARCHAR
强制生成硬盘临时表
浪费更多空间
VARCHAR(65535)==>64K (注意UTF-8)
尽量不用TEXT/BLOB数据类型
若必须使用则拆分到单独的表
举例: CREATE TABLE t1 (
id INT NOT NULL AUTO_INCREMENT,
data text NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB;
大SQL VS 多个简单SQL
传统设计思想
BUT MySQL NOT
一条SQL叧能在一个CPU运算
5000+ QPS的高幵发中,1秒大SQL意味着?
可能一条大SQL就把整个数据库堵死
拒绝大SQL,拆解成多条简单SQL
简单SQL缓存命中率更高
减少锁表时间,特别是MyISAM
用上多CPU
用SELECT * 时
更多消耗CPU、内存、IO、网络带宽
先向数据库请求所有列,然后丢掉不需要列?
尽量不用SELECT * ,叧取需要数据列
更安全的设计:减少表变化带来的影响
为使用covering index提供可能性
Select/JOIN减少硬盘临时表生成,特别是有TEXT/BLOB时
举例
SELECT * FROM tag WHERE id = 999184
SELECT keyword FROM tag WHERE id = 999184
改写OR为IN()
同一字段,将or改写为in()
OR效率:O(n)
IN 效率:O(Log n)
当n很大时,OR会慢很多
注意控制IN的个数,建议n小于200
举例
Select * from opp WHERE phone=‘12347856' or phone=‘42242233' \G
Select * from opp WHERE phone in ('12347856' , '42242233')
改写OR为UNION
不同字段,将or改为union
减少对不同字段进行 "or" 查询
Merge index往往很弱智
如果有足够信心:set global
optimizer_switch='index_merge=off';
举例
Select * from opp WHERE phone='010-88886666' or cellPhone='13800138000';
Select * from opp WHERE phone='010-88886666'
union
Select * from opp WHERE cellPhone='13800138000';
避免负向查询和% 前缀模糊查询
避免负向查询
NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、
NOT LIKE等
避免 % 前缀模糊查询
B+ Tree
使用丌了索引
导致全表扫描
举例
MySQL> select * from post WHERE title like ‘北京%' ;
298 rows in set (0.01 sec)
MySQL> select * from post WHERE title like '%北京%' ;
572 rows in set (3.27 sec)
COUNT(*)的几个例子
COUNT(*)!=count(col)
减少COUNT(*)
MyISAM VS INNODB
不带 WHERE COUNT()
带 WHERE COUNT()
COUNT(*)的资源开销大,尽量不用或者少用
计数统计
实时统计:用memcache,双向更新,凌晨
跑基准
非实时统计:尽量用单独统计表,定期重算
LIMIT高效分页
传统分页:
Select * from table limit 10000,10;
LIMIT原理:
Limit 10000,10
偏移量越大则越慢
推荐分页:
Select * from table WHERE id>=23423 limit 11;
#10+1 (每页10条)
select * from table WHERE id>=23434 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) ;
分页方式四:
程序取ID:select id from table limit 10000,10;
Select * from table WHERE id in (123,456…) ;
可能需按场景分析并重组索引
LIMIT的高效分页
• 示例:
MySQL> select sql_no_cache * from post limit 10,10;
10 row in set (0.01 sec)
MySQL> select sql_no_cache * from post limit 20000,10;
10 row in set (0.13 sec)
MySQL> select sql_no_cache * from post limit 80000,10;
10 rows in set (0.58 sec)
MySQL> select sql_no_cache id from post limit 80000,10;
10 rows in set (0.02 sec)
MySQL> select sql_no_cache * from post WHERE id>=323423 limit 10;
10 rows in set (0.01 sec)
MySQL> select * from post WHERE id >= ( select sql_no_cache id from post limit
80000,1 ) limit 10 ;
10 rows in set (0.02 sec)
用UNION ALL 而非 UNION
若无需对结果进行去重,则用UNION ALL
UNION有去重开销
举例
MySQL>SELECT * FROM detail20091128 UNION ALL
SELECT * FROM detail20110427 UNION ALL
SELECT * FROM detail20110426 UNION ALL
SELECT * FROM detail20110425 UNION ALL
SELECT * FROM detail20110424 UNION ALL
SELECT * FROM detail20110423;
分解联接保证高并发
高并发DB不建议进行两个表以上的JOIN
适当分解联接保证高并发
可缓存大量早期数据
使用了多个MyISAM表
对大表的小ID IN()
联接引用同一个表多次
举例:
MySQL> Select * from tag JOIN tag_post on tag_post.tag_id=tag.id
JOIN post on tag_post.post_id=post.id WHERE tag.tag=‘二手玩具’;
MySQL> Select * from tag WHERE tag=‘二手玩具’;
MySQL> Select * from tag_post WHERE tag_id=1321;
MySQL> Select * from post WHERE post.id in (123,456,314,141)
GROUP BY 去除排序
GROUP BY 实现
分组
自动排序
无需排序:Order by NULL
特定排序:Group by DESC/ASC
举例
MySQL> select phone,count(*) from post group by phone limit 1 ;
1 row in set (2.19 sec)
MySQL> select phone,count(*) from post group by phone order by null limit 1;
1 row in set (2.02 sec)
同数据类型的列值比较
原则:数字对数字,字符对字符
数值列与字符类型比较
同时转换为双精度
进行比对
字符列与数值类型比较
字符列整列转数值
不会使用索引查询
举例:字符列与数值类型比较
字段:`remark` varchar(50) NOT NULL COMMENT '备注,
默认为空',
MySQL>SELECT `id`, `gift_code` FROM gift WHERE
`deal_id` = 640 AND remark=115127;
1 row in set (0.14 sec)
MySQL>SELECT `id`, `gift_code` FROM pool_gift WHERE
`deal_id` = 640 AND remark='115127'
;
1 row in set (0.005 sec)
• 大批量更新凌晨操作,避开高峰
• 凌晨不限制
• 白天上限默认为100条/秒(特殊再议)
• 举例:
update post set tag=1 WHERE id in (1,2,3);
sleep 0.01;
update post set tag=1 WHERE id in (4,5,6);
sleep 0.01;
MySQL子查询
大部分情况优化较差
特别WHERE中使用IN id的子查询
一般可用JOIN改写
举例:
MySQL> select * from table1 where id in (select
id from table2);
MySQL> insert into table1 (select * from table2);
//可能导致复制异常