加载到内存,为减少频繁io,存储不是逐行,而是块/页 更快优化内存,数据缓存,块数据,里面其他没用的行,根据某行访问,其附近的行也可能被访问的原则 外界指令操作数据库sql,sql解析成机器可以识别的指令 日志管理,做灾难恢复

快速查询,避免全表扫描(全表扫描适用数据量小的情况) 像字典
主键、唯一键、普通键 等

左<根<右 平衡二叉树(根部左子树和右子树比不超过1) 二分查找 缺点:如果圈中节点删除复杂度变成Log(N ) 利用树的旋转的特性维持在O(N),但是会增加频繁的io 改进方向:树矮一些,每个节点的io少一些,产生B-tree


复杂度O(N) 合并、分裂、上移、下移节点,保持,使其不会变成线性的,且矮的多 限定孩子数,和深度 ceil(向上取整)

非叶子节点的关键字信息:简单理解为下方,对应的2、3范围区间

每个节点有m个孩子,就是m阶B树

关键字不存储信息,一次读取数据更多,减少磁盘io 解决范围查询

和B数差不多,是B树变体 注意:关键字k不一定是最小值,只是在当前指向指针里的其中一个值(不硬性),但是必须小于ki+1的值(硬性),形成范围。


基于mysql的InnoDB和MyISAM不支持hash 一次计算,减小了逐级访问io,理论上比B+tree好

为什么不用做数据库索引?

BitMap位图索引
mysql不支持 像B+Tree一样,叶子位存储 位图只存 是否 因此特别快 速度特别快 位图锁力度很大,一处修改整个位图都锁 不适合多并发联机事务处理的系统,适合少并发,统计多的系统


密集索引:键值对+其他列 稀疏索引:主键对应行

MyISAM:默认索引,主键、唯一、普通(均稀疏索引) 索引和数据是分开存储的 InnoDB:有且只有一个密集索引 为什么要有一个密集索引? 稀疏索引的叶子节点,并不存储行数据的物理地址。而是存储的该行的主键值,所以非主键索引(稀疏索引)包含了两次查找(查找次级索引自身+查找主键)


非聚集索引,也是一颗平衡树。所有节点都会存储索引列的值,比如这里就是name列的值,叶子节点还会多存储该name值所对应的的聚集索引的值,即主键的值。 不同于聚集索引,真实的数据行不会按照非聚集索引排序存储,但索引项的内容是按顺序存储的。 一个表可以有多个非主键索引,因此会建立多个非聚集索引,每建立一个非聚集索引,都会将该非聚集索引关联的字段数据复制出来一份,用于生成以该列为基础的平衡树。这样的操作会增加表的体积,占用磁盘空间,所以不是索引越多越好。 通过非聚集索引查询数据时,查询到叶子节点上的主键值后,再利用这个主键值查询聚集索引,从而查询到具体的行记录,这个需要遍历两次树。


如何定位并优化慢查询Sql?
具体场景具体分析,只提出大致思路。
MySQL有很多自带的系统变量,咱们可以通过查询一些变量来得知有哪些配置信息:
这里我们是要查询跟慢日志相关的一些信息,
show VARIABLES LIKE '%query%'
然后查询一下慢SQL的数量(注意一下,这里只是记录本次会话的慢SQL的条数,一旦我们关闭了MySQL的客户端,再重启之后,这个慢SQL的条数就会被清零)
show status like '%slow_queries%';
然后我们将慢查询日志打开,sql如下:
set global slow_query_log = on;然后查看一下,发现慢查询日志已经打开了。

然后我们需要设置慢查询时间为1s,sql如下:
set global long_query_time = 1;然后重新连接客户端(为什么要重新连接客户端呢?因为有些系统配置修改后可以立即看到结果,有些则不行,设置慢查询时间属于后者),再执行“show VARIABLES LIKE ‘%query%’“,发现时间已经变化:

我们也可以到MySQL数据库的配置文件my.ini文件中去做修改,这样的修改将是永久性的。我们在这里做的修改,如果重启了数据库的服务,这些配置将会被还原成默认的配置。
接下来我们来制造慢查询:
我已经提前在一张表里灌入了10万多条数据
现在我们写一个慢查询Sql,执行看下时间:

然后执行:show status like ‘%slow_queries%’;结果如下:

打开慢查询日志:

可以看到这里已经记录了慢查询SQL和查询所用的时间。
然后我们用explain对慢sql进行分析

