1、数据库相关概念的介绍sql
一、数据库的访问路径数据库
在SQL语句可以被真正执行以前,优化器必须首先肯定如何访问数据。这包括:应该使用哪个索引,索引的访问方式如何,是否须要辅助式随机读,等等。从一条SQL,到优化器优化,再到引擎进行数据查询,落地到数据的存储页面,这是一个访问路径肯定的过程。缓存
二、索引片微信
索引片即表明谓词表达式所肯定的值域范围,而访问路径的成本很大程度上取决于索引片的厚度。网络
索引片越厚,须要扫描的索引页就越多,须要处理的索引记录也越多,并且最大的开销仍是来自于须要对标进行同步读操做。(数据越大,维护的索引也越大,对增长和修改影响很大)相反,索引片比较窄,就会显著减小索引访问的那部分开销,同时会有更少的表同步读取上。同步读是一个随机IO操做,单次的读取就要耗费10ms左右的时间。所以咱们须要经过谓词来肯定索引片的厚度,过滤的值域范围越少,索引片厚度就越窄。那么谓词必定就能匹配到索引么,或者说匹配的规则是什么?架构
三、肯定匹配列并发
肯定匹配列以后咱们能够知道当前的查询会用到哪些索引,以及匹配到该索引的哪些列,最终能够提早锁定数据的访问范围,为数据的读取节省读取压力。相对于没用匹配到索引的查询,有匹配列的查询,条件过滤是前置的,而没有匹配到索引的查询,条件过滤是后置的,即全表扫描以后,再过滤结果,如此磁盘IO压力过大。另外 “最左匹配”原则也是基于匹配列规则而来,为什么是最左匹配,除了B树的原理以外,还有一个重要的缘由,在核对匹配列的时候,是从头至尾依次检查索引列。因此对因而否可以匹配到索引,where后面的谓词顺序不重要,重要的是索引列的顺序。框架
四、排序函数
物化结果集意味着经过执行必要的数据库访问来构建结果集。最好状况下,只须要返回一条记录,而最坏的状况下须要返回多条记录,须要发起大量的磁盘读取。而排序就是其中一种。在如下状况中,一次fetch调用只须要物化一条记录,不然对结果进行排序的时候就须要物化整个结果集。高并发
2、数据库的架构优化(分库分表)
1.熟悉业务,业务需求是咋样的,要达到什么样的目标。
2.数据库容器预估。
分库分表最重要的是要先作容器预估,依据数据量和业务特性估算出容器/库/表的数量及分库分表规则。同时根据实际需求要根据压测结果来决定;如压测其余一些指标是否知足需求,如QPS、响应时间等。
3.底层路由策略选择及实现
咱们知道常见的分表策略有两种:
hash路由
优势:可实现数据分散,热点分散;
不足:增长数据库节点时,会影响路由策略,需作数据迁移;
分区路由(增量区间路由)
优势:策略支持动态扩容,理论上可无限扩展;
不足:存在数据热点问题,新产生的表,读写频率较高;每次查询须要通过路由策略表。
固然每种策略都不是完美的,只有最适合业务场景的策略才是好的。固然项目能够采用的是两种方式的结合方式。
4.聚合查询及聚合数据同步的实现
上图是数据层改造后的架构图,以前是单表主从模式,改造后为多个分库、基础库。聚合采用了elastic search (如下简称ES)。注:canal是阿里里巴巴开源的数据库中间件(它实际上是模拟了数据库主从复制机制,假装为一个从库,当数据库(为不影响主库生产,咱们监听的是从库)binlog有变化时,canal监听到,经过解析服务解析过滤binlog,把须要的日志过滤出来。解析后,咱们经过发送MQ消息,消息体是表名和主键id,不是整条数据,消费端接到变化的表名和id,实时从库中查询最新数据,同步到ES、聚合表)。查询时,一部分业务会先查询缓存,不存在再查询ES,若是降级,才会查库,正常的聚合查询都不会查到库。
5.历史数据迁移
迁移步骤以下:
前半部分,从扫描到同步到分库是新代码,后面canal到同步ES、聚合表都是复用上面逻辑,这样设计,下降咱们总体工做量,而且保证数据迁移完整。
6.系统关键节点降级
这一部分也很重要,咱们的降级主要有两点,一是canal同步延迟降级,一是ES不可用降级。第一种以下:
若是canal同步延迟,或者从库挂掉,开启开关,扫描主库数据(最近几小时)直接同步到ES、聚合表;这样,即便从库挂掉,也不影响业务数据,这一点很重要。注意:就是若是服务不当心挂掉,怎么处理,通常是降级处理。
ES降级,ES不可用时,关闭ES开关,直接查询聚合表。
3、数据库索引知识点
1.什么是索引?
百度百科是这样描述的:
索引是为来加速对表中数据行中的检索而建立的一种分散的数据结果,时针对表而创建的,它是由数据页面之外的索引页面组成,每一个索引页中的行都含有逻辑指针,以便加速检索物理数据。
索引的误误区:
新建表时不须要建索引,后续才添加索引。续建立索引的成本也相对高不少。实际项目中,在百万级的数据中添加索引须要半个小时,或者更长时间(坑的要死)。
where条件后的字段均建索引。(过多的索引,也会致使索引文件剧增,也还达不到指望中的效果。)
简单SQL不须要索引,联合查询才须要索引。
联合索引的顺序是where条件后字段的前后顺序。(联合索引的顺序,是根据最左前缀原则,以及区分度来区分的,和where条件后字段的前后顺序无关。)
对于区分度小的字段上也新建索引,如状态,性别等字段等。(在区分度较小的字段上新建索引,基本无效,还会增长大量的索引文件。)
索引的区分度:
在说上述问题以前,咱们先来看看另外一个概念,就是区分度。
区分度: 指字段在数据库中的不重复比
区分度在新建索引时有着很是重要的参考价值,在MySQL中,区分度的计算规则以下:
字段去重后的总数与全表总记录数的商。
在实际项目中,根据省份进行查询,同时是百万级的数据,创建了一个普通索引,因为省份的区分度并不高,致使至关于没有创建索引。解决的方法能够是:组合索引。
(一) : 区分度
我的强烈建议, 建索引时,必定要先计算该字段的区分度,缘由以下:
1. 单列索引
能够查看该字段的区分度,根据区分度的大小,也能大概知道在该字段上的新建索引是否有效,以及效果如何。区分度越大,索引效果越明显。
2.多列索引(联合索引)
多列索引中其实还有一个字段的前后顺序问题,通常是将区分度较高的放在前面,这样联合索引才更有效,例如:
select * from t_base_user where name="" and status=1;
像上述语句,若是建联合索引的话,就应该是:
alter table t_base_user add index idx_name_status(name,status);
而不是:
alter table t_base_user add index idx_status_name(status,name);
(二) 最左前缀匹配原则
MySQL会一直向右匹配直到遇到范围查询(>、<、between、like)就中止匹配,好比
select * from t_base_user where type="10" and created_at<"2017-11-03" and status=1, (该语句仅做为演示)
在上述语句中,status就不会走索引,由于遇到<时,MySQL已经中止匹配,此时走的索引为:(type,created_at),其前后顺序是能够调整的,而走不到status索引,此时须要修改语句为:
select * from t_base_user where type=10 and status=1 and created_at<"2017-11-03"
便可走status索引。
(三) 函数运算
不要在索引列上,进行函数运算,不然索引会失效。由于b+树中存的都是数据表中的字段值,但进行检索时,须要把全部元素都应用函数才能比较,显然成本太大。在实际项目中的时候,作报表须要查询月初到月末的数据,一开始咱们采用的是:DATE_FORMAT(o.create_date, "%Y-%m")=#{date},查询很慢由于是全表扫描,函数的运算在“=”的左边,后面进行优化以后采用:o.create_date between #{startDate} and #{endDate},速度明显快起来。
(四) 扩展优先
扩展优先,不要新建索引,尽可能在已有索引中修改。以下:
select * from t_base_user where name="andyqian" and email="andytohome"
在表t_base_user表中已经存在idx_name索引,若是须要加入idx_name_email的索引,应该是修改idx_name索引,而不是新建一个索引。
4、表设计规范
1.选择优化的数据类型
尽可能避免null:若是查询中包含可为NULL 的列, 对MySQL来讲更难优化, 由于可为NULL 的列使得索引、 索引统计和值比较都更复杂。 可为NULL的列会使用更多的存储空间, 在MySQL里也须要特殊处理。 当可为NULL的列被索引时, 每一个索引记录须要一个额外的字节, 在MyISAM里甚至还可能致使固定大小的索引(例如只有一个整数列的索引)变成可变大小的索引。
禁止使用TEXT、BLOB类型:
解读:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,致使内存命中率急剧下降,影响数据库性能
禁止使用小数存储货币(能够把单位算做分,显示和计算时x100,...):
解读:使用整数吧,小数容易致使钱对不上。实际项目中:微信使用的是百分制。
禁止使用ENUM,可以使用TINYINT代替:
解读:
a)增长新的ENUM值要作DDL操做。
b)ENUM的内部实际存储就是整数,你觉得本身定义的是字符串?
单实例表数目必须小于500。
单表列数目必须小于30。
表必须有主键,例如自增主键:
解读:
a)主键递增,数据行写入能够提升插入性能,能够避免page分裂,减小表碎片提高空间和内存的使用。
b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类型能够有效的减小索引的磁盘空间,提升索引的缓存效率。
c) 无主键的表删除,在row模式的主从架构,会致使备库夯住。
禁止使用外键,若是有外键完整性约束,须要应用程序控制:
解读:外键会致使表与表之间耦合,update与delete操做都会涉及相关联的表,十分影响sql 的性能,甚至会形成死锁。高并发状况下容易形成数据库性能,大数据高并发业务场景数据库使用以性能优先。实际项目中有些数据使用了外键的原则致使删除很麻烦,最后去掉了外键约束。
2.命名规则
表——“模块名_表名”。表名最好不要用复数,缘由是在使用ORM框架开发时,代码生成器根据DB生成类定义,表生成了某个实例的类型定义,而不是实例集合。表名不要太长。缘由之一,某些软件对表名最大长度有限制;缘由之二,使用代码生成器每每会根据表名生产类型名称,以后懒人会直接使用这一名称,若是将太长的名称跨网络边界显然不是明智之举。
字段——bool类型用“Is”、“Can”、“Has”等表示;日期类型命名必须包含“Date”;时间类型必须包含“Time”。
存储过程——使用“proc_”前缀。
视图——使用“view_”前缀。
触发器——使用“trig_”前缀。