===============页内数据组织==============

mysql使用数据时会分配一块内存空间,按页存储数据不是按条读取,一页16kb
1.页头
#记录页面控制信息,共56字节
#页和页之间组成双向链表
#页头包括左右兄弟页指针,页面空间使用情况等
2.虚记录
#最小虚记录(比页内最小主键还小)
#最大虚记录(比页内最大主键还大),页内数据最大最小根据主键判断
3.记录堆
#行记录存储区,分为有效记录和已删除记录两种
4.自由空间链表(已删除记录)
#已删除记录组层的链表
5.未分配空间
#页面未使用的存储空间
6.slot区
#连续的指针空间,每个指针指向某条记录
7.页尾
#页面最后信息占8个字节,主要存储页面校验信息
====================页面记录维护===================

页内数据
#innodb主键是聚簇索引,树状结构
1.上图是一个主键聚簇索引,索引树
2.page页与页之间是双向链表
3.页内数据是逻辑有序
4.索引页也是双向链表
-------------1.顺序保证---------------
a.物理连续有续:数组,一块连续的内存空间,
优点:可以用二分查找法快速查找数据
缺点:插入的时候需要数据移动

b.逻辑有序:链表排列,根据链表地址指针存储数据
优点:插入方便,不需要数据移动,在插入时直接改变链表关联关系即可
缺点:无序链表,不能二分查找,需要遍历

总结:选择逻辑有序,可以优化查找,物理有序插入无法优化
--------------2.插入策略--------------
1.使用自由空间链表(逻辑有序)
2.未使用空间:但是是变长数据,不会填满,还是会有碎片空洞,用久也需要收缩整理
--------------3.页内查询--------------
1.遍历(不能遍历)
2.二分查找:innodb使用二分查找,用的是slot区,根据slot二分查找,找到记录链表再进行遍历
类似跳表:记录在某一个区间

============innodb内存管理===========

----------sql执行过程------------
1用户sql先通过
存储引擎
server层调api
2存储引擎分析查找数据可能存在的page中
3看看page在不在内存中
4不在内存中从磁盘上page装载到内存中
5再返回给sever中
------------预分配一块内存空间—————
内存池中分配一块内存空间
内存页面管理:页面映射,页面数据管理
------------
数据以页为单位加载----------
目的:聚合io,不按条加载,按页加载减少io问题
------------
数据内外存交换-------------
内存有空闲页,数据页,更新时会有脏页(需要刷盘)
内存页都被使用:LRU策略(页面淘汰策略)
1、最久没有使用的数据淘汰掉
表头放最热的数据,表尾放最冷的数据

问题:简单LRU,全表扫描的时候会频繁淘汰热数据,最后热数据都会淘汰掉,因此innodb使用LFU,按访问频率淘汰数据
LFU策略:按访问频率淘汰,避免热数据被淘汰
================Mysql内存管理解决方案============

1buffer pool:预分配一块内存池
2page:buffer pool最小单位是page
3free list:空闲页链表,每个freelist节点指向一个内存链表中空闲页,写数据的时候从freelist中找,写完数据时删掉,在LRU list中加一个数据节点
4flush list:脏页链表
内存中有干净的页,没有修改的page
脏页,内存中发生修改的page,需要刷盘,写会磁盘
5page hash表:维护内存与磁盘映射关系的,动态的
6LRU list(优化过的内存淘汰策略):所有的数据页都指向LRUlist
-----------------数据装载过程————————

1先从空闲列表中找到一个空闲页
2如果没有空闲页则从LRUlist做页面淘汰,
3淘汰策略,从LRUlist尾部淘汰,
4如果LRUlist尾部数据在使用中,则做FLush LRU淘汰
5淘汰策略LRUlist中找第一个脏页把脏页刷盘
6再将数据写入LRU链表中
7维护一下pagehash磁盘和内存的映射关系,将关系写到pagehash中
8将空闲页写入内存中,写入LRUold链表中的header中
----------------------FLush LRU淘汰-------------------
1
从尾部找到一个脏页
2找到脏页后flush刷盘
3放到freelist中
4再放到LRU old中
-------------------Mysql使用优化过的LRU策略-------------------

