数据库表设计
设计准则:范式设计(Normal Form)MySQL是关系型数据库,但是要想设计—个好的关 系,必须使关系满足一定的约束条件,此约束已经形成了规范,分成几个等级,一级比 一级要求得严格。除了前面的三范式,后面的巴斯-科德范式,第四第五范式一般在实验室中存在。
第一范式[1NF]:属于第一范式关系的所有属性都不可再分,即数据项不可分。(强调数据表的原子性,是其他范式的基础)
第二范式[2NF]:要求数据库表中的每个实例或行必须可以被惟一地区分。通常在实现来 说,需要为表加上一个列,以存储各个实例的惟一标识。这个惟一属性列被称为主关键字或主键、主码。
第三范式[3NF]:指每一个非主属性既不部分依赖于也不传递依赖于业务主键,也就是在第二范式的基础 上消除了非主键对主键的传递依赖。
即数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系。
反范式设计
范式设计,在实际的业务查询中会大量存在表的关联查询,而大量关联很多时候会非常影响查询的性能。反范式化就是为了性能和读取效率的考虑而适当的对数据库设计范式要求进行违反,允许存在少量的冗余,反范式化就是使用空间来换取时间。
范式设计优缺点
反范式化设计优缺点
这是写热点的分散
InnoDB存储引擎的三大特性:
1.Buffer Pool
2.自适应哈希索引(MySQL内部自行维护)
3.双写缓冲区
同时,InnoDB也支持全文检索
字段数据类型优化
InnoDB使用了聚集索引,将表的主键用来构造一棵B+Tree,并且将整张表的行记录数据存放在该B+树的叶子节点中。也就是所谓的索引即数据,数据即索引。叶子节点就是数据页,数据页上存放的是完整的每行记录,对于主键排序查找和范围查找速度非常快。如果我们没有定义主键,MySQL会使用唯一性索引,没有唯一性索引,MySQL也会创建一个银行列RowID来做主键,然后用这个主键来建立聚集索引。
辅助索引/二级索引
回表
可利用覆盖索引的方式来优化,避免回表,或者缩小查询范围或者尽量定位清晰,减少回表次数。回表操作基本上都是随机IO操作,相对于顺序IO会更慢。
MRR(多范围读取)
每次执行回表操作时都相当于要随机读取一个聚集索引页面,而这些随机IO带来的性能开销比较大。MySQL中提出了一个名为Disk-Sweep Multi-Range Read 的优化措施,即先读取一部分二级索引记录,将它们主键值排好序之后再统一执行回表操作。会相对节省一些IO开销,MRR优化措施的条件比较苛刻,所以我们直接认为每读取一条二级索引记录就立即执行回表操作。
自适应哈希索引
在InnoDB存储引擎内部自己去监控索引表,如果监控到某个索引经常用,那么 就认为是热数据,然后内部自己创建一个hash索引,称之为自适应哈希索引( Adaptive Hash Index,AHI),创建以后,如果下次又查询到这个索引,那么直接通 过hash算法推导出记录的地址,直接一次就能查到数据,比重复去B+tree索引中查 询三四次节点的效率高了不少。 InnoDB存储引擎使用的哈希函数采用除法散列方式,其冲突机制采用链表方式。注 意,对于自适应哈希索引仅是数据库自身创建并使用的,我们并不能对其进行干 预。通过命令show engine innodb status\G可以看到当前自适应哈希索引的使用状况 【数据结构用的数组+链表的方式】哈希索引只能用来搜索等值的查询。
全文检索之倒排索引
什么是全文检索(Full-Text Search)?它是将存储于数据库中的整本书或整篇文章中 的任意内容信息查找出来的技术。它可以根据需要获得全文中有关章、节、段、句、词 等信息,也可以进行各种统计和分析。我们比较熟知的Elasticsearch、Solr等就是全文检索引擎,底层都是基于Apache Lucene的。
倒排索引:将文档中包含的关键字全部提取处理,然后再将关键字和文档之间的对应关系保存起来,最后再对关键字本身做索引排序。用户在检索某一个关键字时,先对关键字的索引进行查找,再通过关键字与文档的对应关系找到所在文档。
SELECT * FROM order_exp WHERE id > 100 AND insert_time = 'a';
对于InnoDB的二级索引来说,记录先是按照索引列进行排序,如果该二级索引是个联合索引,那么会按照联合索引中的各个列依次排序。而单个二级索引是由索引列+主键构成,二级索引列的值相同的记录可能会有好多条,这些索引列的值相同的记录又是按照主键的值进行排序的。但如果从各个二级索引中查询出的结果集并不是按照主键排序的话,就要先把结果集中的主键值排序完再来进行比较,比较耗时。所以内部会自行先将分别获取到的主键id进行排序,再进行操作,最后按照有序的主键值去回表 叫做:Rowid Ordered Retrieval ROR;
SELECT * FROM order_exp WHERE order_no = 'a' OR expire_time = 'b'
满足并集合并的情况同交集合并的情况一致,同时,可以使用交集合并作为并集合并的搜索条件。
SELECT * FROM order_exp WHERE insert_time = 'a' AND order_status = 'b' AND expire_time = 'c' OR (order_no = 'a' AND expire_time = 'b');
会先用交集合并得到一个主键集合,再采用Union索引合并的方式把两个主键集合合并取并集,然后进行回表操作。
C.Sort-Union(排序索引合并):
SELECT * FROM order_exp WHERE order_no< 'a' OR expire_time> 'z'
高性能索引创建策略
1.索引列的类型尽量小:数据类型越小,在查询时进行的比较操作越快(CPU层次);索引所占用的存储空间就越少,在一个数据页内就可以放下更多的记录,从而减少磁盘IO带来的性能损耗,意味着可以把更多的数据页缓存在内存中,从而加快读写效率。
2.创建索引应该选择选择性(离散性)高的列:不重复的索引值(基数)和数据表的记录总数(N)的比值。索引的选择性越高则查询效率越高,因为选择性高的索引可以让MySQL在查找时过滤掉更多的行。唯一索引的选择性是1,也是最好的索引选择性,性能也是最好。
3.前缀索引:索引很长的字符列,会让索引变的大且慢,可考虑用Hash索引(但是不支持范围查询),可选择足够长的前缀索引以保证较高的离散性。
前缀索引是一种能使索引更小,更快的办法,但是无法用前缀索引做order by和 group by,也无法使用前缀索引做覆盖扫描。有时候后缀索引(suffix index)也有用途(例如,找到某个域名的所有电子邮件地 址)。MySQL原生并不支持反向索引,但是可以把字符串反转后存储,并基于此建立前缀 索引。可以通过触发器或者应用程序自行处理来维护索引。
4.只为用于搜索、排序或分组的列创建索引。
5.合理设计多列索引
6.尽可能设计三星索引:
a.索引将相关的记录放到一起则获得一星; b.如果索引中的数据顺序和查找中的排列顺序一致则获得二星; c. 如果索引中的列包含了查询中需要的全部列则获得三星。
7.主键尽量是很少改变的列:如果主键频繁改变,物理顺序会改变,MySQL要不断调整B+树
8.处理冗余和重复索引,删除未使用的索引。