2021/3/24 携程 海波(一面凉经)ACID原理 ReentrantLock底层 Redis数据类型的底层实现 TCP/Http区别 JVM中JDK8默认的垃圾回收器

1MySQL InnoDB 引擎下ACID的实现原理?

** 1(Atomicity)原子性**: 事务是最小的执行单位,不允许分割。原子性确保动作要么全部完成,要么完全不起作用;
2(Consistency)一致性: 执行事务前后,数据保持一致;
3(Isolation)隔离性: 并发访问数据库时,一个事务不被其他事务所干扰。
4(Durability)持久性: 一个事务被提交之后。对数据库中数据的改变是持久的,即使数据库发生故障。

1缓冲池(Buffer Pool)

Buffer Pool中包含了磁盘中部分数据页的映射。当从数据库读取数据时,会先从Buffer Pool中读取数据,如果Buffer Pool中没有,则从磁盘读取后放入到Buffer Pool中。当向数据库写入数据时,会先写入到Buffer Pool中,Buffer Pool中更新的数据会定期刷新到磁盘中(此过程称为刷脏)。

2日志缓冲区(Log Buffer)

当在MySQL中对InnoDB表进行更改时,这些更改首先存储在InnoDB日志缓冲区(Log Buffer)的内存中,然后写入通常称为重做日志(redo logs)的InnoDB日志文件中。

3双写机制缓存(DoubleWrite Buffer)

Doublewrite Buffer是共享表空间的物理文件的 buffer,其大小是2MB.是一个一分为二的2MB空间。
刷脏操作开始之时,先进行脏页**‘备份’**操作.将脏页数据写入 Doublewrite Buffer.
将Doublewrite Buffer(顺序IO)写入磁盘文件中(共享表空间) 进行刷脏操作.

4回滚日志(Undo Log)

Undo Log记录的是逻辑日志.记录的是事务过程中每条数据的变化版本和情况.
在Innodb 磁盘架构中Undo Log 默认是共享表空间的物理文件的Buffer.
在事务异常中断,或者主动(Rollback)回滚的过程中**,Innodb基于 Undo Log进行数据撤销回滚,保证数据回归至事务开始状态.**

5重做日志(Redo Log)

Redo Log通常指的是物理日志,记录的是数据页的物理修改.并不记录行记录情况。(也就是只记录要做哪些修改,并不记录修改的完成情况)
当数据库宕机重启的时候,会将重做日志中的内容恢复到数据库中

1原子性

Innodb事务的原子性保证,包含事务的提交机制和事务的回滚机制.在Innodb引擎中事务的回滚机制是依托 回滚日志(Undo Log) 进行回滚数据,保证数据回归至事务开始状态.

将修改后的数据存入缓冲池(Buffer Pool)中,等待刷脏。
在Log Buffer中写入重做日志(Redo Log),并将日志状态设置为Prepare。
返回MySQL服务层,记录BinLog日志。
将Log Buffer中的日志文件状态设为Commit,并等待日志文件存入盘中。

2那么不同的隔离级别,隔离性是如何实现的,为什么不同事物间能够互不干扰? 答案是 锁 和 MVCC

事务实现方式-MVCC
1什么是MVCC
MVCC是mysql的的多版本并发控制即multi-Version Concurrency Controller,mysql的innodb引擎支持MVVCMVCC是为了实现事务的隔离性,通过版本号,避免同一数据在不同事务间的竞争,你可以把它当成基于多版本号的一种乐观锁。当然,这种乐观锁只在事务级别为RR(可重复读)和RC(读提交)生效MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能

2 MVCC的实现机制
InnoDB在每行数据都增加两个隐藏字段一个记录创建的版本号,一个记录删除的版本号。
在多版本并发控制中,为了保证数据操作在多线程过程中,保证事务隔离的机制,降低锁竞争的压力,保证较高的并发量。在每开启一个事务时,会生成一个事务的版本号被操作的数据会生成一条新的数据行(临时)但是在提交前对其他事务是不可见的对于数据的更新(包括增删改)操作成功,会将这个版本号更新到数据的行中,事务提交成功,将新的版本号更新到此数据行中,这样保证了每个事务操作的数据,都是互不影响的,也不存在锁的问题。

3MVCC下的CRUD
SELECT:
  当隔离级别是REPEATABLE READ时select操作,InnoDB每行数据来保证它符合两个条件:
  1、InnoDB必须找到一个行的创建版本,它至少要和事务的版本一样老(也即它的版本号不大于事务的版本号)。这保证了不管是事务开始之前,或者事务创建时,或者修改了这行数据的时候,这行数据是存在的。
  2、这行数据的删除版本必须是未定义的或者比事务版本要大。这可以保证在事务开始之前这行数据没有被删除。
INSERT:
  InnoDB为这个新行记录当前的系统版本号
DELETE:
  InnoDB将当前的系统版本号设置为这一行的删除ID。