先了解下Explain关键字段:
(1)type:表示的是MySQL找到需要的数据行的方式。

从左到右性能越来越差。当我们看到type为最后两个的时候,表明sql走的是全表扫描。
(2)extra

走索引,调优程序,整体效率提升33%


增加索引


组合索引 最左匹配原则 第一个字段必须等值 按照规则排列的字段 顺序乱了没问题,mysql会优化顺序
数据库创建联合索引

全字段 走联合索引

最左匹配

非最左




共享锁,排它锁MyISAM和InnoDB都适用 MyISAM不支持事务 InnoDB支持事务

场景模拟: 两种数据引擎 数据量大致相同,两百万


MyISAM不支持事务,对表级锁不容易模仿 再现表级锁:增加操作延时,查找时并发更新

这样在查找前一个等待时,可以并发访问另一个表数据请求,从而触发MyISAM表级锁

等待执行完(读锁释放),才更新


读锁未释放,无法更新

释放当前读锁

锁释放,更新成功执行
产生读锁

(读锁未释放)再读,可以读取

创造写锁

写锁未释放,无法读


写锁未释放,无法写


写锁,无法读

利用其支持事务的特点,利用session获取锁,暂时不自动提交 mysql是自动提交事务的 用的二段锁:
两条语句都无需等待,直接更新的,看起来和不支持事务的MyISAM一样。
更新

更新


session关闭

另一个session也关闭

如果不愿意像这样做测试,可以范围查询这样复杂,因为表默认支持了事务二段提交
直接对单行,加共享锁 。对同一个session另一行加共享读

查询

读锁,

需要将读锁(共享锁释放)commit

共享锁


第一个session

第二个session


加读锁motto1

更新motto2(两条根据不同的motto,但被锁住了)

释放读锁commit;
因此不走索引时,就是表级别锁 InnoDB在没有用到索引的时候用的是表级锁 意向锁(表级锁) IS读IX写 锁(和MyISAM的共享锁排它锁很像,避免行轮询,形成一个表锁) sql锁 行级锁 gep锁走普通非唯一索引的

MyISAM

InnoDB

自动锁:InnoDB意向锁(非索引字段引起的表级锁)、MyISAM表锁、insert、update、delete【mysql自动上的】 显式锁:select、等显式加的锁 DML锁:数据操作 DDL锁:对表结构进行变更 乐观锁(不用数据的自带的锁)两种实现方式:版本号,时间戳

体会乐观锁-版本号

模拟场景




版本过期,交给用户按照业务逻辑处理

数据库恢复文件InnoDB redofile

主流数据库会自动避免 假象场景

一个事务读到另一个事务未提交的数据 避免脏读的事务隔离级别: read uncommit(不可以) read commit(可以),规定事务只能读别人已经提交的数据,避免脏读 不需要关闭自动提交,降低事务级别,准备好表和数据 降低事务级别:调整当前session事务隔离级别到最低,方便模拟场景 未提交读:事务允许其他事务读取未提交数据


查询数据隔离级别

表和数据的准备

拿第一条数据做测试

开启各个session事务

session1从账户取走100元

查余额-变900,但是当前session1没提交

脏读:session2这时候查,竟然是900

session1,还未提交 (网络原因失败)回滚,变为1000

session2存入两百,提交:并不知道此时脏读的900回滚1000

上帝视角:脏读的恶果 1000取100(失败)存入200,应该是1200,但现在是1100。少了100
解决方法:
提高事务隔离级别,事务已提交读(read uncommit->read commit) 设置事务隔离级别

验证

提升事务隔离级别后的脏读测试
session1:1100-100,未提交

session2:读到1100。没有读到session1未提交前的修改。避免了脏读

session1:回滚1000->1100

session2:存入200。1100+200=1300
成功避免脏读

理解: 事务a多次读取数据,事务b在a的读取过程中。修改了数据。导致数据a多次读取数据时值不一致 确保环境: 事务a回滚正常,b也都提交commit。两者隔离级别read commit
a,b开启事务

a:1300

b:存入300,未提交:1300+300=1600

a:1300,因为此事务级别,避免了脏读

b:提交修改

a:重新读取1600


第一次读1300,第二次读1600 上帝视角没问题 但如果此时a没有读到新数据,在旧数据进行操作后果很坏。取出的值并不可靠。。。和git的冲突很像
rr(可重复读级)别下是如何避免幻读的 为什么只实现伪MVCC,因为没有实现多版本共存,undoLog只是串行化的后果

