索引
B+树能很好的配合磁盘的读写特性,减少单次查询的磁盘访问次数
- 多级索引:多叉树,每个节点包含多个键和子节点的指针。使得树的高度较低,减少磁盘访问次数。
- 节点大小:磁盘的读写操作以块为单位的,而B+树一个节点的大小通常与磁盘块的大小相等,这使得每个节点通常只需要一次磁盘I/O操作就可以完全加载到内存中,从而减少了磁盘I/O操作的次数,提高了查询效率。
- 顺序存储:B+树的叶子节点是按照键的顺序存储的。范围查询时非常高效,可以顺序访问磁盘上的数据块,而不需要额外的磁盘I/O来查找下一个数据。
- 磁盘预读:由于B+树的节点通常与磁盘块大小相匹配,操作系统可以利用预读(read-ahead)功能来预测并预先加载将要访问的磁盘块。这样,当访问一个节点时,相邻的节点也可能已经被加载到内存中,从而减少了查询的等待时间。
B 树也称 B-树,全称为 多路平衡查找树 ,B+ 树是 B 树的一种变体。B 树和 B+树中的 B 是 Balanced (平衡)的意思。
B 树& B+树两者有何异同呢?
- B 树的所有节点既存放键(key) 也存放数据(data),而 B+树只有叶子节点存放 key 和 data,其他内节点只存放 key。
- B 树的叶子节点都是独立的;B+树的叶子节点有一条引用链指向与它相邻的叶子节点。
- B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程,叶子节点的顺序检索很明显。
- 在 B 树中进行范围查询时,首先找到要查找的下限,然后对 B 树进行中序遍历,直到找到查找的上限;而 B+树的范围查询,只需要对链表进行遍历即可。
综上,B+树与 B 树相比,具备更少的 IO 次数、更稳定的查询效率和更适于范围查询这些优势。
InnoDB存储引擎
InnoDB的逻辑存储结构:
- 表空间:最高层, ibd文件其实就是表空间文件。
- 段: 分为数据段(叶子节点)、索引段(非叶子节点)、回滚段,引擎自动管理段,段用来管理多个Extent(区)。
- 区:表空间的单元结构,每个区的大小为1M。 默认,一个区中一共有64个连续的页。
- 页:是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默认为 16KB。为了保证页的连续性,InnoDB 存储引擎每次从磁盘申请 4-5个区。
- 行:InnoDB 存储引擎数据是按行进行存放的。默认有两个隐藏字段:Trx_id:事务id;Roll_pointer:该记录修改前信息的指针。
内存结构:
- Buffer Pool:在执行增删改查操作时,先操作缓冲池中的数据(若缓冲池没有数据,则从磁盘加载并缓存),然后再以一定频率刷新到磁盘,从而减少磁盘IO,加快处理速度。缓冲池以 Page 页为单位,底层采用链表数据结构管理Page。
- Change Buffer:更改缓冲区(针对于非唯一二级索引页——普通索引),针对没在在Buffer Pool中的数据Page执行DML语句时,会将数据变更存在 Change Buffer 中。在未来数据被读取时,再将数据合并恢复到 Buffer Pool 中,再将合并后的数据刷新到磁盘中。 在缓冲池中进行合并处理,减少磁盘IO。
- Log Buffer:日志缓冲区,保存要写入到磁盘中的log日志数据(redo log 、undo log), 默认大小为 16MB,日志缓冲区的日志会定期刷新到磁盘中。
- 自适应hash索引,用于优化对Buffer Pool数据的查询。
多表查询
联合查询union
union查询就是将多次查询的结果合并起来
union all 是直接将数据合并,union 会对合并之后的数据去重
注意:联合查询的多张表列数必须保持一致,字段类型也需要保持一致
语法:
select 字段列表 from 表A union/union all select 字段列表 from 表B
联表查询 jion
- 内连接:查询A、B交集数据
- 隐式:select 字段 from 表1,表2 where……
- 显式:select 字段 from 表1(inner) join 表2 on……
- 外连接:
- 左外连接:查询左表所有数据(第一部分),以及两表交集数据(第二部分)
select 字段 from 表1 left(outer) join 表2 on……
- 右外连接:查询右表所有数据,以及两表交集数据,可以改成左外
select 字段 from 表1 right(outer) join 表2 on……
- 自连接:与自身连接、必须使用表别名,可以是内连,也可以是外连
select 字段 from 表A 别名A join 表A 别名B on ……
子查询(嵌套查询)
SQL语句中嵌套select语句。
语法:
select * from t1 where column1 = (select column1 from t2)
子查询外部的语句可以是增删改查的任一个
分为:
标量子查询:结果为单个值,操作符:= <> > >= < <=
列子查询:结果为一列,操作符:in、not in、any、some、all
行子查询:结果为一行,操作符:= <> in not in
表子查询:结果为多行多列,操作符:in,常出现在from之后
可以位于select、from、where 后
锁
- 全局锁:全库逻辑备份,flush tables with read lock。在InnoDB引擎中,在备份时加上参数 --single-transaction 来完成不加锁的一致性数据备份。
- 表级锁:锁定粒度大,发生锁冲突的概率最高,并发度最低。
- 表共享读锁/表独占写锁:lock tables 表名 read/write。
- 元数据锁MDL:系统自动控制,为了避免DML与DDL冲突,保证读写的正确性。MySQL5.5中,当对一张表进行增删改查的时候,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。
- 意向锁:为避免DML执行时,加的行锁与表锁的冲突,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。
- 行锁:锁定粒度最小,发生锁冲突的概率最低,并发度最高。由InnoDB引擎实现,是针对索引加锁,而不是记录。
- 行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。RC、RR隔离级别。
- 间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),防止其他事务在这个间隙进行insert,产生幻读。RR隔离级别。间隙锁可以共存。
- 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。RR隔离级别。
在 InnoDB 默认的隔离级别 REPEATABLE-READ 下,行锁默认使用的是 Next-Key Lock。但是,如果操作的索引是唯一索引或主键,InnoDB 会对 Next-Key Lock 进行优化,
- 记录存在,将其降级为 Record Lock(存在),即仅锁住索引本身,而不是范围。
- 记录不存在, 优化为间隙锁 。*
读写分离
- 读写分离是为了将对数据库的读写操作分散到不同的数据库节点上。 能够小幅提升写性能,大幅提升读性能。
- 读写分离基于主从复制,MySQL 主从复制是依赖于 binlog 。
- 主从复制的步骤:
- 库在事务提交时,会把数据变更记录在二进制日志文件 Binlog 中。
- 从库读取主库的 Binlog ,写入到从库的中继日志 Relay Log 。
- 从库重做中继日志中的事件,改变自己的数据
分库分表
水平分:每个库/表的结构一样,所有数据合起来才是一个整的库/表
水平分库:解决海量数据存储和高并发问题
水平分表:解决单表存储和性能问题
垂直分:按业务拆分(微服务)
垂直分库:高并发下提高磁盘IO和网络连接数
垂直分表:冷热数据分离,多表互不影响
实现技术
shardingJDBC:基于AOP原理,在应用程序中对本地执行的SQL进行拦截,解析、改写、路由处理。需要自行编码配置实现,只支持java语言,性能较高。