MySQL小技巧

简单介绍篇

存储引擎

MyISAM是MySQL5.1及之前的版本号的默认存储引擎。MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等。可是MyISAM不支持事务和行级锁,而且有一个毫无疑问的缺陷就是崩溃后无法安全回复。

MyISAM会将表存储在两个文件里:数据文件和索引文件。分别以.MYD和.MYI为扩展名。

InnoDB表是基于聚簇索引建立的。

推荐InnoDB存储引擎

数据类型

MySQL支持的数据类型非常多,整体上分为:数字、字符、日期、JSON。选择正确的数据类型对于获得高性能至关重要。

无论存储那种类型的数据,下面几个简单的原则都有助于作出更好的选择。

1)更小的通常更好

普通情况下。应该尽量使用能够正确存储数据的最小数据类型。

更小的数据类型通常更快。由于他们占用更少的磁盘、内存和CPU缓存,而且处理时须要的CPU周期也更少。

2)简单就好

简单的数据类型的操作通常须要更少的CPU周期。比如,整型比字符操作代价更低。由于字符集和校对规则(排序规则)使字符比較比整型比較更复杂。

3)尽量避免NULL

非常多表都包括可为NULL的列,即使应用程序并不须要保存NULL也是如此,这是由于可为NULL是列的默认属性。通常情况下最好指定列为NOT NULL。除非真的须要存储NULL值。

用NULL会浪费存储空间,由于InnoDB须要一个额外的字节存储。

NULL字段的复合索引无效。

假设查询中包括可为NULL的列,对MySQL来说更难优化,由于可为NULL的列使得索引、索引统计和值比較都更负责。当可为NULL的列被索引时,每一个索引记录须要一个额外的字节,在MyISAM里甚至还可能导致固定大小的索引(比如仅仅有一个整数列的索引)变成可变大小的索引。

日期类型

DATE类型表示日期的年月日部分,范围在1000-01-01到9999-12-31之间。

DATETIME类型表示日期的年月日时分秒部分。范围在1000-01-01 00:00:00到9999-12-31 23:59:59之间。

TIMESTAMP类型表示日期的年月日时分秒部分,范围在1970-01-01 00:00:01UTC到2038-01-19 03:14:07UTC之间。

能够让DATETIME和TIMESTAMP自己主动初始化而且随其它字段的改动而自己主动改动。

比如:

