想要了解mysql的索引,首先要了解mysql的物理存储结构
物理存储结构
mysql的物理存储结构其实就是上图所示,是一个双向链表,每个结点的都是一个数据页,
一行一行的数据就放在这个数据页中
索引为什么会产生索引这么一个东西,很简单,就是加快查询速度啊!
其实索引就和书籍的目录一样,都是为了更快的可以找到所需要的数据
那数据库的索引究竟是什么样子那?
其实就和书籍的目录一样,就是将每一个数据页的最小数据放在目录上,根据这个数据来进行多次的二分查找
页分裂目的:就是进行主键排序,保证所有的数据页的主键都是从小到大排序的
页分裂这个其实也很简单,数据库中那么多数据,一个数据页肯定存储不下,当存储的数据大于一个数据页的时候,就会将新的数据和这个数据页的整体数据进行排序,主键比较大的数据放在新建的数据页中
构成其实我们从索引的作用就可以自己猜想出来索引的组成了
首先,肯定是有对应数据页的主键的最小值,目的很简单,就是用于二分查找,提高查询的速度
根据上一步拿到了想要找的数据所在的数据页,但是具体是那个不知道啊,所以就必须有 数据页页号
B+树众所周知myslq数据库采用的B+树的数 ...
线上数据库时不时的发生性能抖动的问题
当buffer pool中没有缓存页的时候,来了一个查询大量数据的sql,大量脏页刷盘
redolog buffer的所有文件都已经写满的时候,来了一个大量更新的sql,需要将第一个redolog涉及的缓存页更新到磁盘中
解决办法:
由于无法控制缓存页刷新到磁盘的频率,所以只能是加大内存,给buffre pool分配的内存大一点大小
就是加快缓存页到磁盘的速度,那就是才去SSD固态硬盘,众所周知,固态硬盘比机械硬盘快
表结构分类
行锁默认情况下,是不需要锁的,默认是开启MVCC机制的,所以读取数据和修改数据完全不会互相影响,直接根据undolog版本链和ReadView来进行读取即可
共享锁共享锁又被称为S锁
默认查询数据不会开启共享锁的,是走mvcc机制读快照版本,但是可以手动添加
1234#加共享锁select * from vpm_project lock in share mode#查询之后还是更新,这样就给查询语句加上了独占锁select * from vpm_project for update
生产情况下,是不会开启共享锁的,因为根据这样会造成性能下降,而且根据mvcc机制读快也可以保证数据的准确性
如果非要在查询的时候加锁,通常都是在redis/zookeeper分布式锁来控制系统的锁逻辑,因为你如果直接在数据中加上复杂业务的锁逻辑,锁逻辑会隐藏在sql语句中,这对于java来说不太好维护
lock in share mode 只锁覆盖索引
独占锁默认更新数据的时候是开启的
关系
读取数据其实就是 select
修改数据其实就是 update delete in ...
事务
常见问题脏写现在有两个事务A和B,A和B同时在修改一个数据,A先更新,B再更新,但是B回滚了,那么A就是没有写进去,这个数据还是原来的值,这样的情况 称之为脏写
脏读有两个事务A和B,A读取了B已经修改了,但是还没有提交事务的数据,之后B事务回滚了,就会造成A读取的数据和数据库中存储的数据不一样,这样的情况 称之为脏读
不可重复读就是每次读取的数据,都和上次读取的数据不一样
比如一个事务A已经开始事务,读取到了一个数据的值,然后同时一个事务B修改了这数据并且事务提交,那么事务A再次过来查询,就是发现刚才的数据值发生变化了,变成数据B了,如果此时,事务A还没有提交,又过来一个事务C修改了这个数据并且提交,那么事务A再次来查询这个数据就变成了数据C,这种 在一个事务多次查询同一个值,但是查询结果每次都有变化的现象被 称为 不可重复读。
不可重复默认就避免了脏读的问题,因为只有在别的事务已经提交之后,当前在查询的事务才能看到新的值
幻读当一个事务,用一个一样的sql进行多次查询之后,结果每次都会看到比上次多一些数据,这种现象被称为 幻读
长事务尽量不要使用长事务长事务意味着系统里 ...
基本结构
VFS层就是看你对哪个目录中的文件执行的磁盘io操作,把io请求交给具体的文件系统
Page Cache在这个page cache 基于内存的缓存里找你要的数据在不在里面,如果有就基于内存缓存来执行读写,如果没有就继续往下一层走,此时这个请求会交给通用的block层,在这一层会把你对文件的io请求转换为Block IO请求
IO调度层这一层里默认的是用CFQ公平调度算法,也就是说,可能有两个sql请求同时过来,一个比较简单,只是更新磁盘的一个block的数据就可以了,另外一个是读取一个表中的所有数据,如果基于这个默认的CFQ算法,就会让读全表的这个sql先执行,更新一个条数据的sql等到读全表的sql全部执行结束之后才能执行。这样会导致明明应该先执行简单地操作,结果复杂的反而先执行了,不符合我们的预期
于是,在mysql的生产环境,建议采用deadline iO调用算法。它的一个核心思想就是,任何一个IO操作都能不能一直不停的等待,在指定范围内,都必须让他去执行
Block 设备驱动层Io调度层决定了哪个io请求先执行,哪个io请求后执行,此时可以执行的io请求就会交给B ...
排序的基本操作:比较和移动
直接插入排序分为有序区和无序区,每一次从无序区里找一个最小的放在有序区
希尔排序先分组,两个数一组,比较交换,小的放在前面,一直这样比较
冒泡排序两个数比较最小的放在前面,第一轮排序结束,第一个数一定是最小的,每一次从无序区中选取一个最小的
快速排序随机选择一个数进行比较,一般情况下选择第一数为关键字,经过第一轮排序后,关键字前面都是比他小的,关键字后面都是比他大的 先从最后面开始比较,如果比关键字小,关键字和他换位置,然后从最前面往后扫描,比关键字大的,和关键字换位置,然后再从最后面开始扫描
简单选择排序每一次都从无序区中选取一个最小的放在最前面
堆排序每一次都是全部比较,最大的放在前面即根节点,然后输出根节点
堆排序的插入:插入一个数放在最后面,保证父节点的数比子节点的数大即可
堆排序的删除:删除一个数后,保证父节点的数比子节点大即可
归并排序先分组,组内排序 一开始是2个一组,四个一组,八个一组
基数排序不用进行关键字的比较,第一次比较按照个位数排列,第二趟按照十位数排列,第三位按照百位排列
基数排序的移动次数与关键字的排列次序无关
排列趟数和序列 ...
三大特性:保证可见性,不保证原子性,禁止指令重排
不保证原子性测试123456789101112131415161718192021public class demo2 { private volatile static int num=0; public static void add(){ num++; } public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(()->{ for (int j = 0; j <1000 ; j++) { add(); } }).start(); } while (Thread.activeCo ...
三种形式synchronized锁对象有三种形式,普通同步方法,静态同步方法,同步方法块
对于普通同步方法,锁是当前实例对象
对于静态同步方法,锁是当前类的Class对象
对于同步方法块,锁是Synchonized括号里配置的对象
实现细节代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明。但是,方法的同步同样可以使用这两个指令来实现
monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁
Java对象头synchronized用的锁是存在Java对象头里的。如果对象是数组类型,则虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,则用 ...