是什么?解决什么问题?实现原理?
MVCC是多版本并发控制,是通过记录历史版本数据
解决读写并发冲突问题,避免了读数据时加锁,提高了事务的并发性能
MVCC的实现原理是依靠2个隐藏列、undo log日志、Read View实现的
MySQL将历史数据存储在undo log中,两个隐藏列,一个是事务ID,一个是指向历史数据undo log的指针
事务开启后,执行第一条select语句的时候,会创建ReadView,ReadView记录了当前未提交的事务,通过与历史数据的事务ID比较,可以根据可见性规则进行判断,判断这条记录是否可见,如果可见就直接将这个数据返回给客客户端,如果不可见就继续往undo log版本链查找第一个可见的数据
undo log
事务的原子性是通过 undo log 实现的,在事务还没提交前,历史数据会记录在 undo log 中,如果事务执行过程中,出现了错误或者用户执行了 ROLLBACK 语句,MySQL 可以利用 undo log 中的历史数据,将数据恢复到事务开始之前的状态,从而保证了事务的原子性。
redo log
事务的持久性是由 redo log 保证的,因为 MySQL 通过 WAL (先写日志再写数据)机制,在修改数据的时候,会将本次对数据页的修改以 redo log 的形式记录下来,这个时候更新就算完成了,Buffer Pool 的脏页会通过后台线程刷盘,即使在脏页还没刷盘的时候发生了数据库重启,由于修改操作都记录到了 redo log,之前已提交的记录都不会丢失,重启后就通过 redo log,恢复脏页数据,从而保证了事务的持久性。
MVCC
事务的隔离性是由 MVCC 和锁保证的。
比如,可重复读隔离级别下的快照读(普通select),是通过 MVCC 来保证事务隔离性的,当前读(update、select … for update)是通过锁来保证事务隔离性的。
当两个或多个进程因竞争资源,而造成互相等待的现象,若无外力推进,则无法推进下去
public class DeadLockDemo {
public static void main(String[] args) {
final Object objectA = new Object();
final Object objectB = new Object();
new Thread(() -> {
synchronized (objectA) {
System.out.println(Thread.currentThread().getName() + "自己持A锁,希望获得B锁");
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (objectB) {
System.out.println(Thread.currentThread().getName() + "成功获得B锁");
}
}
}, "A").start();
new Thread(() -> {
synchronized (objectB) {
System.out.println(Thread.currentThread().getName() + "自己持B锁,希望获得A锁");
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (objectA) {
System.out.println(Thread.currentThread().getName() + "成功获得A锁");
}
}
}, "B").start();
}
}
方法一:控制台
jps -l
查看端口号,类似linux中的ps -ef|grep xxx
jstack 77860
查看进程编号的栈信息方法二:图形化界面(通用)
win` + `r` 输入`jconsole` ,打开图形化工具,打开`线程` ,点击 `检测死锁
分配资源时,进行预先设计,使用银行家算法保证资源分配在一个安全状态下
安全状态:系统能够按照某种线程推进顺序为每个线程分配所需资源
银行家算法
1、互斥:资源在同一时刻只能被一个线程所占用
2、请求保持:线程对已申请的资源保持不释放
3、不可剥夺:线程获得资源后,未使用完不能被其他线程所剥夺,只有使用完之后主动释放
4、循环等待:若干线程形成一种首尾相接的循环等待的状态
破坏死锁的四个必要条件之一
1、破坏请求与保持条件:一次性申请所有的资源
2、破坏不剥夺条件:如果资源申请不到,可以主动释放已占有的资源
3、破坏循环等待条件:按序申请资源
页缓存,用于缓存文件的页数据,从磁盘中读取到的内容是存储在pageCache里的
详情可参考这篇笔记:https://blog.csdn.net/mys_mys/article/details/131647511?spm=1001.2014.3001.5502
传统IO执行流程
引入DMA
DMA直接内存拷贝,CPU可去处理别的事情
传统文件传输
4次拷贝,4次切换
mmap优化
3次拷贝,4次切换
sendFile优化——零拷贝
2次DMA拷贝,2次切换
参考:https://javaguide.cn/cs-basics/network/tcp-reliability-guarantee.html
1、基于数据块传输
2、对失序数据包重新排序以及去重
3、校验和
4、超时重传
5、流量控制
6、拥塞控制
可以让发送方根据接收方的实际接收能力控制发送的数据量。
实现的方式:接收方会有一个接收缓冲区,如果内核接收到了数据,没有被应用读取的话,接收窗口就会收缩,然后会在tcp报文携带接收窗口的大小,发送发收到后,就会控制的发送流量
Redis持久化是异步进行的,有两种模式,RDB保存快照文件和AOF追加操作日志
RDB模式每隔一段时间会备份一份快照文件,从主进程Fork出一个子进程专门用于持久化,因此恢复速度快,但是容易造成数据丢失;
AOF模式是一种日志模式,记录每条命令的操作日志,重启时通过日志回放进行数据恢复,可以降低数据丢失情况。
补充相关知识点
Redis集群中有多个主节点,每个主节点有多个从节点。当主节点发生故障时,从节点被选举为主节点,继续工作;
Redis集群使用哈希槽的方式实现数据分片存储,有固定的16384个哈希槽(计算key对应槽位置:CRC16(key)%16384)
补充