三大范式
- 第一范式,每个字段必须是原子的,不能再拆分
- 第二范式,非主键完全依赖主键,不能部分依赖
- 第三范式,非主键只依赖主键,不能相互依赖
层层递进,减少数据冗余,保护数据一致性
提问:为什么要反范式?反范式应用的场景
反范式化是为了用"空间换时间",在互联网高并发场景下,读操作的频率往往远高于写操作
场景:比如两张表订单表和用户表,如果要显示订单列表及用户名,需要join用户表,但如果再订单表冗余user_name字段,则查询订单时无需关联用户表,速度更快,代价就是用户名更新时,也要更行订单表
事务
ACID:原子性,一致性,隔离性,持久性
并发事务的问题:
脏读:一个事务读到另一个事务还没提交的数据
不可重复读:一个事务先后读取同一条记录,但读取的数据不同
幻读:一个事务查询数据发现没有对应的数据,但是插入数据时发现又存在
解决方案:对事务进行隔离
事务级别:未提交读------读已提交(RC)------可重复读(默认级别,RR)------串行化
MVCC:
隐式字段
- DB_TRX_ID:最近修改事务id ,记录插入这条记录或最后一次i修改该记录的事务ID
- DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本
- DB_ROW_ID:隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段
readview :读视图时快照读sql执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务id
当前读:
读取是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
快照读:
简单的select就是快照读,读取的就是记录数据的可见版本,有可能是历史数据,开启事务后第一个select语句才是快照读的地方
事务中的隔离性是如何保证的呢?
通过MVCC,多版本并发控制,维护一个数据的多个版本,使得读写操作没有冲突
它的实现主要依赖数据库中记录的隐式字段,undo log日志,readView
第一,隐藏字段,里面有事务id,来记录每一次操作的事务id,还有回滚指针,指向这一个记录的上个版本
第二,
undo log,它是回滚日志,存储老版本数据。undo log是版本链的载体,多个事务并行操作某一行记录,记录不同事物修改数据的版本,通过回滚指针串联成一个链表
最后readView解决的是一个事务查询选择版本的问题
根据它的匹配规则和当前一些事务id判断该访问哪个版本的数据
不同的隔离级别的快照读是不一样的,最终的访问结果不一样
RC:每一次执行快照读时生成ReadView
RR:仅在事务第一次执行快照读时生成ReadView,后续复用
MVCC会导致表膨胀?
会的,MVCC的多版本留存是表膨胀的根本诱因,在高更新/高删除的场景下,大量过期版本未被及时清除
解决方案:长事务是阻塞清理的关键因素,优先解决;innoDB是后台自动清理,可以监控然后确保清理线程数够
主从同步原理
MySQL主从复制的核心就是二进制日志binlog
- 主库在事务提交的时,会把数据变更记录在二进制文件Binlog
- 从库读取主库的二进制文件Binlog,写入到从库的中继日记Relay Log
- 从库的sql线程异步读入Relay log,解析并执行其中的sql操作,最后实现主从同步
一条update语句执行的过程日志顺序(同insert/delete)
Undo Log → Redo Log(Prepare) → Binlog → Redo Log(Commit)
undo log
是逻辑日志,用于记录数据被修改前的信息,保证了事务的原子性和一致性,作用包括两个:提供回滚 和MVCC 。
redo log
是重做日志,记录的是事务提交时数据页的物理修改,是用来实现事务的持久性 ,用于在刷新脏页到磁盘发生错误的时候进行数据恢复使用
Binlog
核心保障主从复制 / 数据恢复
Redo 管物理页,Undo 管行回滚,Binlog 管行复制
undo log页支持重用
Undo Log 页支持重用 ,核心原则是先重用空闲页,后扩展新空间 ,避免 Undo 表空间无限膨胀
Purge 后台线程的清理 是 Undo 页重用的唯一前提 ,无清理则无重用;
长时读事务 是生产中 Undo 页重用率低、空间膨胀的最主要原因,优化慢 SQL 是根治问题的关键
redo log 刷盘策略
Redo Log 刷盘由 innodb_flush_log_at_trx_commit 控制,该参数是唯一核心,取值为0/1/2,生产环境禁止0,生产首选 1(默认),普通库可选 2,性能比1显著提升,避免了每次fsyn()开销,在安全上无数据丢失,最多丢失1秒内的已提交事务。
什么是聚簇索引?什么是非聚簇索引
聚簇索引:数据与索引放到一块,B+树的叶子节点保存了整行数据,有且只有一个
非聚簇索引(二级索引):数据与索引分开存储,B+树的叶子节点保存对应的主键,可以有多个
什么是回表查询
通过二级索引找到对应的主键值,然后到聚簇索引中查找整行数据,这个过程就是回表
什么是覆盖索引
覆盖索引是指二级索引中已经包含了查询需要的所有字段,查询时直接从索引里拿数据,不用再回表
什么是索引下推
原本server层干的话交给了引擎层干这叫"下推",索引下推是针对联合索引的,它是在存储引擎层根据索引条件先进性过滤,再进行回表,来减少回表次数。未启用的时候存储引擎仅过滤索引键"name",而启用后加上可下推条件"age"
MySQL中的超大分页怎么解决
数据量大的时候,limit分页查询,需要对数据进行排序,效率低
解决方案:覆盖索引+子查询
分库分表
分为垂直拆分和水平拆分,垂直分库一般按业务边界来,垂直分表的思路是冷热分离
水平拆分的核心就是选一个合适的分片键
常用的三个策略:
哈希取模:数据分布均匀,但扩容麻烦
范围分片:按时间分,扩容方便,但最新的库压力最大
一致性哈希:在哈希环上分配节点,扩容时只需要迁移相邻节点的部分数据
mysql的存储引擎你知道什么,有什么区别
InnoDB:支持ACID事务,可以commit,rollback/行级锁+表级锁/聚簇索引/通过redo log自动恢复崩溃,数据安全高
MyISAM:不支持事务/只支持表级锁/非聚簇索引/需要工具修复,数据会丢失
因此InnoDB适合高并发写,而MyISAM适合于只读或读多写少