局部性原理

两种主要局部性:

时间局部性(Temporal Locality):如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。

程序循环、堆栈等是产生时间局部性的原因。

空间局部性(Spatial Locality):在最近的将来将用到的信息很可能与正在使用的信息在空间地址上是临近的。

磁盘预读

内存比磁盘的读写速度要快很多,但内存容量要远小于磁盘,而数据、程序的执行要调入内存后才能执行,所以内存和磁盘要经常进行I/O操作,I/O操作是个费事的过程,虽然现代系统已经有了通道(I/O处理机)技术的支持,但这远远不够(CPU的处理速度远远大于磁盘I/O的速度)。

计算机各模块处理速度:CPU > 缓存> 主内存 > 磁盘

为了尽量减少I/O操作,计算机系统一般采取预读的方式,预读的长度一般为页(page)的整倍数。计算机系统是分页读取和存储的,一般一页为4KB(8个扇区,每个扇区125B,8*125B=4KB),每次读取和存取的最小单元为一页,而磁盘预读时通常会读取页的整倍数。根据文章上述的【局部性原理】①当一个数据被用到时,其附近的数据也通常会马上被使用。②程序运行期间所需要的数据通常比较集中。由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),所以即使只需要读取一个字节,磁盘也会读取一页的数据。

mysql默认16k,这个参数在一开始初始化时就要加入my.cnf里,如果已经创建了表,再修改,启动MySQL会报错。

可以通过以下命令查看:

show variables like 'innodb_page_size';

页大小导致的建表报错:

SSD、PCIE 硬盘在使用时厂商或者优化建议需要把innodb_page_size 更改为4k,更改为4k 确实性能上有所提升,但由于线上环境索引使用不规范4k 也导致索引的大小必须小于768。

Specified key was too long; max key length is 768 bytes

OLAP与OLTP

数据分析体系与常规业务类系统在数据存储与使用方面有着很大的区别。

业务类系统主要供基层人员使用,进行一线业务操作,通常被称为OLTP(On-Line Transaction Processing,联机事务处理)。

数据分析的目标则是探索并挖掘数据价值,作为企业高层进行决策的参考,通常被称为OLAP(On-Line Analytical Processing,联机分析处理)。

从功能角度来看,OLTP负责基本业务的正常运转,而业务数据积累时所产生的价值信息则被OLAP不断呈现,企业高层通过参考这些信息会不断调整经营方针,也会促进基础业务的不断优化,这是OLTP与OLAP最根本的区别。

OLAP不应该对OLTP产生任何影响,(理想情况下)OLTP应该完全感觉不到OLAP的存在。

mysql两种存储引擎的不同存储方式

mysql主要分为以下两种存储方式,myisam和innodb。都是存储在磁盘上的,属于snapshot快照模式。

myisam有以下三种数据存储文件: .frm(表信息) .myd(数据) .myi(索引信息)。

innodb有以下两种数据存储文件:.frm(表信息) .idb(数据和索引文件)。

二者不同在于innodb将数据和索引放在同一个文件中存储。

mysql的一些主要特性(关键名词)

关于索引B+Tree的主要数据结构,请参考我的其他文章:数据结构中的内容。

在innodb存储引擎中,通常会有以下几种索引存在:聚簇索引,唯一索引,普通索引。

聚簇索引(聚集索引):在表中创建聚簇索引,该索引的根节点是唯一且非空的key值,其叶子结点存储的是该索引对应的整条记录。

如果表中有主键,那么主键就是聚簇索引。

如果表中没有主键,那么非空的唯一索引是聚簇索引。

如果非空唯一所以也没有,会默认生层一个6个字节的rowid,对用户是不可见的。

注意:聚簇索引,是指唯一且非空。

以下是主键id聚簇索引模型:

主键id聚簇索引模型

回表:

当对其他字段创建一个二级索引时,会创建一棵二级索引树,其叶子节点存放的是一级索引(聚簇索引)的key,当使用select * from table where 普通索引 = '某值'时,会先查找普通二级索引的树,然后根据二级索引树的叶子节点的key值,去聚簇索引获取整条记录,导致io增加(一级索引与二级索引是两颗不同的树)。

二级索引树

通过下图模拟sql的执行回表过程:

