MySQL 索引深度解析
MySQL 索引深度解析一、常见模型1.1 索引实现地点在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。(还记得前面存储引擎层实现了 undo log 的存储吗?)而即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。 实现方式在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。又因为前面我们提到的,InnoDB 使用了 B+树索引模型,所以数据都是存储在 B+树中的。 每一个索引在 InnoDB 里面对应一棵 B+树(想想 everything 先建立索引,然后再便于我们查找文件就知道了,又或者是 ES 的倒排索引,索引的目的就是加快查找速度)。 索引类型 主键索引 - 的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index) 非主键索引 - 的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index) 主键索引和普通索引的查询区别根据上面的索引结构说明,我们来讨论...
MySQL 锁机制深度解析
MySQL 锁机制深度解析一、锁的种类1.1 按加锁粒度分类1.1.1 全局锁概念 顾名思义,全局锁就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read lock (FTWRL)。 当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞: 数据更新语句(数据的增删改) 数据定义语句(包括建表、修改表结构等) 更新类事务的提交语句 典型使用场景 全局锁的典型使用场景是,做全库逻辑备份。也就是把整库每个表都 select 出来存成文本。 以前有一种做法,是通过 FTWRL 确保不会有其他线程对数据库做更新,然后对整个库做备份。注意,在备份过程中整个库完全处于只读状态。 问题分析 但是让整库都只读,听上去就很危险: 如果你在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆 如果你在从库上备份,那么备份期间从库不能执行主库同步过来的 binlog,会导致主从延迟 不加锁的话,备份系统备份的得到的库不是一个逻辑时间点,这个视图是逻辑不一致的(所以在全库逻辑备份的时候要 STW...
MySQL 各种日志深度解析
MySQL 各种日志深度解析一、Double Write(二次写)1.1 脏页刷盘的风险是什么首先介绍一下 IO 的最小单位: 数据库 IO 的最小单位是 16K(MySQL 默认,Oracle 是 8K) 文件系统 IO 的最小单位是 4K(也有 1K 的) 磁盘 IO 的最小单位是 512 字节 因此,存在 IO 写入导致 page 损坏的风险。 1.2 ⭐二次写解决了什么问题提高了 InnoDB 的可靠性,用来解决部分写失败(partial page write 页断裂)。 一个数据页的大小是 16K,假设在把内存中的脏页写到数据库的时候,写了 2K 突然掉电,也就是说前 2K 数据是新的,后 14K 是旧的,那么磁盘数据库这个数据页就是不完整的,是一个坏掉的数据页。 redo 只能加上旧、校检完整的数据页恢复一个脏块,不能修复坏掉的数据页,所以这个数据就丢失了,可能会造成数据不一致,所以需要 Double Write。 当数据库正在从内存向磁盘写一个数据页时,数据库宕机,从而导致这个页只写了部分数据,这就是部分写失效,它会导致数据丢失。这时是无法通过重做日志恢复的,因...
MySQL 缓存机制深度解析
MySQL 缓存机制深度解析一、Buffer Pool(缓冲池)1.1 ⭐Buffer Pool 缓存的东西基本概念我们都知道 InnoDB 存储引擎会把存储的数据划分为多个页,并且是以页作为磁盘与内存交互的基本单位,一个页的大小是 16KB。 由于 Buffer Pool 是 InnoDB 存储引擎内部的结构,所以它们都是用页来划分空间的。 初始化过程在 MySQL 启动的时候,InnoDB 会为 Buffer Pool 申请一片连续的内存空间,然后按照 16KB 划分出一个一个的页,Buffer Pool 中的页被称为缓存页。此时这些缓存页都是空闲的,还没有写入磁盘中的页,之后随着程序的运行才会进行写入操作。 Lazy 初始化所以,在 MySQL 刚刚启动的时候,使用的虚拟内存很大,但是实际使用的物理内存其实很小,因为只有当这些虚拟内存被访问之后,操作系统才会触发缺页中断,申请物理内存,将虚拟地址和物理地址建立映射关系,这还有一个说法叫 lazy 初始化。 缓存内容当然,Buffer Pool 除了缓存索引页和数据页,还会缓存一些其他的页数据。 1.2 ⭐预读失效问题描述根据...
MySQL 存储引擎深度解析
MySQL 存储引擎深度解析一、InnoDB 架构1.1 MySQL 的数据是存放在哪的⭐ibdata1 共享表空间需要注意的是表的数据可以存储在共享表空间里面 也可以存储在独占表空间里面,这个参数由 innodb_file_per_table 控制 默认是开启的。不过实际上这个 ibdata1 文件还是存在的。 原理说明 如果启用了 innodb_file_per_table 参数,需要注意的是每张表的表空间内存放的只是数据、索引和插入缓冲 Bitmap 页,其他数据如: 回滚信息、插入缓冲索引页、系统事物信息、二次写缓冲(Double write buffer)等还是放在原来的共享表空间内。同时说明了一个问题: 即使启用了 innodb_file_per_table 参数共享表空间 ibdata1 还是会不断的增加其大小的 数据库空间占用来源数据库主要的空间占用来源于哪两部分。这里,我们还是针对 MySQL 中应用最广泛的 InnoDB 引擎展开讨论。 一个 InnoDB 表包含两部分,即:表结构定义和数据。 在 MySQL 8.0 版本以前,表结构是存在以.frm 为后缀...
MySQL 架构深度解析
MySQL 架构深度解析一、主从复制1.1 几种常见的主从复制模型主要有三种: 1. 同步复制性能差,可用性也差,一旦一个从库出现问题都会影响到业务 2. 异步复制(默认模型)MySQL 主库提交事务的线程不会等待 binlog 同步完成就会返回客户端结果,一旦主机宕机,数据就会发生丢失 3. 半同步复制MySQL 5.7 版本之后增加的一种复制方式,介于两者之间,事务线程不用等待所有的从库都完成复制,只要有一部分复制成功响应回来就行,即使主库宕机,至少还有一个从库有最新的数据,不存在数据丢失的风险。 二、分库分表2.1 垂直分库分表核心概念根据不同的业务功能/模块,将数据按表结构或库结构进行拆分。 垂直分库将不同业务模块的表拆到不同数据库中 垂直分表将一张表中的列划分为多个子表,按字段分类存储 2.2 水平分库分表核心概念把相同结构的数据,按一定规则划分到多个库或表中。 水平分表将一张表的数据行按规则拆分到多个表中 水平分库不仅拆表,还把多个分表分布到不同数据库中
Java IO 模型深度解析
Java IO 模型深度解析一、同步阻塞 IO (BIO)1.1 核心概念⭐首先解释什么是同步和阻塞。同步,调用线程必须等到操作完成,才能继续执行;阻塞,当条件不满足时,调用线程会被操作系统挂起(sleep),不占用 CPU。 ⭐然后解释为什么是同步阻塞的。同步阻塞 API ServerSocket.accept()、Socket.read()、Socket.write(),而这些 API 是同步阻塞的是因为最终依赖操作系统的阻塞式系统调用(如 accept/read/write) 1.2 底层实现原理⭐并介绍具体是怎么调用到的,介绍 JDK8 的 ServerSocket.accept() 的底层实现原理。通过策略模式(Debug 可以发现在 Windows 下 ServerSocket 内部使用的是 DualStackPlainSocketImpl 至少Vista(支持 IPv4 和 IPv6 同时处理) 或者 TwoStacksPlainSocketImpl 低于Vista),组合持有一个 SocketImpl 抽象类对象来实现的。ServerSocke...
Java HashMap 深度解析
Java HashMap 深度解析一、数据结构1.1 JDK 7 vs JDK 8JDK 7 采用的是数组加链表,没有红黑树 JDK 8 采用的是数组加链表加红黑树,红黑树的引入是为了解决链表过长的问题,提高查询效率 1.2 红黑树转换条件红黑树的升级条件 当链表长度大于等于 8 且数组长度大于等于 64 时,会将链表转换为红黑树 红黑树的退化条件 当链表长度小于等于 6 时,会将红黑树转换为链表 1.3 Entry 结构HashMap 的主干是一个 Entry 数组,Entry 其实就是一个有着键值对的链表节点,其中有 next 节点的指针,用于 Hash 冲突时形成链表,hash 字段是为了后面不用重复计算 hash 值,扩容直接 & 存在的 二、初始化2.1 默认参数HashMap 可以设置初始容量和负载因子: 默认的初始容量 initialCapacity 是 16 默认的负载因子 loadFactor 大小为 0.75 2.2 惰性初始化和 ArrayList 一样,都是惰性初始化,等到实际要用了才会真正构建 table 数组。在第一次 p...
Java ArrayList 深度解析
Java ArrayList 深度解析一、底层原理1.1 ⭐初始化无参构造会将 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 复制给 elementData 数组,这个很有用,便于确保无参构成函数创建的实例在添加第一个元素时,最小的容量是默认大小 10 有参集合构造,有参数值构造如果传递集合的大小或者传递的参数值为 0,会将 EMPTY_ELEMENTDATA 赋值给 elementData 数组,优化创建 ArrayList 空实例时产生不必要的空数组,使得所有 ArrayList 空实例都指向同一个空数组。当然,如果传递的大小不为零,参数值不为零,那么还是会正常构造的 1.2 ⭐扩容机制扩容触发流程add 函数每次添加元素的时候都会调用 ensureCapacityInternal 函数保证容量足够,容量足够才会继续将元素添加到 elementData 数组中 扩容详细过程如果调用 ensureCapacityInternal 函数之后发现容量不够了才会执行扩容逻辑,首先还是调用 ensureCapacityInternal 函数,然后 ensureCa...
Java 新特性深度解析
Java 新特性深度解析一、Java 8 新特性1.1 Optional用于解决空指针异常,使代码更健壮 1.2 Stream 流提供了一种处理集合数据的新方式,支持链式操作和函数式编程,提高代码可读性和效率 1.3 Lambda 表达式允许将函数作为方法参数,或将代码视为数据,简化匿名内部类,使代码更简洁、可读性更强 二、Java 17 新特性2.1 ⭐Record 不可变数据模型核心特性简化POJO类,自动生成构造器、Getter、equals、hashCode、toString 适用场景Record 非常适合 DTO / VO / Query,不适合 JPA Entity 2.2 ⭐Sealed 限制继承范围核心特性精确控制哪些类可以继承/实现,提升类型安全性,提升类型安全性和代码可维护性 与 enum 的对比enum 适合表达简单、无状态的枚举值,而 sealed 类可以看作是 enum 在类型系统层面的升级,适合表达可穷举但具有不同行为和结构的业务状态 适用场景Sealed 非常适合”受控的策略模式(Closed Strategy Set...