UPDATE:
  InnoDB会写一个这行数据的新拷贝,这个拷贝的版本为当前的系统版本号。它同时也会将这个版本号写到旧行的删除版本里。

锁机制

https://blog.csdn.net/zs18753479279/article/details/114622776

3持久性

基于事务的提交机制流程有可能出现三种场景.
1数据刷脏正常.一切正常提交
Redo Log 循环记录.数据成功落盘.持久性得以保证
2数据刷脏的过程中出现的系统意外导致页断裂现象 (部分刷脏成功)
针对页断裂情况,采用Double write机制进行保证页断裂数据的恢复.
3数据未出现页断裂现象,也没有刷脏成功
MySQL通过Redo Log 进行数据的持久化即可

4一致性

从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性

2Redis数据类型的底层实现

Redis底层数据结构有以下数据类型:1简单动态字符串 2链表 3字典 4跳跃表 5整数集合 6压缩列表

1 简单动态字符串

Redis构建了一种名为**简单动态字符串(SDS)**的抽象类型,并将SDS用作Redis的默认字符串表示。在Redis里面,C字符串只会作为字符串字面量用在一些无须对字符串值进行修改的地方(比如打印日志)。当Redis需要一个可以被修改的字符串值时,Redis就会使用SDS来表示字符串

SDS还被用作缓冲区:AOF模块中的AOF缓冲区,以及客户端状态中的输入缓冲区,都是由SDS实现的

2 链表【列表建】
链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度

列表键的底层实现之一就是链表。当一个列表键包含了数量比较多的元素,又或者列表中包含的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现

此外,发布与订阅、慢查询、监视器等功能也用到了链表,Redis服务器本身还使用链表来保存多个客户端的状态信息,以及使用链表来构建客户端输出缓冲区redis-server里维护了一个字典(k,v),字典的键是一个个 频道!而字典的值则是一个链表,链表中保存了所有订阅这个channel客户端

3 字典【哈希键】
字典(映射)是一种用于保存键值对的抽象数据结构
Redis的数据库就是使用字典来作为底层实现的

字典还是哈希键的底层实现之一,当一个哈希键包含的键值对比较多,又或者键值对中的元素都是比较长的字符串时,Redis就会使用字典作为哈希键的底层实现

4 跳跃表【有序集合】
跳跃表是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的
Redis只在两个地方用到了跳跃表,一个是实现有序集合键,另一个是在集群节点中用作内部数据结构

5 整数集合【集合键】
整数集合是集合键的底层实现之一当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis就会使用整数集合作为集合键的底层实现

6 压缩列表 【列表建、哈希键】
压缩列表是列表建和哈希键的底层实现之一
当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现
当一个哈希键只包含少量键值对,而且每个键值对的键和值要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做哈希键的底层实现

3ReentrantLock底层数据结构

Reetrantlock是一个可以重复获得锁的一个互斥锁,它的加锁与解锁都是需要手动执行,也可以多次加锁,同时它还可以指定是由公平锁还是非公平锁实现

public ReentrantLock() {
    
    
        sync = new NonfairSync();
    }
public ReentrantLock(boolean fair) {
    
    
        sync = fair ? new FairSync() : new NonfairSync();
    }

由构造方法可知,reentrantlock是由公平锁和非公平锁实现的,而**公平锁和非公平锁都是继承实现AQS(AbstractQueuedSynchronizer)**的模板方法和底层原理得以实现

1AbstractQueuedSynchronizer(AQS)

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。

AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作**。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。**

//节点元素node的构成:
volatile Node prev; //指向前一个结点的指针
volatile Node next; //指向后一个结点的指针
volatile Thread thread; //当前结点代表的线程
volatile int waitStatus; //等待状态

//AQS其他的数据结构
private volatile int state;//Reentratlock中表示锁被同一个线程重入的次数
private transient Thread exclusiveOwnerThread;//标识锁是由哪个线程拥有

在这里插入图片描述
这里的头指针的头结点表示当前获得这个锁的线程后面的节点表示想要获得这个锁的线程。这是一个先进先出的同步队列,获取锁失败的线程将构造自己的节点并追加到队列的尾部,并且阻塞自己,而当线程释放锁之后,也将尝试唤醒后续节点中处于阻塞状态的线程

2非公平锁NonfairSync

static final class NonfairSync extends Sync

继承Sync,实现了lock和tryAcquire方法
在这里插入图片描述lock方法是默认去用cas的方式去更新state和当前持有线程,如果AQS中state为0,也就是队列为空,那么设置state和当前线程就可以,如果不成功,就加入队列。

4TCP/Http的区别

1TCP连接

手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。

建立起一个TCP连接需要经过“三次握手”:
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

2HTTP连接

HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用
HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的 做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客 户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

5JVM中JDK8默认的垃圾回收器

UseParallelGC 即 Parallel Scavenge + Parallel Old
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/zs18753479279/article/details/115215510