select * from table where key = '某值'
回表过程
索引覆盖
上面由于使用了select * 进行查询,导致了回表。当使用select id from table where 普通索引 = '某值'时,这时使用了索引覆盖(using index)。
举例子,分别执行以下两个sql,在结果一回表的过程中,没有使用到索引覆盖,而在结果二中,使用到了索引覆盖:
explain select * from bssp_sys_menu where parent_id = 101;
explain select id from bssp_sys_menu where parent_id = 101;

结果1:回表

结果2:索引覆盖

尽量使用所应覆盖能减少磁盘的io,因为避免了回表。

最左匹配

先模拟一个联合索引的存储结构是什么样子的,以两个key 做联合索引,仍然是在一棵树上,排序先比较前面的key,在比较后面的key,如下图:

联合索引结构

举个例子描述下最左匹配:

有一张学生表,字段分别是name,age,adrress...等,将name和age做为联合索引(name,age),依靠最左匹配原则,会有下面四种情况查询条件:

1 where name = ? and age =? # 走组合索引
2 where age =? and name = ? # 走组合索引
3 where name = ? # 走组合索引
4 where age =? # 不走组合索引

上面1、2、3中会走组合索引,因为其满足最左匹配原则,至少有name字段是可以匹配到索引,2中之所以能够走索引,是因为在mysql的server中查询优化器会对sql重新排序,达到最优的效果。4则完全无法匹配到组合索引。

索引下推(ICP,Index Condition Pushdown):

要想深入理解 ICP 技术,必须先理解数据库是如何处理 where 中的条件的,对 where 中过滤条件的处理,根据索引使用情况分成了三种:index key, index filter, table filter。

摘自

1. index key

用于确定SQL查询在索引中的连续范围(起始范围+结束范围)的查询条件,被称之为Index Key。由于一个范围,至少包含一个起始与一个终止,因此Index Key也被拆分为Index First Key和Index Last Key,分别用于定位索引查找的起始,以及索引查询的终止条件。也就是说根据索引来确定扫描的范围。

2. index filter

在使用 index key 确定了起始范围和介绍范围之后,在此范围之内,还有一些记录不符合where 条件,如果这些条件可以使用索引进行过滤,那么就是 index filter。也就是说用索引来进行where条件过滤。

3. table filter

where 中的条件不能使用索引进行处理的,只能访问table,进行条件过滤了。

通过上面可以得到mysql的where条件过滤分为三个步骤:index key -> index filter -> table filter.

在 MySQL5.6 之前,并不区分Index Filter与Table Filter,统统将Index First Key与Index Last Key范围内的索引记录,回表读取完整记录,然后返回给MySQL Server层进行过滤。而在MySQL 5.6之后,Index Filter与Table Filter分离,Index Filter下降到InnoDB的索引层面进行过滤,减少了回表与返回MySQL Server层的记录交互开销,提高了SQL的执行效率。

所以所谓的 ICP 技术,其实就是 index filter 技术而已。只不过因为MySQL的架构原因,分成了server层和引擎层,才有所谓的“下推”的说法。所以ICP其实就是实现了index filter技术,将原来的在server层进行的table filter中可以进行index filter的部分,在引擎层面使用index filter进行处理,不再需要回表进行table filter。

MRR(Multi-Range Read Optimization):

MRR 通过把「随机磁盘读」,转化为「顺序磁盘读」,从而提高了索引查询的性能。

在mysql中有两个配置:
mrr: on/off
mrr_cost_based: on/off
#开启mrr
set optimizer_switch='mrr=on';
#基于成本的逻辑判断开关,开启的话mysql会判断是否需要使用mrr
set optimizer_switch='mrr_cost_based=on';
开启mrr并且sql确实用到后会在explain语句中看到,如下:
explain select * from bssp_sys_menu where parent_id between 100 and 104;
MRR

如上图我们在查找范围数据时,会在二级索引拿到数据的主键id,及聚簇索引的key,这时候需要回表到聚簇索引,如果没有MRR,则我们拿到的id可能是乱序的,这样到磁盘读取数据会导致磁盘重复转动,经过MRR排序后,会在聚簇索引顺序读取。

MRR好处:

1、磁盘和磁头不再需要来回做机械运动。

2、可以充分利用磁盘预读。

3、在一次查询中,每一页的数据只会从磁盘读取一次。(MySQL 从磁盘读取页的数据后,会把数据放到数据缓冲池,顺序读能保证每次删除的数据都是使用过的)