mysql使用优化过的LRU策略:
将LRU分为两部分,冷数据和热数据,通过midpoint指针根据5/3的比例切分实现冷热分离
old到new:
1.old冷数据移动策略根据innodb_old_blocks_time参数存活时间判断是否热数据
2.old区页存活时间大于此值则写入LURnew头部
new到old:
保证midpoint指向8/5则从new尾部到old移动头部,只是改变了链表的指针链接
---------------------LRU_new操作---------------------
LRU_new链表操作:

设计思路:
每次读取数据都移动到new链表头,将page放在表头很耗性能,为了减少链表中数据的移动次数
有两个操作:
1freed_page_clock:buffer pool淘汰的页数,计数器
2LRU_new长度:1/4四分之一
原理:当要写入LRU_new表头的时候判断当前时间和上一次成为表头时间之间发生多少次页面淘汰
当前时间页面淘汰次数-上一成为表头页面淘汰次数=页面淘汰数
页面淘汰数和LRU_new长度的1/4分之一比较,大于1/4则才移动到表头中
===========事务实现原理===========

-------------------事务特性-----------------
事务特性:ACID
A:原子性:要么全部成功,要么全部失败
I:隔离性:并行事务之间互不干扰
D:持久性:事务提交后,永久失效
C:一致性:通过AID保证
-------------------
并发问题-------------------
1.脏读:读取到未提交事务的数据
事务B修改数据未提交,这时候事务A读取到了事务B未提交的数据
2.不可重复读:两次读取结果不同
事务B修改数据未提交,这时候事务A读不到修改数据,事务B提交后,事务A才读取到
3.幻读(永远存在):select操作得到的结果数据状态无法支撑后续业务操作
事务A读取快照数据3条,事务B增加一条数据提交变4条,事务A快照数据没变化还是3条,事务A增加4记录时崩溃,已存在
-------------------隔离级别------------------
读未提交内容:最低隔离级别,会读取到其他事务未提交的数据,脏读
读取提交内容:事务提交过程中可以读取其他事务已提交的数据,造成不可重复读
可重复读:每次读取相同的结果集,不管其他事务是否提交,幻读
串行化:所有事务排队
============事务实现原理==========

mvcc:多版本并发控制,解决读写冲突,靠隐藏列
undolog:解决事务一致性问题
redolog:解决事务持久性问题
————————mvcc——————————

多版本:
DB_TRX_ID:事务ID(每个数据修改写入都有一个事物完成)
DB_ROLL_PTR:事务回滚指针
当前读:select for update都是当前读
快照读:select数据都是快照读,读取固定一个版本,可重复读
--------------------mvcc读取哪个版本---------------

1.select读哪个版本由可见性判断
创建快照这一刻
看不到全局所有没有提交的事务
看不到快照之后创建的事务
2Read view实现可见性判断策略:
创建快照时读活跃事务列表(未提交的),根据事务列表ID排序
判断当前快照事务ID是否小于最小事务ID,是则可见
如果快照事务ID大于最大ID说明事务没提交,则回滚事务到上个版本
如果快照事务ID在最大最小之间,则判断是否在活跃事务ID中,不在则说明提交,可见数据
——————undo log—————————

undolog:记录事务多版本
保证事务原子性,回滚日志,存放数据多版本,用于事务回滚等
存储引擎存储最新数据版本,其他版本存放在undolog中
实现数据多版本
delete undo log:用于回滚,提交即清理
update undo log:用于回滚,同时实现快照读,不能随便删除
问题:undo log如何清理
答:依据系统最小活跃事务ID做依据,将最小活跃事务ID之前事务删除
问题:为什么innodb count(*)这么慢?为什么没有计数器?
——————-——redo log——————————

redolog:实现事务持久性,固化
用于,记录数据修改,异常恢复,循环写文件
redolog:有4个文件,writepos是写入指针,checkpoint当前刷盘的位置,checkpoint->writepos:待落盘数据
写入流程:

