MySQL分享
一、数据库结构
语句
DDL(Data Definition Languages):数据定义语句,常用的语句关键字主要包括 create、drop、alter等操作表结构
DML(Data Manipulation Language):数据操作语句,常用的语句关键字主要包括 insert、delete、udpate 和select 等操作数据
DCL(Data Control Language):数据控制语句,用户的访问权限和安全级别。主要的语句关键字包括 grant、revoke 等
结构
连接者:不同语言的代码程序和mysql的交互(SQL交互)
连接池:管理、缓冲用户的连接,线程处理等需要缓存的需求
管理服务和工具组件:系统管理和控制工具,例如备份恢复、Mysql复制、集群等
SQL接口:接受用户的SQL命令,并且返回用户需要查询的结果
解析器:对SQL进行解析,判断语法是否正确
查询优化器:SQL语句在查询之前会使用查询优化器对查询进行优化
举个例子
-- 下面的SQL company 和 name 都有索引,索引类型也相同,字段类型也相同
-- 索引字段能匹配的数据越少,优先使用该索引字段
select * from dept_table where company = '集团' and name = '财务部';
缓存:如果查询缓存有命中的查询结果,查询语句就可以直接去查询缓存中取数据
存储引擎:INSERT DELETE UPDATE SELECT 数据的一种方式
存储引擎名称 | 特点 | 应用场景 |
---|---|---|
InnoDB | 支持事务、行锁、支持MVCC多版本并发控制,并发性高 | 应用OLTP业务系统 |
MyISAM | 不支持事务,MySQL8之后被废弃了,并发很低,资源利用率也很低 | 应用OLAP业务系统,建议生产环境尽量少少使用 MyISAM 存储引擎 |
MariaDB columnstore | 列式存储引擎,高压缩功能 | 数据仓库,OLAP业务系统 |
数据库的引擎级别?
- [ ] A. 软件级别(即安装MySQL时就已经指定引擎类型)
- [ ] B. 数据库级别
- [ ] C. 表级别
OLTP(On-Line Transaction Processing):联机事务处理,比如增删改查,完成一笔交易处理
OLAP(On-Line Analytical Processing):联机分析处理,支持复杂的分析操作,侧重决策支持,做数据分析,决策,比如 ElasticSearch
SQL执行过程
二、事务
事务特性(ACID)
原子性(A):要么全完成,要么全不完成
一致性(C):在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏(比如A给B转账100元,A减少100,B增加100)
隔离性(I):事务之间互不干扰
持久性(D):事务提交后保存起来了
隔离级别(对执行事务起作用)
读未提交(read uncommitted):A事务变更后没提交,B能看到
读已提交(read committed):A事务变更后提交,B才能看到
可重复读(repeatable read):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的
串行化(serializable):同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行
并发问题
脏读:读未提交的隔离级别下,A事务读到B更新却没commit的数据
不可重复读:侧重修改,A事务两次读取同一数据两次不一致
幻读:侧重数据增减,A事务两次读取数据不同
事务隔离级别与并发问题
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read uncommitted) | 是 | 是 | 是 |
读已提交(read committed) | 否 | 是 | 是 |
可重复读(repeatable read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
验证事务的隔离级别
事务A | 事务B |
---|---|
begin; | begin; |
select * from dept_table; --V1 | |
select * from dept_table; --V2 | |
update dept_table set name = '研发部' where id = 1; | |
select * from dept_table; --V3 | |
commit; | |
select * from dept_table; --V4 | |
commit; | |
select * from dept_table; --V5 |
事务相关SQL
# 设置事务隔离级别
set session transaction isolation level read uncommitted;set session transaction isolation level read committed;set session transaction isolation level repeatable read;# 查询当前事务隔离级别
select @@tx_isolation;# 查询全局事务隔离级别
select @@global.tx_isolation;# 开启事务
begin;
start transaction;# 提交事务
commit;# 回滚事务
rollback;
隔离级别的实现
多版本并发控制 MVCC(Multi-Version Concurrency Control): MySQL 的 InnoDB 存储引擎实现隔离级别的一种具体方式,在每行数据都增加两个隐藏字段,一个记录创建的版本号,一个记录删除的版本号,用于实现提交读和可重复读这两种隔离级别。而未提交读隔离级别总是读取最新的数据行,无需使用 MVCC。可串行化隔离级别需要对所有读取的行都加锁,单纯使用 MVCC 无法实现
版本号
系统版本号:是一个递增的数字,每开始一个新的事务,系统版本号就会自动递增
事务版本号:事务开始时的系统版本号
可重复读的操作
- SELECT:读取创建版本 <= 当前事务版本号,并且删除版本为空或大于当前事务版本号的记录。这样可以保证在读取之前记录是存在的
- INSERT:将当前事务的版本号保存至行的创建版本号
- UPDATE:新插入一行,并以当前事务的版本号作为新行的创建版本号,同时将原记录行的删除版本号设置为当前事务版本号
- DELETE:将当前事务的版本号保存至行的删除版本号
插入一条新数据(事务版本号 = 1)
id | name | create_version | delete_version |
---|---|---|---|
1 | Tom | 1 |
执行更新操作(事务版本号 = 2)
update table set name= 'Tom2' where id = 1;
id | name | create_version | delete_version |
---|---|---|---|
1 | Tom | 1 | 2 |
1 | Tom2 | 2 |
删除操作(事务版本号 = 3)
delete from table where id = 1;
id | name | create version | delete version |
---|---|---|---|
1 | Tom2 | 2 | 3 |
查询操作要满足两个条件才能查询出来
- 删除版本号 大于 当前事务版本号或为空,就是说删除操作是在当前事务启动之后做的
- 创建版本号 小于或者等于 当前事务版本号 ,就是说记录创建是在事务中(等于的情况)或者事务启动之前
可重复读情况下的演示
-- 1. T事务(假设:当前事务版本号 = 1)插入一条数据
-- create_version = 1 delete_version = null
insert into `up`.`dept_table`(`id`, `company`, `name`) values (5, '总部', '综合创新部');-- 1. A事务(假设:当前事务版本号 = 2) 查询数据 4条结果
select * from dept_table; -- 2. B事务(假设:当前事务版本号 = 3) 更新ID = 1 的数据
update dept_table set name = '金融服务部' where id = 5;-- 3. A事务继续查询
select * from dept_table;
快照读
当执行select操作时 InnoDB默认会执行快照读,会记录下这次select后的结果,之后select 的时候就会返回这次快照的数据,即使其他事务提交了不会影响当前select的数据,这就实现了可重复读了。快照的生成当在第一次执行select的时候,也就是说假设当A开启了事务,然后没有执行任何操作,这时候B insert了一条数据然后commit,这时候A执行 select,那么返回的数据中就会有B添加的那条数据。之后无论再有其他事务commit都没有关系,因为快照已经生成了,后面的select都是根据快照来的
示例1 B事务的查询结果是否有A新增的数据?
A事务 | B事务 |
---|---|
start transaction; | start transaction; |
insert into up .dept_table (id , company , name ) values (6, '总部', '综合创新部'); | |
select * from dept_table; | |
commit; | |
select * from dept_table; | |
commit |
示例2 B事务的两次查询结果是否相同?第二次查询是否有A新增的数据?
A事务 | B事务 |
---|---|
start transaction; | start transaction; |
select * from dept_table; | |
insert into up .dept_table (id , company , name ) values (6, '总部', '综合创新部'); | |
select * from dept_table; | |
commit; | |
select * from dept_table; | |
commit; |
当前读
对数据修改的操作(update、insert、delete、select …... lock in share mode、select …... for update)都是采用当前读的模式。在执行这几个操作时会读取最新的记录,即使是别的事务提交的数据也可以查询到。假设要update一条记录,但是在另一个事务中已经delete掉这条数据并且commit了,如果update就会产生冲突,所以在update的时候需要知道最新的数据。也正是因为这样所以才导致上面我们测试的那种情况
示例
A事务 | B事务 |
---|---|
start transaction; | start transaction; |
select * from dept_table; -- V1 | |
insert into up .dept_table (id , company , name ) values (6, '总部', '综合创新部'); | |
commit; | |
select * from dept_table; -- V2 | |
update dept_table set name = '新部门' where id = 6; | |
select * from dept_table; -- V3 | |
commit; |
快照读 | 当前读 |
---|---|
select * from table | select * from table for update |
select * from table lock in share mode | |
insert、delete、insert |
三、InnoDB的锁
锁概念
锁作用
数据库锁机制简单来说,就是数据库为了保证数据的一致性,使各种共享资源在被并发访问时变得有序而设计的一种规则
锁机制
MySQL的锁机制比较简单,最显著的特点时不同的存储引擎支持不同的锁机制,我们锁知道的,InnoDB支持行锁,有时也会升级为表锁,MyISAM只支持表锁
行锁和表锁的特点
表锁 | 行锁 |
---|---|
开销小 | 开销大 |
加锁快 | 加锁慢 |
不会出现死锁 | 会出现死锁 |
锁粒度大 | 锁粒度小 |
发生锁冲突的概率高 | 发生锁冲突的概率低 |
并发度相对低 | 并发度相对高 |
InnoDB的锁类型
读锁(共享锁)
简称S锁,若事务T对数据对象加上读锁,则事务T可以读但不能修改,其他事务只能再对该数据加读锁不能加写锁,直到T释放该数据的读锁。即一个事务在读取一个数据行时,其他事务也可以读,但不能对该数据进行增删改的操作
读锁的实现方式
- 自动提交模式下的select查询语句,不需要加任何锁,直接返回查询结果,这是一致性非锁定读
- 通过 select ...... lock in share mode 在被读取的行记录或行记录的范围上加一个锁,让其他事务可以读,但是想要申请写锁,就要被阻塞
-- 查询是否自动提交模式
show variables like '%auto%';
写锁(排它锁)
简称X锁,若事务T对数据对象加上写锁,事务T可以读也可以修改,其他事务不能再对该数据加任何锁,不能读也不能写,直到T释放该数据上的锁。即一个事务获取了一个数据行的写锁,其他事务就不能再获取该行的其他锁,写锁优先级最高
写锁的实现方式
- INSERT、DELETE、UPDATE语句的操作都会对行记录加写锁
- 通过 select ...... for update 对读取的记录行上加写锁,其他任何事务就不能对锁定的行上加任何锁了,否则会被阻塞
MDL锁(meta data lock)
在事务A中开启查询,会自动获得一个MDL锁,事务B就不可以执行任何DDL语句的操作
事务A | 事务B |
---|---|
begin; | begin; |
select * from score_table; | |
alter table score_table add num tinyint(1) not null default 0; -- 阻塞 | |
commit; | |
commit; |
-- 查询进程列表
show full processlist;
意向锁
在MySQL存储引擎InnoDB中,意向锁是表级锁。而且有两种意向锁类型,分别为意向共享锁和意向排他锁
- 意向共享锁(IS):是指在给一个数据行加共享锁前必须先取得该表的IS锁
- 意向排他锁(IX):是指在给一个数据行加排他锁前必须先取得该表的IX锁
其实意向锁的作用跟MDL锁类似,都是防止在事务进行过程中,执行DDL语句的操作而导致数据不一致
InnoDB行锁种类
在默认的事务隔离级别为RR,并且参数 innodb_locks_unsafe_for_binlog = 0 的模式下,行锁的种类有三种
- 单个行记录锁(record lock):锁定一个行记录
注:主键和唯一索引都是行记录的锁模式。在RC隔离级别下,只有 record lock 记录锁模式
- 间隙锁(gap lock):锁定一个区间
- 记录锁和间隙锁的组合叫做 next-key lock:锁定行记录 + 区间
注:普通索引默认的就是 next-key lock模式
单个行记录的锁
-- 查看索引
show index from dept_table;
company 字段有索引
事务A | 事务B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'aaa' where score= 60; -- 出现锁等待 | |
update score_table set name = 'bbb' where score= 70; -- 正常更新 | |
commit; | |
commit; |
company 字段无索引
事务A | 事务B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'bbb' where score= 70; -- 出现锁等待 | |
commit; | |
commit; |
InnoDB的锁是加在索引上的
间隙锁
在RR事务隔离级别,为了避免幻读现象,引入了 gap lock,但它只锁定记录的范围,不包含记录本身,即不允许在次范围内插入任何数据
事务隔离级别RR
事务A | 事务B |
---|---|
begin; | begin; |
select * from score_table where score < 80 lock in share mode; | |
insert into score_table(name, score) values ('dd', 75); -- 等待 | |
commit; | |
commit; |
事务隔离级别RC
-- 查看事务隔离级别
show variables like '%tx_isolation%';
事务A | 事务B |
---|---|
begin; | begin; |
set session transaction isolation level read committed; | set session transaction isolation level read committed; |
select * from score_table where score < 80 lock in share mode; | |
insert into score_table(name, score) values ('dd', 75); -- 可提交 | |
commit; | |
commit; |
证明间隙锁只是针对RR隔离级别才管用,从锁的角度避免了幻读发生
next-key locks
记录锁与间隙锁的组合,当InnoDB扫描表记录时,会先对选中的索引加上记录锁,再对索引记录两边的间隙加上间隙锁
事务A | 事务B |
---|---|
begin; | begin; |
select * from score_table where score < 85 for update; | |
insert into score_table(name, score) values ('dd', 85); -- 等待 | |
commit; | |
commit; |
证明不光锁定 score < 85 这个区间的数据,还包含 85 这个值的本身
锁等待和死锁
锁等待是指一个事务过程中产生的锁,其他事务需要等待上一个事务释放它的锁,才能占用该资源。如果该事务一直不释放,就需要持续等待下去,直到超过锁等待时间,会报一个等待超时的错误
-- 查询锁等待时间
show variables like '%innodb_lock_wait%';-- 修改全局变量方式
set global innodb_lock_wait_timeout = 2;set @@global.innodb_lock_wait_timeout = 5;-- 修改当前事务变量方式
set innodb_lock_wait_timeout = 2;
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,即事务1锁住数据D1,正请求对D2加锁,而事务2锁住了D2,正请求加锁D1,这样就会导致死锁
事务A | 事务B |
---|---|
begin; | begin; |
update score_table set name = 'a' where score= 60; | |
update score_table set name = 'bbb' where score= 70; | |
update score_table set name = 'b' where score= 70; | |
update score_table set name = 'aaa' where score= 60; | |
commit; | |
commit; |
-- 查看死锁展示信息
show engine innodb status;
解决死锁
- 如果不同程序并发存取多个表,或者涉及多行记录时,尽量约定以相同的顺序访问表,可以大大降低死锁的机会
- 业务中尽量采用小事务,避免使用大事务,要及时提交或者回滚事务,可减少死锁产生概率
- 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率
- 对于非常容易产生死锁的业务部分,可以尝试使用升级锁粒度,通过表锁定来减少死锁产生概率
四、索引
理解索引
在工作中,开发人员在开发初期由于多种原因设计表的过程中,没有给后面可能会经常访问的字段增加索引这个概念,由于后期的业务调整需求变更且数据也在不断增长,会根据某些已存在的字段作为条件查询。后面再增加索引会很麻烦,需要考虑现有系统的可用性,还需要考虑到当前增加索引列的内容。所以创建索引需要结合现有业务还要预知后期可能存在的业务变更,使设计的数据表伸缩性更强,在设计初期把数据表设计完善也能够让后面的开发事半功倍。
索引利用好了就是一辆”法拉利“,利用不好就成了”三轮车“了。
索引是什么?为什么要使用索引?
帮助MySQL做高效查询的一种数据结构,好比是一本书的目录,通过目录能快速找到要查看的内容
提高查询效率
索引分类
从存储结构划分:BTree索引(B-Tree或B+Tree索引),Hash索引
从应用层划分:主键索引,唯一索引,普通索引,复合索引,全文索引,SPATIAL
根据键值的逻辑顺序和表数据行的物理存储顺序:聚集索引,非聚集索引
聚集索引:叶子节点存放表中所有行数据记录的信息
非聚集索引:不是聚集索引就是非聚集索引
索引何时无效?
索引一般在 where,order by,group by,表连接的条件列起作用
索引无效:
-- 1. 查询条件的值是null
explain select * from dept_table where name = null;-- 2. NOT条件 比如:<>、in、not in、exists、not exists
explain select * from dept_table where name <> '新部门';explain select * from dept_table where name in ('财务部2'); -- 可以使用索引explain select * from dept_table where name in ('财务部2', '新部门'); -- 不使用索引explain select * from dept_table where name not in ('新部门'); -- 一个和多个都不使用索引explain select * from dept_table where exists (select * from dept_table where name = '新部门');explain select * from dept_table where not exists (select * from dept_table where name = '新部门');-- 3. LIKE通配符在前面
explain select * from dept_table where name like '%部门';explain select * from dept_table where name like '部门%'; -- 可以使用索引explain select * from dept_table where name like '新%门%'; -- 可以使用索引-- 4. 条件列上包括函数
explain select * from dept_table where upper(name) = 'NEW';explain select * from dept_table where name = upper('new'); -- 可以使用索引
索引存到哪里了?用什么方式存储?
索引是存储到磁盘上的文件
MyISAM引擎和InnoDB引擎共有文件
.frm文件:在MYSQL中建立任何一张数据表,在其数据目录对应的数据库目录下都有对应表的.frm文件,.frm文件是用来保存每个数据表的元数据(meta)信息,包括表结构的定义等,.frm文件跟数据库存储引擎无关,也就是任何存储引擎的数据表都必须有.frm文件,命名方式为数据表名.frm,如user.frm. .frm文件可以用来在数据库崩溃时恢复表结构
InnoDB引擎文件
.ibd文件:单表表空间文件,每个表使用一个表空间文件(file per table),存放用户数据库表数据和索引
MyISAM引擎
.MYD:即 my data,表数据文件
.MYI:即 my index,索引文件
索引原理
MySQL为什么没有使用二叉树或红黑树?
推荐一个数据结构网站:www.cs.usfca.edu/~galles/visualization/Algorithms.html
如有 3、1、2、10、9、0、4、6这8个数据
哈希结构
- 直接查询:现在要从8个数中查找6这条记录,只需要计算6的哈希值,便可快速定位记录,时间复杂度为O(1)
- 范围查询:如果要进行范围查询(大于4的数据),那这个索引就完全没用了
二叉树结构(右子树大于左子树)
- 直接查询:现在要从8个数中查找10这条记录,先查找3,6>3,查找10,查找两次就可以了
- 范围查询:如果要进行范围查询(大于3的数据),直接查询大于3的右子树就行了
不使用二叉树原因:如果索引列查入的数据是单边增长的,会导致查询时依旧变慢,最差情况时间复杂度会变成O(N)
红黑树结构(是一种平衡的二叉查找树)
特点:
- 根节点是黑色
- 节点是红色或者黑色
- 每个叶子节点都是黑色的空节点(null)
- 每个红色节点的两个子节点都是黑色的
- 从任意节点到其每个叶子的所有路径都包含相同数量的黑色节点
- 直接查询:现在要从10个数中查找10这条记录,先查找4,10>4,查找6,10>6,查找到8,10>8,查找到9,10>9,查找到10
- 范围查询:如果要进行范围查询(大于6的数据),直接查询大于6的右子树就行了
- 问题:当数据量变大后,树的高度依然是很大,读取I/O磁盘次数还是很多
为什么想要减少磁盘I/O次数?
MySQL的数据实际是存储在文件中,而磁盘IO的查找速度是要远小于内存速度的,所以减少磁盘IO的次数能很大程度的提高MySQL性能
磁盘I/O为什么慢?
磁盘IO时间 = 寻道 + 磁盘旋转 + 数据传输时间
从磁盘读取数据时,系统会将逻辑地址发给磁盘,磁盘将逻辑地址转换为物理地址(哪个磁道,哪个扇区)。 磁头进行机械运动,先找到相应磁道,再找该磁道的对应扇区,扇区是磁盘的最小存储单元
性能对比
机械硬盘的连续读写性能很好,但随机读写性能很差。
- 顺序访问:内存访问速度是硬盘访问速度的6~7倍
- 随机访问:内存访问速度就要比硬盘访问速度快上10万倍以上
随机读写时,磁头需要不停的移动,时间都浪费在了磁头寻址上。 而在实际的磁盘存储里,是很少顺序存储的,因为这样的维护成本会很高
局部性原理与磁盘预读
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分分之一,因此为了提高效率,要尽量减少磁盘I/O。为了达到这个目的,磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。这样做的理论依据是计算机科学中著名的局部性原理:
当一个数据被用到时,其附近的数据也通常会马上被使用。
程序运行期间所需要的数据通常比较集中。
由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。
预读的长度一般为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页得大小通常为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,然后异常返回,程序继续运行。
BTree(B-Tree)和B+Tree闪亮登场
度(m):节点数据存储个数
深度关系(h):h=log(m+1)N
B+Tree相对于BTree区别:
- 非叶子节点只存储索引键,从而降低B+树的高度,进而减少IO次数
- 叶子节点存储整张表的所有行数
MyISAM索引实现
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址
这里设表一共有三列,假设我们以Col1为主键,上图是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示
同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录
所以MyISAM是非“聚集索引”,从索引文件和数据文件分别存储就可以看出。也与下面介绍的InnoDB的“聚集索引”做区分
InnoDB索引实现
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同
第一个不同:MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。InnoDB的数据文件本身就是索引文件,从文件存储上就可以知道,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引
InnoDB主索引(同时也是数据文件)的示意图
可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
第二个不同:与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域
InnoDB辅助索引(辅助索引获得主键索引)的示意图
这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
问题
- InnoDB引擎为什么必须有主键且建议使用整型自增主键?
空间方面:uuid存储空间更大
比较关系:索引之间有比较关系,整型的比较会更快
结构方面(最重要一点):使用自增主键保证插入数据都是向叶子节点后面顺序插入,如果使用uuid比较,生成的uuid不一定顺序插入,导致插入到节点的中间位置,如果该节点已经满了,会导致节点进行分裂变成新的结构
- 为什么InnoDB引擎的非主键索引数据存储的是主键值?
一致性和节省存储空间
- 节点的度设置为多少合适?
B-Tree节点的度设置等于一个页,每次新建节点直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,就实现了一个节点的载入只需要载入一次I/O
- 联合索引的底层存储结构是长什么样?
四、总结
- 数据库结构、一条SQL执行过程
- 事务隔离级别、并发问题、可重复读的实现过程
- 锁的使用、锁等待、死锁
- 索引分类、索引如何存储、索引何时无效、MySQL为什么使用B+Tree作为索引结构、MyISAM索引实现、InnoDB索引实现
- 磁盘读写过程
- MySQL主从复制过程
五、主从复制
事务相关的重要日志
redolog:数据日志
undolog:ibdata1
binlog:逻辑日志