CREATE TABLE aaa (

id bigint(11) NOT NULL AUTO_INCREMENT,

ts timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

dt datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

name varchar(20) DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

主键

整数一般是主键的最好选择。由于他们非常快而且能够使用AUTO_INCREMENT。

假设可能,应该避免使用字符串类型作为主键,由于他们非常消耗空间,而且通常比数字类型慢。尤其在MyISAM表里使用字符串作为主键要特别小心。MyISAM默认对字符串使用压缩索引,这会导致查询慢非常多。

对于全然“随机”的字符串也很多加注意,比如MD5()、SHA1()、或者UUID()产生的字符串。

这些函数生成的新值会随意分布在非常大的空间内,这回导致INSERT以及一些SELECT语句变得非常慢:

由于插入值会随机地写到索引的不通位置,所以是的INSERT语句更慢。

这回导致页分裂、磁盘随机訪问,以及对于聚簇存储引擎产生聚簇索引碎片。

SELECT语句会变得更慢。由于逻辑上相邻的行会分布在磁盘和内存的不同地方。

随机值导致缓存对全部类型的查询语句效果都非常差。由于会使得缓存赖以工作的訪问局部性原理失效。

假设整个数据集都一样的“热”。那么缓存不论什么一部分特定数据到内存都没有优点;假设工作集比内存大,缓存将会有非常多刷新和不命中。

假设存储UUID值。则应该移除“-”符号;或者更好的做法是,用UNHEX()函数转换UUID值为16字节的数字,而且存储在一个BINARY(16)列中。检索时能够通过HEX()函数来格式化为十六进制格式。

UUID()生成的值与加密散列函数比如SHA1()生成的值有不同的特征:UUID值虽然分布也不均匀,但还是有一定顺序的。虽然如此,还是不如递增的整数好用。

索引

B-Tree索引

B-Tree索引能够加快訪问数据的速度,由于存储引擎不再须要进行全表扫描来获取须要的数据。取而代之的是从索引的根节点開始进行搜索。

B+树索引并不能找到一个给定键值的详细行。

B+树索引能找到的仅仅是被查找数据行所在的页。然后数据库通过把页读入内存,再在内存中进行查找,终于得到要查找的数据。

限制:

假设不是依照索引的最左列開始查找,则无法使用索引。

不能跳过索引中的列。

假设查询中有某个列的范围查询。则其右边全部列都无法使用索引优化查找。

Hash索引

高性能索引策略:

分区表

分区功能并非在存储引擎层完毕的。

分区有水平分区和垂直分区两种,可是MySQL不支持垂直分区。

大多数DBA会有这样一个误区:仅仅要启用了分区,数据库就会变得更快。这个结论是存在非常多问题的。事实上,分区对于某些SQL语句性能可能会带来提高。可是分区主要用语高可用性,利于数据库的管理。在OLTP应用中。对于分区的使用应该非常小心。

假设仅仅是一味使用分区,而不理解分区是怎样工作的。也不清楚你的应用如是使用分区。那么分许极有可能仅仅会对性能产生负面的影响。

MySQL数据库支持下面几种分区类型:

RANGE分区:

LIST分区:

HASH分区:

KEY分区:

不论创建哪种类型的分区,假设表中存在主键或者是唯一索引时,分区列必须是唯一索引的一个组成部分。

唯一索引能够是同意NULL值的。而且分区列仅仅要是唯一索引的一个组成部分,不须要整个唯一索引列都是分区列。

当建表时没有指定主键和唯一索引时,能够指定不论什么一列为分区列。

规范篇

规范存在意义

保证线上数据库schema规范

降低出问题概率

方便自己主动化管理

规范须要长期坚持。对开发和DBA是一个双赢的事情

核心规范

不在数据库做运算:cpu计算务必移至业务层

控制单表数据量:单表记录控制在1000w

控制列数量:字段数控制在20以内

平衡范式与冗余:为提高效率牺牲范式设计。冗余数据

拒绝3B:拒绝大sql,大事物,大批量

基本命名和约束规范

表字符集选择UTF8 。假设须要存储emoj表情,须要使用UTF8mb4(MySQL 5.5.3以后支持)

存储引擎使用InnoDB

变长字符串尽量使用varchar varbinary

不在数据库中存储图片、文件等

单表数据量控制在1000w下面

库名、表名、字段名不使用保留字

库名、表名、字段名、索引名使用小写字母。下面划线切割 ,须要见名知意

库表名不要设计过长,尽可能用最少的字符表达出表的用途

索引规范

单个索引字段数不超过5。单表索引数量不超过5,索引设计遵循B+ Tree索引最左前缀匹配原则

选择区分度高的列作为索引

建立的索引能覆盖80%基本的查询,不求全,解决这个问题的主要矛盾

DML和order by和group by字段要建立合适的索引

不在索引做列运算

避免索引的隐式转换

避免冗余索引

不要用外键

关于索引规范,一定要记住索引这个东西是一把双刃剑,在加速读的同一时候也引入了非常多额外的写入和锁,降低写入能力,这也是为什么要控制索引数原因。

之前看到过不少人给表里每一个字段都建了索引,事实上对查询可能起不到什么作用。

冗余索引样例

idx_abc(a,b,c)

idx_a(a) 冗余

idx_ab(a,b) 冗余

隐式转换样例

字段:remark varchar(50) NOT Null

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 WHEREdeal_id = 640 AND remark=‘115127’; 1 row in set (0.005 sec)

字段定义为varchar,但传入的值是个int,就会导致全表扫描。要求程序端要做好类型检查

字段规范

全部字段均定义为NOT NULL ,除非你真的想存Null

字段类型在满足需求条件下越小越好,使用UNSIGNED存储非负整数 ,实际使用时候存储负数场景不多

使用datetime存储时间

使用varchar存储变长字符串 ,当然要注意varchar(M)里的M指的是字符数不是字节数;使用UNSIGNED INT存储IPv4 地址而不是CHAR(15) 。这样的方式仅仅能存储IPv4。存储不了IPv6

使用DECIMAL存储精确浮点数,用float有的时候会有问题

少用blob text

SQL规范

尽量不使用存储过程、触发器、函数等

避免使用大表的JOIN。MySQL优化器对join优化策略过于简单

避免在数据库中进行数学运算和其它大量计算任务

SQL合并,主要是指的DML时候多个value合并,降低和数据库交互

UPDATE、DELETE语句不使用LIMIT,easy造成主从不一致

不用select *

慎用count(*)

避免负向%

请使用同类型比較

sql语句尽可能简单

一条sql仅仅能在一个cpu运算

大语句拆小语句以降低锁时间

一条大sql能够堵死整个库

简单的事务

事务时间尽可能短

bad case:

上传图片事务

OR改写为IN()

or的效率是n级别

in的消息时log(n)级别

in的个数建议控制在200以内

select id from t where phone=’159′ or phone=’136′;

=>

select id from t where phone in (’159′, ’136′);

OR改写为UNION

mysql的索引合并非常弱智

select id from t where phone = ’159′ or name = ‘john’;

=>

select id from t where phone=’159′

union

select id from t where name=’jonh’

limit高效分页

limit越大,效率越低

select id from t limit 10000, 10;

=>

select id from t where id > 10000 limit 10;

使用union all替代union

union有去重开销

使用load data导数据

load data比insert快约20倍。

posted @ 2018-03-21 13:03  llguanli  阅读(124)  评论(0编辑  收藏  举报