当前读: 当前加锁 快照读: 不加锁的条件,在事务隔离级别不为Serializable可串行化,的前提下,才成立 当Serializable可串行化时,快照读退化成当前读 强制的进行排序,在每个读读数据行上添加共享锁。会导致大量超时现象和锁竞争。 快照读是为了,提升并发性能的考虑。多版本并发控制,及MVCC(是行级锁的变种,很多情况下避免了加锁的操作 ,因此开销更低) 由于是多版本并发,快照读很可能读到的不是最新版本,可能是之前的历史版本

为啥正删改也是当前读呢?
在rdbms(关系型数据库) update和delete差不多,insert稍有不同(可能会触发唯一键的冲突检查)

rc
2rc 12

2rr 34

12开启事务
1

2更新并提交 600

1快照读 600

2当前读 600

rc隔离级别下快照读、当前读 是一样的
2rr事务隔离级别
3当前600

4修改成600

3当前读 300

3快照读 600 没有变化

rr事务隔离级别下 当前读最新值 快照读,很有可能读到旧值
2rr下:快照读可能读到最新值
3先不读 4直接更新
4直接更新 0

3当前读 0

3快照读 0 这次是新值

总结2rr
rr事务首次调用快照读的操作很关键,创建快照的时机,决定了快照的版本。
刚刚是update,试试insert和delete也一样
快照读会产生无法读取最新的情况
解决rr级别下避免幻读,先解决这个问题

insert 只再事务回滚需要,事务提交后就可以立即丢弃 update、delete事务回滚需要,快照读也需要,所以不能随便删除,只有当数据库所使用的快照中不涉及该日志记录,对应的日志记录才会被删除
InnoDB做了很多操作,暂不细说 先锁定行-将行信息复制一份-修改数据-回滚指针指向复制


可见性
readView:遵循可见性算法,将事务id取出与活跃id对比。如果>=这些id,则回滚取出undolog,直到小于活跃事务id。
保证获取到当前数据最稳定版本


gap锁(间隙锁): gap索引树中插入新数据的间隙 锁,锁定一个范围,但不包括记录本身 作用:防止同一事务的两次当前读,出现幻读的情况 什么时候不存在:rc 及更低事务级别(ru)是没有的,因此rc,ru无法避免幻读的原因 什么时候存在:rr,Serializable序列化,避免幻读
rr下gap锁出现的场景

删改查,当前读
两种加锁情况
1读2新加:只行锁就行,不用gap 普通索引,当前索引,以及主键索引对应的记录,要都上锁。
主键加锁:
原因当删除和更新操作同时进行时,如果不加锁,就达不到串行操作

创建表

表数据

事务隔离级别rr,同时开启两个事务

1走的是唯一索引,理论gap上锁9周围

2插入执行成功

结论: 当前读走的是唯一索引,并且命中数据,是不会加gap锁的
12事务先都回滚


1删除不存在的7

2插入不存在的8

1查询部分数据

2 插入4可以,插入7,8(5——9范围内的数据失败)



1 全部命中

2 成功插入7,8


如果锁住当前只有两行,另外一个事务b插入同样为9再提交,事务a再查就出现3个9。因此需要添加gap锁 gap锁和非唯一索引相关,下方(],不会对所有gap都上锁,此例中只会对9相关区间上gap锁,预防幻读发生 防止范围内插入 此时事务a未提交前,事务b插入9是不可以的,因为整个区间被gap锁住了

官方文档gap范围划分说明

创建非唯一键索引

数据

rr级别下,2开启事务

1删除id为9的数据,【此时有两行9数据】

2添加数据【被gap锁住,等待,避免了幻读】

测试区间【这个区间,需要加上主键的值才能做出精准的判断】

(6,11]
成功插入5

无法插入7【证明间隙锁存在】

成功插入有边界12

跟所对应主键有关


主键的排序是在6——11的范围内的,所以被锁住

类似锁表,对所有的gap都锁 也可以起到防止幻读的效果

无id索引

数据

开启2事务
1删除9,【如果走非唯一索引区间5——11】

插入2【验证 不走索引gap都被锁住】
相比表锁,这样代价更大应该避免

redo lock
undo lock
ben lock








group by一张表特点:除分组字段,其他都是函数

验证其中一个学生的成绩总和没问题

会把结果集放到一张临时表中

两张表
where一定要在group by之前


Group by 之后使用,充当过滤条件 如果省略group by,having和where一样


测试只有having 对比 where

和where一样

