目录mysql
- 目录
- 前言
- 正文
- 1.表结构优化●
- 1.1拆分字段
- 1.2字段类型的选择
- 1.3字段类型大小的限制
- 1.4合理的增长冗余字段
- 1.5新建字段必定要有默认值
- 2.索引方面●
- 2.1索引字段的选择
- 2.2利用好mysql支持的索引下推,覆盖索引等功能
- 2.3惟一索引和普通索引的选择
- 3.查询语句方面●
- 3.1避免索引失效
- 3.2合理的书写where条件字段顺序
- 3.3小表驱动大表
- 3.4可使用force index()防止优化器选错索引
- 3.5事务语句顺序
- 4.分库分表●
- 结语
前言面试
mysql的优化是咱们常常都会提到的一个话题,也是重中之重,在不少大厂中会有专门的DBA来作这件事情,甚至更过度的是连应届生的招聘岗位要求上都写了须要懂一点sql优化,最近moon一直在写关于mysql的文章,包括以前写的索引相关,其实也都是为了这篇文章作个铺垫,因此你懂了吗,今天我将从表结构、索引、查询语句、分库分表这四个维度来和你们聊聊,在工做中,怎么进行sql优化?算法
正文sql
1.表结构优化●数据库
优化sql最基本的条件时要有一张表,那么咱们怎么经过一张表来达到sql语句优化的目的呢?微信
1.1拆分字段mysql优化
咱们给出一个场景,想象本身是一家包子铺老板,天天都要结帐,因而确定会有一张帐户余额表,来记录包子铺的总资产并发
CREATE TABLE `accout_balance` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键', `account` varchar(64) NOT NULL DEFAULT '' COMMENT '帐户', `balance` decimal(16,2) DEFAULT NULL COMMENT '余额', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
但是后来包子铺的生意作的愈来愈好,老板陆陆续续开了几百家店,后来竟然作成了全国连锁店。函数
老板很开心,可是他发现了一个问题,因为生意太火爆,因此**每时每刻都会有人结帐,并且他们的系统愈来愈卡了,这是为何?高并发
咱们来分析下:
每时每刻都会有人结帐,结帐后会修改accout_balance(帐户余额表)的balance(余额)字段,因此这张表是一张热表,而每一次修改都会开启一个事务(update语句就至关于一个事务),因此在高并发的状况下,问题显而易见,针对同一行数据,一个事务必需要等上另外一个事务执行完成以后才能执行本身的更新语句,因此愈来愈慢。(行锁)
那么咱们怎么针对这种状况来优化呢?moon的思路是控制并发度。
咱们目前的状况是几百家分店都会操做这同一行记录,那么咱们就能够把这一行记录分红多行,也就是说,把帐户的余额分红N份,这样每次增长的时候选择其中的一条记录增长,冲突的几率也变成了以前的N分之一。
1.2字段类型的选择
这种优化应该是比较常见的,咱们就长话短说。
好比针对UUID这种数据咱们能够直接使用char(36)来做为该字段的类型,或者说在表示boolean这种数据格式的时候,咱们就能够直接使用tinyint(2)做为咱们的字段类型,在咱们提早可预知字段的大小的时候,最好在类型上直接限制,避免浪费存储空间占用。
1.3字段类型大小的限制
这点在咱们公司的sql建表规范上就会明确写到。
我这里简单的举个例子,好比varchar,要使用varchar(255),这里会有几点考量:
一.255恰好会消耗一个字节的存储单元,可是256会致使消耗两个字节的存储单元。(这个针对UTF-8编码)
二.若是你要在varchar上创建索引,255会是一个彻底索引,而266以上只能用到最左前缀(MySQL的每一个单表中所建立的索引长度是有限制的,且对不一样存储引擎下的表有不一样的限制。myisam表,单列索引,最大长度不能超过1000 bytes,不然会报警,可是建立成功,最终建立的是前缀索引。innodb表,单列索引,超过 767 bytes的,给出warning,最终索引建立成功,取前缀索引(取前 255 字符)),最左前缀的弊端就是没法用到mysql提供的覆盖索引的加速功能了。
三.此外在onlineddl的时候,255如下能够用inplace的方式,256须要rebuild。(Inplace方式:这是原生MySQL 5.5,以及innodb_plugin中提供的建立索引的方式。所谓Inplace,也就是索引建立在原表上直接进行,不会拷贝临时表。相对于Copy Table方式,这是一个进步。 Inplace方式建立索引,建立过程当中,原表一样可读的,可是不可写。 )
1.4合理的增长冗余字段
在咱们刚开始学习mysql的时候,就会了解到数据库的三范式,而在实际的使用过程当中,为了性能,咱们也能够抛弃数据库的三范式。
moon在以前的公司就有这样的问题,一条sql语句要连5张表,正常一个查询下来可能要1分多钟,因此这条sql过重了,而在moon的细心观察下发现,其中两张表都只用到了其中一个字段,而后我就和DBA商量下将这两个字段冗余到了其它的两个表中(业务有关联),结果这条sql语句的执行时间就变成了十几秒。
1.5新建字段必定要有默认值
好处以下:
1.节省空间。
大致看上去,好像设置能够为空的时候更节省空间,但实际上,他比NOT NULL要多占用一个bit的空间,用来判断该字段是否为空。
2.索引失效索引分裂
引用到null,索引会失效。还看到一个说法:空更新到非空时,若是空间不足,有可能会引发索引分裂。
3.减小因空值出现的计算错误等
count()在遇到null值时,这条记录不会计算在内。
2.索引方面●
2.1索引字段的选择
通常状况下,能够经过慢查询日志选择出一些热sql语句,给select条件后以及where条件后的字段加索引。
2.2利用好mysql支持的索引下推,覆盖索引等功能
select a from user where b = 5;
此时给a和b字段增长索引,这样能够利用mysql的覆盖索引加速的功能,省去了回表的过程。
select a from user where c = 5 and d > 5;
此时给c和d字段增长索引,也能够在判断的时候也能利用到索引下推的功能,也就是说mysql在判断c=5后,发现d也是索引,会直接找到d判断d>5,若是不给d增长索引此时也是须要回表的。
其次对于组合索引: (a,b)这种索引一旦创建,就不须要再给a创建索引了,mysql的最左前缀原则支持组合索引或者字符串类型的索引最左N个单位的索引创建。反之,若是你此刻创建的是(a,b)索引,可是你的业务却还须要一个b的单独索引,那么就能够考虑给b单独新建索引了。 若是如今你的表中只有a索引,可是业务需求须要(a,b)索引,必定要记得,先增长索引,而后再创建索引,否则可能会致使服务挂掉。moon有个朋友的同事,在新增索引的时候,选择了先删除,后增长,这样就致使的在删除后到新增前的这段空白期,出现了不少慢查询sql,同时请求量有很大,业务没法在短期内处理完,只能慢慢等待,最后致使服务挂掉。
2.3惟一索引和普通索引的选择
若是咱们能在业务意义上保证某个字段是惟一的,而且这张表又是一个常常写入数据的表,那么这里moon推荐你用普通索引,而不是惟一索引,缘由以下:
一.在读取数据的时候,普通索引在查到知足第一个条件的记录后,会继续查找下一个记录,直到第一个不知足条件的记录。而惟一索引,查找到第一个知足条件的记录时,就直接中止了。这样看来其实惟一索引更好,可是实际观察来看,这种性能的差别微乎其微,何况咱们还能够在查询语句上用limit 1来限制。重点是第二点。
二.在更新过程当中,普通索引的更新由于不用考虑惟一性,会将此次更新操做直接写入change buffer中,以后会按期或者再次访问到这个数据页的时候持久化到磁盘当中。而惟一索引的更新不能用change bufer,缘由是要在表中判断是否已经有该条记录,因此会有一个将数据页读入内存的IO操做,而IO操做又是很消耗资源的。
3.查询语句方面●
3.1避免索引失效
一.最佳左前缀法则(带头索引不能死,中间索引不能断
二.不要在索引上作任何操做(计算、函数、自动/手动类型转换),否则会致使索引失效而转向全表扫描
三.不能继续使用索引中范围条件(bettween、<、>、in等)右边的列,如:
select a from user where c > 5 and b = 4;
四.索引字段上使用(!= 或者 < >)判断时,会致使索引失效而转向全表扫描
五.索引字段上使用 is null / is not null 判断时,会致使索引失效而转向全表扫描。
六.索引字段使用like以通配符开头(‘%字符串’)时,会致使索引失效而转向全表扫描,也是最左前缀原则。
七.索引字段是字符串,但查询时不加单引号,会致使索引失效而转向全表扫描
3.2合理的书写where条件字段顺序
这里其实也是最左前缀原则。在一些后需维护开发工做中,能够观察表中的联合索引,当你新写的sql有where条件时,尽可能在where条件的书写顺序按照联合索引的顺序。
3.3小表驱动大表
join查询在有索引条件下,驱动表有索引不会使用到索引,被驱动表创建索引会使用到索引。
MySQL 表关联的算法是 Nest Loop Join,是经过驱动表的结果集做为循环基础数据,而后一条一条地经过该结果集中的数据做为过滤条件到下一个表中查询数据,而后合并结果。若是还有第三个参与Join,则再经过前两个表的Join结果集做为循环基础数据,再一次经过循环查询条件到第三个表中查询数据,如此往复。因此,小表驱动大表所创建的链接次数也远比大表驱动小表所创建的链接次数要小的多。
能够经过EXPLAIN分析来判断在sql中谁是驱动表,EXPLAIN语句分析出来的第一行的表便是驱动表。
3.4可使用force index()防止优化器选错索引
在咱们肯定要使用某个索引的时候可使用force index()强制只用某个索引,避免在某些状况下优化器选择错误致使查询效率下降。
优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句。
优化器会结合是否使用临时表、是否排序、扫描行数等因素进行综合判断。
固然是用force index 也是有弊端的,若是你的索引起生了变化,而你的sql语句没有即便更改,那么这里就会报错。
3.5事务语句顺序
当开启一个事务有多个语,而且其中有更新语句,要把更新语句放在事务的最后面,由于更新语句会上行锁,而且释放锁的时间是这个事务结束,放在事务最后执行能够有效缩短上锁时间。
4.分库分表●
在以上你能作到优化的极致条件下,因为数据量很大,可能仍是会面临着慢查询的状况出现,那么这时候咱们就要考虑分库分表了。
moon这里简单的和你们举个例子:
一家作客服系统的公司,业务量很大,客户不少,天天可能有上千万的数据量,若是你将这些数据都放在一张表里面,毫无疑问,会死的很惨。这时候咱们能够考虑和业务相关的方式来进行分表,好比说你有10000家客户,你能够每一百家客户放在一张表上,这样平均下来一天该表可能只能几十万条数据,这样是能够接受的。可是时间久了,你会发现以前的数据都是没有用的,客户关心的都是最新产生的数据,那么咱们就能够分库,将这些客户不关心的数据放在这个冷库中,以提升线上热数据的查询效率。
结语
mysql优化的路还很长,固然以上这也不是所有的优化方案,可是会基本覆盖全部你在平常开发中能用到的优化小技巧,对于通常的面试官来讲,足以吊打他了,可是我要提醒你的是,面对DBA,仍是能够尽可能乖一点,嗯,言至于此~
文章首发于个人微信公众号:moon聊技术,欢迎你们关注
下期见,老铁们~