MySQL认知

91 阅读6分钟

说明:个人认知记录篇,仅代表个人观点。

设计规范

三范式

三范式在开发过程中可能已经熟练运用了,但是你现在还真的记得它吗?数据库设计理论上让我们设计要满足三范式

一范式:字段不可拆分,即不能定义为 name_age,而是要分开存储name与age字段

二范式:字段与主键直接关联

三范式:一张表中,不能有其他属性影响到与主键关联的字段。说人话:保持字段清爽,不要冗余。

可是在工作中真的能保持满足三个范式吗,第一范式与第二范式可能能满足,但是为了提高查询效率,减少跨表,冗余字段还是经常有吧。

+执行过程

你是不是遇到过以下场景:

Q:请你说一下sql语句的执行?
A:执行顺序为fromjoinonwhere,group by...

可是他/她想听的真的是这个吗?

image.png

客户端发起一条sql语句:select * from use where name={} and age={};
交给sqlserver做数据拼装转为:select * from use where name = 'aa' and age = 12;并做优化计划。
将当前数据存储内存中。
若是update操作,将执行操作前的数据写入undolog中。
将当前业务语句写入redolog中并标记为prepare。
执行sql操作;更新bufferpool中数据。
写入binlog。
阶段提交
- 写入binlog
- 最后将redolog中数据状态标记为commit

内部设计

MySQL用于存储数据信息,数据一般与业务关联,关联的数据必定会设计到业务。所以采用支持事务的InnoDB引擎。InnoDB中采用B+树作为数据存储结构。

InnoDB作用

本质上为了减少与磁盘的IO次数,提高查询效率。

mysql中的索引与数据都是保存在磁盘上的,随着数据存储越多,文件也会增大,而磁盘读取数据时并不是需要什么读取什么,而是以页为单位做范围读取,磁盘中查询数据的大小一般为4K或8K。InnoDB提供的为16K,并以K-V的形式获取。K为索引需要的值。而V需要待定。

确定V的结构

支持K-V的数据结构有很多,如哈希表、二叉树、AVL、BTS、红黑树。

哈希表虽根据K直接取出数据,查询效率高,但存储数据无序,做范围查询极端情况将遍历整个哈希表。

二叉树、AVL、BTS、红黑树虽然都为二叉树,查询速度为logn,效率上满足。但在大量数据时,树的层高增加,最终差的情况演变为链表查询。

为了解决层高的影响,引入B树也称为多叉树,一个节点内能存储大量内容。但是数据与索引都保存在同节点中,所以数据量增大后,依然存在树高影响效率问题。

B+树,一个支持多叉树,索引与数据分开存放,索引放在非叶子节点中,数据保存在叶子节点。

B+树

B+树中,索引放在非叶子节点,数据放在叶子节点中。数据保存按照顺序存储效率最高,这也就是为什么推荐使用主键自增的原因。若采用无序主键,插入数据时会造成上层节点发生变动,影响大量结构。

索引

索引,目的为了提高查询效率,但用之不当将影响效率的双刃剑。

聚簇索引 一般定义的主键就为聚簇索引,若没有显示定义主键信息,采用全局唯一且非空的字段,都不满足的情况下最后隐式生成row_id作为聚簇索引。

二级索引 除聚簇索引外,定义在其他字段上的索引都为二级索引。一个二级索引都是一颗B+树。

组合索引 为了加快查询效率,联合多个字段定义为组合索引。

引发问题

回表问题

大量使用二级索引,但只根据二级索引不能拿到需要的字段,还需要借助主键索引完成整条数据的查询。

慢查询

慢查询是针对所有的sql语句,并不指代某一种情况。可能是sql自身或网络波动,都会造成。慢查询需要手动开启,开启后数据记录在undolog中,long_query_time ,开启后默认时间为10s。

高效使用索引

索引上不做操作:不在索引上添加函数或二次操作。

全值匹配:尽量不要使用select *,防止查询到无效的列,需要什么查什么,尽最大可能去贴近索引。

最左匹配原则:主要针对组合索引的说明,如定义了 (1、2、3)索引,使用规则要满足(1),(1、2),(1、2、3)任选其一。

事务&产生问题

事务是为了确保数据执行的原子性。但是并发情况下,各端操作不同,将会出现 脏读、不可重复读、幻读

脏读:A事务做更新提交,B事务获取到A提交的内容,A发生异常回滚,B读取内容发生变化

不可重复读:A事务做第一次查询后,B事务做更新,A事务再次查询获取内容不一致。

幻读:A事务第一次做数量查询,B事务插入一条数据,A事务再次获取数量不一致。

隔离级别

未提交读:读取了还未提交的数据,将出现“三花问题”(脏读、不可重复读、幻读)都要发生

已提交读:读取到其他事务提交后的内容。会发生不可重复读和幻读问题。

可重复读:事务前后读取到的数据都是一样的。

串行化:一切操作都到等到上一个操作完成并提交。(都顺序执行了,肯定不会发生什么异常情况啊)

MVCC

多版本并发控制。事务执行时,通过将数据、tx_id做记录,并通过point指向下一个事务记录,形成串行化。

image.png MVCC主要通过readview做控制,重要属性包括m_id,min_trx_id,max_trx_id,creator。

m_id是个数组结构,存储正在执行还未提交的事务id,如 [80,100]
max_trx_id: m_id中最大值
min_trx_id:m_id中最小值
creator_id:创建事务信息

MVCC解决脏读

同事务中操作后,通过MVCC形成版本链。m_id中记录了活跃的事务id。做查询时,遍历版本链,若遍历到的版本信息事务id在m_id中,说明该数据还未提交,需要继续向后遍历,直到数据的事务id不在活跃版本数组中。

MVCC还会出现不可重复读

image.png 虽然能通过排查活跃id的方式解决脏读问题。但是多次查询之间存在数据更新,依然会造成数据不可重复读问题。MVCC中通过在第一次并只拿一次readview信息,拿到第一次查询数据的事务id。在事务未提交前根据记录的事务遍历拿到相同数据。

出现的幻读问题

为什么第二次能查出新增的内容? image.png

MVCC本质是控制的数据版本,相对于这种新增的数据,在之前没有这样的数据。新增前版本链中无数据,新增后版本链上存储在数据。所以能查到