-
特点
- 是一个类,存储指向动态分配对象的指针,负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。普通指针如果忘记释放之后容易造成内存泄漏
- shared_ptr:采用计数器方式;unique_ptr:不支持普通的拷贝和复制操作;weak_ptr:弱引用,打破环形引用;
内联函数是C++的增强特性之一,用来降低程序的运行时间。当内联函数收到编译器的指示时,即可发生内联:编译器将使用函数的定义体来替代函数调用语句,这种替代行为发生在编译阶段而非程序运行阶段。
值得注意的是,内联函数仅仅是对编译器的内联建议,编译器是否觉得采取你的建议取决于函数是否符合内联的有利条件。如何函数体非常大,那么编译器将忽略函数的内联声明,而将内联函数作为普通函数处理。
参考:https://www.cnblogs.com/qg-whz/p/4641479.html
在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据所指对象的实际类型来调用相应的函数,如果对象类型是派生类,就调用派生类的函数,如果对象类型是基类,就调用基类的函数。
虚表:虚函数表的缩写,类中含有virtual关键字修饰的方法时,编译器会自动生成虚表
虚表指针:在含有虚函数的类实例化对象时,对象地址的前四个字节存储的指向虚表的指针
会有两张虚表:
-
含freelist的分配器
- 空闲空间管理方式
- 隐式空闲链表:将空闲链表信息与内存块存储在一起。主要记录大小,已分配位等信息
- 显示空闲链表:单独维护一块空间来记录所有空闲块信息
- 分离适配:将不同大小的内存块放在一起容易造成外部碎片,可以设置多个freelist,并让每个freelist存储不同大小的内存块,申请内存时选择满足条件的最小内存块
- 位图:除了freelist之外,还可以考虑用0,1表示对应内存区域是否已分配,称为位图
- 怎样分配内存块
- 首次适应
- 最佳适应
- 循环首次适应
- 空闲空间管理方式
-
buddy分配器
按照一分为二,二分为四的原则,直到分裂出一个满足大小的内存块;合并的时候看buddy是否空闲,如果是就合并。
可以通过位运算直接算出buddy,buddy的buddy,速度较快。但内存碎片较多。
操作系统将物理内存映射为连续的虚拟内存 (通过TLB) ,并提供了一些与虚拟内存相关的 API (VirtualAlloc, VirtualFree...) 对虚拟内存进行管理,在虚拟内存 API 之上又构建了Heap Memory Memory API (HeapAlloc...),而 C 的内存管理机制 (malloc, free) 就构建在 Heap Memory Memory API之上
参考1:https://zhuanlan.zhihu.com/p/51855842
参考2:https://zhuanlan.zhihu.com/p/344377490
参考:ByteDanceGuide.md#6-c-中虚函数与纯虚函数的区别
- 利用lambda表达式可以编写内嵌的匿名函数,用以替换独立函数或者函数对象;
- 每当你定义一个lambda表达式后,编译器会自动生成一个匿名类(这个类当然重载了()运算符),我们称为闭包类型(closure type)。那么在运行时,这个lambda表达式就会返回一个匿名的闭包实例,其实是一个右值。所以上面的lambda表达式的结果就是一个个闭包。闭包的一个强大之处是其可以通过传值或者引用的方式捕捉其封装作用域内的变量,前面的方括号就是用来定义捕捉模式以及变量,我们又将其称为lambda捕捉块。
在 C++ 中,父类和子类通常不在同一个虚函数表(vtable)中。虚函数表是一种用于实现多态的机制,它是一个存储虚函数地址的表格。每个类(包括父类和子类)都有自己的虚函数表,其中存储了该类中声明的虚函数的地址。子类会继承父类的虚函数表,并在自己的虚函数表中添加新的虚函数或重写继承的虚函数。
当通过指针或引用调用虚函数时,编译器会根据对象的实际类型查找虚函数表,并调用相应的虚函数实现。因此,父类和子类的虚函数表是分开的,子类的虚函数表中包含了继承自父类的虚函数和子类新增或重写的虚函数。
- 代码区(Code Segment):存放可执行程序的机器码
- 数据区(Data Segment):存放已初始化的全局和静态变量, 常量数据(如字符串常量)
- 全局区:block started by symbol,存放未初始化的全局和静态变量
- Heap:从低地址向高地址增长。容量大于栈,程序中动态分配的内存在此区域
- Stack:从高地址向低地址增长。由编译器自动管理分配。程序中的局部变量、函数参数值、返回变量等存在此区域
-
分配和管理方式不同
- 堆是动态分配的,其空间的分配和释放都由程序员控制。
- 栈由编译器自动管理。栈有两种分配方式:静态分配和动态分配。静态分配由编译器完成,比如局部变量的分配。动态分配由_alloca()函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无须手工控制。
-
产生碎片不同
- 对堆来说,频繁的 new/delete 或 malloc/free 可能会造成内存空间的不连续,造成大量的碎片,使程序效率降低。
- 对栈而言,则不存在碎片问题,因为栈是先进后出的"队列",永远不可能有一个内存块从栈中间弹出。
-
增长方向不同
- 堆由低地址向高地址增长
- 栈由高地址向低地址增长
-
vector:和数组类似,拥有一段连续的内存空间,并且起始地址不变。因此能高效的进行随机存取,时间复杂度为 O(1)
连续存储结构:vector是可以实现动态增长的对象数组,支持对数组高效率的访问和在数组尾端的删除和插入操作,在中间和头部删除和插入相对不易,需要挪动大量的数据。它与数组最大的区别就是vector不需程序员自己去考虑容量问题,库里面本身已经实现了容量的动态增长,而数组需要程序员手动写入扩容函数进形扩容。
-
list:由双向链表实现的,因此内存空间是不连续的。只能通过指针访问数据,所以 list 的随机存取非常没有效率,时间复杂度为O(n); 但由于链表的特点,能高效地进行插入和删除。
非连续存储结构:list是一个双链表结构,支持对链表的双向遍历。每个节点包括三个信息:元素本身,指向前一个元素的节点(prev)和指向下一个元素的节点(next)。因此list可以高效率的对数据元素任意位置进行访问和插入删除等操作。由于涉及对额外指针的维护,所以开销比较大。
- 区别:
- vector 的随机访问效率高,但在插入和删除时(不包括尾部)需要挪动数据,不易操作。
- list 的访问要遍历整个链表,它的随机访问效率低。但对数据的插入和删除操作等都比较方便,改变指针的指向即可。
- vector中的迭代器在使用后就失效了,因为插入或者删除元素会导致内存的重新分配,而 list 的迭代器在使用之后还可以继续使用,因为 list 中插入或删除元素只需要调整链表中的指针,不涉及内存的重新分配
- 指针是一个变量,存储的是一个地址,引用跟原来的变量实质上是同一个东西,是原变量的别名
- 指针可以有多级,引用只有一级
- 指针可以为空,引用不能为 NULL 且在定义时必须初始化
- 指针在初始化后可以改变指向,而引用在初始化之后不可再改变
- 引用本质是指针常量,
int &ref = a实际上为int *const ref = &a
-
浏览器查询 DNS
获取域名对应的IP地址: 具体过程包括浏览器搜索自身的DNS缓存、搜索操作系统的DNS缓存、读取本地的Host文件和向本地DNS服务器进行查询等。对于向本地DNS服务器进行查询,如果要查询的域名包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析(此解析具有权威性);如果要查询的域名不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析(此解析不具有权威性)。如果本地域名服务器并未缓存该网址映射关系,那么将根据其设置发起递归查询或者迭代查询;
另可参考:域名解析过程
-
浏览器获得域名对应的IP地址以后,浏览器向服务器请求建立链接,发起三次握手;
-
TCP/IP链接建立起来后,浏览器向服务器发送HTTP请求;
-
服务器接收到这个请求,并根据路径参数映射到特定的请求处理器进行处理,并将处理结果及相应的视图返回给浏览器;
-
浏览器解析并渲染视图,若遇到对js文件、css文件及图片等静态资源的引用,则重复上述步骤并向服务器请求这些资源;
-
浏览器根据其请求到的资源、数据渲染页面,最终向用户呈现一个完整的页面。
传输层
-
TCP是面向连接的,UDP是无连接的;
什么叫无连接?
UDP发送数据之前不需要建立连接
-
TCP是可靠的,UDP不可靠;
什么叫不可靠?
UDP接收方收到报文后,不需要给出任何确认
-
TCP只支持点对点通信,UDP支持一对一、一对多、多对一、多对多;
-
TCP是面向字节流的,UDP是面向报文的;
什么意思?
面向字节流是指发送数据时以字节为单位,一个数据包可以拆分成若干组进行发送,而UDP一个报文只能一次发完
-
TCP有拥塞控制机制。UDP没有,网络出现的拥塞不会使源主机的发送速率降低,这对某些实时应用是很重要的,比如媒体通信,游戏;
-
TCP首部开销(20字节)比UDP首部开销(8字节)要大,UDP 的主机不需要维持复杂的连接状态表
4. HTTP 与 HTTPS 有哪些区别?参考1
-
HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。
-
HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
-
HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
-
HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。
Distribute Denial of Service:分布式拒绝服务
防范操作:
- 高防服务器
- 添加黑名单
- DDoS清洗
- CDN加速:CDN 服务将网站访问流量分配到了各个节点中,这样一方面隐藏网站的真实 IP,另一方面即使遭遇 DDoS 攻击,也可以将流量分散到各个节点中,防止源站崩溃。
6. 简述 HTTP 1.0,1.1,2.0 的主要区别 参考1
HTTP1.0 最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而 HTTP1.1 则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时 HTTP1.1 也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:
- 缓存处理,在 HTTP1.0 中主要使用 header 里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
- 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
- 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
- Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
- 长连接和流水线,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
-
慢启动和拥塞避免
-
快重传和快恢复
当发送方连续收到三个重复确认时,就把慢开始门限减半,然后执行拥塞避免算法。不执行慢开始算法的原因:因为如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方认为现在网络可能没有出现拥塞。 也有的快重传是把开始时的拥塞窗口cwnd值再增大一点,即等于 ssthresh + 3*MSS 。这样做的理由是:既然发送方收到三个重复的确认,就表明有三个分组已经离开了网络。这三个分组不再消耗网络的资源而是停留在接收方的缓存中。可见现在网络中减少了三个分组。因此可以适当把拥塞窗口扩大些。
-
OSPF属于IGP(内部网关协议),主要作用是在网络内部发现、计算路由
-
BGP属于EGP(外部网关协议),主要作用是在不同网络之间传递、控制路由(路由来源于IGP)
一个孤立的网络只需要IGP协议就可以实现内部互通,而需要和外部互通的时候就需要EGP协议(IGP本身也需要存在),不存在只运行BGP没有IGP的这种说法。
OSPF(Open Shortest Path First开放式最短路径优先)是一个内部网关协议(Interior Gateway Protocol,简称IGP),用于在单一自治系统(autonomous system,AS)内决策路由。是对链路状态路由协议的一种实现,隶属内部网关协议(IGP),故运作于自治系统内部。著名的迪克斯加算法(Dijkstra)被用来计算最短路径树。
OSPF分为OSPFv2和OSPFv3两个版本,其中OSPFv2用在IPv4网络,OSPFv3用在IPv6网络。OSPFv2是由RFC 2328定义的,OSPFv3是由RFC 5340定义的。与RIP相比,OSPF是链路状态协议,而RIP是距离矢量协议。
10. TCP 的 keepalive 了解吗?说一说它和 HTTP 的 keepalive 的区别?参考1
-
TCP
在双方长时间未通讯时,如何得知对方还活着?如何得知这个TCP连接是健康且具有通讯能力的?
TCP的保活机制就是用来解决此类问题,这个机制我们也可以称作:keepalive。保活机制默认是关闭的,TCP连接的任何一方都可打开此功能。如果在一段时间(保活时间:tcp_keepalive_time)内此连接都不活跃,开启保活功能的一端会向对端发送一个保活探测报文。
-
若对端正常存活,且连接有效,对端必然能收到探测报文并进行响应。此时,发送端收到响应报文则证明TCP连接正常,重置保活时间计数器即可。
-
若由于网络原因或其他原因导致,发送端无法正常收到保活探测报文的响应。那么在一定**探测时间间隔(tcp_keepalive_intvl)后,将继续发送保活探测报文。直到收到对端的响应,或者达到配置的探测循环次数上限(tcp_keepalive_probes)**都没有收到对端响应,这时对端会被认为不可达,TCP连接随存在但已失效,需要将连接做中断处理。
-
-
HTTP
HTTP协议简介中提到http协议是一个运行在TCP协议之上的无状态的应用层协议。它的特点是:客户端的每一次请求都要和服务端创建TCP连接,服务器响应后,断开TCP连接。下次客户端再有请求,则重新建立连接。
在早期的http1.0中,默认就是上述介绍的这种“请求-应答”模式。这种方式频繁的创建连接和销毁连接无疑是有一定性能损耗的。所以引入了keep-alive机制。http1.0默认是关闭的,通过http请求头设置“connection: keep-alive”进行开启;http1.1中默认开启,通过http请求头设置“connection: close”关闭。
keep-alive机制:若开启后,在一次http请求中,服务器进行响应后,不再直接断开TCP连接,而是将TCP连接维持一段时间。在这段时间内,如果同一客户端再次向服务端发起http请求,便可以复用此TCP连接,向服务端发起请求,并重置timeout时间计数器,在接下来一段时间内还可以继续复用。这样无疑省略了反复创建和销毁TCP连接的损耗。
-
参考:https://blog.csdn.net/lpf463061655/article/details/108460311
TCP/UDP在传输层,IP在网络层
寻路
-
构造一个UDP报文,TTL分别为1,当这个报文到达第一个路由器后,TTL减去1后为零,报文被丢弃,然后路由器发送ICMP报文(时间超过)给源主机。
-
构造UDP报文,TTL为2,同样的,第二个路由器会返回ICMP报文(时间超过)给源主机
以此类推,注意发送过程中的UDP报文使用的端口都是非法端口,以使最后达到的时候无法交付,不然你就不知道什么时候应该停
-
直到TTL 到达某个值,而这个值可以使这个UDP报文刚好到达目的主机,由于报文的端口号不合法,目的主机返回ICMP(端口不可达)报文给源主机。
上述过程中,路由器和目的主机返回的ICMP报文中均含有它们的地址,所以得到了路径。
实际实现中,源主机每次发送三个相同TTL的UDP报文,因为现实中网络环境比较复杂,可能会有丢包情况发生。
注意:ICMP 并不属于高层协议,存在与网络层,分为两类:差错报告报文和询问报文
本地域名服务器 --> 根域名服务器 --> 顶级域名服务器 --> 权限域名服务器
参考:MyGitHub
**IO多路复用(IO Multiplexing)**是指单个进程/线程就可以同时处理多个IO请求
-
管道(Pipe)
管道是半双工的,数据只能向一个方向流动;
需要双方通信时,需要建立起两个管道;一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据;
只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)
-
命名管道
-
消息队列
-
信号(Signal)
-
共享内存和信号量(Semaphore):初始化操作、P操作、V操作;
-
套接字(Socket)
- 先来先服务
- 最短作业优先
- 时间片轮转
- 多级反馈队列
参考:https://blog.csdn.net/qq_41943585/article/details/91357110
ls: 列出目录中的文件和子目录,用于查看文件和目录的列表信息。cd: 切换当前工作目录,用于在不同的目录之间进行切换。mkdir: 创建新目录,用于创建新的目录。rm: 删除文件或目录,用于删除指定的文件或目录。cp: 复制文件或目录,用于复制文件或目录到指定位置。mv: 移动文件或目录,用于移动文件或目录到指定位置,也可用于文件或目录的重命名。cat: 查看文件内容,用于查看文件的内容。grep: 在文件中查找匹配的字符串,用于在文件中搜索指定的字符串模式。chmod: 修改文件或目录的权限,用于设置文件或目录的访问权限。chown: 修改文件或目录的所有者和所属组,用于更改文件或目录的所有者和所属组。ps: 查看系统中的进程,用于列出当前系统中正在运行的进程。top: 实时显示系统中的进程状态,用于实时查看系统中运行的进程和其资源使用情况。grep: 在文件中查找匹配的字符串,用于在文件中搜索指定的字符串模式。tar: 创建、解压和管理 tar 归档文件,用于创建、解压和管理 tar 格式的归档文件。ssh: 远程登录到其他 Linux 主机,用于通过安全的方式远程登录到其他 Linux 主机。
参考:https://www.cnblogs.com/xuxinstyle/p/9609551.html
参考:ByteDanceGuide.md#2-mysql-为什么使用-b-树来作索引对比-b-树它的优点和缺点是什么
参考:https://juejin.cn/post/6844904073955639304
-
聚集索引:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。索引的叶子节点就是对应的数据节点
-
非聚集索引:该索引中索引的逻辑顺序与磁盘上行的物理存储顺序不同,一个表中可以拥有多个非聚集索引。涉及到二次查询:非聚集索引叶节点仍然是索引节点,只是有一个指针指向对应的数据块,此如果使用非聚集索引查询,而查询列中包含了其他该索引没有覆盖的列,那么他还要进行第二次的查询,查询节点上对应的数据行的数据
事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚
- InnoDB支持事务,可以进行Commit和Rollback;
- MyISAM 只支持表级锁,而 InnoDB 还支持行级锁,提高了并发操作的性能;InnoDB 支持外键;
- MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢;
- MyISAM 支持压缩表和空间数据索引,InnoDB需要更多的内存和存储;
- InnoDB 支持在线热备份
参考:https://www.jianshu.com/p/6b45b150bfbf
- 未提交读:事务中的修改,即使没有提交,对其它事务也是可见的。可以保证读取没有损坏的数据
- 提交读:一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。可以避免脏读
- 可重复读:保证在同一个事务中多次读取同一数据的结果是一样的。可以避免不可重复读的发生
- 可串行化:强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。该隔离级别需要加锁实现,因为要使用加锁机制保证同一时间只有一个事务执行,也就是保证事务串行执行。
ToDo
- 悲观锁:认为数据随时会被修改,因此每次读取数据之前都会上锁,防止其它事务读取或修改数据;应用于数据更新比较频繁的场景;
- 乐观锁:操作数据时不会上锁,但是更新时会判断在此期间有没有别的事务更新这个数据,若被更新过,则失败重试;适用于读多写少的场景。乐观锁的实现方式有:
- 加一个版本号或者时间戳字段,每次数据更新时同时更新这个字段;
- 先读取想要更新的字段或者所有字段,更新的时候比较一下,只有字段没有变化才进行更新
-
binlog:
binlog用于记录数据库执行的写入性操作(不包括查询)信息,以二进制的形式保存在磁盘中。binlog是mysql的逻辑日志,并且由Server层进行记录,使用任何存储引擎的mysql数据库都会记录binlog日志在实际应用中,
binlog的主要使用场景有两个,分别是主从复制和数据恢复。 -
Redo log:具体来说就是只记录事务对数据页做了哪些修改
用于保持一致性
-
Undo log:原子性 底层就是通过
undo log实现的。undo log主要记录了数据的逻辑变化,比如一条INSERT语句,对应一条DELETE的undo log,对于每个UPDATE语句,对应一条相反的UPDATE的undo log,这样在发生错误时,就能回滚到事务之前的数据状态。
==ToDo==
==ToDo==
如果线程A锁住了记录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发生了死锁现象
- 主要原因:系统资源不足; 进程运行推进的顺序不合适;资源分配不当等。
- 避免死锁:破坏出现死锁的4个必要条件中的某一个:不让线程循环等待
由于后台的SQL语句是拼接而来的。其中的参数是由用户提交的,如果用户在提交参数时,在其中掺杂了一些SQL关键字或者特殊符号(比如: or # --),就可能会导致 SQL 语句的语意发生变化。从而执行一些意外的操作(在不知道密码的情况下也能登陆,甚至在不知道用户名和密码的情况下也能登陆),这就是 SQL 注入攻击
防止 SQL 注入攻击的关键是对用户输入的数据进行严格的输入验证和参数化查询。以下是一些防止 SQL 注入攻击的常用方法:
- 使用预编译语句或参数化查询:使用预编译语句(如 Prepared Statement)或参数化查询,可以将用户输入的数据作为参数传递给数据库查询,而不是直接将用户输入的数据拼接到 SQL 查询语句中。这样可以防止恶意 SQL 代码的注入。
- 避免动态拼接 SQL 查询语句:避免将用户输入的数据直接拼接到 SQL 查询语句中,因为这样容易受到 SQL 注入攻击。如果需要动态构建 SQL 查询语句,可以使用安全的方式,如使用参数化查询。
参考:什么是SQL注入攻击?
选择合适的索引类型:常见的索引类型包括B+树索引、哈希索引、全文索引等,每种类型都有其适用的场景和优缺点。选择合适的索引类型可以根据数据表的特点、查询需求和性能要求进行。
独立索引:不要使用在索引时函数,例如避免:id + 1 = 5,直接使用 id = 4
多列索引:使用组合索引
索引顺序:让选择性最强的索引列放在前面,也就是不重复的索引值和记录总数的比值,比值越大,选择性越强,区分度也就越大
前缀索引:VARCHAR 等类型的列之索引开始的部分字符
覆盖索引:索引包含所有的查询字段
==ToDo==
原子性、一致性、隔离性、持久性
- 左连接(Left Join):左连接以左边的表为主表,将左表中的所有记录与右表中匹配的记录联接在一起,同时包含左表中没有匹配记录的 NULL 值。如果右表中没有与左表匹配的记录,那么结果中右表部分将包含 NULL 值。
- 右连接(Right Join):右连接以右边的表为主表,将右表中的所有记录与左表中匹配的记录联接在一起,同时包含右表中没有匹配记录的 NULL 值。如果左表中没有与右表匹配的记录,那么结果中左表部分将包含 NULL 值。
- 事务支持:InnoDB是支持事务(ACID属性)的存储引擎,而MyISAM不支持事务。这意味着在InnoDB存储引擎下,可以进行事务操作,支持事务的提交(commit)和回滚(rollback)等操作,可以保证数据的一致性、完整性和持久性,适合于需要高度并发、复杂事务处理的应用场景。
- 锁级别:InnoDB支持行级锁(Row-level locking),而MyISAM只支持表级锁(Table-level locking)。这意味着在InnoDB下,可以对表中的某一行进行锁定,而不影响其他行的并发访问;而在MyISAM下,对表进行锁定将会影响整个表的并发性能,可能导致性能瓶颈。
- 并发性能:由于InnoDB支持行级锁,因此在高并发读写的场景下,多个事务可以并发地操作不同的行,从而提高了并发性能。而MyISAM只支持表级锁,对整个表的锁定可能导致并发性能较低。
- 数据缓存:InnoDB支持基于缓冲池(Buffer Pool)的数据缓存,可以将数据加载到内存中以提高查询性能。而MyISAM没有内建的缓存机制,需要依赖操作系统的缓存来提供数据缓存。
- 数据完整性:InnoDB支持外键(Foreign Key)和约束(Constraint),可以在数据库层面保障数据的完整性和一致性。而MyISAM不支持外键和约束,需要在应用层面进行处理。
- 数据文件存储:InnoDB将数据和索引存储在同一个文件中,而MyISAM将数据和索引存储在不同的文件中。这意味着在InnoDB下,一个表的所有数据和索引都可以在一个文件中进行管理,便于备份和管理;而在MyISAM下,需要备份多个文件。
- 其他特性:InnoDB支持多版本并发控制(MVCC)和崩溃恢复(Crash Recovery),提供更好的容错性和数据恢复能力。MyISAM没有这些特性,因此在高可用性和数据恢复方面相对较弱
18. 数据库有哪些常见索引?数据库设计的范式是什么? 中等 参考1
-
物理存储:聚簇索引、二级索引
-
字段特性:主键索引、唯一索引、普通索引(索引可能不唯一)、前缀索引
-
字段个数:单列索引、联合索引
- 第一范式(1NF):要求数据库中的每个字段都是不可再分的原子值,不允许字段中包含多个值或多值属性。
- 第二范式(2NF):在满足1NF的基础上,要求数据库中的每个非主键字段完全依赖于整个主键,即不存在部分依赖关系。
- 第三范式(3NF):在满足2NF的基础上,要求数据库中的每个非主键字段不依赖于其他非主键字段,即不存在传递依赖关系。
- JOIN:JOIN 是内连接(Inner Join),它只返回两个表中完全匹配的行,即两个连接表中的连接字段(关联字段)在两个表中都有匹配的值的行。如果没有匹配的值,那么这些行将被忽略,不会出现在结果中。JOIN 只返回连接字段匹配的行,不返回 NULL 值。
- LEFT JOIN:LEFT JOIN 是左连接(Left Outer Join),它返回左表中的所有行,以及右表中与左表中连接字段匹配的行。如果右表中没有匹配的值,将返回 NULL 值。左连接会保留左表中的所有行,不论右表中是否有匹配的值。
联合索引
==ToDo==
参考 T17
- 公平锁:公平锁遵循严格的**先来先服务(FIFO)**的调度策略,即等待时间最长的线程优先获取锁。当一个线程请求锁时,如果锁当前被占用,该线程会被放入一个队列中,按照请求顺序等待获取锁。公平锁的优点是可以避免线程饥饿现象,确保所有线程都有机会获取到锁,但可能会导致额外的线程上下文切换和性能开销。
- 非公平锁:非公平锁则没有先来先服务的限制,当一个线程请求锁时,如果锁当前被占用,该线程会直接尝试获取锁,而不会进入队列等待。如果获取失败,线程可能会再次尝试,也可能会被放入队列中等待。非公平锁的优点是可以减少线程上下文切换和性能开销,但可能会导致某些线程长时间无法获取到锁,从而出现线程饥饿现象。
Java 里中的概念