刷盘时机:
参数innodb_flush_log_at_trx_commit

-----------刷盘策略:-----------
1.innodb_flush_log_at_trx_commit = 0
每次发生commit写入内存+每秒中写文件刷盘,
特点:数据库挂的时候会丢1秒数据
2.innodb_flush_log_at_trx_commit = 1
每次提交一个事务都写文件并刷盘
特点:最多丢1次事务
3.innodb_flush_log_at_trx_commit = 2
每次提交事务,都写入操作系统文件+每秒刷1次盘
特点: 系统挂的时候,最多也是丢1次事务
总结:
选2
------------redolog意义:--------------
1.体积小,记录页的修改,比整页写入刷盘代价低
内存中更新的时候,如果每页操作都刷盘,1次刷盘要更新16kb*1大小
而redolog只记录数据更新页的修改,比16kb*1小的多
2.redolog是末尾追加顺序写入一个文件里,刷盘是根据页的位置随机写,发生改变的页不固定
================锁实现================

-----------锁粒度:------------
行级锁、
间隙锁、
表级锁
-----------锁类型:-----------
共享锁:读锁,可以同时被多个事务获取,阻止其他事务对记录的修改
排他锁:写锁,只能被一个事务获取,允许获得锁的事务修改数据
#所有当前读(写入)都加排他锁
#当前读包括:select for update,update,delete操作
============行级锁================
锁都是作用在索引上的
#分为聚簇索引和二级索引
聚簇索引
#主键索引,数据类型是B+树
二级索引
#除主键,其他字段上的索引,通过二级索引找到对应主键,再通过回表,利用主键索引找到pk
主键索引:

delete from user where uid = 103
#由于只用到主键索引,所以用主键索引,锁住103

唯一索引:
delete from user where phone =134
#当条件为二级索引时,先通过二级索引锁住数据,然后通过回表,利用主键索引,锁住103
———————加锁有4种情况————————

rc级别:没有间隙锁
rr级别:有间隙锁
1.唯一索引 * rc级别:
2.唯一索引 * rr级别:
3.非唯一索引 * rc级别

4.非唯一索引 * rr级别
#rr级别解决了幻读
#例子:当执行delete时,返回结果影响2条事务不提交,这时另一个又来个事务插入134两条记录
#在我的事务中又执行delete事务,还是返回结果又影响2条,这就时产生了幻读
#rr级别解决幻读的方法是在这两条区间上加间隙锁

============间隙锁================

间隙锁:只能解决当前读产生的幻读。
#解决可重复读模式下的幻读,
#锁住的位置是两条记录之间的GAP
#保证两次当前读返回一致的记录,不产生幻读
比如:131-134锁住的是区间
1:a
10:b
100:c
10000:d
删除一个100~10000区间,都被锁住,其他事务提交不了修改,只能等待区间锁释放

===============表级锁==================
全表扫描会表锁:
RC级别:
delete from user where phone = 134
phone没有索引,所以一条一条锁住全锁住
RR级别:
一条一条锁住,但是还会增加间隙锁

================加锁过程=================
1.一条sql进行update
2.sever层获取一条记录,当前读,到innodb
3.innodb找到数据加锁返回server
4.server进行update更新给到innodb
5.innodb成功后返回给sever结果
6.sever获取下一条记录,再重复进行
7.锁一条一条家,最后提交把锁全部释放

===================场景=================

update t_user set XX=XX where name = ‘f’
#第一条sql走name索引,name=‘f',命中两条记录,回表命中120,130主键两条记录
select * from t_user where age > 33 for update
#第二条sql走age索引,age>33,命中两条记录,回表同样命中123,130主键两条记录
锁是一条一条加的,这时候两个事务并行起来了
1.执行事务1锁住一条记录,f:120
2.事务2锁住一条记录,35:130
3.事务1又开始想锁,130,但是130锁被事务2锁住
4.事务2又开始想锁,120,但是120锁被事务1锁住
5.双方都持有对方的资源,又想要对方的资源,这时候发生死锁了
就会所谓产生的死锁