大厂面试题分享第二期
- 如果执行了一条命令,"select count(*)from…",使用哪个引擎更快,为什么?
- 垃圾回收器 CMS 和 G1的区别
- 介绍一下CMS和G1
- CMS(并发)垃圾收集器
- G1垃圾回收器
- HTTPS和HTTP的区别主要如下:
- HTTPS 解决了 HTTP 的哪些问题?
- 主要介绍一下混合加密方式
- 非对称加密算法
- TCP和UDP区别是什么?
- Leetcode25:K个一组翻转链表
如果执行了一条命令,“select count(*)from…”,使用哪个引擎更快,为什么?
EXPLAIN SELECT COUNT(*)FROM user;
EXPLAIN SELECT COUNT(列名) FROMuser;
EXPLAIN SELECT COUNT(1)FROM user;
执行效果上:
- count(*)包括了所有的列,在统计时 不会忽略列值为null的数据。
- count(1)用1表示代码行,在统计时,不会忽略列值为null的数据。
- count(列名)在统计时,会忽略列值为空的数据,就是说某个字段的值为null时不统计。
执行效率上:
- InnoDB引擎:count(字段)<count(1)=count(*)
- InnoDB通过遍历最小的可用二级索引来处理select count*)语句,如果二级索引不存在,则通过扫描(主键索引)聚集索引来处理。
- InnoDB已同样的方式处理count(1)和count(*)·
InnoDB中索引分为聚簇索引(主键索引)和非聚簇索引(非主键索引),聚簇索引的叶子节点中保存的是整行记录,而非聚簇索引的叶子节点中保存的是该行记录的主键的值。所以,相比之下,非聚簇索引要比聚簇索引小很多,所以MySQL会优先选择最小的非聚簇索引来扫表。所以,当我们建表的时候,除了主键索引以外,创建一个非主键索引还是有必要的。
- MyISAM引擎:count(字段)<count(1)<= count(*)。
- MyISAM存储了数据的准确行数,使用 count(*)会直接读取该行数,只有当第一列定义为NOT NULL时,count(1),才会执行该操作,所以优先选择count(*)
count(列名)会遍历整个表,但不同的是,它会先获取列,然后判断是否为空,然后累加,因此count(列名)性能不如前两者。
垃圾回收器 CMS 和 G1的区别
区别一:使用的范围不一样:
- CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用
- G1收集器收集范围是老年代和新生代。不需要结合其他收集器使用
区别二:STW的时间:
- CMS收集器以最小的停顿时间为目标的收集器。
- G1收集器可预测垃圾回收区 的停顿时间(建立可预测的停顿时间模型)
区别三: 垃圾碎片
- CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片·
- G1收集器使用的是“标记-整理”算法,进行了空间整合,没有内存空间碎片。
介绍一下CMS和G1
CMS(并发)垃圾收集器
CMS(Concurrent-Mark-Sweep),是一款并发的、使用标记-清除算法的垃圾回收器,该回收器是针对老年垃圾回收的。
-
CMS 收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。停顿时间越短(低延迟) 就越适合与用户交互的程序,良好的响应速度能提升用户体验
-
CMS 的垃圾收集算法采用 标记-清除算法,并且也会 “Stop-the-world”
-
初始标记(Initial-Mark) 阶段: 在这个阶段中,程序中所有的工作线程都将会因为 “Stop-the-World” 机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出 GC Roots 能直接关联到的对象。
-
并发标记(Concurrent-Mark) 阶段: 从 GC Roots 的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行
-
重新标记(Remark) 阶段: 由于在并发标记阶段中,程序的工作线程会和垃圾收集线程同时运行或者交叉运行,因此为了修正并发标记期间,因用户线程继续运作而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有工作线程
-
并发清除(Concurrent-Sweep) 阶段: 此阶段清理删除掉标记阶段判断已经死亡的对象,释放内存空间。由于不需要移动存活对象,所以这个阶段也可以与用户线程同时并发的
CMS垃圾收集器虽然减少了暂停应用程序的运行时间,但是它还是存在着内存碎片问题,于是为了去除内存碎片问题,同时有保留CMS垃圾收集器低暂停时间的优点,
G1垃圾回收器
G1垃圾回收器包括以下步骤:
- 初始标记(Initial Mark):这一步主要标记GC Roots引用的对象为存活。GCRoots是垃圾回收的起始点,通常是活跃的对象。
- 并发标记(Concurrent Mark):在这一阶段,垃圾回收器会并发地遍历堆中的对象图,将初始标记阶段标记为存活的对象引用的对象也标记为存活。
- 最终标记(Final Mark or Remark):此步骤会再次检查并标记在并发标记阶段可能被漏标的对象,确保所有存活的对象都被正确地标记。同时,任何不再关联的对象也会被标记。
- 并发清理(Cleanup):这一步骤将存活的对象复制到其他Region,确保没有内存碎片的产生。G1垃圾回收器通过这种方式优化内存使用,并提高后续的垃圾回收效率。
G1垃圾回收器对老年代的清理策略是选择存活度最低的区域进行回收,这样可以高效地回收内存,这也是G1(Garbage first)名称的由来。在清理阶段,G1使用复制算法,确保内存碎片的最小化。
HTTPS和HTTP的区别主要如下:
由于HTTP协议传输的数据是明文格式的,因此发送隐私信息时存在着安全风险。为确保数据传输安全性,网景公司推出了SSL(Secure Sockets Layer)协议,并在其基础上创造出了HTTPS通信方式。简而言之,HTTPS是一种安全、支持加密传输和身份认证功能的网络通信方式,它是在SSL与HTTP结合后诞生的。
- http是超文本传输协议,信息是明文传输,存在安全风险问题,https则是在TCP和HTTP层之间加入了SSL/TLS安全协议,使得报文能够加密传输
- HTTP链接建立相对简单,TCP三次握手之后便可进行HTTP的报文传输,而HTTPS在TCP三次握手之后,还需要进行SSL/TLS的握手过程,才可进行加密报文传输
- 端口不一样 HTTP默认端口号80 HTTPS默认端口到是443
- https协议需要向CA(证书权威机构申请数字证书),来保证服务器的身份是可信的
HTTPS 解决了 HTTP 的哪些问题?
HTTP 由于是明文传输,所以安全上存在以下三个风险:
- 窃听风险,比如通信链路上可以获取通信内容,用户号容易没
- 篡改风险,比如强制植入垃圾广告,视觉污染,用户眼容易瞎。
- 冒充风险,比如冒充淘宝网站,用户钱容易没,
HTTPS 是如何解决上面的三个风险的?
- 混合加密的方式实现信息的机密性,解决了窃听的风险。
- 摘要算法(哈希算法)的方式来实现完整性,它能够为数据生成独一无二的「指纹」,指纹用于校验数据的完整性 解决了算改的风险。
- 将服务器公钥放入到数字证书中,解决了冒充的风险。
主要介绍一下混合加密方式
HTTPS 采用的是对称加密和非对称加密结合的「混合加密」方式:
在通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。
在通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。.采用「混合加密」的方式的原因:
- 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换。
- 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢
通过混合加密的方式可以保证信息的机密性,解决了窃听的风险。
非对称加密算法
非对称加密算法中共有两个密钥:
- 一个是公钥,这个是可以公开给所有人的;
- 一个是私钥,这个必须由本人管理,不可泄露,
这两个密钥可以双向加解密的,比如可以用公钥加密内容,然后用私钥解密,也可以用私钥加密内容,公钥解密内容。流程的不同,意味着目的也不相同:
- 公钥加密,私钥解密,目的是为了保证内容的传输安全,因为被公钥加密的内容,其他人是无法解密的,只有持有私钥的人,才能解密出实际的内容【客户端向服务器发送消息,但可能存在中间人伪造了一份公钥私钥】
- 私钥签名,公钥验签,目的是为了保证消息不回被冒充,因为私钥不可泄露的,如果公钥能正常解密出私钥加密的内容,就能证明这个消息是来源于持有私钥身份的人发送的。【CA证书机构,确保客户端拿到的是服务器的公钥,而不是中间人的公钥】
数字证书认证机构工作流程
在计算机里,这个权威的机构就是 CA(数字证书认证机构),将服务器公钥放在数字证书(由数字证书认证机构颁发)中,只要证书是可信的,公钥就是可信的。数字证书的工作流程如下:
TCP和UDP区别是什么?
- 连接:TCP 是面向连接的传输层协议,传输数据前先要建立连接;UDP 是不需要连接,即刻传输数.据。
- 服务对象:TCP是一对一的两点服务,即一条连接只有两个端点。UDP 支持一对一、一对多、多对多的交互通信
- 可靠性:TCP是可靠交付数据的,数据可以无差错、不丢失、不重复、按序到达。UDP 是尽最大努力交付,不保证可靠交付数据。但是我们可以基于 UDP传输协议实现一个可靠的传输协议,比如 QUIC协议
- 拥塞控制、流量控制:TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。UDP则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。
- 首部开销:TCP首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项!字段则会变长的。UDP首部只有8个字节,并且是固定不变的,开销较小。
- 传输方式:TCP是流式传输,没有边界,但保证顺序和可靠。UDP报文传输,是一个包一个包的发送,是有边界的,但可能会丢包和乱序。
Leetcode25:K个一组翻转链表
- 链表分区为已翻转部分+待翻转部分+未翻转部分
- 每次翻转前,要确定翻转链表的范围,这个必须通过 k 此循环来确定
- 需记录翻转链表前驱和后继,方便翻转完成后把已翻转部分和未翻转部分连接起来
- 初始需要两个变量 pre 和 end,pre代表待翻转链表的前驱,end 代表待翻转链表的末尾
- 经过k此循环,end 到达末尾,记录待翻转链表的后继 next = end.next
- 翻转链表,然后将三部分链表连接起来,然后重置 pre 和 end 指针,然后进入下一次循环
- 特殊情况,当翻转部分长度不足 k 时,在定位end 完成后,end==null,已经到达末尾,说明题目已完成,直接返回即可
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0);
dummy.next = head;
//每次开始前 pre start end都在翻转链表的前驱节点
ListNode pre = dummy;
ListNode start = dummy;
ListNode end = dummy;
while(end.next!=null){
for(int i=0;i<k&&end!=null;i++) end = end.next;
if(end == null) break;
start = pre.next;
ListNode next = end.next;
end.next = null;
pre.next = reverse(start);
//start此时在翻转链表的前驱 pre和end不在
start.next = next;
pre = start;
end = pre;
}
return dummy.next;
}
public ListNode reverse(ListNode start){
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
while(start!=null){
ListNode next = start.next;
start.next = cur.next;
cur.next = start;
start = next;
}
return dummy.next;
}
}