个人认为以下军规主要为了适应海量数据场景,对于业务复杂性系统并一定完全按照此军规
一,核心军规
- 不在数据库做计算,cpu计算务必移至业务层
- 控制单表数据量,单表记录控制在千万级
- 控制列数量,字段数控制在20以内
- 平衡范式与冗余,为提高效率可以牺牲范式设计,冗余数据
- 拒绝3B(big),大sql,大事务,大批量
二,字段类军规
- 用好数值类型
tinyint(1Byte)
smallint(2Byte)
mediumint(3Byte)
int(4Byte)
bigint(8Byte)
bad case:int(1)/int(11)
bigint
从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数据(所有数字)。存储大小为 8 个字节。
int
从 -2^31 (-2,147,483,648) 到 2^31 - 1 (2,147,483,647) 的整型数据(所有数字)。存储大小为 4 个字节。int 的 SQL-92 同义字为 integer。
smallint
从 -2^15 (-32,768) 到 2^15 - 1 (32,767) 的整型数据。存储大小为 2 个字节。
tinyint
从 0 到 255 的整型数据。存储大小为 1 字节。
- 有些字符转化为数字
用int而不是char(15)存储ip
- 优先使用enum或set
例如:`sex` enum (‘F’, ‘M’) ;ENUM保存的是TINYINT
- 避免使用NULL字段
NULL字段很难查询优化
NULL字段的索引需要额外空间
NULL字段的复合索引无效
bad case:
`name` char(32) default null
`age` int not null
good case:
`age` int not null default 0
- 不在数据库里存图片
三,索引类军规
- 谨慎合理使用索引
改善查询、减慢更新
索引一定不是越多越好(能不加就不加,要加的一定得加)
覆盖记录条数过多不适合建索引,例如“性别” ;添加区分度高的索引能过滤掉80%的数据
- 字符字段必须建前缀索引
- 不在索引做列运算
-bad case:
select id where age +1 = 10;
-good case:
$curDate = date('Y-m-d'); $res = mysql_query('select * from order where date < = $curDate');
- innodb主键合理使用自增列
主键建立聚簇索引
主键不应该被修改
字符串不应该做主键
如果不指定主键,innodb会使用唯一且非空值索引代替
- 不用外键,请由程序保证约束
四,sql类军规
- sql语句尽可能简单
一条sql只能在一个cpu运算
大语句拆小语句,减少锁时间
一条大sql可以堵死整个库
- 简单的事务
事务时间尽可能短
bad case:
上传图片事务
- 避免使用触发器,用户自定义函数,请由程序取而代之
- 前导模糊查询不能使用索引
bad case:
select * from order where desc like '%XX'
good case:
select * from order where desc like 'XX%'
- 避免使用not或!= 使用in替代,如果序列是连续的话使用between
- 区分in和exists, not in和not exists
select * from 表A where id in (select id from 表B)
上面sql语句相当于
select * from 表A where exists(select * from 表B where 表B.id=表A.id)
区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。
关于not in和not exists,推荐使用not exists,不仅仅是效率问题,not in可能存在逻辑问题。如何高效的写出一个替代not exists的sql语句?
原sql语句
select colname … from A表 where a.id not in (select b.id from B表)
高效的sql语句
select colname … from A表 Left join B表 on where a.id = b.id where b.id is null
- 如果明确知道只有一条结果返回,使用limit 1
- 不用select *
消耗cpu,io,内存,带宽
这种程序不具有扩展性
- limit高效分页
limit越大,效率越低
select id from t limit 10000, 10;
应该改为 =>
select id from t where id > 10000 limit 10;
- 使用union all替代union,union有去重开销
- 尽量不用连接join
- 务必请使用“同类型”进行比较,否则可能全表扫面
bad case :
select * from user where phone=13800001234
-使用varchar(20)存储手机号,解读:
a)涉及到区号或者国家代号,可能出现+-()
b)varchar可以支持模糊查询,例如:like“138%”
- 如果业务大部分是单条查询,使用Hash索引性能更好,例如用户中心
select * from user where uid=? select * from user where login_name=?
原因:
B-Tree索引的时间复杂度是O(log(n))
Hash索引的时间复杂度是O(1)
- 打散批量更新
- 使用性能分析工具
show profile;
mysqlsla;
mysqldumpslow;
show slow log;
show processlist;
show query_response_time(percona)
explain;
EXPLAIN 查看SQL执行计划。
下面来个简单的示例,标注(1,2,3,4,5)我们要重点关注的数据
- type列,连接类型。一个好的sql语句至少要达到range级别。杜绝出现all级别
- key列,使用到的索引名。如果没有选择索引,值是NULL。可以采取强制索引方式
- key_len列,索引长度
- rows列,扫描行数。该值是个预估值
- extra列,详细说明。注意常见的不太友好的值有:Using filesort, Using temporary