innodb存储引擎学习总结
注明:《MySQL技术内幕 InnoDB存储引擎》个人学习总结
第一章:mysql体系结构和存储引擎
1.1定义数据库和实例
- 数据库:物理操作系统文件或其他文件类型集合,frm、myd、myi、ibd
- 实例:后台线程+共享内存组成mysql数据库,实例用于操作文件,表现为一个进程,一个数据库对应一个数据库实例
- 单进程多线程
1.2mysql的结构体系
- 为什么mysql不支持全文索引?
mysql支持,MyISAM、Innodb都支持全文索引
- mysql速度快因为它不支持事务吗?
快是相对应用来说,ETL对于MyISAM来说更快,如果是OLTP那么就是Innodb更快
- 数据量大于1000w时mysql的性能会急剧下降吗?
mysql是数据库而不是文件,行数上涨速度变慢,但是正确配置mysql也是可以承受的
数据库:文件集合
数据库实例:用户和操作系统之间的一个软件,用于查询,增删改数据库文件,只有通过数据库实例才能处理数据库的文件信息。
mysql的组成部分
- 连接池组件
- 管理服务和工具、
- sql接口组件
- 查询分析器
- 优化器
- 缓冲
- 插件式存储引擎
- 物理文件
1.3mysql存储引擎
- 用户可以根据mysql的预定义的存储引擎接口编写自己的存储引擎
1.3.1innodb存储引擎
-
支持事务
-
设计目标面向在线事务(OLTP)处理应用
-
行锁+支持外键
-
数据放到一个逻辑表空间,表单独放到一个ibd文件上面
-
支持裸设备row disk建立表空间
-
存储使用的是聚集方式通过rowid
1.3.2MyISAM存储引擎
- 不支持事务,表锁设计,支持全文索引,面向OLAP数据库应用
- 只缓存数据索引但是不缓存数据文件
- 存储引擎表MYD(数据文件)+MYI(索引文件)
1.3.3NDB存储引擎
- 集群存储引擎
- 数据全部存入内存,主键查找速度很快,使用B+树索引
1.3.4Memory存储引擎
- 数据存储在内存
- 如果系统崩溃数据也会消失
- 使用哈希索引
- 使用表锁,不支持text和blob类型的数据
1.3.5Archive存储引擎
- 只支持insert和select操作
- 通过zlib压缩数据行进行存储
- 可以高速插入数据,适合用于做日志
存储引擎不同处理的表的方式也是不同的,所以最后表的大小也是不同的。
1.5连接mysql
- 连接进程和数据库实例进行通信。实际上就是进程之间的通信
1.5.1TCP/IP
- 这种是mysql什么时候都可以提供的连接方式
- client与mysql实例之间的通信
- 连接到之后会检查用户的权限视图,然后判断是否client连接过来
1.5.2命名管道和共享内存
- 两个进程在同一个服务器上,使用命名管道
- –enable-name-pipe配置
- –shared-memory共享内存。
1.5.3Unix域套接字
- 进程要在同一台服务器
- 配置套接字文件路径–socket=/tmp/mysql.sock
第二章:Innodb存储引擎
2.1innodb的概述
- 第一个支持ACID事务的mysql存储引擎
- 行锁、支持mvcc、外键、一致性非锁定读
- 应用OLTP项目
- 存储超过1TB数据
- 插入更新800次/s
2.2Innodb的版本
2.3Innodb的体系结构
- 内存池
- 维护线程、进程访问的多个内部数据结构
- 缓存磁盘上面的数据,方便读取
- 重做日志缓冲(redo log)
- 后台线程负责刷新内存池的数据
2.3.1后台线程
- 多线程所以有很多个后台线程负责不同任务
类型
- Master Thread:核心后台线程,负责异步刷新内存脏页、合并插入缓冲、undo页的回收
- IO Thread:异步IO处理IO请求,这个线程就是处理IO的回调,读线程(4个)总是小于写线程(4个)
- Purge Thread:回收事务提交生成的undo日志。
- Page Cleaner Thread:脏页刷新。分担Master Thread的负担
2.3.2内存
1.缓冲池
- innodb基于磁盘存储
- 通过缓冲池提高性能,减少IO
- 缓冲池其实就是一个内存区域,通过内存操作数据速度快来提高效率。而且每次修改都是先修改内存,并且等待一段时间再统一刷新脏页。
- 存储的数据页包括索引页、数据页、undo页、插入缓冲(insert buffer)、自适应hash索引、innodb存储的锁信息、数据字典信息
- 可以拥有多个缓冲池,减少数据库内部资源竞争也就是并发问题。通过innodb_buffer_pool_instances进行设置
2.LRU List、Free List和Flush List
- 怎么管理这些内存区域
LRU
- 缓冲池通过LRU(最近最少使用) List进行管理。但是这个LRU有一个midpoint位置,每次新加入的页先存储mid后面再次调用才会考虑送到LRU的头部节点。可以通过innodb_old_blocks_pct默认37(不活跃的数据页)来进行控制。mid后是old,mid前是new(活跃的数据页)
- 这种LRU策略防止了读取一个表全部页的时候把热点页给弄出去了。
- innodb通过innodb_old_blocks_time来表示读取到mid后的数据等待多久才能加入到头部
FREE
- 空闲页基本放到这个FREE位置进行保存。
- 如果需要使用那么就从FREE链表中删除并且移动到LRU列表中
- buffer pool size:缓冲池的大小
- database pages:LRU列表的页数量
- free buffers:free列表的页数量
- 缓冲池还可能需要分配给lock信息、insert buffer
- page made young:显示LRU移动到前端的次数
- buffer hit rate:缓冲池命中率,如果小于95需要看看是不是被全表扫描导致LRU被切换了很多热点页。
unzip_LRU列表
- 就是把页给压缩了
- 对不同压缩范围的页分别管理
- 检查4KB的unzip_LRU看是否有空闲页
- 有直接使用
- 否则检查8KB的unzip_LRU列表
- 如果一有空闲页那么就分开2个4KB,存放到4KB的列表
- 如果没有就申请16KB,把页分成8KB ,2个4KB的页存储到unzip_LRU是4KB的链表中
Flush列表
- 脏页列表,脏页存在LRU也存在FLUSH列表
- modified db pages显示了脏页的数量
3.重做日志缓冲
- 就是redo log的缓冲通常是8MB,三种情况需要刷新到磁盘
- master Thread每秒进行刷新
- 每个事务提交的时候
- 重做日志缓冲池空间小于1/2的时候
4.额外缓冲池
- 内存堆方式进行管理
- 存储LRU、锁、等待等信息
2.4CheckPoint技术
- 解决每次修改就刷新脏页到磁盘太频繁导致IO成本很高的问题
- Write Ahead Log先写入到日志再修改页。
如果重做日志很大,而且缓冲池很大,那么是不是就不需要把缓冲池的数据刷新回磁盘。但是需要符合下面的条件。
- 缓冲池可以缓存数据库所有的数据
- 重做日志无限大
- 恢复代价太高,因为如果mysql运行太久的话,那么一下子需要恢复大量的数据成本是非常高的
checkpoint解决的问题
- 缩短数据库恢复时间
- 缓冲池不够用刷新脏页到磁盘
- 重做日志不可用,刷新脏页
两种checkpoint
-
Sharp Checkpoint,关闭数据库前把脏页都刷新到数据库,innodb_fast_shutdown=1,如果运行时使用这种刷新方式导致数据库有一段时间不可用
-
Fuzzy Checkpoint
- master thread checkpoint:每秒或者每10秒异步刷新一次脏页
- flush_lru_list checkpoint:如果lru列表空间不够那么就要flush一些页回去磁盘
- async flush checkpoint:重做日志不可用,保证重做日志可用,刷新脏页到磁盘
- dirty page too much checkpoint:脏页太多,需要进行刷新。
2.5Master Thread工作方式
2.5.1在innodb1.0.x之前的master thread
- 最高优先级别
- 主循环、后台循环、刷新循环、暂停循环。
主循环方式
每秒一次操作
- 日志刷新到磁盘,事务没有提交
- 合并插入缓冲
- 刷新100个脏页到磁盘
- 切换到background loop
2.6Innodb相关特性
1.插入缓冲
应用场景是由于插入到非聚簇索引的话,那么就可能需要随机访问数据页导致的性能下降的问题。
- insert buffer是物理页的组成部分
- 解决非聚簇索引列,而且不是唯一索引的一个插入问题,先把插入的记录放到insert buffer。
- 如果索引页在内存直接插入,否则就需要存入insert buffer,看上去好像已经插入到索引叶子节点上。然后等待一段时间,把插入记录更新到磁盘。
- 原理实际上就是通过把多个插入操作合并成一个插入到同一页面的操作,那么就不需要频繁随机IO而是只读取一次页就能更新多个插入记录。
需要满足的条件
- 索引是辅助索引
- 索引不是唯一的
2.Change Buffer
insert buffer的升级版
对一条记录进行更新
- 记录标记为删除
- 记录真正删除
delete buffer对应第一个标记删除操作,purge buffer对应第二个真正删除。
3.insert buffer内部实现
- b+树,存放在系统表空间ibdata1
- searchKey保存了表空间、版本、页的偏移位置。
- 如果插入数据的时候发现页不存在内存那么就构造出search key插入到insert buffer中,
metadata
- IBUF_REC_OFFSET_COUNT:排序每个记录进入insert buffer的次序。
关于change buffer
总结:它相当于是多个物理页,解决的问题是非聚簇索引的插入更新导致的页分裂,还有就是插入的位置不好,导致需要随机取页的问题。change buffer的内部结构是一个B+树。对于每次插入第一时间做的是看看索引页在不在内存,不在那么就存入记录到change buffer。并且等待到待插入的非聚簇索引页加载进内存,或者是非聚簇索引没有可用空间,或者是由后台主线程进行的一次插入合并。
两次写的理解
两个page的意思其实就是刷新脏页,对于这些脏页先写到doublewrite buffer。然后先写到系统表空间,这里分配了两个连续的区存储这些表空间。作为一个副本,然后再同步到这个真正的表空间上。好处就是如果系统崩溃导致刷新脏页失败,而且刚好写这个页就写了一半(磁盘写),导致页是损坏的,如果下次还是使用redo log恢复写,问题就是你不知道上次的磁盘写到底写了多少脏页的数据进去。所以两次写的好处就是先保存到共享表空间,形象点说就是脏页存到共享表空间就是我先保留一份老师的画作的模板,然后才更新到数据文件,也就是我自己要根据老师给我修改的画作来修改之前没改过的画作业。如果不用两次写就是说我直接在自己的画上面乱改。改了一半发现画错了,改不了了。但是两次写的话,就可以根据老师修改的画换了自己画错的那张。如果之前没有让老师多画一张给我作为保存,那么画错了也没有任何画作为参照(可能例子不太形象,或者是我理解错了,欢迎大家指出),对应磁盘的真正表空间页的话意思就是页写了一半但是系统崩溃导致页其实是错误的,无法继续更新,但是两次写就可以解决这个问题,提前保留模板,坏了可以先通过模板补上,然后再通过redo log来修改内存那份再次进行刷新操作。
自适应哈希索引理解
这里的自适应哈希索引其实就是在索引页上面创建的,目的很简单就是处理那些热点记录。比如热点记录被连续访问多次,而且访问的语句都是一样的,那么就可以直接建立一个哈希索引。好处就是B+树只能够通过多个节点递归访问,但是哈希索引可以立刻定位。加快了访问的速度
AIO
异步IO意思就是,语句访问多个索引页的时候,可以同时发送多条IO请求。相对应就是SIO同步IO请求,那就是查一个页之后再去查另外一个页速度很慢。而且AIO还能够计算页是不是连续的,如果是可以整合到一次的IO获取。
刷新邻接页
第三章:文件
3.1参数文件
- my.ini
- 参数类型包括动态(可以在mysql实例运行的时候修改),静态(只能在参数文件上面修改)
3.2日志文件
-
错误日志:定位mysql启动错误等问题
-
慢查询日志:定位可能存在问题的sql语句,参数是long_query_time。分为了逻辑读取(缓冲池+磁盘),物理读取(磁盘)
-
查询日志:记录所有对mysql的请求日志
-
二进制查询日志
- 没有提交的事务先写到缓存,提交之后才会存入磁盘
- 注意而设置好缓存的一个大小binlog_cache_size
- 作用
- 恢复:选择某个备份时间来恢复
- 复制:主从机
- 审计:判断是否有注入攻击
- 有个参数是sync_binlog就算写了on,也就是每次提交都进行刷新日志到磁盘。问题是如果提交到一半,但是日志已经刷新了,这个时候系统宕机,事务回滚导致的数据库不一致怎么办?处理方法可以是设置innodb_support_xa为1,保证innodb的数据文件和二进制日志文件同步
- 日志格式
- statement:保存日志的更新语句(比如update,那么就直接把逻辑语句保存)
- row:保存表的行数据修改情况(行记录的各种信息,占用大量空间)
- mixed:折中。两个都使用。默认是statement,如果出现下面情况使用的是row
- 存储引擎是NDB
- insert delay
- 临时表
- 用户定义函数
- 使用UUID(),USER()等函数。
- pid文件
- 表结构定义文件:用于MyISAM
3.6Innodb存储文件
表空间理解
innodb里面有多个表,每个表都可以有自己的单独表空间,或者是存储到共享表空间。表空间的组成可以是多个文件也可以是一个文件。主要看表存储的数据大小
重做日志文件
二进制文件和重做日志文件最大的区别就是
- 二进制文件存储的是语句的逻辑操作
- 但是重做日志文件记录的是物理页修改的具体情况。
- 而且重做日志写入到磁盘的最小单位是扇区所以就算系统宕机也能够保证写入到磁盘。
- 重做日志的3种刷新
- 0提交事务并不会把日志刷新到磁盘,等待主线程刷新
- 1commit的时候把日志刷新到磁盘
- 2异步写入到磁盘。
- 0和2造成的问题可能就是系统宕机导致的写入丢失,但是只要操作系统和服务器没有宕机那么仍然可以保证日志不会丢失。
第四章:表
4.1索引组织表
- 其实就是依靠索引和按照主键顺序存放记录的表。
- 如果表没有设定唯一非空键那么自动就会创建6字节的row_id作为索引组织表的一个主键
4.2Innodb逻辑存储结构
- 表空间包含多个段
- 段包含多个区和零碎页
- 区包含多个页(256个页,每个页16KB)
- 每个页(页有很多种类型,存储的结构大致相似)上面又有很多记录
- 而且表空间一开始并不是直接分配64个页的,而是先有32个零碎页,如果空间不够那么才会分配更多的页给当前的空间。
4.3行记录格式
分类
- compact
- redundant
上面这两种最大的不同就是null的表示形式不同,而且前面的信息存储也有差异。compact存储的是每个列的长度,但是redundant直接存储的是每个列的偏移位置。
行溢出的处理
- 关于这个地方,行溢出处理的方式其实就是通过数据页存储一部分+BLOB页存储另外那些溢出的部分。因为一个页最大的存储容量是16KB,但是如果一个列存储的容量太多,比如varchar(65535)就会导致需要多个BLOB页来存储这个记录。
- 而且对于latin1才是能够存储65535个字符(每个字符一个字节),但是对于utf-8和GBK来说一个字符占用的多个字节实际上存储没有这么多的字符个数。
4.4数据页的结构
-
File Header
- 存储的大概就是下一个和上一个页的位置的链表结构
- 页存在表空间的什么位置
- 页属于哪个表空间
- 最后被修改的日志的LSN
- 页的类型
-
Page Header
- 存储多少个槽(目录)
- 存储多少条记录
- 第一个空间节点的位置
- 最后插入的位置
- 当前页属于哪个索引
。。。
-
Infimum和Supremum Record:相当于就是最小和最大的记录。主要的作用可以用于间隙锁等的使用
-
User Records:就是我们实际插入的数据
-
Free Space:空闲空间
-
Page Direcotory:目录结构,存储多个槽,每个槽能够存储的记录数主要是取决于记录的一个n_owned。主要的作用就是可以通过目录使用二分法快速定位某条记录。因为B+树只是找到了页,但是记录可以通过页目录来快速定位。
-
File Tailer:checksum(需要进行函数计算)+lsn和file Header的对应两个字段进行比较。保证页的一个完整性。意思就是用来校验,上一次到底有没有完整把更新的数据写入到页。
4.6约束
- 基本约束(略)
索引和约束之间的区别?
你会发现索引和约束的定义都是同一个语句。但是
- 索引是数据结构,相当于是一个目录指向各种数据。
- 但是对于约束目的是为了保证数据的完整性。他们目的不同。逻辑意思也不相同
4.8分区表
分区表的简单定义可以这么说,就是根据某个分区列,按照列的取值进行分区(range类型),也就是按照列的某种取值规则进行把数据分区到不同的表空间文件。好处就是可以通过分区快速查询,而且删除方便。但是必须要按照业务需求进行分区,不然分区就会失效。
- range分区类型
这种类型类型,假设现在是time日期字段,根据2010,2011,2019年进行分区。那么就会产生多个表空间文件。并且如果要查询2010年的对应的行数据可以通过2010的分区文件直接查找。
- List分区
相当于分区是一个离散值。比如现在是某个表a字段 in(1,2,3,4,5)加入到p0分区文件,那么只要我插入的记录里面a的字段值是在(1,2,3,4,5)里面其中一个就可以插入到p0的分区上。如果再创建一个分区是a in(6,7,8)加入到分区p1,那么只要符合离散值条件就可以加入到p1分区上。
- Hash分区
这种分区需要指定能够分配多少个分区,并且根据某个函数计算给定的分区列得出要送到的分区位置。相当于就是一个hash操作,把每个列根据函数分配到不同分区。使用的函数是Hash分区自己提供的
- Key分区
也是hash操作,但是不同的就是使用的是mysql提供的hash函数。
- columns分区
range和list分区的升级,能够处理非整数来计算出分区的位置。
子分区
- 相当于就是在分区的基础上面再次分区
- 注意的点
- 只能是在range和list后面建立key或者是hash分区
- 而且每个分区的子分区数量一定是相等的。
- 使用subpartition。
- 子分区的名字唯一
不同分区对于null的处理
- range对于null直接放到最左边的分区,问题就是删除p0分区的所有数据,但是null仍然存在不会被删除
- list需要指定哪个分区可以保存null,不然会直接报错
- 对于key和hash来说直接把null看做0送到函数去处理之后送到对应的分区。
分区和性能
- 对于OLAP(在线分析处理)来说分区是很有必要的,比如分析游戏日志,那么只需要通过timestamp进行分区和查询大大加快了速度
- 但是对于OLTP(在线事务处理),比如游戏,交易,blog等这样的,查询的数据可能就只是几条,如果这个时候使用分区,仅仅只是对主键进行分区的话,而且查询的对象也只是主键,那么还是可以提高查询效率。问题是如果要查询游戏对象name那么就需要把全部分区进行扫描,原本B+树只需要2-3次的
第五章:索引与算法
5.1-5.3
二分查找
其实就是通过数组每次按照要查询的值与中间的值进行比较,然后切分一半的数组然后继续比较。直到中间的值和要查询的值相等,或者是数组已经切分到不能再切分的情况。这种通常是用于page directory。因为B+树只能找到页,页里面这么多记录,所以只能通过page directory来进行二分查找加快查询的速度。
二叉查找树和二叉平衡树
- 二叉查找树的特点就是左小右大,那么查询的时候就能够快速定位,速度非常快。
- 但是对于二叉查找树如果大部分节点分布在左子树或者是右子树,那么和顺序查找的速度几乎没有差别
- 所以这个时候需要二叉平衡树+二叉查找树,让树的左右子树的高度相差1或者没有差额,那么就能够把查询的速度提高到logn。而不是n
B+树
- B+树其实就是每个节点可以有多个关键值指向,扇出范围大,所以可以避免二叉平衡查找树的一个弊端就是树太高了,树太高的问题就是每个节点存储对应的记录,每次只能获取两个节点,导致IO成本很高,获取索引节点还要继续IO去获取另外下一层的节点。
- 但是B+树的索引节点和叶子节点分开让扇出范围变大,而且节点之后是一个双向链表,可以定位第一个节点之后快速获取后面所有的节点,不需要像B树那样还要回溯然后遍历。
5.4B+树
- 直接看二进制的文件就可以发现对于每一层的每个节点上面的索引记录,可以通过记录头获取到下一个记录的位置。
- 而且每个记录都记录了每个对应数据页的偏移位置。但是这些数据页并不是连续的。而是通过双向链表连接起来的。
页分裂带来的影响
- 页分裂可能会造成有些地方使用不了。比如1,2,3,4,5,6,7,8,9是一个页但是现在如果加入一个10,那么就会分成两个页1,2,3,4作为一个,5,6,7,8,9,10作为另一个,问题就是1,2,3,4这个页已经不能插入任何的数据,因为4-5之间的数字不能用作为主键。
- 所以对于这样主键页插入最好的分裂方式就是新插入的节点作为分裂记录来分裂整个页
- 如果是随机插入那么用中间的记录作为分裂记录也是可以的。
B+索引的管理
- table:表名
- non_unique:非唯一索引
- key_name:索引名字
- column_name:索引列名称
- collation:列是什么方式存储在索引。A是B+树,null是hash索引
- cardinality:基数,越大越好。越小索引就可以删了
- sub_part:比如索引列是varchar(100),但是索引列只使用了varchar(6)也就是前6个字符。
- packed:关键字有没有被压缩
- null:索引列是否包含null值
- index_type:索引的类型。
Fast Index Creation
原本如果对辅助索引进行增删的流程
- 创建临时表
- 原表数据搬运到临时表
- 删除原表
- 临时表更名
出现的问题是导致大量读操作会被阻塞。导致查询的速度非常慢。
但是有了FIC之后
- 创建索引只需要加上s锁,能够读,但是不能写
- 而且在删除的时候只是标记那些索引页为可用,并没有真正删除
Online Schema Change
- 在线架构改变,也就是让表在添加索引的时候也可以进行读写操作。使用触发器来把这些在修改过程的语句加入到新创建的一个临时表,最后再合并到同一个表中。更重要的是合并的时候可以先把原表的数据写到外部文件减少对原表的锁定时间,最后再把外部文件和修改途中记录DML的表一起合并到临时表。
- 问题是要求表没有外键和触发器,而且不能进行同步到slave
Online DDL
- 可以解决FIC的创建临时表阻塞写操作,而且还能解决同步的问题。
- 可以选择inplace或者是copy算法,copy就是临时表操作,inplace就是直接在原来的表进行操作。类似于FIC。
- 对于Lock部分
- none不加锁,并发度最高。
- share在索引创建或者删除的时候先加上一个share锁。
- exlusive独占锁这种会直接阻塞访问表的所有线程,但是不需要创建临时表。
- default:这种是根据情况来选择上面的加锁情况。
- 原理就是在修改表的时候先把修改中,有别的事务插入的DML操作日志写入到缓存,然后修改表完成之后再把这些日志的DML操作写入到新表中。
5.5Cardinary
- 其实就是每个列的一个基数,如果列的基数大说明重复的个数不多,使用索引效率非常高。
- 而且基数的统计是通过随机抽取叶子节点进行统计的,所以每次查询发现不同是正常的。
5.6
MRR优化
muti-range read
- 这种其实就是把随机存取变成了顺序存取,原理其实就是先把辅助索引的键值拿出来,然后根据rowid进行排序,然后再去到聚簇索引进行搜索。那么取出来的页被缓存,而且主键如果是按照顺序的话,可能记录就在同一个页减少了很多随机存取导致页取出来又放回去的问题。
- 第二种优化就是对联合索引的一个优化,他可以把范围查询改变条件进行查询,比如现在查询的是key1>1000 and key1<2000 and key2=1000。正常来说先查询2000>key1>1000的结果集然后过滤key2不是1000的,但是如果开启了MRR,那么就会把范围条件变成(1000,1000),(1001,1000)这种的离散化查询,直接获取结果并且可以减少过滤使用的时间。
Index Condition Pushdown优化
- 这种其实就和上面那种类似,但是不一样的地方就是它是针对于where后面的条件。在取索引数据的同时已经开始判断是否符合where条件,把判断交给了存储引擎进行处理。
5.7哈希算法
- 如何从内存快速获取一个页,就是靠哈希算法。
- 通常的哈希算法是h%m,这里的m是hash数组的个数,h是参与计算的关键字,h%m就是对应的hash函数。
innodb的哈希算法
- hash数组的槽数m是根据要保存的内存页数*2的质数
- 这里的关键字k计算是页的spaceid<<20+spaceid+offset然后通过除法散列获取。
- 而且要记住这里是存储引擎的一个缓存池。
5.8全文索引
- 通常使用的是倒排索引
- 对于innodb来说全文索引的实现方式就是一个倒排索引,单独一个文件存放,主要是在文档提交之前先把单词插入到倒排索引里面去。倒排索引有6个Auxiliary Table,并且按照word的一个latin编码进行的一个分区。word + ilist,这里的word就是单词,ilist就是单词所在的文档id和文档的具体位置信息。
- 而且全文索引更新插入先是存放到FTS Index Cache缓存中,并且按照固定时间进行统一刷新到Auxiliary Table里面。缓存的操作会被redo日志记录,所以宕机了也是可以恢复的。
- 还有一个stop word列表也就是一个专门存放不需要加入到倒排索引的单词列表。
- 暂时有三种类型的表,倒排索引的Auxiliary 表,stopword列表的表,还有就是保存具体文档的表,这里的文档类型实际上对应着数据库的text,一条记录就是一个文档。
全文搜索的方式
Natural Language
- 默认方式。
- 语法 march (索引列) against(值)
- 而且可以统计对应的每个文档的一个相关性和查询出来的文档个数
Boolean
- +说明是一定要出现的单词
- -说明不能出现
- '>'后面出现这个单词那么就会增加相关性
- <如果后面的单词出现就会减少相关性
- "表示短语的意思
- *表示这个单词开头,比如like * 也就是可以是likea,likeab,likeabc,但是如果是lika那么就是错的。
- ~出现后面的单词相关性就会减少。
- @distance,这里的distance说明两个单词之间的一个字节上面的距离。
- 语法 march(“索引列”) against(’+post -sss’ in boolean mode )
第六章:锁
6.1-6.4
一致性非锁定读
- 这个其实就是依靠undo链的一个mvcc机制。也就是读取的时候并不会加上任何锁,原因是读取的数据是undo链上面的数据,因为每次修改更新的时候其实都会有一个undo日志,用于回滚的, 通常是事务提交之后,如果是插入操作那么就会立即删除,但是如果不是插入操作,而是更新操作,那么这个undo日志是不会删除的,它会记录修改记录的事务id,并且让现在的数据(实际上还是可以存在于undo链)的rollpointer指向之前的undo日志。比如第一次把1修改为2,第二次把2修改为3。那么undo链就是3->2->1。然后对于事务是否可见就依靠trx_id和mvcc机制
一致性锁定读
- 这种读就需要加上对应的共享锁,就是在他读取过程整个事务是不能够修改的,但是可以加上s锁,并且让其他事务可以读。
行锁
行锁类型
- 共享锁也就是s锁,支持其他事务读取,但是本事务也只是可以读而已。
- 独享锁x锁,不支持其他事务读取和写,但是本事务可以读写
行锁的三种算法
- record lock:锁定当前记录
- gap lock:锁定当前记录到前一条记录的范围,不允许插入,不包括锁定当前记录
- next -key lock:同gap lock,但是还对当前记录进行了锁定。
对于next-key lock来说通常和gap lock同时使用,next-key lock给当前记录加上锁,而且还能够在当前记录和链表前一条记录之间做上一个范围锁,防止插入,也就是防止了幻读。但是仅仅这样还是不行的,比如插入记录1,3,6(假设这个列是普通索引)。那么问题就是现在我在3上next-key lock锁上了,但是如果我现在如果要插入一条同样是3的记录,那么导致3的记录就是有3条了。为什么会这样子?原因就是你没办法锁住插入那条记录,所以只能加上范围锁,所以最好的方法就是在3和6之间加上一个gap lock锁住(3-6)那么就没办法插入了导致幻读问题。但是对于主键,非空唯一索引来说next-key lock需要退化成record lock。
第八章:备份与恢复
8.1备份与恢复概述
- Hot Backup热备份
在数据库运行的过程中进行备份。
备份文件
-
逻辑备份:相当于就是备份所有sql语句,使用文本的形式进行保存
-
裸文件备份:直接备份数据文件,但是速度非常快
备份数据库
- 完全备份:一下子把数据库进行一次备份
- 增量备份:在完全备份的基础上逐渐加上后来修改的备份
- 日志备份:就是二进制日志文件备份。
- Clod Backup冷备份:数据库没有运行的时候进行备份。
- Warm Backup温备份
8.2冷备份
- 备份frm文件
- 共享表空间文件,独立表空间文件
- 还有数据库配置文件
优点
- 简单,复制就行
- 不同操作系统和mysql版本恢复简单
- 恢复直接把文件恢复到指定位置
- 恢复快,不需要运行任何sql语句
缺点
- 但是冷备份文件占用空间大
- 不轻易跨平台,文件大小写敏感和浮点数格式