• Mysql之innodb


    1、存储结构

    InnoDB存储引擎的逻辑存储结构和 Oracle大致相同 ,所有数据都被逻辑地存放在一个空间中 ,我们称之为表空间 ( tablespace ) 。表空间又由段 ( segment ) 、区 ( extent ) 、页 ( page ) 组成,InnoDB存储引擎的逻辑存储结构大致如图所示。

    段(segment)

    段是表空间文件中的主要组织结构,它是一个逻辑概念,用来管理物理文件,是构成索引、表、回滚段的基本元素。上图中显示了表空间是由各个段组成的,常见的段有数据段、索引段、回滚段等。InnoDB存储引擎表是索引组织的(index organized),因此数据即索引,索引即数据。那么数据段即为B+树的叶子节点(上图的leaf node segment),索引段即为B+树的非叶子节点(上图的non-leaf node segment)。

    创建一个索引(B+树)时会同时创建两个段,分别是非叶子节点段和叶子节点段,非叶子节点段用来管理(存储)B+树非叶子(页面)的数据,叶子节点段用来管理(存储)B+树叶子节点的数据;也就是说,在索引数据量一直增长的过程中,所有新的存储空间的申请,都是从“段”这个概念中申请的。

    区/簇(extents)

    innodb里的段(segment)又由多个区组成,在代码中被称为extent,区是由64个连续的页(page)组成的,每个页大小为16KB,即每个区的大小为1MB。一个区是物理上连续分配的一个段空间,每一个段至少会有一个区,在创建一个段时会创建一个默认的区。如果存储数据时,一个区已经不足以放下更多的数据,此时需要从这个段中分配一个新的区来存放新的数据。一个段所管理的空间大小是无限的,可以一直扩展下去,但是扩展的最小单位就是区。

    页(page)

    InnoDB有页(page)的概念,可以理解为区的细化,页是InnoDB磁盘管理的最小单位。

    常见的页类型有:

    数据页(B-tree Node)。

    Undo页(Undo Log Page)。

    系统页(System Page)。

    事务数据页(Transaction system Page)。

    插入缓冲位图页(Insert Buffer Bitmap)。

    插入缓冲空闲列表页(Insert Buffer Free List)。

    未压缩的二进制大对象页(Uncompressed BLOB Page)。

    压缩的二进制大对象页(Compressed BLOB Page)。

    在逻辑上(页面号都是从小到大连续的)及物理上都是连续的。在向表中插入数据时,如果一个页面已经被写完,系统会从当前区中分配一个新的空闲页面处理使用,如果当前区中的64个页都被分配完,系统会从当前页面所在段中分配一个新的区,然后再从这个区中分配一个新的页面来使用;

    2、表空间

    表空间可以看做是InnoDB存储引擎逻辑结构的最高层 ,所有的数据都是存放在表空间中。InnoDB存储引擎默认会有一个共享表空间 ibdata1(最大限制64TB) ,即所有数据都放在这个表空间内 。如果我们启用了参数innodb_file_per_table ,则每张表内的数据会放到自己的独立表空间(最大限制4TB)

    对于启用了innodb_file_per_table的参数选项,需要注意的是 ,每张表的表空间内存放的只是数据、索引和插入缓冲 ,其他类的数据,如撤销( Undo) 信息、系统事务信息、 二次写缓冲 (double write buffer ) 等还是存放在原来的共享表空间内。这也就说明了另一个问题:即使在启用了参数innodb_file_per_table之后,共享表空间还是会不断地增加其大小。

    3、事务

    事务开始时,生成一个事务ID,读取系统事务表,找到一个空闲的undo段,读取段头块,段头里面找到空闲的一行,把事务ID写进去,一个事务就这样开始了。

    当修改数据行时:

    (1)事务ID会写到修改的数据行里

    (2)数据行的修改前的数据会保存到undo段的数据页

    (3)修改的数据行里面的回滚指针同时会指向之前数据对应的undo页。

    这个事务没有提交,还没结束。此时去修改别的数据行,它们也会有自己对应的undo页,这些同一个事务的undo页会一个个的连起来(事务数据块链表)。而段头的第一个事务槽会指向最后一个undo页(事务数据块链表的末尾),而undo页依次向前指。因为这样rollback的时候就会逆着回滚(修改时是顺序,回滚当然是逆序了…)

    4、索引结构B+树

    基本概念

    首先有几个基本的概念:

    (1)每一层每个节点能存储的key的个数+1,称为B+树的m(或者阶数),比如下图为3+1=4阶B+树

    (2)第一层根节点到叶子结点的路径长度,称为B+树的高度h

    (3)叶子结点和非叶子结点(索引节点)

    (4)关键字(key)--表的主键

    (5)指针--子节点地址

    (6)数据(data)--表中一条记录

    (7)每层节点是有序递增的

    (8)小于父节点的在左,大于在右

    (9)非叶子结点的健值都存在于叶子节点中

    B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构。B-Tree结构中每个节点不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大增加每个节点存储的key值数量,降低B+Tree的高度。

    B+Tree相对于B-Tree有几点不同:

    (1)非叶子节点只存储关键字信息

    (2)所有叶子节点之间都有一个双向链表指针

    (3)数据记录都存放在叶子节点中

    为什么使用B+Tree?

    MySQL 是基于磁盘的数据库系统,索引往往以索引文件的形式存储的磁盘上,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级(每次存取都是按页来操作的),所以要尽量减少索引树的高度。

    (1)B+树的一个节点刚好也是一页,而且通常索引树的阶数会超过100,树的高度

    (2)B+树索引节点不存储数据,因此一个索引节点可以存储更多的索引节点, 每个节点能索引的范围更大更精确,也意味着 B+树单次磁盘IO的信息量大于其它树状结构,I/O效率更高

    (3)B+树的数据全部存储在叶子节点,而叶子节点是双向链表,可以很高效的实现区间查询,这种场景在mysql很常见

    聚簇(主键)索引、二级索引、联合索引

    聚簇索引:InnoDB存储引擎表是索引组织表,即表中数据按照主键顺序存放。而聚簇索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子结点存放的即为整张表的行记录数据,每一个表只能有一个聚簇索引

    二级索引:表中除了聚簇索引外其他索引都是二级索引(Secondary Index),与聚簇索引的区别是:二级索引的叶子节点不包含行记录的全部数据。其叶子节点除了包含键值以外,还包含一个书签(bookmark),该书签的值存放是聚簇索引的key,然后通过聚簇索引找到对应的行数据

    联合索引:联合索引也是二级索引的一种,联合索引的中的节点元素是有序元组结构,比如,在建树的过程中是按顺序比较a和b,全部比较完了才插入树中,当查询的时候也是按顺序比较,因此就有最左匹配原理

    注意:如果没遵循最佳左前缀法则、范围查询的右边会失效、like查询用不到索引等等

    失效原理:

    a顺序:1,1,2,2,3,3

    b顺序:1,2,1,4,1,2

    大家可以发现a字段是有序排列,b字段是无序排列(因为B+树只能选一个字段来构建有序的树)

    一不小心又会发现,在a相等的情况下,b字段是有序的。

    大家想想平时编程中我们要对两个字段排序,是不是先按照第一个字段排序,如果第一个字段出现相等的情况,就用第二个字段排序。这个排序方式同样被用到了B+树里。

    我们分析一个能走到索引的case1:

    select * from testTable where a=1 and b=2

    首先a字段在B+树上是有序的,所以我们可以通过二分查找法来定位到a=1的位置。

    其次在a确定的情况下,b是相对有序的,因为有序,所以同样可以通过二分查找法找到b=2的位置。

    范围查询失效case2:

    select * from testTable where a>1 and b=2

    首先a字段在B+树上是有序的,所以可以用二分查找法定位到1,然后将所有大于1的数据取出来,a可以用到索引。

    b有序的前提是a是确定的值,那么现在a的值是取大于1的,可能有10个大于1的a,也可能有一百个a。

    大于1的a那部分的B+树里,b字段是无序的,所以b不能在无序的B+树里用二分查找来查询,b用不到索引。

    也就是说,如果左边字段不确定,那右边的字段无法走到索引(因为是无序的,所以没法二分查找)

    插入、删除等操作

    参考:B树和B+树的插入、删除图文详解 - 黄文博 - 博客园

    5、小技巧

    (1)子查询改用连表查询(执行子查询时,MYSQL需要创建临时表,查询完毕后再删除这些临时表,有性能消耗)

    (2)比较的列最好是相同数据类型

    (3)多用数字运算

    (4)尽量声明列为NOT NULL,省去NULL值检查

    (5)BLOB和TEXT大对象,如果是精确查询,则建立散列索引(用MD5创建散列值,并新建一列来存放该散列值);如果是模糊查询,则可以建立前缀索引

    (6)有索引的列条件写在where最前面

    (7)联合索引遵循左匹配原理

    (8)上线前的sql语句,最好用explain,看下执行计划,是否走到预期的索引

    (9)如果有时区场景,请使用timestamp替代datetime

    6、MySql集群

    主从复制(异步)

    Mater会为每个slave开启一个独立的异步Binlog-dump线程来发送数据,Slave会向Master发送偏移量并接收Binlog中的增量信息

    半同步复制

    为了进一步保证数据的一致性,在异步同步的基础上,提出了半同步复制机制。在主库每次事务成功提交时,必须等待其中一个从库也接收到Binlog事务并成功写入中继日志后,才返回commit成功操作给客户端。当然如果从库宕机或者网络故障,或者通信超时后,会自动降级为异步复制

    高可用架构MHA

    MHA由两部分组成,Manager(管理节点)和Node(数据节点),Manager会定时探测集群中的master节点,当mastar出现故障时,它可以自动将最新数据的slave提升为新的mastar,然后将所有其他的slave重新指向新的master,并重新数据同步。

    MHA在故障切换过程中,Manager会尝试从宕掉的主服务器上获取Binlog日志,但有可能由于硬件故障而无法获取,导致最新数据丢失,此时可采用半同步复制,只要有一个slave收到最新的Binlog日志,Manager就会从Slave中获取Binlog日志,从而保证数据的一致性。

    Binlog监听中间件

    (1)开源的canal

    (2)阿里精卫

    7、性能(单机)

    (1)插入性能8000TPS左右,和服务器参数有关

    (1)查询性能10000QPS左右,和业务耗时以及数据库最大连接数,以及服务器参数有关

    8、锁机制

    Mysql之Innodb锁模式和死锁解析

    9、事务隔离级别

    事务的隔离级别举例_一文彻底读懂MySQL事务的四大隔离级别_weixin_39712016的博客-CSDN博客

    10、redo和undo

    MySQL redo和undo日志详解 | 学习笔记_Kevin_Opt的博客-CSDN博客_mysql redo undo日志

  • 相关阅读:
    使用移动平均数调整曲线 by Python
    Cassandra 安装部署
    OpenCV从入门到入坟
    jQuery+AJAX请求的统一封装
    实战 || 某软件股份有限公司通用漏洞挖掘
    戏剧影视设计制作虚拟仿真培训课件提升学生的参与感
    json & ajax & i18n
    文档图片阴影去除
    【从零开始学习 SystemVerilog】3.1.1、SystemVerilog 控制流—— while 和 do-while 循环
    北漂七年拿过阿里、腾讯、华为offer的资深架构师,分享经验总结
  • 原文地址:https://blog.csdn.net/y1250056491/article/details/125459260