数据库

索引失效的原因有哪些?(查询没有走索引是为什么)(美团,携程,腾讯云智,京东,得物,度小满,腾讯多次考,作业帮,shopee多次考,哈啰,满帮多次考,腾讯,阿里)page 95 page 32

为了防止索引失效,可以采用哪些方法?(58同城)

创建了联合索引,但查询条件不遵守 最左匹配原则 。如abc查询条件使用bc。

使用模糊查询,like %xxx,数据库无法用索引,因为前置通配符会使得匹配从任意位置开始,只能全表扫描。

查询条件部分索引没有命中。查询条件中使用or,而且or的条件中有一个列索引没有命中,优化器会选择全表扫描。

查询子句中操作符左右两边数据类型不一致,发生 隐式转换 。

操作符问题,!=和not in会导致全表扫描,无法利用索引。

注:in操作符不会导致索引失效。

如何判断一个索引是否会被优化器选中?(字节)

MySQL用成本选择索引,有不同的指标,结合权重,选择成本最小的执行计划。指标如下。访问路径,如ref索引查找,range范围扫描,index全索引扫描,all全表扫描,用访问路径估算磁盘io代价。索引的选择性,用返回的行数估计代价。

查询可以强制走索引吗?(美团)page 37

可以,用force index,告诉查询优化器生成执行计划时优先考虑指定的索引。

B+树相对于B树的优点是什么?/MySQL为什么用B+树不用B树(shopee多次考,快手,百度,得物,作业帮,快手)

MySQL为什么用B+树不用跳表?(58同城)page 92

B+树和B树区别?page 94

节点存储内容:1.B树内部节点(非叶子节点)既存储关键字也存储数据记录。数据可能分布在所有节点中,在查找过程中可能在中间节点找到目标数据。2.B+树内部节点仅存储关键字,用于查找,不存实际数据。所有数据记录都存放在叶子节点中,叶子节点用指针连接,形成链表。

查找效率:1.B树由于数据可能存储在内部节点,因此找单个数据项可以更快。2.B+树所有查询都要访问叶子节点。

范围查询:1.B树范围查询要对各个节点分别遍历,因为各节点之间没有顺序链表连接。2.B+树叶子节点之间通过指针相连,范围查询很高效。

B+树对跳表:1.磁盘io友好。B+树节点与磁盘页大小匹配,每个节点可以存储多个key。而跳表是基于指针的结构,不容易利用磁盘块的连续性。2.高扇出、低树高:B+树一个节点包含较多子节点,使树较低,提高查询效率。跳表理论也是log n查找性能,但随机访问导致效率不如B+树。3.范围查询。B+树叶子节点通过链表连接高效。跳表也支持范围查询,但节点是动态分配的,当要查找一个元素时,从顶层链表头结点顺序遍历,直到遇到不小于目标值的元素,如果当前值等于则结束;如果大于目标或到达链表末端,则回到前一个节点并垂直下降到下一层,重复上述过程。这个过程访问到的节点是分散的。

讲讲b+树

每个节点中子节点个数不超过m,也不小于二分之m。

根节点的子节点个数可以不超过二分之m,这是一个例外。

m叉树只存储索引,不存储实际数据,类似跳表。

用双向链表将叶子节点串联在一起,方便按区间查找。

一般根节点存储在内存中,其他节点存储在磁盘中。

参考:https://time.geekbang.org/column/article/77830

B+ 树中,将叶子节点串起来的链表,为什么是双向链表?page 1

方便SQL语句中的升序和降序排序。

讲一下 mysql 底层为什么要是 B+树而不是 B 树(美团)page 1

节点存储内容:1.B树内部节点(非叶子节点)既存储关键字也存储数据记录。数据可能分布在所有节点中,在查找过程中可能在中间节点找到目标数据。2.B+树内部节点仅存储关键字,用于查找,不存实际数据。所有数据记录都存放在叶子节点中,叶子节点用指针连接,形成链表。

查找效率:1.B树由于数据可能存储在内部节点,因此找单个数据项可以更快。2.B+树所有查询都要访问叶子节点。

范围查询:1.B树范围查询要对各个节点分别遍历,因为各节点之间没有顺序链表连接。2.B+树叶子节点之间通过指针相连,范围查询很高效。

b+树在 IO 方面和 b 树有什么区别(美团)page 1

B+树内部节点只存key和子节点指针,因此同样大小磁盘页B+树能容纳更多子节点指针,树高更低,io次数更少。

叶子节点用双向链表连接,是顺序io。

为什么MySQL用B+树不用hash(为什么MySQL用B+树不用哈希表)(B+树和哈希表区别)page 1

B+树是多路平衡搜索树,非叶子结点存储索引,叶子结点存储实际数据,B+树节点大小与磁盘页匹配,减少io次数。B+树叶子结点用双线链表连接,适合范围查询。

哈希表用哈希函数将键映射到桶,适合等值查询,无法处理范围查询。哈希表是无序的,不适合orderby。

为什么MySQL用B+树不用红黑树(B+树和红黑树区别)page 1

红黑树vsB+树:B+树多叉树结构,节点存储多个键,适合磁盘io减少寻道次数,用于数据库。红黑树二叉结构,内存操作更高效。

B+树和跳表区别 page 3

B+树对跳表:1.磁盘io友好。B+树节点与磁盘页大小匹配,每个节点可以存储多个key。而跳表是基于指针的结构,不容易利用磁盘块的连续性。2.高扇出、低树高:B+树一个节点包含较多子节点,使树较低,提高查询效率。跳表理论也是log n查找性能,但随机访问导致效率不如B+树。3.范围查询。B+树叶子节点通过链表连接高效。跳表也支持范围查询,但节点是动态分配的,当要查找一个元素时,从顶层链表头结点顺序遍历,直到遇到不小于目标值的元素,如果当前值等于则结束;如果大于目标或到达链表末端,则回到前一个节点并垂直下降到下一层,重复上述过程。这个过程访问到的节点是分散的。

bst二分查找树和avl平衡二叉树有什么区别(B站)

普通的二分查找树没有自平衡机制,节点只需满足左子树小于根,右子树大于根,可能退化为链表。

平衡二叉树在每个节点维护高度,保证任意节点左右子树高度差不超过1。每次插入删除后,若不平衡,则用旋转操作调整。

介绍一下回表查询?(京东,百度,得物,作业帮,腾讯)page 90 page 32

回表是在使用非聚集索引进行查询时,数据库引擎先通过索引找到对应的主键值,然后根据主键值再找到聚集索引中完整的数据行。

回表原因。非聚集索引只存储了索引列和主键值,不存储数据行。因此当查询需要的数据不在索引列时,需要通过主键值回表到聚集索引查找完整数据。

回表会增加查询IO操作,尤其是数据量大的情况。

减少回表的方法:覆盖索引。在索引中包含查询所需的所有列,避免回表。

索引合并。合并多个索引结果。

聚集索引和非聚集索引的区别?⾮聚集索引⼀定回表查询吗?(聚簇索引和非聚簇索引区别)(网易重要,58同城,shopee,字节)第二个问题可以考场景题,给个sql问。page 90 page 32

聚集索引是什么?(作业帮)

聚集索引决定了物理存储顺序,聚集索引的叶子节点存储了实际数据行。非聚集索引叶子节点存储了索引key和主键值。

不一定,是否需要回表查询取决于索引是否覆盖了查询中所需的所有列。聚集索引叶子节点存储的是实际数据行,非聚集索引叶子节点存储的是主键值。

employees,其中包括员工的编号(employee_id)、姓名(name)、部门(department)、薪水(salary)等字段。我们在 employee_id 字段上创建了一个聚集索引,name上创建了辅助索引。 SELECT salary FROM employees WHERE employee_id = 1001; 不会回表查询,因为查询条件是主键索引,可以找到数据行。 SELECT salary FROM employees WHERE name= ‘张三’; 会回表查询,因为name是普通索引,找到索引项后还要根据主键值找数据行。 SELECT employee_id FROM employees WHERE name= ‘张三’; 不会回表查询 ,name是普通索引,找到索引项后得到他的主键值,而刚好主键值就是要查询的数据。

讲一讲慢查询怎么处理?慢SQL怎么处理?(慢查询如何查找和优化?)(慢查询怎么优化?)(百度,度小满,得物,腾讯多次考,美团)page 122

有哪些常⻅的 SQL 优化 ⼿段 ?

做过MySQL调优吗?(得物,作业帮多次考)

说一下MySQL常用优化方式(恒生,帆软,滴滴考过,快手)

查看慢查询日志,EXPLAIN FORMAT=JSON查看SQL是否命中索引,优化SQL(如调整查询结构(如小表驱动大表),拆分复杂查询,使用子查询优化),加索引,查看连接池个数,buffer pool大小(如果buffer pool命中率小于99%则他太小)。

如果单表数据量过多,即使SQL走索引,性能也不好,要考虑热点数据用redis,然后考虑主从复制或主主复制,如果还是慢,则先做垂直拆分,将大的系统分为多个小的系统。最后才是水平拆分,选择一个合适的sharding key。

Explain语句常用指标(解释一下执行计划字段的含义)(高德地图,满帮)page 1

key表示实际使用的索引名称。key_len表示所选索引的最大可能长度。ref表示索引列和表中列的比较关系。rows表示优化器估计需要读多少行才能找到匹配行。filtered表示经过where条件过滤后剩余的行百分比。extra比如,using index索引覆盖,using temporary使用临时表,using filesort使用文件排序。type表示访问类型,system表中只有一行最快,const最多匹配一行,eq_ref连接时对每个前置表组合都至多返回一行,ref普通索引等值查询,range索引范围扫描,index全索引扫描,all全表扫描。

多表连接怎么优化?(美团)page 35

建立索引。1.连接条件字段上要有合适的索引。2.用覆盖索引避免回表。

调整连接顺序。让数据量较小的表作为驱动表,用EXPLAIN FORMAT=JSON检查连接顺序。

数据预过滤。在连接前用where过滤,减少连接时需要处理的记录。

多表连接的情况下,如果要分页查询该怎么改造?(美团)page 35

先分页,再关联。先对主表做分页查询,再用子查询或者用MyBatis的resultmap中的Collection标签单独加载关联数据。

联合索引的原理讲一下。(百度,作业帮)page 95

构建原理:联合索引将多个列组合起来形成一个键,结构中复合键按照字典序排列。

B+树结构:联合索引的复合键作为B+树的关键字,节点中的数据也是按照复合键顺序存储。

最左匹配原则:只有查询条件中包含索引最左边的一部分或全部连续列时,数据库才能利用这个索引。

覆盖索引:当查询所需字段都包含在联合索引内时,数据库可直接从索引中获取数据,无需访问表。

讲一下索引下推?(得物,蚂蚁,滴滴)page 94 page 32

索引下推减少了从存储引擎读取全表行的次数。当 MySQL 使用索引来定位数据时,传统流程是:先通过索引查找到对应的行,然后再从数据文件中读取完整的行,再在 MySQL 层面对 WHERE 条件进行判断。而启用了 ICP 后,如果 WHERE 条件中有部分条件仅依赖于索引中的列,MySQL 会把这部分条件下推给存储引擎进行判断。

具体来说,执行过程大致如下:1.存储引擎首先扫描索引,只读取索引项,而不立即读取完整的行数据。2.对于每个索引项,先用索引中的列来判断那些可以利用索引判断的条件。3.如果这些条件不满足,则直接跳过,无需再去读取整行数据。4.如果条件满足,再通过索引项定位到完整行数据,然后在 MySQL 层面判断剩余条件,最终决定是否返回该行数据。

索引底层的数据结构了解么?Hash 索引和 B+树索引优劣分析(字节考过,作业帮)

B+树是平衡树结构,叶子结点在同一层,节点内数据按顺序排列。每个非叶子节点仅保存子节点关键字,实际数据存储在叶子节点中。

哈希索引用哈希函数将key映射到固定桶中,每个桶存对应哈希值的记录。

查找效率。哈希索引时间复杂度是O 1.B+树索引查找效率是O log n。

范围查询。哈希索引不支持范围查询,因为哈希函数结果是无序的。B+树索引支持范围查询,因为叶子结点按顺序排列。

并发性能。哈希索引并发性能好,因为插入删除操作简单。B+树索引并发性能较差,因为插入删除可能涉及到节点的分裂和合并。

B+树双向链表为什么快?B+树为什么加双向链表(小红书)

范围查找 时,当找多个行不在同一个页时,可以通过指针直接去找下一个页,没有双向链表还得从根节点去找。 或者说,没有双向链表,还要回溯,回到上层节点再遍历,双向链表直接顺着next指针往下走就行。

当要删除一个节点时,需要知道前驱,如果不是双向链表,需要从头开始遍历。类似于回答:为什么AQS要用双向链表?

mysql建立索引需要注意什么?(百度)

选择合适的列。选择在where子句或join操作中频繁使用的列。

考虑索引类型,B+树索引适合范围查询,哈希索引适合等值查询。

避免过度索引,过多索引会增加insert,update和delete操作的开销。

注意索引顺序,要符合最左匹配原则。

为一个表加索引怎么不影响业务?加锁会锁表怎么办?(如何在线添加索引而不影响正常业务?如果加索引时加锁会锁住整个表,该怎么办?)(在表上新增一个字段时,如果这个表正在进行读写操作,应该如何处理以确保不影响现有操作?)(生产环境如果对一个千万级大表加字段,怎么避免长时间加锁)(shopee,腾讯音乐)page 96

在线添加索引,MySQL5.6版本支持在线添加索引,algorithm=inplace,lock=none。

分阶段操作:如果版本不支持在线加索引,可以在业务低峰期操作,或者分阶段进行,先添加部分索引。

也可以借助第三方工具,如gh-ost(github开源的工具),原理是:1.创建一张结构为目标状态的新表。2.用触发器捕捉旧表数据变动,同步新表。3.同步完成后,将新表替换为旧表。

ALTER TABLE your_table ADD INDEX idx_name (column_name), ALGORITHM=INPLACE, LOCK=NONE;

pt-online-schema-change –alter “ADD INDEX idx_name (column_name)” D=your_database,t=your_table

binlog格式有几种?(或者问MySQL主从复制有哪些,饿了么考过,腾讯)page 32

statement格式,记录执行的sql语句。

row格式,记录每一行数据的变更。

mixed格式,混合,根据实际情况选择statement或row。

主从复制的原理了解吗?可能存在什么问题?(binlog怎么实现的主从同步?)(介绍下主从复制的流程?)(作业帮,美团,腾讯)page 128

流程: 主库写入数据:当主库接收到写操作时,将操作记录到binlog。 从库请求日志:从库定期向主库请求最新binlog。 主库 binlog dump线程 发送日志,从库 IO线程 写入relay log,应用日志。复制的过程是异步的,主库提交事务后,不会等待从库执行完毕。

主从复制类型: 异步复制:主库写入数据后立即返回,不等从库确认。

半同步复制:主库写入数据后,等待至少一个从库确认收到日志后再返回。

全同步复制:主库写入数据后,等待所有从库确认收到日志后再返回。

问题: 数据一致性问题:可以使用半同步复制技术,提高一致性。

binlog日志像事务类的操作有回滚之类的各种情况,那么怎么解决同步的从节点也能够在一段时间内保持最终的一致性(如何在 Binlog 存在事务回滚等操作时,仍能让从库在一定时间内与主库达到最终一致性?)(腾讯)page 1

回滚本身不产生任何binlog事件,从库自然也不用对该事务做任何变更。因为只有在事务真正Commit提交时,才把整个事务写入binlog;而事务回滚rollback时,innodb不会把任何内容写入binlog,只会修改undolog中的日志。

##

redis读写,定时同步到mysql。如果读写用两个redis,一个读一个写,那么万一写的过期了,读的没过期,整个读写的链路是什么样的,怎么读怎么写。(飞猪)

读写的流程。Redis主节点负责写,从节点负责读。1.写流程,客户端写数据到主节点,主节点异步将命令复制到从节点。2.读流程,客户端先从Redis从节点读,命中则返回;未命中则用旁路缓存模式,从MySQL查询最新数据并写入Redis。3.同步到MySQL,采用增量同步,在每次写Redis,生成更新事件异步写MySQL,也可定期批量扫描Redis变更并写MySQL。

写Redis过期但读Redis仍存在。原因。1.Redis3.2版本前,从节点不会主动检查key的过期时间。2.即使是Redis3.2以上版本,若用相对过期命令expire,从节点开始执行过期时间的时间可能滞后于主节点,导致从节点key存活时间延长。解决方案。1.用绝对过期时间命令,expire at,并保持主从时钟同步。2.在应用层额外处理,在读到缓存数据检查时间戳,如发现过时,则忽略该值并回数据库获取新数据、更新缓存再返回。

介绍下binlog不同格式的使用场景(腾讯)page 1

statement格式,记录原始SQL语句,典型场景是开发测试环境,以及业务逻辑简单、SQL确定性强的场景。

row格式。典型场景是线上生产库,业务强一致性要求高。以及多主或GTID环境下。以及基于binlog的审计和增量备份,闪回工具如MariaDB Flashback。

mixed格式。典型场景是线上业务,要兼顾性能和安全。

模糊查询会走索引吗?(百度)page 95

1.查询的数据在数据表中存在非索引字段。 like “xxx”走索引 like “xxx%”走索引 like “%xxx”索引失效,全表扫描 like “%xxx%”索引失效,全表扫描 百分号在字符串后面,查询的开始部分是确定的,可以利用索引。

2.查询的数据在数据表中都有索引。 like “xxx”走索引 like “xxx%”走索引 like “%xxx”不会全表扫描,而是全扫描二级索引树 like “%xxx%”不会全表扫描,而是全扫描二级索引树

所有的Or条件查询都会导致索引失效吗?(百度)

不一定,如果Or条件所有条件都建立索引时索引也不会失效。

讲一讲身份证号码怎么加索引?(百度)page 95

倒序存储索引:身份证后6位相对不易重复。步骤:1.存储时,将身份证号码用reverse函数倒序存储。2.在倒序存储的值上创建前缀索引。

hash字段索引:1.在表中增加一个整数字段存储身份证号码hash值,如crc32函数。2.在hash字段上创建索引。3.插入和更新时,同时计算并保存哈希值。4.查询时,先用hash索引定位,然后再匹配身份证号码。

mysql底层的存储的数据结构是什么?(美团)page 94

取决于存储引擎。对于innodb,存储结构包含以下部分:

数据页:innodb将数据按页存储,每个页包含多个数据记录,页包含页头、数据区等。

B+树索引:1.聚集索引:主键索引采取聚集索引,记录直接存储在B+树叶子节点中。2.二级索引:也基于B+树,但叶子结点存储的是主键值,借此通过聚集索引找到记录。

事务日志:1.redolog记录事务对数据的修改。2.undolog实现MVCC。

对于myisam,数据和索引分别存在独立文件中,索引采用B树结构。

唯一索引和聚簇索引的区别?(百度)page 94 page 31

唯一索引保证列数据唯一性,索引内部维护唯一的数据结构,可以是非聚集索引,如二级唯一索引。

聚集索引按照索引键的顺序重排列表中的数据,使得数据物理存储顺序与索引顺序一致。表的叶子节点存储的是完整数据行。

普通索引中放的是主键还是主键的指针(贝壳)page 1

普通索引中存放的是主键值,二级索引相当于在非主键列上排序,最终指向的是主键值而不是物理地址。主键值是恒定的,即使发生页分裂,二级索引也不用更新。但缺点是存储主键值会增加二级索引大小。

唯一索引和主键的区别?(阿里)page 94

空值的处理:1.主键列不允许空值。2.在多数数据库中,唯一索引允许列出现null值。

数量限制:1.每个表只能有一个主键。2.一个表可以有多个唯一索引。

逻辑意义:1.主键还承担数据记录的标识作用,用于建立外键关系。2.唯一索引没有逻辑意义,用于物理层面的唯一性。

索引类型:1.在innodb中,主键一般是聚集索引。2.唯一索引一般是非聚集索引。

从数据结构层面解释,主键索引,唯一索引,普通索引区别(贝壳)page 1

主键索引。整个表的数据直接存储在这个B+树叶子节点上。叶子节点除了索引key,还包含该行所有其他列值。

唯一索引。叶子节点不存完整数据,只存索引key和主键值。其中索引key是唯一的。

普通索引。叶子节点存储索引key和主键值列表,因为允许重复,一个key可以对应多个主键值。

什么情况下考虑建立普通索引,什么情况下考虑建立唯一索引(普通索引和唯一索引,应该怎么选择?)(得物)page 93

从性能角度,在可以由业务逻辑保证唯一性场景下,优先普通索引。

利用change buffer优势:1.普通索引:在更新操作中,如果目标页不在内存中,普通索引可以把变更记录写入change buffer,避免一次随机io读入磁盘,这对写操作频繁、更新后不被立即查询的场景(如日志)效果好。2.唯一索引:为保证唯一性,更新必须先将页读入内存进行冲突检测,无法利用change buffer。

更新后查询的场景:即写后读,即使用普通索引,由于需马上触发merge操作,减少随机io 优势也打折扣,普通索引与唯一索引性能差别不明显。

MySQL主键索引和普通索引谁的性能好一些?如果在10万数据下,谁的性能更好?page 93(腾讯)

mysql里主键索引为什么比其它索引快(满帮)

主键索引性能更好。主键索引是唯一非空,通常是自增的。

在innodb引擎中,主键作为聚集索引,数据行存储在主键索引叶子节点中。而普通索引叶子节点存储的是主键值。

在10万的数据规模下,数据量有限,性能差距不大。如果在用主键查询场景下,或者高并发场景,主键索引性能比普通索引更好。

在用非主键查询时,特别是发生索引覆盖查询时,普通索引性能更好。

讲讲主键索引,二级索引,聚簇索引,非聚簇索引(蚂蚁)page 37

主键索引是基于表中主键字段建立的索引,在innodb中用聚簇索引实现。二级索引是基于非主键字段建立的索引。聚簇索引是将数据实际存储和索引节点放在一起的索引。非聚簇索引是指索引的叶子结点存储的是数据行的主键值,不是实际数据。

为什么二级索引叶子节点要放主键值而不是一个指针(字节)page 1

页分裂。B+树插入数据时如果发生页分裂,原有记录可能移动到其他页,如果二级索引保存的是物理地址,记录迁移后地址就会失效,必须更新所有二级索引指针,维护成本高。

uuid做主键和自增id做主键性能有什么区别?(作业帮)page 93

插入性能:1.自增ID顺序生成,在B+树索引中插入是追加在末尾,减少了页面拆分和碎片化。2.UUID生成是随机的,数据插入散步在整个索引树,引发页面分裂和索引碎片。

存储空间:1.自增ID用int,4字节。2.UUID占用16字节。

分布式系统下的唯一性:1.UUID保证全局统一。2.自增ID在单机和主从复制模式下表现良好,但在多节点分布式系统会出现主键冲突,需要雪花算法解决。

同一条SQL,不同规模的数据会走同一条索引吗?

索引选择的基本原则。索引是基于查询的条件和数据的分布来决定的。数据库优化器会根据统计信息和查询计划来选择最优的索引。

数据规模的影响。对于不同规模的数据,数据库优化器会选择不同的索引。比如,对于小规模数据,全表扫描可能比使用索引更快,因为索引查找本身也有开销。而对于大规模数据,使用索引可以显著减少查询时间,因为全表扫描代价 非常高。

有一种说法, 单表数据量超过2000万, innodb性能就会降低,为什么?(京东)page 93 page 31

这种说法是一个经验值,并非innodb硬性限制。

innodb内部用B+树管理数据索引,理想情况B+树高度保持3层,查询时只需经过根节点和两层索引定位数据。

另外,表中每行数据的大小、字段数量和字段类型会影响一个16KB的页能存储的记录数。不同的行大小,理论上3层B+树能存放的记录数从百万到5亿条不等。因此,2000万这个数字只是参考值,不能适用于所有场景。

参考:https://juejin.cn/post/7165689453124517896

MySQL B+树里面节点里面存的什么信息(字节)page 1

节点大小16KB,包含头部、用户数据、目录等。

页的格式,固定头部开销128字节,还有目录开销,剩下的存储记录或索引项。

行的格式,包含行头记录数据类型等,还有可变字段列表记录如VARCHAR实际长度,还有null位图记录null列是否为空,还有mvcc信息即事务id和回滚指针,最后才是实际列数据。

MySQL里面join是怎么去做优化的(美团)page 1

join有两种算法,index nested loop join(n l j),小表驱动表逐行取值,用被驱动表大表的二级索引一行行回表,缺点是逐行回表无法批量化,随机io较多。第二种是Block nested loop join(b n l),将驱动表一整块读入join buffer,每次扫描整个驱动表完成匹配,缺点是他是m乘以n次比较,cpu消耗大,其次他会污染buffer pool的LRU层次。

multi range read优化。他能加速回表。具体地,先在二级索引上扫描,把主键id收集到read random buffer;对id排序后,再按照顺序到聚簇索引批量读取行数据。他把大量随机延迟转为近似顺序读,减少io延迟。可以调整MySQL的optimizer swich的mrr为on,mrr cost based为off开启multi range read优化。

针对n l j,MySQL提出batched key access(b k a)算法。在n l j中,每行驱动一次,b k a则先把一定行数(由join buffer size参数决定)的小表字段批量读入join buffer,一次性发给大表去索引查找,再批量回表,配合MRR实现全流程顺序化。

针对b n l,需要减少全表扫描和buffer pool污染。我们应尽量不用b n l,如果不得已,则应增大join buffer size,让更多小表数据一次装入,减少循环次数。或者为被驱动表相关的字段加索引,转化为b k a。

参考:https://time.geekbang.org/column/article/80147

数据库b+树索引能存多少数据?(美团)page 93

b+树是平衡树,每个节点可以存储多个键值对。非叶子节点存储键值和指向子节点的指针,而叶子结点存储实际数据。

影响存储容量的因素。 节点大小。b+树每个节点有固定的大小。 键值大小。键值越大,单个节点能存储的键值对就越少。 树的高度,b+树高度决定了从根节点到叶子节点的路径长度。高度越低,查询效率越高。

一百万个long型数据存到b+树里要几层?(得物)page 92 page 31

假设仅存储单个long值,实际记录约8字节,加上行头、事务ID、回滚指针约26字节。

叶子节点容量:1.每个innodb页扣除页格式开销后大概15KB存放数据。2.如果单条记录26字节,每页存放585条记录。3.存储100万个记录需要叶子页数为1710个叶子节点。

非叶子节点容量:1.非叶子节点中,每个索引项含key、子节点头指针、行头,共19字节。2.同样每个页15KB大致存放801条索引记录,但考虑页目录开销,实际787条索引记录。

构建树高:1.叶子节点1710个。2.第二层:每个节点指向最多787个叶子节点,所以要3个节点。3.根节点:指向这3个第二层节点。因此需要三层B+树。

B+树做索引比红黑树好在哪里?page 92 page 30

较低树高和磁盘io优化:1.B+树是多路平衡树,每个节点存储多个key,树高度较低。2.而红黑树是二叉树,每层只能分出两个节点,相同数据量树高度更高。

有序叶子节点链表:b+树所有数据都存在叶子节点中,通过链表连接,适合范围查询,而红黑树的顺序遍历需要中序遍历。

当我有一个字段a,检索一个a等于10这一条数据,在B+树上的检索过程是什么?(说一下查询语句在B+树上的检索过程?)(B+树叶子结点是怎么查询的?)(快手,高德地图)page 91 page 30

内节点索引查找阶段:1.起始点:从根节点开始。2.关键字比较:在内节点中,从左到右遍历存储的关键字,由于B+树内节点存储的是分割值而不是实际数据,因此我们比较目标值10与节点中的key:如果10小于等于第一个key,则沿着最左边指针进入对应子节点;如果10落在两个key之间,则选择对应区间的指针;如果10大于所有key,则沿最右边指针进入子节点。3.递归下降,重复上述过程,在每一层都选择子节点,直到到达叶子节点。

叶子节点查找阶段:1.定位记录:在叶子节点中,所有key及对应的记录或指向记录的指针都按照顺序排列。2.匹配:在叶子节点采取二分查找找目标值10。

B+树叶子结点除了存储数据还有什么?非叶子节点存了哪些数据?(B+树叶子节点存的是什么)(B+树非叶子节点存的是什么)(蚂蚁)page 44

B+树存的是什么数据?(百度)

叶子节点(实际数据): 页目录 ,记录行:头信息,事务ID,回滚指针。

非叶子节点(索引数据):包含页目录,索引记录:指针,行标头,索引值。 <!– 指针:叶子结点之间通过指针相互连接,形成一个有序链表。

索引信息:叶子结点除了存储数据外,还存储索引信息,如键值。

元数据:如数据版本信息,时间戳。 –>

结合B+树结构讲讲为什么一定要最左匹配才行?(快手)page 91

有序性。当查询条件包含索引最左列时,所有满足条件的记录在B+树叶子节点中是有序排列的。这样数据库可直接定位到起始位置,然后顺序扫描,效率高。如果缺少最左条件,如只对B过滤,记录分散在整个B+树上,无序,退化成全表扫描。

索引结构。B+树内部节点存储指向叶子结点指针,这些指针基于索引key最左到最右顺序组织。只有最左匹配,才能沿着树的路径找到叶子节点。

减少回表。当查询条件最左匹配时,索引数据已包含或足以定位到目标记录,避免回表(即使用非聚集索引进行查询时,数据库引擎先通过索引找到对应的主键值,然后根据主键值再找到聚集索引中完整的数据行)。

MySQL 的隔离级别是基于锁实现的吗?(网易)page 102

MySQL幻读如何解决?(作业帮)page 102

是的,隔离级别是基于锁和mvcc实现的,读已提交使用共享锁读取已提交数据,可重复读使用mvcc实现,快照读使用mvcc解决幻读,当前读使用记录锁加间隙锁解决幻读,可串行化对读使用共享锁,对写使用排他锁。

innodb是如何实现可重复读的?(mvcc的实现原理是什么?)(MVCC 的查询过程)(京东)page 1

InnoDB 对 MVCC 的具体实现(快手)page 102

讲一讲可重复读实现原理?(作业帮,腾讯)page 102

通过隐藏列,读视图,undolog,实现mvcc。隐藏列包含db trx id表示最近修改该行的事务id等。读视图使得事务只能看到最近修改事务id小于他的行。undolog保存事务修改之前数据的版本。

注:1.如果数据事务ID小于读视图的最小活跃事务ID,则数据在当前事务开启之前存在,显示。

2.如果数据事务ID大于读视图的最大事务ID,则数据在读视图创建之后产生的,不显示。

3.数据事务ID在活跃事务中:存在,则读视图创建时事务已经commit,数据可以显示。

4.数据事务ID不在活跃事务中,则我这个读视图生成时刻,你这个事务还在活跃,还没有commit,你修改的数据,不显示。

参考:https://juejin.cn/post/6871046354018238472,活跃事务两个情况容易出错。

事务1

begin ; select a from didi_scene where id=1; update didi_scene set a=a+1 where id=1; commit ;

事务2: begin ; select a from didi_scene where id=1; update didi_scene set a=a+1 where id=1; commit ; 假设事务1的执行时刻是1 3 4 5 6,事务2的执行顺序是2 7 8 9 10,使用manual提交,可重复读隔离级别,那么最终a的值是多少?(滴滴)

事务2执行select语句,在可重复读隔离级别下,事务2的第一次一致性读会使用事务开始时读到的快照,由于事务2的快照视图是在事务1更新之前建立的,事务2读到的是旧版本a等于1。当事务2执行update语句,此时事务2基于读到的值加1,丢失了事务1的修改,最终的值为2。

如果是读已提交,每次select都会看到他开始执行那一刻已提交的最新数据,即当前读。事务2执行select语句读到的是已提交的a等于2,因此最终a的值是3。

隔离级别 普通 SELECT SELECT … FOR UPDATE / UPDATE / DELETE
READ COMMITTED 当前读(current) 当前读(current)
REPEATABLE READ 快照读(snapshot) 当前读(current)

两个事务修改同一条记录,这个时候再去读这个记录会怎么样?读到的结果是一样的还是不一样的?(淘天)

不一样,只要两个事务都没有提交,他们看到的是当前事务的版本,且一个事务会阻塞。

mysql Read View都有什么字段(美团,拼多多)page 37

m creator trx id。存创建这个read view的事务id。

m ids。一个数组,记录了生成read view时还未提交的事务id。

m low limit id。大于low limit id的对当前事务不可见。

m up limit id。小于up limit id的对当前事务可见。

RR隔离级别,一个事务创建一个read view。RC隔离级别,每次执行查询生成一个新read view。

读已提交和可重复读隔离级别下mvcc实现的区别是什么?(shopee,字节)page 87

MVCC在读已提交和可重复读下产生读视图的区别是什么?(京东)page 133

区别。

读已提交和可重复读是通过readview实现的,区别是创建readview的时机不同。

读已提交在每个select语句执行前都会重新生成一个readview。

可重复读在执行第一条select时,生成一个readview,整个事务期间readview不会变化。

undolog记录的两个隐藏列说一下(快手)page 90 page 30

undolog记录了事务中对数据的修改操作,两个列是roll_pointer和trx_id。

roll_pointer:是指向undolog的指针,每当对数据行修改时,MySQL生成一条新的undolog 记录,并将roll_pointer指向这条记录。

trx_id:记录了修改该数据行的事务ID,trx_id可以判断当前事务是否能看到该数据行的某个版本。

参考:https://blog.csdn.net/Weixiaohuai/article/details/117867353

要去删数据, delete from t where a = ‘xxx’, a 是普通索引,基于这个 SQL 场景,能把在 RC 下面和 RR 下面,把它们加锁的一个区别说一下?(在数据库中执行 DELETE FROM t WHERE a = ‘xxx’ 语句时,锁的行为会因事务的隔离级别,有什么不同?) (京东)page 43

读已提交,事务获得对索引a上xxx,1的记录锁,主键1上的记录锁。

可重复读,事务获得对索引a上xxx,1的next key锁(范围是(?, xxx]), 索引a上yyy,2的间隙锁(范围是(xxx, yyy)) ,如果没有数据yyy,则锁定(xxx,正无穷)。主键1的记录锁。

注:如果 LOCK_MODE为 x,说明是 next-key 锁;如果 LOCK_MODE为 X,REC_NOT_GAP ,说明是记录锁,如果 LOCK MODE为 x,GAP ,说明是间隙锁。

注2:可重复读下,对普通索引命中的索引项加临键锁,下一个索引项加间隙锁,主键索引命中的索引项加记录锁。读已提交下,对普通索引加记录锁,对主键索引加记录锁。

什么是覆盖索引(得物,作业帮,美团)page 85

一个索引中恰好包含某条SQL查询所需读取的所有列,从而数据库只需扫描索引就能返回结果,无需回表,回到聚簇索引去读取其他列。

参考:https://juejin.cn/post/6844903967365791752

索引这么多优点,为什么不对表中的每⼀个列创建⼀个索引呢?(使⽤索引⼀定能提⾼查询性能吗?)

索引并不都是好的,创建索引和维护索引也需要耗费资源和时间。

最左前缀匹配原则了解么?

在用联合索引时,查询条件应尽可能从索引最左列开始匹配。

如何查看某条 SQL 语句是否⽤到了索引?

EXPLAIN FORMAT=JSON关键字分析SQL语句,返回的 key列 展示了查询使用的索引。

MySQL 如何使⽤乐观锁和悲观锁?

乐观锁,可以用version版本字段,在update时检查版本号是否匹配,匹配则增加版本号。悲观锁,使用select for update加行级锁。

乐观锁的实现原理?悲观锁实现原理?(58同城,shopee多次考,字节)page 29

乐观锁:通过版本号version或时间戳timestamp或CAS(先比较再更新)实现,每次更新数据时,会检查当前版本号或时间戳是佛与读取时一致,如果不一致则说明数据已经被其他线程修改,更新失败。

悲观锁:通过数据库行级锁如select for update或Java中的synchronized实现。

乐观锁和悲观锁的​业务场景举例?(乐观锁的使用场景)(悲观锁的使用场景)(业务上 什么情况使用悲观锁,什么情况使用乐观锁?)(淘天)page 43

乐观锁的场景:抢火车票,同时查询的人很多,虽然具体座位票只能卖给一个人,但余票可能很多,而且不能预测哪些查询者会购票,适合乐观锁。

悲观锁的场景:社交平台的动态或者帖子写入,对于大多数帖子,其他用户的访问不频繁。

乐观锁,悲观锁解决什么问题?流量比较大,更新比较频繁的话,一般用哪种锁合适?(蚂蚁)

乐观锁假设在数据更新时冲突概率较低,因此读数据不加锁,在提交更新时检查数据是否被其他事务修改过,如果未修改则更新,否则更新失败。适合读多写少。

悲观锁假设数据更新时冲突概率较大,因此读取数据时就加锁。悲观锁适合写多读少场景。

流量大,更新频繁场景,悲观锁更合适。

CAS(compare and swap)了解吗?原理是什么?(快手,小红书)page 72 page 29

如何解决CAS的ABA问题?(百度,淘天)page 29

CAS跟哪个CPU底层的指令有关系吗?(快手)page 29

CAS包含三个参数,内存地址、预期旧值、要设置的新值。他会检查内存位置上值是否等于预期值,如果相等则将内存位置更新为新值;否则,不修改并返回当前实际值。这整个过程是原子操作。

很多地方用到了cas,比如Concurrenthashmap采用cas和Synchronized保证并发安全。cas的原理是 unsafe 类中的native方法 compareandswapobjecct 实现,它调用本地代码来做,和具体操作系统和cpu密切相关,比如x86架构的cmpxchg指令。cas存在aba问题,一个值原来是a,变成了b,又变回了a,cas是检查不出来变化的,但实际上被更新了两次。

aba问题通过加上版本号或者时间戳解决。

版本号:在每次修改数据时,同时更新一个版本号。cas操作不仅比较数据的值,还比较版本号。

时间戳:记录每次修改的时间。cas操作时,同时比较数据的值和时间戳。

参考:file:///E:/BaiduNetdiskDownload/024-100023901-%E4%B8%93%E6%A0%8F%E8%AF%BE-%E7%8E%8B%E5%AE%9D%E4%BB%A4-Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%EF%BC%88%E5%AE%8C%E7%BB%93%EF%BC%89/04-%E7%AC%AC%E4%BA%8C%E9%83%A8%E5%88%86%EF%BC%9A%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7%E7%B1%BB%20(14%E8%AE%B2)/21%E4%B8%A8%E5%8E%9F%E5%AD%90%E7%B1%BB%EF%BC%9A%E6%97%A0%E9%94%81%E5%B7%A5%E5%85%B7%E7%B1%BB%E7%9A%84%E5%85%B8%E8%8C%83.pdf

CAS是一个复合的指令还是像事务的指令(元戎启行)

c a s本质上是一条cpu原子指令,他保证对单个内存地址先比较再交换的不可分割的原子操作,因此他既不是复合指令,也不是有多地址一致性和回滚语义的事务特性。

乐观锁会发生死锁吗?(携程)page 89

不会。

乐观锁如何保持数据一致?(shopee)page 89

更新步骤:读取数据以及版本号。

更新时用where子句检查版本号是否一致,并递增版本号。检查更新行数,如果为0,则数据被其他事务修改。

SELECT data, version FROM example WHERE id = 1;

UPDATE example
SET data = 'new data', version = version + 1
WHERE id = 1 AND version = current_version;

MySQL 中常⻅的⽇志有哪些? page 29

二进制日志binlog,慢查询日志slow query log,redolog和undolog。

慢查询的原因?(阿里)page 89 page 29

索引失效。1.没有建立索引。2.查询中对索引字段用了函数、隐式类型转换或模糊匹配,或创建联合索引没有遵守最左匹配,使用了not in操作符,导致全表扫描。

SQL语句不合理。如包含了不必要的子查询、嵌套查询。

客户端和数据库连接数过小,会限制SQL查询并发数,增大连接数可以提升速度。

innodb会有一层内存Buffer pool提升查询速度,一般命中率大于99%,如果低于这个值,可以考虑增大Buffer pool大小。

MySQL突然的抖动存在的原因可能有哪些?(哈啰,场景题)page 89

抖动和innodb的脏页刷盘(flush)有关,就是平时更新操作只在内存和redolog中完成,但在某个时刻必须把内存脏页刷到磁盘上,这个过程可能导致短暂性能下降,下面是原因:

redolog写满导致全量刷盘。redolog空间耗尽时,系统停止新的更新操作,等待从新checkpoint到当前写入位置之间的日志对应的所有脏页全部刷入磁盘,直到留出足够空间。

内存不足导致数据页淘汰。缓冲池内存不足以装载新数据页时,会淘汰最久未使用的页,如果淘汰脏页,就要先写入磁盘。

脏页比例过高时的加速刷写。innodb根据当前脏页比例和redolog使用情况计算一个百分比,决定刷盘速度。当脏页比例超过默认75%时或redolog未刷部分过大时,系统会加速刷盘。

邻居页连带刷页机制。当一个数据页要刷写时,如果其邻居页也是脏页,且参数innodb flush neighbors被启用(在ssd上应设置为0),会一起刷写。

不合理的配置。如innodb io capacity设置低于实际磁盘IOPS,导致脏页积累。 <!– 抖动原因是刷脏页(内存和磁盘页内容不一致),将内存页写入磁盘。

第一种:redolog写满了,进行checkpoint操作,写入磁盘。

第二种:系统内存不足,要淘汰数据页,如果淘汰的是脏页,就要写到磁盘。

第三种:MySQL认为系统空闲时,有机会就把一些脏页写入磁盘。 –>

参考:https://time.geekbang.org/column/article/71806 <!– 数据库连接池:连接数不足或过多。

锁:多个事务同时请求相同资源。

慢查询:存在未优化的SQL,导致数据库负载高。

硬件:磁盘IO瓶颈,内存不足。 –>

看到一个慢查询就加索引,有什么隐患?(作业帮)page 88

写操作变慢:索引可以加速查询,但增加写操作(插入,更新,删除)的开销,每次写都要更新索引。

额外存储空间。

一条SQL可以同时用上多个索引吗?(作业帮)page 88 page 29

参考。

针对单个数据表查询,SQL优化器默认只会选择一个最优索引使用,但在某些场景会用上多个索引。发生在数据库查询优化器决定用索引合并策略时,如查询条件出现多个OR,且每个条件有单列索引,优化器会用这些索引,在执行阶段合并,但效率不如联合索引。

索引合并类型:Union Scan:将多个索引查询结果取并集,去掉重复项。

Intersection Scan:将多个索引查询结果取交集。

Sort-Union Scan:类似于Union Scan,但在合并前需要对结果进行排序。

为什么有合并索引?合并索引和联合索引有啥区别?(作业帮)page 88 page 29

联合索引是将多个列组合成一个单一索引,在某些场景,联合索引可以包含查询所有列,避免回表。

索引合并是查询优化策略,当查询涉及多个条件但没有合适联合索引时,mysql会分别利用各个单列索引结果,将这些结果进行合并(交集或并集)得到最终结果。 <!– 实现方式:合并索引:通过数据库管理系统内部机制实现,开发者不需要手动创建,而是通过优化查询计划来利用现有索引。

联合索引:他需要开发者手动创建,如MySQL中可以通过CREATE INDEX idx_name_age ON users(name, age)创建联合索引。 –>

binlog,undolog,redolog他们的作用和使用场景是什么?(得物)page 88 page 29

binlog:记录数据库所有更改操作,用于数据备份和主从复制。

使用场景:主库binlog会传输到从库,从库根据binlog重放操作。

redolog:记录事务对数据库所做的 所有物理更改,保证事务持久性。

使用场景:数据库崩溃时,redolog可以恢复未完成的事务,是innodb特有的。

undolog:记录事务执行前的数据状态,用于事务回滚和MVCC。

使用场景:当事务需要回滚时。undolog提供所需的信息。在MVCC中,undolog用于生成数据旧版本,避免脏读。

redolog写到内存里如何保证能刷盘(字节)page 1

redolog先写入redolog buffer后,用主动和被动刷盘方式。

调整flush log at Trasaction commit参数,他有三种模式,第一种是innodb每隔一秒刷新到磁盘。第二种是每次事务提交时刷新到磁盘。第三种是每次事务提交时只写到内存的page Cache,后台线程每隔一秒刷新到磁盘。

调整log buffer size,buffer的大小决定redolog写入内存后,后台线程被动刷新到磁盘的时机。

为什么RedoLog 使用 WAL 技术(为什么RedoLog 使用预写日志技术)page 1

保证持久性和减少磁盘随机写入。

原因一,事务提交时,先保证redolog已刷盘保证持久化,不用等脏页写入磁盘。如果崩溃,只需重放redolog,就能把数据页补写到正确状态。原因二,日志写入是纯顺序写,速度高于脏页的随机写,脏页可以延后由后台线程批量异步写。

binlog 主要记录了什么?

主要记录了 MySQL 数据库中数据的所有变化(数据库执行的所有 DDL 和 DML 语句)。

binlog刷盘时机怎么选择?page 88 page 29

事务在执行过程中,先把日志写入 binlog cache 中,只有事务提交时,才把binlog cache持久化到磁盘上的binlog文件中。默认每次提交事务的时候将binlog写入磁盘,即sync binlog设置为1。

异步刷盘,配置sync binlog大于1,或利用group commit。

什么情况会重新生成binlog?page 87 page 29

达到大小限制。当前binlog文件达到max_binlog_size配置上限时,mysql自动关闭当前文件并生成新文件。

手动刷新日志。执行flush logs命令,mysql会立即创建一个新的binlog文件。

数据库重启。服务重启后,会生成一个新binlog文件。

重置主库。用reset master命令清空所有binlog并重置编号。

redolog 如何保证事务的持久性?page 87 page 29

1.预写日志wal原则。事务对数据页修改前,先将修改记录写入redolog。 2.同步刷盘fsync,数据库调用操作系统提供的同步写入。 3.崩溃恢复,启动时扫描redolog,将已提交未持久化的数据重新写入磁盘。

redolog什么情况会出现丢失数据?page 87 page 40

redolog写入内存时宕机怎么办?(得物,腾讯)page 40

如redolog写入log buffer 但未写入page cache,page cache是文件系统缓存,此时 db 崩溃,就会出现数据丢失。

redolog写入page cache但未写入磁盘, 操作系统 崩溃,出现数据丢失。

MySQL 执行完一条语句后会立即落盘吗?(猿辅导)page 87 page 29

⻚修改之后为什么不直接刷盘呢?page 87

不会。

1.内存缓冲与日志。innodb写时,先将数据修改写入内存缓冲池,记录到redolog中。

2.事务提交与日志刷新。事务提交时,如innodb flush log at trx commit设置为1,则在事务提交时将redolog刷新到磁盘,但实际数据页不会立即写入磁盘。

3.后台刷盘。数据页刷新由后台刷盘线程异步进行。

性能非常差!InnoDB 页的大小一般为 16KB,而页又是磁盘和内存交互的基本单位。这就导致即使我们只修改了页中的几个字节数据,一次刷盘操作也需要将 16KB 大小的页整个都刷新到磁盘中。而且,这些修改的页可能并不相邻,也就是说这还是随机 IO。

ssd的底层原理,顺序io和随机io的性能差异在ssd中也存在吗?(字节)page 157

ssd基于nand闪存实现数据存储。也存在,ssd随机写,他每次请求会涉及不同的闪存块,因为闪存必须先擦除后写入,不连续的块增加延迟。

binlog 和 redolog 有什么区别?(快手)page 86 page 26

为什么会有redolog和binlog两套log(作业帮)

binlog用于数据库还原,属于数据级别的数据恢复,主从复制是binlog的应用场景。redolog属于事务级别的数据恢复。 redolog 是 InnoDB引擎特有 的,binlog是所有存储引擎共有的,binlog是MySQL的server层实现的。redolog是物理日志,采用预写日志WAL机制,记录的是某个页的修改。binlog属于逻辑日志,记录的是数据库执行的所有DDL和DML语句。

undolog 如何保证事务的原⼦性?page 86

undolog记录每条数据修改前的旧值。

当事务需要回滚时,innodb会根据undolog记录,将数据恢复到修改前状态。

undolog记录格式:事务ID,表ID,行ID,修改前的数据。

回滚段:undolog存储在回滚段中,每个回滚段有多个undolog。

除了保障事务原子性,undolog还有什么用?page 86

mvcc的实现依赖于读视图和undolog,如果数据行不可见,则通过这一行的指针找到undolog的历史版本。

MySQL 如何存储 IP 地址?page 86

ipv4存储。用内置函数inet aton将点分十进制ipv4地址转换为32位无符号整数,存储到unsigned int字段中。

ipv6存储。ipv6地址为128位,可以用binary(16)类型存储。用inet6 aton将ipv6地址转换为16字节数据。

MySQL主从之间同步的速度如何提升?page 86(百度) page 26

1.调整MySQL binlog配置,配置参数比如binlog cache size,sync binlog,innodb flush log at trx commit等,减少磁盘IO。

2.使用并行复制。MySQL5.7以上支持并行复制,可以通过设置slave parallel workers启用并行复制,加快从库回放速度。

3.减少不必要的数据同步,过滤不必要同步的库或表,使用replicate wild do table和ignore table参数实现。

4.存储优化。根据业务调优innodb参数,如innodb buffer pool size,保证读写速度。

主从同步延迟如何解决?(MySQL主从同步延迟如何解决?)(B站)page 18

主从同步延迟是一个事务在主库提交完成t1到备库执行完成t3之间的时间差。可以在备库seconds behind master参数看。

延迟原因。1.主库写入和备库消费速率不匹配。主库dml操作并发量大,binlog生成很快,而备库的复制线程速度跟不上。2.备库性能不足。备库配置不足容易产生cpu io竞争。3.大事务。在主库中要等待整个事务执行完才写binlog,备库回放有延迟。4.备库单线程处理binlog瓶颈。

解决方法。1.按表并行。将不同表事务发送到不同线程。但存在热点表问题。2.按行并行。要求binlog为row格式,并行度更高,但对于大事务cpu和内存消耗大。3.按库并行。4.MySQL5.7。用参数slave parallel type提高并行度。调整binlog group Commit sync delay参数让主库提交事务的时间更长,让更多的事务处于prepare阶段。

简单说⼀下⼤表优化的思路(大表如何优化)page 85 page 26

SQL与索引优化。不仅是简单给SQL建索引,而要根据具体业务场景、数据分布考虑。利用索引覆盖、延迟关联,减少全表扫描。

引入缓存。使用memcached、Redis,将热点数据预存在内存中。

读写分离。1.复制策略,主从主主复制,写操作集中在主库,读操作分摊到从库。2.在应用层实现读写分离,或用工具如360 atlas辅助管理。

分区表。mysql自带的分区表功能,大表按照时间划分成多个逻辑分区。但SQL必须包含分区字段作为查询条件。

垂直拆分。根据业务耦合度,将系统拆分成多个独立子系统。

水平切分。选择合适的分片key很关键。

–>

参考:https://www.zhihu.com/question/19719997

MySQL分库分表怎么分?(怎么分库分表?)(分库分表方案)(淘宝购物场景-区分用户订单和商家订单)(库存系统设定,高并发读的情况下怎么扛住。数据一致性怎么保证。怎么加锁的,锁的粒度在流程中锁了什么?)(分库分表:分买家库、卖家库,如果设计分库分表键可以快速对应到买家表/卖家表?)(讲讲分库分表)(分库分表之后,id主键如何处理)(分片键如何选择)(作业帮)page 87

1.直接通过主键解析。如果主键中嵌入了分库分表键,利用解析主键可以直接找到数据所在的库和表。但这种方案的适用范围有限,因为大多数时候主键是按照业务中的部分场景生成的。

2.引入中间表。可以额外建立一个中间表来存储订单 ID、买家 ID 和卖家 ID 等关键字段。当需要用卖家维度来查询时,先根据卖家 ID 从中间表获取对应订单,再依据订单中携带的买家 ID 定位源库和数据表。优点:借助中间表可以支持不同维度的查询。缺点:中间表可能成为写入瓶颈,而且一旦业务变化需要支持更多查询条件时,表结构往往会变得僵硬,不够灵活。

3.二次分库分表(冗余分库分表)。针对非主流查询需求,比如卖家订单查询,可以考虑复制一份关键数据,再按照卖家 ID 做一层分库分表。优点:查询时直接定位到按照卖家分片的库和表,从而提升查询性能。缺点:复制的数据与源数据之间可能存在数据一致性问题,同时也增加了额外的存储和同步成本。这时必须要设计好数据同步机制及重试、异常处理方案。

4.利用中间件(如 Elasticsearch)。对于复杂且变动多样的查询场景,可以考虑同步部分业务数据到 Elasticsearch 这类搜索引擎,利用它支持灵活的查询和全文检索。当然这也需要考虑数据同步延迟和部分字段同步不足的情况,通常作为补充手段使用。

5.广播查询。如果无法明确定位数据所在的库和表,最后的兜底方案是对所有分库分表的节点进行广播查询,但这种方法对数据库性能影响极大,一般只在非常特殊的场景下作为最后手段。

6.数据同步及一致性问题。(1)同步监听(利用 binlog 或 Canal 等工具) 通过监听数据库写入操作(如 binlog),将数据异步同步到中间表或冗余库中,针对同步失败则需要设计重试机制,严重时可能需要人工介入修复。(2)双写策略。在应用层写入主库及副本库时,采用双写的方式同时更新多个数据源,不过这种方式在分布式环境下维护较复杂,需要额外设计分布式锁或其他一致性保障机制。

分片算法有哪些?

范围分片、哈希分片和查表法。

范围分片如按时间分片,按订单号分片。

哈希分片如分片键是用户号,拿用户号除以分片数,得到的余数就是分片号。

查表法就是没有分片算法,决定某个值落在哪个分片上,靠人为分配,分配的结果记录在一张表里,每次查询先查表。

MySQL分库分表的情况下如何根据某个字段排序查询到前10个数据(腾讯)

考虑分库分表的规则、排序的字段、表的数据量、查询的实时性要求,假设分库分表是按照某个字段哈希,排序的字段不是分库分表的字段,是大表场景,不要求查询的实时性。

解决方案。

方案一。应用层合并。在应用程序并行向各个分库发送查询,每个库执行select orderby colomn desc limit k,k取值大于等于10,然后将各库返回结果排序取前10。缺点是开销大,要从所有分片拉取数据。

方案二。用sharding sphere中间件。他将用户SQL,带orderby的查询广播到所有分片,如sharding sphere将limit N改为limit int max,每个分片返回所有排序结果。缺点会扫描所有分片并传输大量数据,仅适合小范围数据。

方案三。数据汇总,额外维护一个独立全局汇总表,存储排序字段和主键信息。用binlog把各分片的更新同步到全局表,然后对该表进行排序查询。适合非实时结果的排行榜需求。缺点是数据存在延迟且需要维护成本。。

binlog怎么同步更新ElasticSearch(京东)

MySQL主库开启binlog并设置为row模式;为canal创建专用用户并授权复制权限。如果用从库作为binlog源,还要开启log slave updates。

启动canal server,配置数据库地址账号、订阅的库表。canal启动后向主库请求binlog并解析增量。

数据处理。canal收到binlog形成书屋事件组,并交给canal Adapter或kafka消费。使用canal Adapter直接连接ES,将数据写入索引。使用kafka时,canal server将事件发给kafka,由logstash更新ES。

数据一致性的保证。canal捕获并按事务分组推送事件,ES端用唯一id作为文档id,避免重复。canal客户端在消费消息后回复ack提交位点,保证一致。消费端在收到事件后,先写入本地存储,然后发送ack,再异步将记录推入ES,保证一致。

多大的表需要分表?(腾讯音乐)page 1

有一个经验值2000万的数据量要进行分表,但这是一个经验值,并非innodb硬性限制。

innodb内部用B+树管理数据索引,理想情况B+树高度保持3层,查询时只需经过根节点和两层索引定位数据。

另外,表中每行数据的大小、字段数量和字段类型会影响一个16KB的页能存储的记录数。不同的行大小,理论上3层的B+数能存放的记录数从百万到5亿条不等。因此要结合具体场景分析。

分库分表:分买家库、卖家库,如果设计分库分表键可以快速对应到买家表/卖家表?(淘天) page 26

主键生成。在订单主键嵌入分片信息,订单id包含买家id。

引入中间表。订单表按照订单id分库分表,然后建立买家id和卖家id和订单id的中间表。卖家查询时,在中间表根据卖家id找到订单基本信息,再定位到买家id。缺点是中间表的写入压力大。

二次分库分表。复制数据,针对卖家查询二次分库分表。将部分关键字段数据复制到一个新数据源中,按照卖家id分库分表。缺点是有数据同步一致性问题。

用中间件支持查询。引入ElasticSearch,只同步订单中与搜索相关的字段,避免同步大字段如text。

广播查询作为兜底方案。在所有分库分表上查询。

讲讲MySQL为什么有时候会选错索引?page 1

扫描行数估算不正确。优化器在执行SQL前,用统计信息如索引基数估算扫描行数,但当索引统计信息没有及时更新时,估计值可能偏大。

索引使用与回表权衡。如一个执行计划要频繁回表,尽管索引本身扫描行数少,总体成本可能较高。有时优化器因为错误估计回表代价,导致执行计划性能不好。

解决方案。1.修正统计信息,用analyze table重新统计索引信息。2.强制走索引,force index。3.改写SQL或调整索引设计。调整查询条件,新增删除索引。总的来说,当执行速度不符合预期时,依次排查,先用EXPLAIN FORMAT=JSON查看执行计划,判断行数是否正常,如异常,执行analyze table更新统计信息,如问题未解决,用force index或改写SQL引导优化器选择合适索引。

讲讲怎么给字符串字段加索引 page 1

用前缀索引。

分析索引选择性。为选取合适前缀长度,用SQL语句评估不同前缀长度区分度。统计distinct值的数量。如果前缀索引不满足需求,可用反向索引。

分库分表的底层原理了解吗?(腾讯)

为什么单表数据量过大会查找效率变慢?为什么磁盘查找会变高?(字节)

  1. 单表数据量大的影响
  • 数据量增加 :随着单表数据量的增加,数据库需要处理的数据量也会随之增加。
  • 索引效率下降 :索引是为了加速查询而存在的,但当数据量过大时,索引的维护成本会变高,查询效率会下降。

    1. 查找效率变慢的原因
  • 索引失效 :当数据量过大时,索引可能无法完全加载到内存中,导致索引失效,查询需要进行全表扫描。
  • 磁盘I/O增加 :全表扫描意味着需要从磁盘读取大量数据,磁盘I/O操作的次数和时间都会增加,导致查询效率变慢。

    1. 磁盘查找变高的原因
  • 磁盘碎片 :随着数据量的增加,磁盘碎片也会增加,进一步增加了磁盘查找的时间。

读写分离如何实现?

将主库的变更复制到从库。首先在主库上通过log-bin配置文件启用二进制日志,然后再从库上通过relay-log配置启用复制,最后在从库上执行Change master to 命令指向从库。

为什么要分库分表?有哪些常⻅的分库分表⼯具?

首先要提升性能,减少单表数据量。然后提高可拓展性,实现 更高的并发 ,最后是 提高可用性 ,即使某个库出现问题,也只会影响部分业务。

常用的分库分表工具有MyCat,基于阿里的Cobar架构。以及shardingsphere,Apache的开源分布式db中间件。

怎么在服务器不停机情况下进行平滑分库分表?(大表需要迁移到另外一个数据库,给出能想到的方案)(数据库大表数据迁移怎么做?)(美团)page 26

双写方案。1.同步数据。新库作为旧库的从库,通过binlog或canal增量同步。2.业务改造。 先关闭新库同步 ,再修改业务代码,写操作同时写入旧库和新库,新库写入可以采取异步方式,同时 记录写入失败的数据 ,便于后续补写。3.数据校验。由于关闭新库同步和开启双写的衔接问题可能产生数据不一致,采用抽样或分批校验数据一致性。4.灰度切换。先将部分读流量如10%切换到新库,逐步放大到50%再到100%,保障过程稳定性。5.回滚。若新库出现问题,回滚到旧库。

级联同步方案。1.多级同步。新库配置为旧库从库,同步;同时再配置一个备库作为新库的从库。2.切换流程。首先切换读流量到新库,待数据写入完全一致后,在业务低峰期 暂停写入 ,再将写流量切换到新库。3.回滚。新库存在问题,先将读流量切换到备库, 暂停应用写入 ,将写流量切换到备库,这样所有流量都切换到了备库。

缓存的迁移:1.Redis的数据迁移可以用双写或级联同步。2.memcached的同步可以用副本组预热。

深度分⻚如何优化?(shopee,快手)page 78

深度分页指的是当查询结果集非常庞大时,用户请求的是结果集的某个较深的页面。

用where结合主键id排序,如select * from where id 大于上次最大id orderby id limit 页大小。缺点是需要维护offset,并保证排序字段唯一,且单调递增。

用覆盖索引和子查询。延迟关联,先在子查询仅查询主键,再根据主键关联回原表获取其他列。select 某某 from inner join (select id from orderby id limit offset, 页大小) on 条件。

数据冷热分离如何做?page 85

冷热数据的识别,如几年前的日志、过期订单或者访问量低的数据等,当做冷数据。

数据库设计,热表存储近期的数据,冷表存储历史数据。

冷数据的迁移 ,可以用xxl-job分布式调度平台定时扫描数据库,找出冷数据,批量复制到冷库,然后从热库中删除。或者用rocketMQ异步迁移。

内连接和外连接区别?(网易)page 85 page 26

内连接不保留任何表中的未匹配记录。 外连接,保留至少一个表中的未匹配记录。

on和where区别?(网易)page 84 page 26

on子句用于join,on指定两个表之间如何匹配数据。如果使用外连接如left join,把条件写在on中会导致条件失效。

where子句在所有表join连接完成后,对整个结果集过滤,如果使用外连接如left join,把条件写在where中会过滤记录。

参考:https://www.runoob.com/w3cnote/sql-join-the-different-of-on-and-where.html <!– on用于连接操作,where用于过滤结果集。 on在连接操作时先执行,where在连接操作后执行。

在使用left join时,不管on中的条件是否为真,都会返回左边表的记录。

where是临时表生成后,再进行过滤,这时没有left join的含义,不是必须包含左边的记录。 –>

SQL里having和where区别?(shopee,字节)page 84 page 24

where在数据分组之前对行过滤,where子句不能用聚合函数。

having在对数据分组之后,对分组结果过滤,having子句可以用聚合函数。

char 和 varchar 的区别是什么?

char固定长度,如char10不管存的是什么都是10个字符空间,varchar可变长度,如varchar10,如果存储abc,只用3个字符空间。char的性能更好。

decimal 和 float/double 的区别是什么?存储钱应该⽤哪⼀种?

float 和 double 是浮点数,适合需要处理非常大或非常小的数值的场景,但它们可能会有舍入误差。decimal 是定点数,可以提供精确的十进制表示,适合需要精确计算的场景。存储钱应该使用decimal。

为什么不推荐使⽤ text 和 blob?

TEXT 和 BLOB 类型字段的大小可能非常大,导致索引性能下降。MySQL允许对这些字段建立全文索引,但由于字段内容的体积,建立和维护这些索引的开销较大。

为什么索引能提⾼查询速度?

索引可以 减少磁盘I/O操作 ,因为它只要读取索引部分而不是整个数据表, 全表扫描非常耗时 。另外,索引可以加速排序操作order by子句,因为索引本身是排序好的数据结构。

redis

Redis为什么快?(为什么用Redis)(滴滴考过,得物)page 84 page 56

redis底层用哪些措施保证读写效率高?(shopee)page 56

首先Redis 的大部分操作都在内存中完成,并且采用了高效的数据结构,因此 Redis 瓶颈可能是机器的内存或者网络带宽,而并非 CPU。

其次Redis 采用 单线程模型 可以避免了多线程之间的竞争,省去了多线程切换带来的时间和性能上的开销。

最后,Redis 采用了 I/O 多路复用机制处理大量的客户端 Socket 请求,IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听 Socket 和已连接 Socket。采用reactor模型。

单线程模型有什么缺点,有什么不太适用的场景(字节)

在有多个业务逻辑有先后顺序的场景用单线程串行执行不太适用。如对账系统,他的查询订单,查询物流单,对账操作,写入差异库业务功能如果用单线程模型,则没有考虑业务逻辑之间的依赖。比如查询订单和查询物流单他们是没有依赖的,可以用两个单独的线程处理。其次查询订单,查询物流两个操作也可以和对账操作,写入差异库并行,因为当进行对账操作时,可以同时去执行下一轮的查询操作。

Redis 是单线程的,多线程能利用多核,这能否弥补因线程切换产生的上下文开销?(字节)page 1

单线程能避免锁竞争。

reactor具体什么样的结构?有哪几种?redis是哪一种(腾讯)page 84

reactor的工作原理:

事件循环event loop:redis主线程运行一个事件循环,监听事件,如连接事件,读写事件。

事件分发器event demultiplexer:事件发生,他将事件发送给处理器。实现包括select、poll、epoll。

事件处理器event handler。负责业务逻辑。

有单线程reactor,多线程reactor,主从reactor。单线程是一个负责分发和处理,而多线程是主线程负责监听和分发,其他线程负责处理。主从是主reactor负责接收连接,分配给从reactor进行处理。redis是单线程reactor。

为什么MySQL不能用单线程跟redis一样快?(shopee)page 84

数据处理方式不同:1.redis是内存数据库,redis用单线程模型,通过事件驱动避免多线程的锁竞争和上下文切换开销。2.MySQL是关系型数据库,要处理复杂的查询,事务,索引等操作。这些操作涉及大量磁盘IO和数据结构维护,单线程模型无法充分利用多核CPU性能。

数据持久化:1.redis持久化通过异步方式进行的,不会阻塞主线程。2.MySQL数据持久化涉及到复杂的日志记录和事务管理,单线程模型无法高效处理这些操作。

缓存穿透和缓存击穿有什么区别?缓存雪崩和缓存击穿有什么区别?分别怎么解决?(讲讲缓存穿透)(讲讲缓存击穿)(讲讲缓存雪崩)(滴滴考过)page 141

缓存穿透:1.定义:用户请求的key在缓存和数据库中都不存在。2.场景:攻击者利用不存在的key发起恶意请求。3.解决方案:布隆过滤器,在请求到达前过滤明显不存在的key;缓存空对象,将查询结果为空的请求缓存。

缓存击穿:1.定义:一个热点key的缓存突然失效,此时大量请求直接访问数据库,导致数据库压力骤增。2.解决方案:首先是用分布式锁,缓存失效时,加锁使得同一时间只有一个线程查询数据库,重建缓存。其次是预先更新缓存,在热点数据即将过期前提前更新缓存。最后是不给热点数据设置过期时间,并结合后台异步更新策略刷新。

缓存雪崩:1.定义:大量缓存在同一时间失效,导致大量请求直接打到数据库上,引发数据库崩溃风险。2.场景:大量key使用相同的TTL。3.解决方案:(1)TTL随机:设置不同的缓存过期时间;(2)多级缓存,一级缓存失效使用二级缓存;(3)限流降级,对突发请求限流。(4)后台更新机制,用定时后台线程主动更新缓存,而不是缓存过期后业务线程更新,同时如缓存系统因内存不够踢掉数据时,业务线程可以用消息队列通知后台线程刷新缓存。

缓存穿透怎么解决?缓存击穿怎么解决?缓存雪崩怎么解决?(重要)

缓存穿透可以通过缓存空对象和布隆过滤器解决。

缓存击穿可以通过设置热点数据 不过期 以及 分布式锁 解决,分布式锁避免对同一资源并发读写竞争。

缓存雪崩可以通过缓存数据 随机过期时间 解决,并通过redis哨兵实现高可用。

缓存穿透如果用空值法的话,如何避免大面积的内存被白白占用?(美团)page 35

短TTL。有大量请求命中空值时设置短的过期时间。

布隆过滤器预检。对不存在的数据,布隆过滤器直接返回空值。

缓存击穿已经发生了怎么办?(携程)page 83

快速重建缓存:仅允许一个线程查询数据库重建缓存,其他请求等待,用分布式锁(如Redis setnx或redlock)保证互斥。

兜底策略:如业务容忍,返回旧数据,即软过期数据。

为什么布隆过滤器可以解决缓存穿透的问题?(网易)page 83

快速判断数据是否存在。当请求到来时,布隆过滤器可以快速判断该key是否存在于数据集合中,如果不存在,则直接拦截请求,避免进入缓存和数据库层。

降低数据库访问压力。缓存穿透是用户请求的key在缓存和数据库中都不存在。利用布隆过滤器可以过滤掉这部分请求。 <!– 原理。通过多个哈希函数将元素映射到位数组中,将对应位置的位设置为1。

怎么解决缓存穿透。 预先过滤。在查询缓存之前,通过布隆过滤器判断数据是否可能存在。不存在直接返回。 –>

import java.util.BitSet;
import java.util.Random;

public class BloomFilter {
    private BitSet bitSet;
    private int size;
    private int[] hashSeeds;

    public BloomFilter(int size, int[] hashSeeds) {
        this.size = size;
        this.bitSet = new BitSet(size);
        this.hashSeeds = hashSeeds;
    }

    public void add(String key) {
        for (int seed : hashSeeds) {
            int hash = hash(key, seed);
            bitSet.set(hash % size, true);
        }
    }

    public boolean mightContain(String key) {
        for (int seed : hashSeeds) {
            int hash = hash(key, seed);
            if (!bitSet.get(hash % size)) {
                return false;
            }
        }
        return true;
    }

    private int hash(String key, int seed) {
        int hash = 0;
        for (char c : key.toCharArray()) {
            hash = hash * seed + c;
        }
        return Math.abs(hash);
    }

    public static void main(String[] args) {
        int[] seeds = {31, 131, 1313};
        BloomFilter bloomFilter = new BloomFilter(10000, seeds);
        bloomFilter.add("existingKey");

        System.out.println(bloomFilter.mightContain("existingKey")); // true
        System.out.println(bloomFilter.mightContain("nonExistingKey")); // false
    }
}

布隆过滤器产生碰撞怎么办?(腾讯)page 156 page 24

布隆过滤器的设计天然就得容忍碰撞。布隆过滤器用多个哈希函数将一个元素映射到一个位数组多个位置,不可避免多个不同元素映射到同一位上,这就是碰撞。

解决:1.优化参数:选择合适的位数组长度m和哈希函数个数k,存在公式k=(m/n)ln2使误判最小,n为预计插入元素数量。2.选择合适哈希函数。

现有一个爬虫,反复爬取冷门数据,造成热数据在缓存中剔除,导致缓存雪崩问题,你有什么方案?(shopee)page 83

数据隔离分区。1.逻辑隔离。将冷数据和热数据分区存储,使用不同缓存实例或不同命名空间。冷数据的缓存不影响热数据。2.物理隔离。将热数据放在内存更充裕、性能更高的缓存中,冷数据放在另一个缓存中。

优化TTL策略:1.差异化TTL,为热数据设置较长TTL,冷数据设置较短TTL。2.随机过期时间,TTL增加随机性,避免同一时间大量数据同时过期导致缓存雪崩。

限流与请求过滤。1.限流,对爬虫的访问做限流,如用IP、user agent方式限制。2.不缓存冷数据。对那些明显是爬虫产生的冷数据请求,不缓存直通后端查询。

优先级淘汰策略。为热数据设置更高的优先级。 <!– 冷热数据分离:将冷门数据和热门数据分别存储在集群不同实例上,避免冷门数据占用热门数据的缓存空间。

限流与熔断:对爬虫请求进行限流,防止过度消耗系统资源,可以用Guava的RateLimiter实现。同时设置熔断机制,当缓存系统负载过高时,自动熔断爬虫请求,可以用Hystrix,Resilience4j实现。 –>

服务熔断,降级,雪崩的区别?(腾讯)page 1

雪崩是局部服务或资源响应缓慢不可用,导致调用方线程阻塞,进一步拖垮整个系统连锁故障。被依赖的服务响应发生延迟,导致调用者积压请求,最终整体可用性崩溃。

熔断是在调用远程服务时,监控失败次数,超过阈值后断开对该服务的调用,直接返回错误,快速失败fail fast。用有限状态机实现,有三个状态,关闭(调用远程服务)、半打开(尝试调用远程服务)、打开(返回错误)。

当调用失败次数累积阈值,熔断从关闭态切换到打开态。如成功调用一次,重置失败次数。当熔断处于打开态时,启动一个超时定时器,超时进入半打开态。当熔断处于半打开态时,请求可以达到 后端服务,累积成功次数后状态切换到关闭态。如出现失败则进入打开态。

降级是在检测到系统或依赖服务存在风险时,放弃部分非核心功能或降低功能质量,保证核心业务可用性。形式有两种。1.开关降级,代码中埋点,根据配置中心的降级开关动态开启关闭非核心功能。2.返回默认数据,限制流量,异步写入,降低频率等策略。

redis主从复制的原理了解吗?怎么实现的?(得物,作业帮)page 82 page 24

同步连接与初始化。从节点连接到主节点,并发送一个PSYNC命令请求数据同步,这个过程有两种情况。1.全量同步,第一次连接或断开时间较长,此时,主节点fork一个子进程,生成rdb,发送给从节点。2.部分同步,从节点短时间重连,且主节点复制积压缓冲区replication backlog仍保存了断开期间的写命令,则主节点只传送缺失部分命令。

持续数据同步。1.命令传播,同步完成后,主节点持续将收到的写命令异步发送给从节点,主节点发送写命令后不会等待从节点确认。

redis主从如果redis 主节点完成操作了,从节点还没同步,主节点就挂了怎么办?(腾讯)page 58

异步复制。redis默认异步复制,意味着写操作一旦返回成功,可能实际上还未传播到所有从节点,主节点挂掉,新选举的主节点将缺失这部分数据。

解决方案。用wait。写操作后,调用wait命令等待指定数量从节点确认收到数据。

设计的权衡。异步复制是在性能和数据一致性的权衡,如对数据一致性要求高,可以用wait命令或用其他中间件,但会对性能造成影响。

redis 高可用的几种手段讲一下。(百度,得物多次考)page 82

主从复制:Redis原生支持,将数据异步复制到从节点,主节点故障,从节点也能提供部分数据服务。但单纯的复制方案不能自动完成故障切换,需要手动干预。

哨兵模式:1.监控,监控主从节点状态。2.通知,当发生故障时通知系统管理员。3.自动故障转移,主节点失效时,自动从从节点选举出新主节点,并让其他从节点重新复制。4.服务发现,客户端可以通过哨兵获取当前可用的主节点地址。

集群。集群实现了数据分片和高可用。1.数据分片,将数据按照槽分布到不同节点上。2.节点复制,每个主节点可以配置一个或多个从节点。3.当集群某个主节点挂掉时,从节点自动升级为主节点。4.无中心化。集群内部通过gossip进行通信和状态共享,避免单点故障。 <!– 主从复制:主节点负责写,从节点负责读。主节点故障时,可以将从节点升级为主节点。

哨兵模式:他是个分布式系统,用于 监控redis主从节点的状态 。当主节点出现故障时,哨兵会自动进行故障转移。哨兵还负责监控从节点状态,保证高可用。

集群模式:它是分布式解决方案,通过分片将数据分不到多个节点上,每个节点负责一部分数据。集群模式,每个节点都可以处理读写请求,某个节点故障时,集群自动进行故障转移。 –>

Redis是AP还是CP的?page 1

redis是CP的,保证一致性,分区容错性,牺牲可用性。在发生网络分区时,redis会拒绝服务。

redis主从模式相关的命令知道哪些吗?如何配置(腾讯)page 82 page 24

常用命令:

1.slaveof或replicaf将当前实例设置为某个主节点的从节点。解除主从关系,执行slaveof no one。

2.info replication查看当前实例的复制状态。

3.role判断当前Redis实例的角色。master slave或哨兵。

4.wait在主节点上至少等待指定数量的从节点确认写操作。

配置方式是在从redis.conf文件中配置。

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:b176b97aaf854e8a60247e9bb0c53a65b4cc3905
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

redis开启集群模式之后如果全挂了怎么继续保证可用性?(腾讯)page 82

多机房/多可用区部署。将redis集群分布在不同的物理机房或可用区中,避免单个数据中心的故障导致全局中断。

备份。配置rdb aof持久化数据。冷备份和热备份,在不同地理位置部署冷备份或热备份集群,主集群故障时迅速切换。

自动化运维。用k8s和运维工具,实现故障自动检测,系统自动重新调度资源。

z set为什么采用跳表,而不使用哈希表或平衡树实现呢?(Z Set为什么使用跳表实现)(网易)page 82

Redis 为什么⽤跳表,⽽不⽤平衡树、红⿊树或者B+树?

有序性。哈希表虽然O 1查找效率,但本身无序,无法支持按分数排序。跳表天然支持有序,在O log n完成插入删除查找。

实现复杂度。平衡树虽然是有序的,O log n的操作,但需要处理旋转和复杂的平衡调整。跳表的实现简单。

跳表索引结构是什么样的?(跳表的结构是什么样子的)(百度,作业帮多次考,美团,shopee)page 42

跳表怎么实现的?(B站)page 21

跳表怎么搜索/查找的?(百度)

跳表的结构是什么样的?(哈啰)

跳表的查询过程?(高德地图)

多层链表结构。底层链表是完整有序链表。上层节点只保留下层链表部分节点,这些节点作为索引。每个节点通过多个Forward指针连接到同一层的后续节点。

查找过程。从顶层开始,沿着该层foward指针前进,直到遇到下个节点的值大于目标值。当前层无法向前查找时,下降到下一层。

跳表的时间复杂度为什么是 O logn(高德地图)

可以证明跳表的高度是Log n,且每层节点的平均查找次数是O 1级别。

redis的跳表,什么时候查询效率最低?key要怎么设计可以避免查询效率低的情况(拼多多)page 81

退化为单链表。当跳表中大部分节点的层级都仅为1时,跳跃指针失效,查询只能顺序遍历所有节点,退化到O n。

key设计。1.数据分片,避免所有数据存储在一个有序集合中。(1)根据业务场景拆分,将不同模块、类型、区域的数据存储在不同key下。(2)以及根据时间维度拆分,采用YYYYMMDD格式按日期拆分。2.score设计。保证唯一性,避免大量元素用相同score,如业务允许,在score中添加微小偏差,如附加时间戳。 <!– 参考。待定。

大量元素有相同的分数score,这些元素在跳表同一级上形成较长的链表。 –>

什么具体场景选择跳表?跳表有什么优势?(百度)

适用场景。1.实时数据,需要频繁对数据进行动态更新,同时保持数据有序性,如排行榜、实时统计。2.并发场景,跳表结构简单,容易实现细粒度锁。

跳表优势。1.高效平均性能,查找插入删除平均O log n。2.实现简单,相对于平衡树,不需要复杂旋转。 <!– 场景选择:

有序数据集合:适合用于需要频繁进行范围查询和有序遍历场景。

动态数据:适合需要频繁插入和删除的动态数据。

优势: 查找插入删除O logn。

实现简单。

空间复杂度可控O n。 –>

在数据量比较少时,跳表相对于其他数据结构缺点是什么?(网易)page 81

内存开销高。跳表每个节点需要维护多个层级指针,数据量小时,开销相对于实际数据不经济。

缓存局部性差。跳表节点分散在内存中,缺乏连续性,降低cpu缓存命中率。 <!– 空间复杂度。跳表在数据量少时,空间复杂度显得比较高。因为跳表需要维护多层索引,即使数据量不多,也需要为每一层分配空间。

插入删除复杂度。虽然跳表插入删除平均复杂度是Log n,但是在数据量少时,可能并不比其他数据结构如链表的O 1有优势。 –>

设计一个抖音视频热度排行榜,假设存量两百亿,日增量一千万?(字节)page 81

微博热搜,根据热搜词做排行榜怎么做?排行榜支持top10查询。(shopee)

数据指标。1.评分公式:$score=\alpha \ast 播放数+\beta \ast 点赞数+\gamma \ast 评论数+\delta \ast 分享数-\lambda \ast 视频年龄$。2.数据指标来源。用户对视频的行为通过埋点实时上报到消息队列。

架构设计。1.数据采集层,消息队列kafka。接收每天一千万新视频行为数据,以及两百亿存量的更新。2.实时计算层。(1)流式处理。flink实时消费消息队列数据,更新策略可采用近似计算,热度较高的保持精确计算。(2)缓存热榜,redis z set。将实时计算后的热门视频写入z set。将热门视频数据根据视频类别、地域分散到多个z set,每个z set只负责维护部分数据实时热度。redis Cluster自动进行数据分片和故障转移。 3.离线批处理层。spark,针对全量数据进行周期性离线计算,结果写入HBase。 <!– 使用z set,将 视频id 作为元素,视频的热度作为分数,可以实时更新排行榜。但是,如果日增量非常大如1000万,直接添加到z set可能导致性能问题,因为z set的插入复杂度是O log n。

流量压力 的考虑 为了应对这种情况,可以考虑以下几种优化方案:

  1. 分片 :将 z set 分片到多个Redis实例上,每个实例负责一部分数据。这样可以分散流量压力。
  2. 批量处理 :将日增量数据先缓存起来,然后批量插入到 z set 中,减少单次插入的次数。
  3. 增量更新 :只更新发生变化的视频热度,而不是全量更新。这样可以减少不必要的操作。 –>

z set 的应⽤场景是什么?项⽬哪⾥⽤到了?

z set 类型用于排序场景,比如排行榜、电话和姓名排序等,也可以用于实现延迟队列,延迟是指把要做的事推迟后再做,z set有score属性存储延期时间。比如hash单独一个key的过期时间可以用z set。

直播打赏榜单用redis哪种数据结构?(拼多多)

用redis的z set。 为什么要用z set,z set每个元素有一个分数,可以根据分数排序,这对直播打赏合适,金额就是分数。 将uid作为元素,打赏金额作为分数,插入z set,如用户a打赏100元,我们可以执行 zadd live_reward 100 live_rewardA 的操作。 获取榜单可以用zrevrange命令,从高到低获取排名靠前用户,如zrevrange 0 9 withscores获取前10名用户。

Redis几种数据类型/数据结构怎么实现的?(讲讲Redis几种数据类型底层原理)(网易,蚂蚁,百度,作业帮,shopee,快手,阿里)page 81

redis数据结构的应用场景(京东,拼多多,快手)page 81

redis有五种数据类型,String List Hash Set Z set。String的实现是基于SDS的,拼接字符串不会造成缓冲区溢出。List基于 quick list 实现,quick list是双向链表,内部有多个节点,每个节点是一个zip list或者linked list。hash基于list pack或哈希表实现。set基于整数集合或哈希表实现。z set基于list pack或跳表实现。

String:存储简单键值对;存储用户会话信息;计数器统计网站访问量。通过setnx实现分布式锁。

List:消息队列;任务队列。

Set:存储唯一性统计,如网站pv,以及抽奖去重场景。

Z set:排行榜功能;延迟任务调度。

hash:存储用户信息,用户的属性作为哈希字段;分布式会话管理。

bitmap:记录大量用户的状态,签到、在线状态统计。

讲一下zset,移除某个成员用什么方法?添加用什么方法?怎么判断一个成员在不在某一个集合当中?(京东)

移除成员,z rem key member。

添加成员,z add key score member。

判断是否在z set中,z score key member,如果存在则返回他当前score,不存在则返回空。或者用z rank key member,他以score从小到大排序,返回member的索引,从0开始,如果不存在则返回空。

redis中Z Set存储元素数量在多少时会从ziplist切换到skiplist?(redis中Z Set存储元素数量在多少时会从ziplist切换到跳表?)(拼多多)

当元素个数大于128个时或任意元素的字符串长度超过64个字节则切换为跳表。

redis中string长度限制为多少(网易)page 80

最多512M字节数据。

redis相关数据结构,为什么每种数据类型一般都有两种数据结构?(哈啰)page 80

为了满足不同场景,比如List可以用linkedlist和ziplist。

内存优化。数据量小时,redis选择紧凑的编码方式,减少内存开销。

性能。当数据规模扩大时,redis自动将数据结构转换为更适合大数据量的版本。 <!– 性能优化:不同数据结构在不同操作上有不同性能。比如双向链表插入删除高效,而压缩链表存小数据更节省内存。redis可以根据场景选择最优数据结构。

灵活性:开发者可以根据需求选择合适的数据结构。 –>

redis cluster和redis 哨兵的区别是什么?(百度)page 80

目的。1.redis 哨兵。(1)高可用和自动故障转移。当主节点故障时,自动故障转移,选出新主节点,通知客户端更新连接信息。(2)不支持数据分片,每个redis实例存的数据是完整的。2.redis Cluster。(1)数据分片,Cluster将数据自动分片到多节点上,实现水平扩展,同时,内置故障转移机制。(2)集群感知客户端。客户端需要支持集群模式,因为访问可能遇到槽重定向。

数据分布。1.redis 哨兵数据不分片。2.redis Cluster将数据按照槽分布到不同节点。

故障转移。1.redis 哨兵通过独立哨兵进程监控主从状态,主节点不可用通过投票选举新主节点,故障转移是独立于业务节点的外部监控方式。2.redis Cluster内置自动故障转移机制,每个节点既扮演主节点也可以有从节点,集群内部协同完成故障检测和切换。 <!– redis cluster主要用于分布式存储,通过分片sharding将数据分布在多个节点上,提供高可用和横向扩展能力。

哨兵用于高可用管理,监控redis实例状态,自动进行故障转移fail over。

数据分布。redis cluster数据分片存储在多个节点上,每个节点负责一部分数据,客户端可以直接与集群中任意节点通信,节点间通过gossip协议交换信息。

redis 哨兵不涉及数据分片,管理单个或多个redis实例的可用性。

故障处理。redis cluster当节点故障时,集群会自动进行数据迁移和重新分片。 redis 哨兵当主节点故障时,会进行故障转移,选举新的主节点,并将从节点切换到新的主节点。 –>

讲一讲redis分片算法(shopee)page 75

客户端分片。1.取模算法,将key经哈希函数计算后,再对节点数量取模得到目标节点。2.一致性哈希,如ketama,在节点增减时减少大规模数据重新分配。

redis Cluster分片。redis Cluster是redis内置的方案,将整个key划分为16384个哈希槽。1.哈希槽计算,对key用crc16算法,对16384取模得到哈希槽编号。2.槽分配,每个节点负责部分哈希槽,客户端请求时,只需计算key的哈希槽,然后将请求重定向到负责该槽的节点。3.容错,redis Cluster用gossip协议维持通信。

一致性哈希具体步骤:构建一个哈希环,将redis实例和数据键值映射到这个环上,当要查找数据时,沿着环顺时针找到最近的实例。

虚拟节点:解决一致性哈希实例分布不均问题,每个物理实例对应多个虚拟节点,从而均匀分布数据。

范围分片:根据键值范围将数据分配到不同实例上。

redis一致性哈希如何处理数据倾斜问题?(腾讯)page 22

虚拟节点,每个物理实例对应多个虚拟节点,均匀分布数据。

z set的时间复杂度是什么?page 86

跳表的时间复杂度?(作业帮)page 86

z set如果元素比较多,那么用跳表实现,跳表的增删查的时间复杂度都是O log n,但是 最坏情况下是o n ,这时没有建立有效的索引。

本地缓存和分布式缓存有什么区别?如何选择?pege 74

redis和本地缓存有什么优缺点?(58同城)

存储位置。本地缓存存储在应用程序所在进程内存中,数据仅对当前实例可见。分布式缓存存储在独立缓存服务器中,通过网络供多个实例共享。

数据一致性。本地缓存适用于更新不频繁场景,分布式缓存保证全局一致性。

拓展性。本地缓存受限于本机内存,某个节点故障会导致全部数据丢失。而分布式缓存用redis Cluster实现自动分槽和自动故障转移。

如何选择。业务规模,对于单体应用可以用本地缓存,对于复杂的系统选择本地缓存加分布式缓存双层缓存架构。

多台机器使用本地缓存缓存同一数据,某一台机器更新数据之后其他机器怎么感知?(本地缓存之间的一致性怎么保证?)(腾讯)page 58

缓存失效策略。当某机器更新数据后,发送通知(如消息队列)告诉其他机器使他们本地缓存失效。失效后,下次请求会重新加载最新数据。

本地缓存有哪些缺点?page 74

数据一致性。在分布式系统上,每个节点用独立的本地缓存,当数据库更新时,不同节点缓存无法同步。

内存资源消耗。本地缓存存储在应用服务器内存中。缓存数据量大时,会影响服务器整体性能。

拓展性差,每个节点独立管理自己的缓存。

什么是多级缓存?为什么要用?

多级缓存是本地缓存+分布式缓存的多级缓存方案。使用的原因是本地缓存的访问速度远大于分布式缓存。

如何保证缓存和数据库数据的一致性?(缓存和数据库数据不一致怎么办?)(怎样保证数据一致性)(Redis缓存与MySQL数据一致性)(腾讯)page 103 page 16

MySQL和redis如何保证数据一致性?(怎么保证redis和数据库的数据一致性?)(京东,携程,得物多次考,作业帮多次考,58同城,满帮,字节,好未来)

redis处理数据读取不一致问题,如果出现异常我们不从数据库事务的角度 如何解决?(滴滴)

首先一致性存在两种情况,1.缓存中有数据,缓存中数据值和数据库中的值相同。2.缓存中没有数据,那么数据库中的值必须是最新值。不符合这两种情况的才是不一致问题。

对于读写缓存场景:1.同步直写策略。为保证原子性,在业务层使用事务机制。2.异步写回策略(针对非关键数据)。(1)先更新数据库,再删除缓存。让后续请求缓存未命中时从数据库拉取数据,并重新写入缓存。这种情况极端情况可能出现脏读,但因为缓存速度远远大于数据库,出现概率较低。(2)先删除缓存,再更新数据库。这种方案容易出现场景:删除缓存后,读请求会从数据库拉取数据,如果此时数据库未更新,则可能写入旧数据到缓存中,造成不一致。

延迟双删策略。更新数据库前删除缓存。更新数据库之后,等待一段时间,再删除缓存。

分布式锁。如果有一条数据在频繁更新,采用分布式锁可以保证同一时间只有一个请求在进行数据库更新和缓存操作。

对于只读缓存场景。分为两种情况。1.新增数据,数据直接插入数据库,不涉及缓存。2.更新或删除数据。需要在更新数据库后删除缓存。但如果不是原子的,也会出现先删除缓存再更新数据库和先更新数据库再删除缓存情况。

Redis和MySQL用旁路缓存模式,如果 Redis 删除失败,如何保证数据一致性?(腾讯)

异步重试,将待删除的key发给消息队列,由后台消费者重复尝试删除。重试策略设置最大次数、重试间隔。

旁路缓存模式,为什么不先改数据库数据再去更新缓存呢?(数据在删改操作时,如果不是删除缓存值,而是直接更新缓存的值,你觉得和删除缓存值相比,有什么好处和不足?)(当对数据进行删除或更新操作时,直接更新缓存与删除缓存,各有什么优缺点?)(为什么不是写mysql的时候就把redis写了,而是写mysql再删缓存?)(美团,淘天)page 2

如果先更新数据库,再更新缓存,有两种不一致场景。1.写读并发,线程a先更新数据库,线程b读命中缓存,读到旧值,之后线程a更新缓存成功,后续读请求命中新值。线程a未更新缓存前,短暂读到旧值。2.写写并发,线程a和线程b同时更新同一条数据,更新数据库的顺序是先a后b,但更新缓存的数据是先b后a,这导致数据库和缓存不一致。

讲讲Redis主从复制原理。page 13

第一次同步。1.建立连接和协商。从库发送psync告知主库自己要同步的数据,由于从库还没有主库信息,psync命令中runid,offset为默认值。主库收到后,以fullresync响应,告知从库runid和复制进度。2.全量复制。主库通过bgsave命令生成rdb,从库收到后加载。3.增量复制。在全量复制过程中,主库依然在处理写操作,为防止丢失,主库用复制缓冲区记录从rdb生成后收到的命令。全量复制完成后,主库发送命令给从库。

增量复制。1.长连接命令传播。全量复制后,主库和从库用长连接实时同步写操作。2.repl backlog buffer。为应对网络中断,从库断连重新连接时,主库根据从库当前复制进度(slave repl offset)判断差距,如果差距在环形缓冲区(repl backlog buffer)内,则主库只需发送这段时间的增量命令。

Redis主从集群有延迟,主节点刚存进去数据,还没同步就挂了,这时从节点没有数据, 那怎么办?(Redis主从同步延迟怎么解决?)(Redis主从延迟怎么解决?)(美团)

讲讲Redis哨兵底层原理。(哨兵机制是什么) (美团)page 59

周期性监控。定时任务隔一定时间检测主节点状态。

心跳检测和主观下线。哨兵定期发送ping info命令,如规定时间内没收到响应,将实例标记为主观下线。这个判断是基于单个哨兵的检测。

客观下线。当多个哨兵对同一个主节点判定为主观下线时,通过通信收集各自的判断结果。当投票数quorum达到配置的阈值后,哨兵判定为客观下线,触发故障转移。

故障转移。由于哨兵自身有多个实例参与监控,要选一个哨兵leader负责故障转移,参考Raft协议。1.哨兵用sentinel ask master state to other sentinels函数向所有监控同一主节点的哨兵发起查询。2.投票。候选哨兵向其他哨兵请求投票,其他哨兵作为跟随者一轮只投一票。3.投票时比较哨兵的epoch信息,只有当候选者epoch大于等于自己的epoch,且主节点记录的leader epoch较低时,才投票支持候选者。4.最终超过半数的哨兵成为leader,负责选择从节点成为主节点、修改配置过程。

故障转移。1.选取新主节点。从slave选择一个作为master。选取slave Priority高(数值小)的slave。若优先级同,则选择复制偏移量最大的slave,如果仍相同,则选择runid小的。2.角色转换。leader向选中的slave发送slave no one命令,升级为master。3.之后leader让剩余slave指向新master,用发布订阅机制将master信息发给客户端。

Redis哨兵主库挂了,怎么不间断服务?page 21

检测故障。1.哨兵进程会定期通过 PING 命令检测所有 Redis 实例的健康状态。2.当某个哨兵发现主库的响应超时时,会标记主库为“主观下线”。3.由于存在误判风险,多个哨兵会共同判断,只有当大部分(N 个哨兵中超过 N/2+1 个)都标记主库为下线时,才将主库正式标记为“客观下线”,并触发故障转移流程。

选举新主库。1.优先级(由配置项 slave-priority 控制)筛选,若存在明确的优先级最高的从库,则直接选取为新主库。2.如果优先级相同,则依据各个从库的复制进度进行比较(通常选取复制进度最接近旧主库,也就是 slave_repl_offset 最大的从库),从而确保新主库的数据尽可能接近最新状态。3.如果复制进度也一致,再以实例的ID(通常选择 ID 数值最小的实例)作为最后的判断标准。

通知与切换。一旦选举出新的主库,哨兵会通知其他从库执行 replicaof 命令,重新指向新主库进行数据同步。

讲讲Redis哨兵选举的流程。(腾讯)page 35

故障检测。1.主观下线。每个哨兵节点每秒向所有被监控Redis实例(包括master、slave以及其他哨兵)发送ping命令,如果配置时间内没有收到有效回复,则该哨兵认为此实例主观下线状态。2.客观下线,当一个哨兵检测到master主观下线后,向其他哨兵发送sentinel is master down by addr命令征求意见。如果达到quorum哨兵数一半加1,则客观下线。

哨兵leader选举。1.候选者。每个检测到master客观下线的哨兵都是候选者。2.候选者向其他哨兵发送命令请求投票,包含候选者runid,epoch。

故障转移。1.选取新主节点。从slave选择一个作为master。选取slave Priority高(数值小)的slave。若优先级同,则选择复制偏移量最大的slave,如果仍相同,则选择runid小的。2.角色转换。leader向选中的slave发送slave no one命令,升级为master。3.之后leader让剩余slave指向新master,用发布订阅机制将master信息发给客户端。

Redis哨兵模式中,主节点宕机怎么保证数据是最新的?(腾讯)page 12

用wait命令同步写入。

配置min replicas to write和min replicas max lag。min-replicas-to-write:要求主节点在写时,有至少指定数量的从节点处于健康状态并能及时同步数据。min-replicas-max-lag:指定从节点与主节点之间的最大允许复制延迟。

讲讲Redis切片集群的底层原理(Redis cluster集群的原理)(Redis集群的原理)(Redis的集群方式)(B站,腾讯)page 53

哈希槽机制。Redis集群将数据空间划分为16384个槽,映射过程如下。1.客户端根据key用crc16算法计算出16位的值。2.将值对16384取模,确定槽。

槽与实例的映射。集群启动后,各实例相互交换自己管理的槽信息,形成完整的哈希槽映射表,由客户端用CLUSTER SLOTS 命令缓存。

客户端数据定位机制。客户端发送请求流程。1.根据key计算槽,根据本地缓存的槽和实例映射关系将请求路由到Redis节点。2.若数据不在该节点上,该节点返回重定向命令moved、ask。moved告知客户端槽已永久迁移到其他节点,客户端更新本地缓存并重发请求。ask告诉目标槽正在迁移,此时客户端发送asking命令给目标节点重试。

除了官方的还了解过其他的Redis高可用方案吗?

codis,他用proxy层做请求路由和分片,客户端只感知一个虚拟地址。他支持故障节点自动剔除和迁移,需要依赖Zookeeper管理元数据。

tweproxy。

在Redis Cluster与MySQL分库分表并存场景下,如何设计双写策略以避免数据不一致(淘天)

除了官方的还了解过其他的Redis高可用方案吗? page 1

基于代理的分片方案、跨数据中心多活方案。

方案一,代理。1.Twemproxy,客户端只连接proxy,proxy负责将请求一致性哈希到redis实例。2.codis,也是作为代理,依赖于Zookeeper。

方案二,跨数据中心的多活方案,如dynomite,他是Netflix开源的代理层,能够实现跨数据中心的主从,多个数据中心都可以读写。

微博上面的热搜,打在了集群的分片上,分片扛不住怎么办?(小红书)

整体策略。混合云,结合公有云和自有机房,在流量突增时增配弹性资源。

微服务网格。将热搜拆分成独立微服务,用服务网格Istio统一流量管理。

实时热点检测。建立监控系统动态计算业务链路流量,当访问量超出阈值时触发热度报警,并自动广播给模块,各业务根据预设方案快速进行扩容。

限流。在接入层对请求速率进行限流,用令牌桶算法控制并发量。针对缓存击穿,用分布式锁保证只有一个请求回源加载数据。

Redis集群为什么采用gossip协议同步元数据 page 1

gossip协议体现了Redis的简单的设计理念。

节点发现。gossip协议是点对点的流言传播机制,节点随机选择其他节点交换各自已知的集群状态信息,随交换次数增多,所有节点最终会收敛到一致的视图。例如一个新节点用cluster meet命令加入已有集群后,只需一到两个心跳周期即可让大部分已有节点知晓新节点状态。相比之下,paxos或raft共识协议需要大多数节点参与、进行leader选举和日志复制,开销更大;而如Zookeeper集中式心跳心跳机制虽然开销较小,但引入单点瓶颈。

故障检测。Redis每个节点都定期向少量随机节点发送ping请求,观察可能下线的节点pfail,只有大多数节点用gossip报告某节点不可达才标记为fail。一旦主节点被判定为fail,剩余主节点进行简单的多数投票,将一个副本升级为新主节点。与raft的leader选举相比,Redis的故障切换更简单且没有日志约束,但同样实现了故障恢复。

Redis什么时候用 Cluster 什么时候用主从(高德地图)

如数据量在单机范围内,但QPS较高要用读扩展,用主从。

当数据量和QPS超出单机能力,用集群分摊槽的压力。

讲讲分布式事务的2阶段提交,3阶段提交page 18

2阶段提交。1.准备阶段。协调者向参与者发送prepare请求,询问能否提交事务。参与者收到请求执行本地事务预处理如锁定资源,将结果返回给协调者。2.提交阶段。协调者根据参与者回复判断,如果都回复可以提交,则Commit。如果有一个无法提交,则abort。缺点是协调者阻塞。

3阶段提交。1.询问阶段。协调者向参与者问是否可以提交,参与者返回是否提交。2.预提交。如果都可以提交,协调者不直接提交,向所有参与者发送pre Commit,参与者锁定资源,回复确认。3.协调者发出Commit或abort。

多级缓存一致性怎么保证?(腾讯)

先修改数据库,再删除redis仍然有不一致怎么办?(腾讯)

双删策略。分布式锁。消息队列。

常见的缓存更新策略有哪些?

旁路缓存模式。写先更新db然后直接 删除cache 。读从cache读取数据读到直接返回,没有则找db,再把db 数据放到cache中。

读写穿透模式。写先查cache,cache不存在直接更新db,若cache存在则先更新cache然后cache自己更新db。读,从cache读取数据,读到就返回,读不到先从db读,写入cache后返回。

异步缓存写入模式,和读写穿透模式差不多,不过在写的时候如果cache存在则先更新cache,然后异步更新db 。

写db后删除cache是因为cache存放的数据要服务端进行大量计算才能得出,如果频繁修改db,就会导致经常更新cache,而cache可能都没有被访问过。

旁路缓存模式的缺陷有首次请求数据一定不在cache的问题,通过热点数据提前放入cache解决。 另外是写频繁导致cache数据频繁删除,影响缓存命中率。如果是强一致性场景,则更新db时同步更新cache,不过要加一个分布式锁保证更新cache时候不存在线程安全问题。如果cache删除失败,隔一段时间重试,设置重试次数,达重试次数还没成功,则把这个失败的key放到队列中,等这个cache能用后,在把key删除。

什么是 Redis Module?有什么⽤?项⽬使⽤过吗?

Redis Module 是 Redis 4.0 引入的一项功能,允许开发者通过插件的方式扩展 Redis 的核心功能。用处,module可以拓展数据结构和命令,如布隆过滤器,图等数据结构。常见的Redis module有redisearch,redisjson。

如何基于 Redis 实现分布式锁?

redis的setnx可以实现分布式锁的互斥性,如果key存在,set不会成功,返回0。释放锁使用del直接删除key。可以通过设置过期时间防止锁死,如set suo 1 ex 3 nx,3秒删除。结合看门狗机制,如renewExpirationAsync方法实现续期。nx是not exist的缩写,ex是expire的缩写。

MySQL怎么实现分布式锁?page19

用数据库原子性和唯一性约束。

创建锁表。有唯一约束字段,当进程要获取资源的锁时,尝试向表插入一条记录,用唯一性约束 确保同一时刻只有一条记录能成功插入。

释放锁。进程完成共享资源操作后删除记录释放锁。

Redis 可以做搜索引擎么?

可以,采用rediseasrch,可以在redis上实现全文搜索,模糊匹配,排序,过滤等功能。

Redis为什么使⽤ ListPack 替代 ZipList? page 1

ziplist是一种紧凑的编码格式,用于redis的z set和hash的底层实现。在ziplist中prevlen是可变长编码,如新元素长度是超过253字节的,后续元素prevlen要从1字节拓展到5字节,这又会导致元素自身的长度增加4字节,如果他也超过了253字节,再后续的元素也要1变5.。在listpack中只记录当前节点的长度。因此解决了连锁更新的问题。

Redis 批量操作的⽅式有哪些 ?page 1

用scan和del,删除指定模式的key,如”test*“删除以test前缀的key。

用pipeline管道传输。一次性发送多条命令到服务器,减少RTT。

为什么会有 Redis 内存碎⽚?如何清理 Redis 内存碎⽚ ?page 1

内存碎片可以理解为那些不可用的空闲内存。产生的原因是redis的 zmalloc 方法向操作系统申请的内存空间大于数据存储需要的空间,另外当redis某个数据删除时,这个空间也不会释放给操作系统。通过配置config set activedefrag yes清理内存碎片。

缓存预热如何实现 ?page 1

有两种实现方式。

第一种,使用定时任务,比如 xxl-job,来定时触发缓存预热的逻辑,将数据库中的热点数据查询出来并存入缓存中。

第二种,使用消息队列,比如 Kafka,来异步地进行缓存预热,将数据库中的热点数据的主键或者 ID 发送到消息队列中,然后由缓存服务消费消息队列中的数据,根据主键或者 ID 查询数据库并更新缓存。

什么是Redis哨兵? 有什么⽤?page 3

Redis哨兵是Redis内置高可用解决方案,在检测到主节点故障时,自动进行故障转移和配置更新。

健康监控。哨兵定期向主从节点发送ping和info命令。如在down after milliseconds没有响应 标记为主观下线,多个哨兵通信后,达到quorum则认为客观下线。

自动故障转移。哨兵用投票在多个从节点选一个晋升为主节点。考虑从节点优先级slave Priority和复制进度replication offset。

通知和配置更新。用发布订阅将主节点状态变化通知给客户端。

哨兵 如何检测节点是否下线?主观下线与客观下线的区别?

主观下线,节点认为某个redis节点已经下线,但不确定,需要其他节点的投票。客观下线,法定数量,过半的节点认为某个redis节点已经下线,那么他就是真下线了。如果客观下线的是master,那么哨兵中会有一个leader节点负责故障转移。

哨兵 是如何实现故障转移的?

当哨兵检测到主节点故障时,会自动进行故障转移。故障转移的过程:检测故障,选举新的主节点,通知客户端和从节点切换。

哨兵监控主节点。哨兵会定期向主节点发送ping命令检查健康状态,如果某个节点在规定时间内没有响应则认为故障。如果大多数哨兵认为主节点故障则进入故障转移过程。

哨兵会从现有从节点选出一个新的主节点,选举基于raft算法。

哨兵会同通知客户端新的主节点地址。

哨兵 如何选择出新的 master(选举机制)?

slave必须是在线才能参加新的master选举。选举有3个维度。第一个slave优先级,配置replica-priority,手动设置。第二个是复制进度,哨兵总是希望选出与旧master数据差异最小的slave升职为新的master。第三个是运行id,万一有多个slave的优先级和复制进度一样的话,选择runid小的成为新的master。

redis哨兵可以防⽌脑裂吗?page 1

集群脑裂怎么办?(作业帮)1

可以,脑裂就是网络问题,导致主从节点失去联系,从节点选出自己的主节点,等网络恢复,旧主节点会被降级为从节点,再进行同步复制的时候,就会丢失写入旧主节点的数据。

解决方法是 master写入时,必须写入若干个slave ,可以配置min-replicas-to-write x以及min-replicas-max-lag x,设置写master至少写入的slave数量。

例如,设置 min-replicas-to-write 为 1,min-replicas-max-lag 为 10,表示主节点只有在至少有一个从节点在 10 秒内响应时才能写入数据。

为什么需要 Redis Cluster?解决了什么问题?有什么优势?

解决的问题。单点故障。在没有集群的情况下,如果单个redis实例出现故障,则整个服务都会终端,而redis cluster通过分布式架构,将 数据分散到多个节点 上,即使某个节点宕机,其他节点仍然可以服务。

数据容量限制。单个redis实例的内存容量是有限的,当数据量超过单个实例内存时,要用redis cluster拓展存储。

负载均衡。在高并发下,单个redis不能承受大量读写请求,redis cluster可以将请求分散到多个节点上,实现负载均衡。

优势。高可用性。redis cluster通过主从复制和自动故障转移实现高可用。

数据分片。redis采用哈希槽将数据分布到多个节点上,避免单个节点性能瓶颈。

Redis Cluster 是如何分⽚的?

使用槽的概念来管理分片,整个集群分为16384个槽,每个槽包含一定范围的健,redis用crc16哈希算法将键映射到某个槽,redis用Gossip协议传播集群状态信息,如节点状态和槽分配信息。

如何确定给定 key 的应该分布到哪个哈希槽中?

首先计算key的crc16哈希值,假如结果为12345,然后计算槽编号,12345%16384,结果为4597,查找redis集群的槽到节点的映射表,找到负责4597的节点。

为什么 Redis Cluster 的哈希槽是 16384 个?(为什么Redis集群哈希槽是16384个)page 73

哈希槽太大会导致gossip消息太大,消耗太多带宽。

哈希槽总数越少,对存储哈希槽信息的 bitmap 压缩效果越好。

Redis Cluster 的主节点通常不会扩展太多,16384 个哈希槽已经足够用了。

Redis Cluster ⽀持重新分配哈希槽吗?

redis-cli -a pass123 –cluster reshard 已存在节点IP:端口。指定要移动的哈希槽数量以及接收节点的ID。

Redis Cluster 扩容缩容期间可以提供服务吗?

扩缩容期间可以提供服务。提供了 重定向 机制。ASK 重定向:可以看做是临时重定向,后续查询仍然发送到旧节点。MOVED 重定向:可以看做是永久重定向,后续查询发送到新节点。

扩容期间会同步原实例增量数据,扩容过程中实例进入只读状态。

Redis Cluster 中的节点是怎么进⾏通信的?

基于gossip协议通信,节点之间会发送多种gossip信息。meet信息,将一个节点添加进cluster。ping pong信息,交换节点的状态信息,疑似下线状态pfail和下线状态fail。fail信息,a节点发现b节点pfail,并在下线报告有限期内cluster内半数以上节点将b标记未pfail则a广播fail消息通知大家b节点为fail。

Java基础

讲一讲netty的NIO和传统的NIO有什么区别?(netty和NIO区别?)(得物)page 73

抽象层次。1.传统NIO基于selector、Channel、buffer底层api,开发者需要手动管理连接注册、事件轮询、数据读取写入。2.netty封装底层复杂性,提供Channel、ChannelPipeline、ChannelHandler。

性能优化。1.传统NIO要开发者自行设计线程模型。2.netty内部实现了高效事件循环机制,采用boss worker线程模型。boss线程监听连接请求,worker线程处理io操作。

内存管理。1.传统nio用bytebuffer作为缓冲区,容易引起GC压力。2.netty引入bytebuf支持引用计数和内存池化,支持零拷贝。 <!– 设计理念: 传统NIO提供基本非阻塞IO操作,如Selector,Channel和Buffer,是底层API,需要开发者处理很多细节,如事件循环,线程管理。

Netty NIO:基于NIO的高性能网络应用框架,封装了NIO的复杂性,提供更高层次抽象。

性能: 传统NIO:在高并发场景下,开发者需要处理大量细节,容易出现性能瓶颈。

Netty NIO:有内存管理和零拷贝技术提高了性能。 –>

谈谈对 Java 注解的理解,解决了什么问题?

Java 注解(Annotations)是一种元数据(metadata),用于为程序元素(如类、方法、变量、参数等)提供附加信息。注解不会直接影响程序的逻辑,但可以通过编译时或者运行时的工具进行处理,以实现如自动化配置功能。

解决的问题,如 减少样板代码 ,提高代码可读性并与框架结合。

注解的实现原理?page 1

注解本质是一个 继承了Annotation 的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。

Java 反射?反射有什么缺点?为什么框架需要反射?

反射是一种机制,允许我们在运行时检查和操作类,方法,字段等。简单来说,反射让我们在编译时不确定的情况下,动态获取类的信息并调用方法。

反射的优点是可以在运行时动态创建对象调用方法,在spring框架中非常有用。

反射的缺点是性能问题,反射比直接调用方法要差,因为反射涉及到大量的内部检查和动态解析。安全性。反射可以访问和修改类的私有成员,可能会破坏封装性。

为什么框架需要反射。很多框架,使用反射实现依赖注入。通过反射,框架可以在运行时根据配置文件或注解创建和管理对象。

反射在实现aop时也非常重要,他允许我们在不修改源代码情况下,动态的插入横切逻辑。

动态代理的实现方式有哪些?怎么选择对比?

jdk动态代理和cglib动态代理两种方式。 jdk动态代理。通过实现invocation handler接口,并用newProxyInstance方法来创建代理对象。 cglib动态代理。通过生成目标类的子类实现的,使用enhancer创建代理对象。 选择和对比。 目标类是否实现了接口,如果实现了接口,我会优先考虑jdk动态代理,因为他更轻量级。 性能。cglib因为直接操作字节码所以通常比jdk动态代理稍微快一些。 依赖。如果项目中已经引入cglib,或者有其他组件依赖cglib,那我会优先考虑cglib动态代理。

Java 泛型了解么?什么是类型擦除?介绍⼀下常⽤的通配符?

泛型是Java5引入的特性,允许类,接口和方法能操作对象的类型,实现代码的参数化类型。泛型在编译时检查类型,避免运行时ClassCastException。类型擦除,指泛型类型在编译后被擦除,在运行时不存在泛型信息。问号 是无界通配符,可以接受任意类型。问号 extends T,上界通配符,传入的类型必须是T的子类型,上界通配符只能从父类读取数据,不能添加数据。

内部类了解吗?匿名内部类了解吗?

定义在一个类内部的类,匿名内部类是没有名字的内部类,在使用时定义和实例化。应用场景如按钮点击事件处理器。

BIO,NIO,AIO 区别?(满帮)page 73

Java怎么实现网络IO高并发编程?(腾讯)

bio是最传统的io模型,在读写操作时会 阻塞 当前线程,直到操作完成。这意味着如果一个线程在等待io完成时,他不能做其他事情。

nio是java1.4中引入的,支持非阻塞io操作。这意味着 线程在发起io请求后可以继续执行其他任务 ,而不必等待io操作完成。nio引入了 channel 和 buffer 的概念,通过 selector 实现单线程管理多个channel,提高并发处理能力。适合高并发的网络请求场景。

aio是java7引入的,提供了异步io操作。在aio中, io操作会立即返回 ,操作完成后通过 回调或Future对象通知应用程序 。这种方式适合处理长时间io操作,如文件读写或网络传输。

java nio中buffer的三个参数(满帮)page 73

capacity,position,limit。

capacity:Buffer可存储的最大数据量。一旦Buffer创建,capacity就不变了。

position:当前读取或写入的位置。初始position为0,每次读取或写入一个数据后,position会自动增加。

limit:Buffer可以读取或写入的最大位置。写模式下,limit等于capacity。读模式下,limit等于当下的position。

final,finalize,finally之间的区别 page 1

final用于声明变量,不可重写方法,不可继承的类。 finalize是 Object 类的一个方法,用于在对象被垃圾回收之前执行清理工作。 finally用于定义无论是否发生异常都会执行的代码块。

Java并发进阶

线程池处理任务的流程了解吗?(介绍一下线程池工作流程)(讲一下线程池参数)(讯飞,阿里考过,满帮多次考,腾讯多次考,快手)page 107

提交一个任务,如果核心线程没有满,就创建一个线程,如果满了,就是会加入等待队列,如果等待队列满了,就会增加线程,如果达到最大线程数量,就会按照一些丢弃的策略进行处理。

详细说一下拒绝策略(讲讲拒绝策略)(美团)page 1

abortPolicy,默认,抛出rejectedExecutionException,调用者任务直接失败。适合任务不可丢失,要及时fail fast的场景。

callerRunsPolicy,调用线程自己执行任务,有背压效果,即下游处理慢,导致上游阻塞。适合可容忍延迟但不丢任务的场景。

discardPolicy,直接丢弃新提交任务,不抛异常。适合可以容忍丢失的任务的场景。

discardOldestPolicy,丢弃队列最旧的任务,将新任务加入。

自定义拒绝策略,当任务很关键,不可丢失时,应将失败的任务记录到数据库或消息队列,之后补偿执行。具体可实现RejectedExecutionHandler,在RejectedExecution方法中执行落库报警异步补偿逻辑。

现在有一个场景 往线程池提交一个任务但是这个任务里有一个子操作也是往相同的线程池提交一个任务(线程池参数:核心线程 5,最大线程 10,阻塞队列 10,拒绝策略调用提交任务的线程执行)会有什么问题(使用CallerRunsPolicy调用提交任务的线程执行拒绝策略时,如果往线程池提交任务且任务里有子操作,也是往相同线程池提交任务,会有什么问题)(美团)page 1

子操作不会执行,因为子操作是在shutdown方法调用后才提交的,线程池会拒绝他。CallerRunsPolicy策略规定,当线程池已关闭时,直接丢弃被拒绝的任务。

什么情况会用无界队列,什么情况会用有界队列(美团)page 1

无界队列。适合不用背压,生产速率可控场景。

有界队列。适合需要背压,线程池队列达上限用拒绝策略,以及让上游限流、降级。

线程池去做一个未捕获的全局异常处理,该怎么做(百度)page 1

继承ThreadPoolExecutor,重写afterExecute。如果任务执行时直接抛出异常,t不为null,自定义方法进行日志记录。

如果是submit方法提交的任务,异常被封装在Future里,调用get方法解包,拿到异常,自定义方法进行日志记录。

介绍一下线程安全的类(讲讲并发容器)(美团)page 1

并发容器有List,Map,Set,Queue。

List的实现类如CopyonWriteList,适合读多写少场景。

Map的实现类如Concurrenthashmap,jdk 1.8用CAS和synchronized节点锁以及链表过长转为红黑树优化。不支持null key和null value。

Set的实现类如CopyOnWriteSet,他是基于CopyOnWriteArrayList实现的。

Queue的实现类如ArrayBlockingQueue,LinkedBlockingQueue,他们默认无界,最好指定长度,避免OOM。

ArrayBlockingQueue和LinkedBlockingQueue加锁方式的区别(京东)

ArrayBlockingQueue入队和出队共用同一个Reentrantlock,生产者和消费者都一起争抢。LinkedBlockingQueue入队和出队有两个Reentrantlock。

JUC 的Queue容器

Queue是JUC中最复杂的容器,可以从两个维度分类。一个维度是阻塞和非阻塞,阻塞指的是当队列已满时,入队阻塞;当队列已空时,出队阻塞。另一个维度是单端和双端,单端指的是只能队尾入队,队首出队;而双端指的是队首队尾皆可入队出队。juc里阻塞队列用blocking关键字标识,单端队列用queue标识,双端队列用deque标识。

单端阻塞队列,实现有ArrayBlockingQueue,LinkedBlockingQueue等。

双端阻塞队列,实现是LinkedBlockingDeque。

单端非阻塞队列,实现是ConcurrentLinkedQueue。

单端非阻塞队列,实现是ConcurrentLinkedDeque。

AQS 原理了解么?AQS 组件有哪些?(讲讲AQS底层原理)(阿里,小红书,得物,腾讯,美团)page 72

Java中的锁,可重入锁是什么,底层实现(满帮)

参考。

AQS思想是利用一个 volatile 的整数 state 来表示同步状态,用getState,setState操作,线程通过CAS改变state的值,如果成功则获取锁成功,失败进入等待队列,等待被唤醒。并通过一个 FIFO 的双向链表管理获取不到锁的线程。AQS是基于CLH队列的,线程竞争时封装成节点,按照FIFO加入等待队列,每个节点包含线程引用和等待状态如signal condition canceled propagate。

操作方法。1.acquire,tryAcquire。子类如Reentrantlock实现自己的tryAcquire,在状态为0时直接尝试cas获得锁。如果tryAcquire失败,进入acquireQueued,当前节点被包装成节点加入队列。2.release,tryReleasse,用CAS修改state。

CLH 队列线程如何实现阻塞(高德地图)

AQS借鉴了CLH队列思想管理阻塞线程,和CLH不同,AQS自旋若干次后,让线程挂起park。

当线程尝试获取锁失败并入队后,调用shouldParkAfterAcquire方法,决定是否调用park阻塞,主要的过程如下:如果pred waitstatus 是SIGNAL,说明前驱会在释放时通知后继,线程可以安全park;如果pred waitstatus 大于0,前驱已取消,我们要跳过他,重新设置队列关系,然后重试;如果pred waitstatus等于0,则用CAS将前驱状态设为SIGNAL,告诉前驱“释放时要喊我”,再重试。

LockSupport park 和 wait 区别(高德地图)

wait必须在持有对象监视器时调用,并调用后释放监视器;park不依赖监视器,也不检查是否持有锁,不释放锁。

AQS线程怎么睡眠的/AQS怎么定时等待的,用的什么方法?(58同城)

参考。 通过lockSupport park方法使线程进入睡眠状态,用unpark方法唤醒指定的线程。

元空间中有什么?(小红书)

存储类元数据比如类的结构、方法信息、字段信息的区域。 永久代有固定大小限制,容易导致内存溢出,元空间则没有固定大小,他使用的是本地内存,可以根据需要动态扩展。

工作内存是在堆里面还是在栈里面?

工作内存不是直接存在于堆或栈中, 他包含线程执行时所需的数据副本。

在jvm实现中,工作内存通常对应于CPU的寄存器和高速缓存。

工作内存(本地内存)是java内存模型抽象出来的概念,并不真实存在,它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

Java 内存区域和 JMM 有何区别?

Java内存区域包含五个部分,虚拟机栈,本地方法栈,程序计数器,方法区,堆。他是执行Java程序的内存布局。Java内存模型定义了多线程如何进行内存访问的规则。有主内存和工作内存的概念。主内存是共享的,工作内存是独享的。

happens-before 原则说一下

他是一个偏序关系,如A happens before B,那么a的所有写操作在b开始之前对b可见,JMM定义了几条规则,如在一个线程内,顺序的前面的操作先于后面的操作。 对一个锁的解锁先于加锁动作。 对volatile变量的写操作先于读操作。

参考:file:///E:/BaiduNetdiskDownload/024-100023901-%E4%B8%93%E6%A0%8F%E8%AF%BE-%E7%8E%8B%E5%AE%9D%E4%BB%A4-Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%EF%BC%88%E5%AE%8C%E7%BB%93%EF%BC%89/03-%E7%AC%AC%E4%B8%80%E9%83%A8%E5%88%86%EF%BC%9A%E5%B9%B6%E5%8F%91%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80%20(13%E8%AE%B2)/02%E4%B8%A8Java%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B%EF%BC%9A%E7%9C%8BJava%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E5%8F%AF%E8%A7%81%E6%80%A7%E5%92%8C%E6%9C%89%E5%BA%8F%E6%80%A7%E9%97%AE%E9%A2%98.pdf

线程池有什么⽤?为什么不推荐使⽤内置线程池?

提高性能,线程创建和销毁开销大,线程复用可以减少开销。提高稳定性,线程池可以限制最大线程数。

为什么不推荐内置的线程池,如FixedThreadPool,他用了无界队列,如果提交过快,任务会无限积压,导致OOM错误。推荐使用ThreadPoolExecutor自定义线程池,指定核心数,最大线程数,饱和策略。

内置线程池有哪些?(java里常用的线程池有哪些?)(百度,快手,美团)page 1

fixedthreadpool,数量线程固定的线程池。singlethreadexecutor,只有一个线程的线程池。cachedthreadpool,可以根据实际情况调整线程数量的线程池,若所有线程都在工作,又有新任务提交,则会创建新的线程处理任务。scheduledthreadpool,定期执行任务的线程池。

定时线程池,他是周期性执行这个任务,他这种机制底层怎么做的?(Java定时线程池底层原理)(京东)page 1

定时线程池通过scheduledThreadPoolExecutor类实现,继承自threadpoolExecutor。

delayedWorkQueue:特殊的队列,基于堆,存储定时任务,按照任务的执行时间排序。

周期任务调度:当一个任务被提交时,他会计算任务首次执行时间,并放到这个队列中。任务执行后,会重新计算下次执行时间,再放到队列中。

任务的提交:当调用 scheduleAtFixedRate 或 scheduleWithFixedDelay 方法时,任务会被包装成一个 ScheduledFutureTask,并计算首次执行时间。

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
// 每5秒执行一次任务
executor.scheduleAtFixedRate(() -> {
    System.out.println("Task executed at: " + new Date());
}, 0, 5, TimeUnit.SECONDS);

Java自己的定时消息有什么实现机制(字节)page 1

java util timer。用一个后台线程和优先队列管理任务。

juc的ScheduledExecutorService,基于threadPoolExecutor,用delayQueue实现调度,他是无界队列。

在开发的时候,可能我们的任务要产生任务b,然后任务 a 的往下执行可能要依赖任务 b 的结果。那如果说我把这两个任务都扔给线程池的话,它会出现什么样的问题(如果任务 A 依赖任务 B 的结果,而将它们同时提交到线程池中,会有什么问题?)(京东)page 84

可能进入死锁,任务a在等待任务b的结果,而任务b无法获得执行的线程。

解决方案。用supplyAsync异步执行任务B,任务A依赖B的结果,用thenCompose将两者串联起来,通过thenAccept回调处理最终结果。

// 创建一个固定大小为1的线程池
ExecutorService executor = Executors.newFixedThreadPool(1);
// 提交任务 B
Callable<String> taskB = () -> {
    System.out.println("任务 B 开始执行");
    Thread.sleep(2000); // 模拟耗时操作
    System.out.println("任务 B 执行完成");
    return "结果 B";
};
// 提交任务 A,任务 A 依赖于任务 B 的结果
Callable<String> taskA = () -> {
    System.out.println("任务 A 开始执行,等待任务 B 的结果");
    Future<String> futureB = executor.submit(taskB);
    String resultB = futureB.get(); // 阻塞等待任务 B 完成
    System.out.println("任务 A 获取到任务 B 的结果: " + resultB);
    return "结果 A";
};
// 提交任务 A
Future<String> futureA = executor.submit(taskA);
try {
    // 获取任务 A 的结果
    String resultA = futureA.get();
    System.out.println("任务 A 执行完成,结果: " + resultA);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
} finally {
    executor.shutdown();
}

线程池内部,它其实每个线程都是一个worker,你能说这个 worker 他去执行任务的一个逻辑是什么样的?(线程池中,每个 worker 是怎样循环检查队列并执行任务的?)线程池每个线程它都有一个 run 方法,run 里面的内部底层执行逻辑是什么样的?(讲讲线程池中每个线程的 run 方法底层执行逻辑)(京东)page 84

用一个while循环,跳出条件是isRunning为false。

用taskQueue take方法获取任务,如果队列为空,阻塞。take方法还考虑了线程池整体状态,如线程池调用了shutdown方法,take方法会返回null;对于非核心线程,如等待新任务时超出keep alive时间,take返回null。

执行获取到的任务的run方法。

捕获异常。

实现 Runnable 接⼝和 Callable 接⼝的区别(58同城)

runnable适用于不需要返回值的简单后台任务,callable适用于需要返回值或抛出异常的复杂任务。

如何给线程池命名?为什么建议给线程池命名?

根据线程池用途来命名,比如taskexecutor,iothreadpool,workerpool等。java中可以用threadfactory创建自定义命名的线程,使用哪个threadfactorybuilder创建命名的线程池。

建议命名的原因。调试和监控,在生产环境中,如果线程池出现问题,通过命名可以快速定位哪个线程池出了问题,方便调试和监控。

ThreadFactory namedThreadFactory=new ThreadFactoryBuilder().setNameFormat("my-thread-pool-%d").build();
ExecutorService pool=new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),namedThreadFactory);

如何动态修改线程池参数?

ThreadPoolExecutor提供了一些set方法动态调整参数,如setCorePoolSize

Semaphore 有什么⽤?原理是什么?(百度)

semaphore是Java并发包的一个同步类,用于控制并发访问线程数量,

原理基于AQS,通过许可计数器管理线程对资源的访问,它维护一个整型变量permits表示许可数量。

许可证:Semaphore内部维护许可证池,线程访问资源前通过acquire方法获取许可证,访问结束后release方法释放。

公平性:Semaphore通过构造函数可以设置公平性。

CountDownLatch 有什么⽤?原理是什么?(百度)

CountDownlatch用于等待多个线程完成任务后再继续执行。

CountDownlatch通过计数器控制线程的等待和执行。

CountDown方法:每个线程调用后计数器值减一。

await方法:主线程调用,等待状态,直到计数器值变零。

不可重用:要再次使用需要创建新的CountDownlatch实例。

参考:file:///E:/BaiduNetdiskDownload/024-100023901-%E4%B8%93%E6%A0%8F%E8%AF%BE-%E7%8E%8B%E5%AE%9D%E4%BB%A4-Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%EF%BC%88%E5%AE%8C%E7%BB%93%EF%BC%89/04-%E7%AC%AC%E4%BA%8C%E9%83%A8%E5%88%86%EF%BC%9A%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7%E7%B1%BB%20(14%E8%AE%B2)/19%E4%B8%A8%E4%B8%A8CountDownLatch%E5%92%8CCyclicBarrier%EF%BC%9A%E5%A6%82%E4%BD%95%E8%AE%A9%E5%A4%9A%E7%BA%BF%E7%A8%8B%E6%AD%A5%E8%B0%83%E4%B8%80%E8%87%B4%EF%BC%9F.pdf

多线程中,等其他线程执行完,主线程再执行的方法有哪些?(得物)page 71

用join方法:主线程可以调用其他线程join方法,等他执行完毕再继续执行。

用countdownlatch:主线程可以创建一个countdownlatch,并设置计数器初始值,其他线程完成任务后调用countDown方法减少计数器的值。主线程调用await方法等待计数器归零。

用cyclicBarrier:一组线程相互等待。

用ExecutorService和Future:主线程提交任务给ExecutorService,用Future的get方法获取任务执行结果。

对于futuretask,还有一些带回调的future。然后这些 future,如果线程池没有处理完,主线程去 get 的时候可能会进行阻塞,你能把它内部阻塞的一个机制说一下吗(当 Future 的任务还未执行完毕时,主线程调用 get 方法会被阻塞,这个阻塞机制是怎样实现的?)(京东)page 71

当get调用时,awaitdone方法判断当前Futuretask是否完成,如未完成,将当前线程封装成等待节点放入阻塞链表,调用lock support park方法将自己阻塞。

当任务在另一个线程中完成时,进入finish completion方法,future task状态从new 变为completing,根据执行结果设为normal或Exceptional。finish completion遍历加入阻塞链表的节点,调用unpark方法将阻塞线程唤醒。

public V get() throws InterruptedException, ExecutionException {
    synchronized (this) {
        while (!isDone()) {
            wait(); // 主线程进入等待状态
        }
        // 返回结果或抛出异常
        if (isCancelled()) {
            throw new CancellationException();
        }
        return result;
    }
}
protected void done() {
    synchronized (this) {
        notifyAll(); // 任务完成后,唤醒所有等待线程
    }
}

多线程的 Future出现异常怎么办?(京东)

可以在get时捕获异常,或者换为使用completable future,用exceptionally方法处理异常,支持链式编程。

主线程开一个线程池,主线程和新建线程如何传递任务(贝壳)

用Future和Completablefuture实现传递。

两个子线程正在运行,想要在主线程用future的get获取更早结束的线程的结果。(在两个子线程同时运行的情况下,如何让主线程通过 future.get() 方法获取先完成的那一个线程的结果?)(拼多多)page 71

用completableFuture,用cf supplyAsync方法提交两个任务,用cf anyOf返回一个先完成的结果。

参考:file:///E:/BaiduNetdiskDownload/024-100023901-%E4%B8%93%E6%A0%8F%E8%AF%BE-%E7%8E%8B%E5%AE%9D%E4%BB%A4-Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AE%9E%E6%88%98%EF%BC%88%E5%AE%8C%E7%BB%93%EF%BC%89/04-%E7%AC%AC%E4%BA%8C%E9%83%A8%E5%88%86%EF%BC%9A%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7%E7%B1%BB%20(14%E8%AE%B2)/24%E4%B8%A8CompletableFuture%EF%BC%9A%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E6%B2%A1%E9%82%A3%E4%B9%88%E9%9A%BE.pdf

CyclicBarrier 有什么⽤?原理是什么?

cyclicbarrier适用于多线程任务需要在某个同步点上等待的场景。

原理,创建时,内部维护一个计数器parties跟踪已到达屏障点的线程数,线程调用 await 方法时,计数器会递减,并使当前线程阻塞,等待其他线程到达await,当所有线程都到了,如果定义了 barrierAction ,会在这时候执行,然后所有线程继续执行。然后屏障被重置。

多个任务的编排可以怎么做?项⽬⽤到了 CompletableFuture 吗?

CompetableFuture可以用于任务编排。顺序执行,可以用thenApply,thenAccept,thenRun,将多个任务串联起来,lamda表达式。并行执行,用allOf和anyOf,并行执行多个任务。

CompletableFuture怎么处理异常?

使用 exceptionally 方法。如果异步任务抛出异常,exceptionally会捕获并返回一个默认值或执行一个替代计算,而不是直接失败。 使用 handle 方法,类似于exceptionally,但是他允许你在处理异常同时,也能处理正常的结果。handle方法接受两个参数,一个是正常结果,另一个是异常,可以根据情况返回不同的值。 使用 whenComplete 方法,类似于handle方法,但他不会改变CompletableFuture的结果,适用于日志记录。

CompletableFuture<String> future=CompletableFuture.supplyAsync(()->{
  if(someCondition){
    throw new RuntimeException("Oopps!");
  }
  return "Success";
});

我们可以用exceptionally处理这个异常:

future.exceptionally(ex->{
  System.out.println("Caught exception: "+ex.getMessage());
  return "Fallback value";
});
future.handle((result,ex)->{
  if(ex!=null){
    System.out.println("Caught exception: "+ex.getMessage());
    return "Fallback value";
  }
  return result;
});

```java   future.whenComplete((result, ex) -> {  if (ex != null) {   System.out.println(“Caught exception: ” + ex.getMessage());   }  else System.out.println(“Result: ” + result); });  


## 主线程能捕获子线程异常吗?如何捕获?(小红书,58同城)page 71
不能,子线程异常不会自动传播到主线程。但是可以通过线程类的接口捕获子线程异常,使用thread setUncaughtExceptionHandler设置异常处理器。

## 主线程和子线程共用的是一个栈吗(58同城)page 70
不是。

## 多线程如果一个线程进行更新操作,如何让其他线程及时发现最新值?(如何保证多线程中其他线程能及时看到更新后的值?)(满帮)page 70
## (主线程的变量要给子线程用,怎么办?多个线程都想给,怎么办?)(如何在多个线程中共享主线程的变量?)(怎么在多线程环境中进行上下文数据的传递)(腾讯,得物)
TransmittableThreadLocal,他是阿里巴巴开源工具,解决线程池中数据传递问题。通过TtlRunnable和TtlCallable来包装任务。

# JVM(进阶)

## 垃圾回收算法有哪些?(饿了么考过,得物多次考)page 70

标记-清除算法:标记-清除算法分为“标记“和“清除“两个阶段,首先通过可达性分析,标记出所有需要回收的对象,然后统一回收所有被标记的对象。

复制算法:原理是,将内存分成两块,每次申请内存时都使用其中的一块,当内存不够时,将这一块内存中所有存活的复制到另一块上。然后再把已使用的内存整个清理掉。

标记-整理算法:复制算法在 GC 之后存活对象较少的情况下效率比较高,但如果存活对象比较多时会执行较多的复制操作,效率就会下降。而老年代的对象在 GC 之后的存活率就比较高,所以就有人提出了“标记-整理算法”。标记-整理算法的“标记“过程与“标记-清除算法”的标记过程一致,但标记之后不会直接清理。而是将所有存活对象都移动到内存的一端。移动结束后直接清理掉剩余部分。

分代回收算法:分代收集是将内存划分成了新生代和老年代。分配的依据是对象的生存周期,或者说经历过的 GC 次数。对象创建时,一般在新生代申请内存,当经历一次 GC 之后如果对还存活,那么对象的年龄 +1。当年龄超过一定值(默认是 15,可以通过参数 -XX:MaxTenuringThreshold 来设定)后,如果对象还存活,那么该对象会进入老年代。

## 标记-清除算法是怎么标记的,怎么确定是不是垃圾?(GC root是怎么标记的?)(美团)page 1
GC从GC roots出发,沿对象引用图递归遍历,将可达对象标记为活跃状态。每个对象用一个标记位记录访问状态。

GC roots是标记阶段可达性分析起点,他们本身被认为是永远可达的。可以作为GC Root的对象, 虚拟机栈的引用 ,包含线程栈栈帧内的引用。 方法区的引用 ,包含类的静态字段,这些引用代表类加载过程中持有的对象。 本地方法栈中的引用 ,如native方法中持有的引用对象。只要一个对象在标记阶段结束后没有被任何GC roots遍历到,则该对象不可达。


## G1垃圾回收算法是标记整理还是标记复制?(美团)page 47
复制算法。G1是基于区域的复制算法。先标记区域内存活对象,让后将这些存活对象复制到另一些区域中。 

## 什么时候会OOM,OOM怎么排查?(OOM问题如何排查和解决)(美团,网易多次考,度小满,得物,满帮,快手)page 83
识别OOM发生的时间和场景,是系统刚启动时,
还是在处理大量请求时?
是在某个特定的业务逻辑中,
还是在某个特定的服务节点上?

查看日志和监控数据。我们要看系统日志和监控数据,用filebeat监控。自动退出配置,配置exitOnOutOfMemoryError,OOM JVM自动退出,结合k8s重启机制实现故障恢复。jstat -gcutil $pid 1000,每秒输出jvm内存使用情况,看老年代使用率。


处理。配置heap dump on out of Memory Error,jvm在发生OOM时生成一个heapdump。

分析代码和内存使用情况。比如在使用java集合类时,没有及时调用clear方法,可能导致集合对象一直占用内存。用MAT分析,查看直方图和支配树,找消耗内存最多的对象。定位类后,看引用链,with incoming References。检查OOM最后一刻,是哪个线程抛出异常,看栈信息。

解决:1.堆内存问题,调整大小,新生代老年代比例。2.系统线程问题,unable to create new Native thread,减少线程数量。3.metaspace问题,调大maxmetaspacessize扩容。4.direct buffer Memory问题,默认64M字节可调整。5.gc overhead limit exceeded,gc花费过多时间,增加堆容量,或减少对象长期驻留。



<!-- 优化内存管理。我们要对代码进行优化,比如用软引用或弱引用管理不怎么重要的对象,避免占用过多内存。

注:添加-XX:+HeapDumpOnOutOfMemoryError启动参数,查看heapdump。 -->

<!-- ```java
public static void main(String[] args) throws InterruptedException {
        List<byte[]> dataList = new ArrayList<>();

        while (true) {
            // 模拟不断地往集合中添加数据,每个数据大小为 1MB
            byte[] data = new byte[1024 * 1024];  // 1MB
            dataList.add(data);

            // 你可以在这里加一个条件来模拟一定时间后出现OOM
            if (dataList.size() % 1000 == 0) {
                System.out.println("Current list size: " + dataList.size());
                Thread.sleep(1000);  // 每秒打印一次当前列表大小,模拟不断增长的内存占用
            }
        }
    }
``` -->

<!-- ## Java内存泄漏怎么排查?(阿里)page 70
内存泄漏是程序运行中,由于某些对象不再使用但未被垃圾回收器回收,导致内存占用不断增加,最终可能导致系统性能下降。

借助工具分析:用VisualVM,MAT,查看堆内存使用情况。

检查代码逻辑:通常内存泄漏是由于代码中的错误导致的。尤其在使用容器类,线程,文件IO要特别注意资源的释放。

通过日志分析问题:添加日志可以分析程序运行状态,对象的创建和销毁信息。

检查JVM参数:可以帮助我们分析内存使用情况。可以通过-XX:+HeapDumpOnOutOfMemoryError参数,在溢出时生成内存快照。还可以通过-XX:+PrintGCDetails打印GC日志。

参考:https://juejin.cn/post/6908665391136899079 -->

## 线上的项目CPU异常飙高怎么排查解决?(当线上项目的 CPU 使用率突然飙升时,你会如何进行排查和解决?)(CPU异常飙高怎么解决?)(CPU飙高的排查思路)(得物多次考,百度,shopee,阿里,腾讯,滴滴,网易,满帮)page 120
问题发现。压测中,监控到CPU使用率飙高,登录线上服务器,用top命令看进程CPU占用,定位Java进程,为看到具体线程,执行top -Hp $pid,获得线程ID,将线程id转化为16进制,用jstack命令结合grep找线程栈信息,jstack 1893 | grep -A 200 11a7,其中1893是Java进程pid,-A 200表示匹配到目标行后,再额外打印其后200行,11a7是线程id,看调用了什么方法。

补充。除了上述手动排查方法,也可以借助arthas用thread -n 3看CPU占用高的线程。

分析应用日志和gc日志。针对原因做出修改,如修复无限循环、优化加锁或同步代码;gc问题,则进行调整gc策略。完成修改后,在预发布环境进行压力测试,保证cpu恢复正常水平。


<!-- 线程分析。使用jstack工具,生成线程转储thread dump,分析哪些线程占用了大量CPU资源,查看高CPU线程的堆栈跟踪,定位到具体代码行。

注:jstack 1234 > thread_dump.txt,分析文本。 -->

```bash
"Thread-1" #6 prio=5 os_prio=0 tid=0x00007fd539501800 nid=0x2f85 runnable [0x00007fd53960f000]
   java.lang.Thread.State: RUNNABLE
    at com.example.MyService.calculateImportantData(MyService.java:120)
    at com.example.MyService.processData(MyService.java:70)
    at com.example.MyService.run(MyService.java:35)
    at java.lang.Thread.run(Thread.java:750)

死锁会导致cpu飙高吗?(满帮)

死锁不会导致cpu使用率飙高,让线程进入阻塞状态。但如果使用的锁为自旋锁、活锁或CAS,这会造成cpu使用率飙高。

JVM内存模型哪些区域会OOM?(得物,shopee,腾讯)page 119

堆内存:当Eden区满时,会触发minor gc,当老年代满时,会触发Full gc。如果gc后仍然无法为新对象分配足够空间,就会抛出OOM。

方法区:用于存储类元数据,常量池,字段和方法信息。当无法分配时会抛出OOM。

直接内存:他不是JVM运行时数据区一部分,但通过NIO包的类可以直接分配直接内存,当直接内存不足时,会抛出OOM。

本地方法栈:执行本地方法。当它无法为新栈帧分配空间,会抛出OOM。

虚拟机栈:存储方法的局部变量,操作数栈,方法出口。当它无法为新的栈帧分配空间时,会抛出OOM。

Java内存飙升怎么解决?(百度)page 69

识别内存泄漏:通过分析堆转储文件heap dump识别内存泄漏。

监控和分析:用jvisualvm,jprofiler进行监控,找出内存使用异常的代码段,并优化。

栈中存放什么数据,堆中呢?

线程变量是在堆里面还是在栈里面?(58同城)page 69

栈,存局部变量,方法调用信息,操作数栈,方法返回地址。堆存对象实例,数组。

普通局部变量分配在线程自己栈内存上。线程变量(threadlocal变量)存储在堆里面,每个线程在访问threadlocal时,实际上通过threadlocal对象作为键,从当前线程的threadlocalmap获取对应的值。

⼤对象放在哪个内存区域?

大对象通常会被分配到堆内存区域。堆内存用于存储所有的对象实例和数组。当对象较大时,它们会在堆中分配较大的空间,并且可能会影响垃圾回收的性能。

对象的访问定位的两种⽅式是什么?

句柄方式:对象的 引用 实际上是一个指向句柄的指针,句柄本身包含了 对象的实际地址 和一些其他信息。

直接指针方式:引用直接指向对象在内存中的位置。句柄相当于\ p才能获得对象实例信息,直接指针*p即可。

为什么需要GC?

因为他自动管理内存,解决了内存泄漏问题,即内存中被占用但无法使用的区域,解决了内存碎片化问题,提高内存使用效率。

讲⼀下可达性分析算法的流程。 哪些对象可以作为 GC Roots 呢?page 1

算法流程,定义GC Roots,确定哪些对象可以作为起点。从GC Roots开始遍历,遍历所有直接引用的对象。标记对象,将所有访问过的对象标为可达状态。回收不可达对象,进行垃圾回收。

可以作为GC Root的对象, 虚拟机栈的引用 ,包含线程栈栈帧内的引用。 方法区的引用 ,包含类的静态字段,这些引用代表类加载过程中持有的对象。 本地方法栈中的引用 ,如native方法中持有的引用对象。

垃圾回收期间发生的动态变化怎么处理,比如一个对象之前是垃圾,现在不是了,会不会回收(美团)page 1

不会回收。初始标记用可达性分析标记对象。并发标记用写屏障维护记忆集存储可达对象,当应用线程给某个对象写新引用时,GC捕捉这次写操作,写屏障保留他,GC不会回收。

如果Java类A中持有类B对象的普通引用,且类B持有类C的普通引用,假如类A不再被使用,那B、C会被可达性分析回收吗?(字节)

类B和类C最终都会被垃圾回收器(GC)回收。

如何判断对象是否死亡?

引用计数法 ,为每个对象维护一个引用计数器,记录有多少个引用指向对象,不能处理两个对象相互引用问题。

可达性分析 ,定义哪些对象可以作为GC Root,从Root进行dfs,不可达的对象进行回收。

如何判断⼀个常量是废弃常量?如何判断⼀个类是⽆⽤的类?

如果字符串常量池中的字符串(例如“abc”) 没有任何 String 对象引用它 ,这个常量被认为是废弃常量。

无用类的判断条件,如果这个类的所有 实例已被回收 ,他就是无用类。如果加载这个类的 classloader已经被回收 那么他就是无用类。

默认的垃圾回收器是哪⼀个?ZGC 了解吗?

讲讲JVM常见垃圾回收器?(58同城)page 69

Serial GC。用单线程进行垃圾回收。gc时暂停所有线程。

Parallel GC。用多线程并行执行垃圾回收。

CMS。采用并发标记清除法,产生内存碎片,可能引发Full GC。

G1。将堆划分为多个大小相等的区域,根据区域中垃圾比例确定回收顺序。

ZGC。jdk11引入,采用并发和染色指针保证GC停顿在毫秒级。

G1垃圾回收器哪些过程可以和用户线程并行(拼多多)

并发标记与用户线程并发,筛选回收部分阶段与用户线程并发。筛选回收分为两个阶段,cleanup pause阶段,stw标记空闲区,更新卡表;并发cleanup阶段,此时可以与用户线程并发,释放RSet;mixed gc pause阶段,stw回收空间。

ZGC垃圾回收器为什么快?(ZGC底层原理?)(无暂停垃圾回收器底层原理)(百度)page 123

并发复制。传统垃圾回收器在进行对象复制时,要STW,ZGC能让gc线程在应用线程正常运行时并发复制。

读屏障。为解决应用线程与gc线程同时操作对象不一致问题,引入读屏障。当应用线程访问一个对象时,判断该对象是否被gc线程迁移,如已迁移,则读屏障用转发表把对象的引用更新为新地址。

染色指针。ZGC用地址空间中没有用到的高位作为状态标志位,linux对64位的地址空间只用了48位,且寻址范围已经达到了256T,很少有这么大的内存,因此ZGC借用42位到45位用于区分不同的地址视图(Marked0、Marked1、Remapped)。这种技术可以将一个物理对象映射到三个不同虚拟地址上,对象迁移前后都可访问。

分阶段。ZGC分为三个阶段。1.mark阶段,短暂的stw扫描root,将他们的地址视图设置为marked0。2.relocate,gc线程并发迁移marked0的对象到新区域,更新转发表,新的对象地址视图设置为remapped。3.remap,对所有对象全局扫描,交替使用 Marked0 与 Marked1 两套标记。

讲⼀下 CMS 垃圾收集器的四个步骤 (饿了么)page 81

初始标记,暂停应用程序执行,标记所有GC root可达对象。并发标记,CMS与应用程序并发,通过遍历对象图标记可达对象。重新标记,停止应用程序,标记在并发标记 未捕获 的对象。并发清除,CMS与应用程序并发运行,清理对象。

CMS 和 G1 的优缺点(饿了么)page 1

cms的优点,简单,参数少。cms的缺点,不做内存整理,容易产生内存碎片。存在并发失败问题。

g1的优点,支持在jvm参数设置自定义停顿目标,延迟可控。g1的缺点,参数很多,调优门槛比cms高。对humongous object大对象如超过分区一半大小的对象的处理方式单一,容易导致停顿。

CMS用了哪些垃圾回收算法?用在哪个年代?(百度)page 68

参考。 标记清除算法。

CMS用在老年代。

CMS一般和哪个垃圾回收器配合使用(哈啰)

CMS是老年代垃圾回收器,配合新生代的ParNew和Serial使用。

CMS垃圾回收停顿了几次?为什么?(蚂蚁)page 68

CMS的过程,初始标记,并发标记,重新标记,并发清除。

两次。分别是初始标记和重新标记阶段。

初始标记,CMS需要标记所有与GC Roots直接关联的对象,这个阶段所有应用线程会暂停。

重新标记。在并发标记时,应用线程可能会修改对象图,为了确保标记准确,CMS要在并发标记后进行一次所有应用线程暂停的重新标记。

G1垃圾收集器(腾讯)page 68

G1是Java9以后的默认收集器,将堆内存划分为多个区域,采用并发标记和混合收集的方式。

初始标记:标记从根对象直接可达的对象。

并发标记:标记存活的对象。

最终标记:修正并发标记中由于线程修改导致的引用变化。

筛选回收:选择回收区域。

ZGC垃圾收集器(腾讯)page 67

标记:识别存活对象。

转移:将存活对象移动到新的内存区域。

重定位:更新对象的引用。

CMS和G1垃圾收集器区别?(小红书,拼多多,字节,美团)page 67

内存布局。 CMS使用传统分代内存布局,分为新生代和老年代。 G1将堆分为多个相等大小的区域region,每个区域可以是新生代或老年代。

工作原理:

CMS采用初始标记, 并发标记 ,重新标记, 并发清除 。而G1还会优先处理垃圾最多的区域region。因此G1更适合大内存场景。G1采用初始标记, 并发标记 ,最终标记,筛选回收。

停顿时间:

CMS虽然减少了停顿,但在并发清除阶段可能出现“浮动垃圾”,导致Full GC。G1通过分区收集和可预测的停顿时间,减少了Full GC频率。

并发标记要解决什么问题?并发标记带来了什么问题?如何解决并发扫描时对象消失问题?

并发标记解决GC中应用程序停顿的问题,允许GC程序和应用程序并发执行。问题,有对象可能在并发标记汇总被回收,导致遗漏一些对象。解决方法有双重标记法,在并发标记前,使用初始标记创建快照,标记根对象。

G1 垃圾收集器的步骤

初始标记,标记GC根关联对象。并发标记,与应用程序并发运行,标记所有可达对象。最终标记,标记并发标记期间 变化的对象 。筛选和清理,回收标记的对象。

G1怎么处理浮动垃圾?(百度)page 67

浮动垃圾:在垃圾收集器标记和清除阶段之间,由于对象引用关系发生变化而不可达的对象。

并发标记阶段:标记所有存活对象。

重新标记阶段:修正由于并发标记期间对象引用变化导致的标记错误。

混合收集阶段:回收年轻代和老年代垃圾,这个阶段会处理 浮动垃圾 。

G1用增量更新和SATB快照技术解决浮动垃圾。

SATB技术在垃圾回收开始时对堆内存进行快照,记录所有对象的引用关系。即使垃圾回收过程中引用关系发生变化,他也会记录。

JVM 中的安全点和安全区各代表什么?

安全点是程序执行的特定时刻,所有线程都可以安全暂停下来进行垃圾收集。安全区是在程序执行的代码段内,GC可以安全进行,不用考虑异常问题。

堆内存相关的 JVM 参数有哪些?

-Xms设置jvm堆初始内存堆大小,如-Xms512m设置为512MB,-Xmx设置堆最大大小。

GC性能指标了解吗 ?调优原则呢?(讲讲GC垃圾回收性能指标)(讲讲垃圾回收调优原则)page 1

GC性能指标关注 停顿时间 和 垃圾回收效率 。调优原则是降低 full gc 频率和减少full gc时间。

讲讲你的GC调优思路(讲讲你的垃圾回收调优思路)(讲讲jvm调优思路)page 46

调优目标。关注停顿时间和吞吐量。如果业务对响应时间敏感,则控制停顿时长;对于批处理类任务,关注吞吐量。

定位问题。用参数print gc details开启gc日志,用j stat jmap观察gc运行状态,看堆中哪些对象较多,注意Humongous 对象(大对象)的内存碎片。

针对G1调优。1.区的划分。用G1 heap region size参数调整区大小,平衡大对象和碎片问题。2.暂停时间,用G1 mixed gc count target控制每次mixed gc包含的区数量,降低单次暂停时长。3.full gc的原因,用print adaptive size Policy参数看原因。

自适应调优。现代g1引入很多自适应机制,用print adaptive size policy参数看jvm为何做出调整。

网络

DNS解析的过程是什么样的(dns的递归查询的过程)(滴滴考过)page 67

本地DNS缓存查询:首先,操作系统会检查本地DNS缓存,看是否已经解析过该域名。如果有缓存记录,直接返回IP地址,解析过程结束。

递归查询:如果本地缓存没有记录,操作系统会向配置的DNS服务器(通常是 ISP提供的DNS服务器 )发起递归查询请求。

迭代查询:DNS服务器收到递归查询请求后,会进行迭代查询。首先,它会检查自己的缓存,如果没有记录,则会向 根DNS服务器 发起查询。

根DNS服务器:根DNS服务器会返回顶级域名服务器(如.com、.org等)的地址。

顶级域名服务器:DNS服务器再向顶级域名服务器查询,顶级域名服务器返回权威域名服务器的地址。

权威域名服务器:最后,DNS服务器向权威域名服务器查询,权威域名服务器返回域名对应的IP地址。

返回结果:DNS服务器将查询结果返回给操作系统,操作系统将结果缓存并返回给应用程序。

递归查询与迭代查询:递归是客户端向dns服务器发起的请求,要求服务器返回最终结果。迭代是dns服务器之间的查询方式,服务器逐层向上级查询,得到结果。

DNS劫持和DNS污染了解吗?(元戎启行)

DNS劫持是攻击者篡改DNS查询请求或响应,使得合法域名解析到恶意IP。DNS污染是攻击者向递归解析器或缓存服务器注入伪造的DNS记录,导致后续合法用户从缓存中获取错误的IP。

解决方案,用多DNS源比对,同时查询公网多个可信DNS,对比返回IP是否一致。其次用dig trace命令跟踪递归过程,检查每级NS记录。

GET 和 POST 的区别?(字节考过)page 67

GET用于 获取查询资源 。POST用于 创建或修改资源 。GET是幂等的而POST是不幂等的。GET请求的参数通常在URL中,而POST请求的参数通常在请求体body中,有多种编码格式,如json等。get请求时幂等的所以可以被浏览器或网关缓存,而POST不适合被缓存。get和post如果用http协议那么都不安全,因为是明文的,要用https。

你知道粘包和拆包吗?为什么会发生?怎么解决?(讲讲粘包和拆包?为什么会发生粘包和拆包?怎么解决粘包和拆包?)(TCP如何解决粘包?)(字节考过,得物,腾讯,阿里)page 97

粘包是发送方发送的 多个小数据包被接收方一次性接收 ,导致接收方无法区分这些数据包的边界。 拆包是发送方发送的一个大数据包被接收方分成多次接受,导致接收方无法一次性获取完整数据。

为什么? tcp是流式协议 ,不保留消息的边界,只保证数据顺序和可靠性。 缓冲区大小, 发送方和接收方的缓冲区大小不一致 ,可能导致数据包被拆分或合并。 nagle算法会延迟小数据包的发送,可能粘包问题。

如何解决?

消息定长。发送方和接收方约定每个消息长度固定。

消息分隔符,在每个消息的末尾加上特定的分隔符,接收方根据不同分隔符区分不同的消息。

tcp滑动窗口有什么用?原理是什么?(携程)page 66

滑动窗口的作用。用于流量控制。滑动窗口允许 接收方告诉发送方自己当前的缓冲区大小 ,避免发送方发送过多数据导致接收方缓冲区溢出。

滑动窗口的原理是通过动态调整窗口大小控制发送方和接收方之间的数据传输。窗口大小由 接收方通过tcp报文头的窗口字段告知发送方 。

滑动机制。当发送方收到接收方的确认时,窗口向右滑动,允许发送方发送更多数据,如果接收方没有及时确认,窗口大小可能缩小。

redis实现滑动窗口限流?(作业帮,腾讯,阿里国际)page 66

用redis z set数据结构:每个 请求时间戳作为z set成员,请求次数作为分数 。

滑动窗口维护:通过定时任务或每次请求时,删除过期的请求,保证窗口记录有效。

限流判断:每次请求时,判断当前窗口内的请求次数,如果超过设定阈值,拒绝请求。

zerrangeByScore删除窗口外的请求记录。

zcard统计当前窗口内请求次数。

zadd记录当前请求时间戳。

客户端和服务端会话断开瞬间,是怎么样的?(网易)

心跳机制检测。如果系统中实现了心跳机制比如tcp keep-alive,在连接断开后,心跳包无法发送或接受,从而触发心跳超时,服务端会检测到连接异常,会触发相应的异常处理逻辑,如关闭连接。

客户端在断开连接后,会进入重连逻辑,尝试重新建立连接,如果重连失败,可以提示用户网络异常。

数据一致性。如果在断开瞬间有未完成的数据传输,可能导致数据不一致,需要进行事务回滚或数据重传。

为什么 DNS 协议使⽤ UDP?DNS只使⽤了 UDP 吗?page 79

因UDP不需要TCP那样建立连接,DNS查询通常是小数据包,UDP可以显著减少延迟。但是DNS并不只使用UDP,当DNS响应数据超过512字节时,会使用TCP。

TCP 是如何保证传输的可靠性?(⾥⾯涉及到的知识点⾮常多,每个都能挖掘不少问题,例如重传机制、流量控制、拥塞控制。如果⽬标是⼤⼚的话,⼀定要吃透,⾯试经常会问的)(得物,作业帮)page 79

讲讲流量控制和拥塞控制(shopee多次考)page 79

确认应答机制。发送方发送数据包后,会等待接收方的确认应答。如果没有收到确认,发送方会重新发送数据包。

序列号和重传机制。tcp为每个数据包分配一个序列号,接收方根据序列号来重组数据包的顺序。如果发送方一定时间内没有收到某个数据包的确认应答,他会认为数据包丢失,并 重新发送数据包 。

流量控制和拥塞控制。tcp使用 滑动窗口 机制进行流量控制,确保发送方发送速率不超过接收方的处理能力。同时,tcp还实现了拥塞控制,通过动态调整发送速率避免网络拥塞。

校验和,tcp在每个数据包中都包含一个校验和,检测数据包在传输过程中是否发生了错误。如果接收方检测到错误,他会丢弃该数据包,并要求发送方重新发送。

注:

拥塞控制:

拥塞窗口控制发送方可以发送的数据量。

慢启动:每收到一个确认,窗口大小翻倍。

拥塞避免:当拥塞窗口达到慢启动阈值,窗口大小不翻倍,而是线性增长。

快速重传:发送方连续收到三个重复的确认,认为有数据包丢失,立即重传。进入快速恢复阶段,将拥塞窗口减半。

超时重传:如果发送方一定时间内未收到确认,进行重传,重新进入慢启动。

拥塞控制算法:TCP reno,TCP Tahoe,BBR(Bottleneck Bandwidth and Round-trip propagation time)。

HTTP 报文的内容简单说⼀下! HTTP 请求报⽂和响应报⽂中有哪些数据?(shopee,蚂蚁)page 65

请求报文,包含请求行,请求头部,空行,请求体。

请求行包括请求方法、请求目标、和协议版本。请求头部包括请求附加信息,比如host,user agent等。

响应报文,包含状态行,响应头部,空行,响应体。

http2.0(腾讯)page 65

多路复用:HTTP2允许在同一个TCP连接同时发送多个请求和响应,解决了HTTP1.1队头阻塞问题。

二进制分帧:HTTP2将数据分成小二进制帧传输,而不是HTTP1.1中文本格式。

头部压缩:HTTP2用hpack算法压缩头部。

HTTP2.0怎么实现头部压缩的(腾讯音乐)page 1

http1中每次请求用纯文本形式重复发送头部字段,http2用hpack进行头部压缩。

静态表。协议内置字典,对常见头字段名比如cookies分配固定索引。

动态表。客户端和服务端各维护一张可增长的会话级别字典,对每次新出现的头字段名或字段key value对,插入动态表分配索引。

哈夫曼编码。对静态表和动态表之外的任意字符串用静态哈夫曼编码。

HTTP/2 的多路复用一条 TCP 和 HTTP/1.1 的使用多个 TCP 连接方式,哪个性能更好?(腾讯)

http2的多路复用性能更好。

HTTP/2.0 和 HTTP/3.0 有什么区别?(字节考过)page 65

基础协议。http2是基于 tcp 协议的,而http3是基于 quic 协议的,quic协议基于udp,使得http3在连接建立和数据传输时更高效。

连接建立。http2中,建立连接需要经过 tcp三次握手 。http3通过quic协议,在 一个数据包中完成连接建立和加密 。

多路复用。http2和http3都支持多路复用,即在一个连接上同时传输多个请求和响应。但是http3通过quic多路复用,可以更好处理网络拥塞和丢包。

头部压缩。http2采用 hpack 进行头部压缩,http3采用 qpack 。qpack设计上考虑了quic的特性,可以更高效处理动态表的更新和同步。

Linux中的epoll和select这些多路复用了解过吗?(腾讯多次考,百度,难)page 65

select poll epoll的区别?(得物)page 20

IO多路复用模型(shopee多次考)

epoll为什么快?(腾讯)page 63

select用fd set位图数组记录进程文件描述符。调用时将要监控的文件描述符加到fd set,将set复制到内核态,内核遍历,检查是否就绪,返回。select只支持水平触发LT,即只要文件描述符处于就绪态,不管用户能否马上处理,都每次返回。缺点是文件描述符数量受限1024,且性能开销大,每次调用都遍历整个set。

poll和select类似,但不用fd set,而是struct poll fd结构体数组,优点是没有固定数量限制,缺点是仍性能开销大,线性扫描以及复制开销,只支持水平触发LT。

epoll是Linux特有,用epoll create创建epoll内核对象,用epoll ctl注册文件描述符和事件。内核内部用红黑树管理注册的文件描述符,为就绪事件维护链表,调用epoll wait时,只返回就绪链表中的文件描述符。epoll支持水平触发LT和边沿触发ET,边沿触发模式下,事件只在状态变化时触发,这要程序在收到通知后循环读取直到E AGAIN。

参考:https://time.geekbang.org/column/article/149204

epoll 机制示意图

select poll epoll这几种是同步的还是异步?(shopee,腾讯,淘天)page 65

同步的,他们作用是监听多个文件描述符状态变化,并在有事件发生时通知应用程序进行处理。

epoll在不考虑服务器内存 网卡的情况下最大支持多少并发?(腾讯)page 64

受限于linux对文件描述符个数的配置。默认这个值在1024到65535之间。可以通过limits.conf修改提升到上百万。

epoll怎么知道文件描述符上发生了事件?(腾讯)page 64

epoll的触发模式?(腾讯)page 20

边缘触发:epoll在遍历双向链表时,会把socket从双向链表中移除,然后读取这个socket的事件。

水平触发:epoll遍历双向链表时,移除socket,读事件,如果socket返回了感兴趣的事件,那么当前socket会再被加入双向链表,下次调用epoll_wait时,还能拿到这个socket。

讲一下linux守护进程处理日志。(百度)

Syslogd日志守护进程,接受和处理系统和程序的日志信息。

linux怎么看某个进程存不存在?怎么看文本最后几行?(百度)

kill -0 pid存在返回0,tail -n 20。

websocket底层原理?(阿里)page 64

握手阶段:

websocket连接建立开始于一个http请求,客户端发送一个特殊的http请求,请求头包含upgrade:websocket和connection:upgrade,sec-websocket-key。

服务端接受请求,返回http 101状态码,响应头也包含三个字段,它的sec-websocket-key是根据客户端的生成的。

数据传输:

握手成功后,http协议升级为websocket协议。websocket数据帧被设计为轻量级,包含头部和数据部分,头部包含控制信息,如帧类型(文本,二进制,关闭连接),掩码标志(防止缓存中毒攻击),数据长度。

心跳机制:

客户端和服务端可以定期发送ping帧,对方收到后立即回复pong帧。

连接关闭:发送关闭帧,对方收到后也会发送一个关闭帧作为响应,然后双方关闭连接。

HTTPS 加密过程是怎么样的?

1.客户端向服务器发送 HTTPS 请求,

2.服务器将公钥证书发送给客户端。

3.客户端验证服务器的证书。

4.如果验证通过,客户端生成一个用于会话的对称密钥。

5.客户端使用服务器的公钥对对称密钥进行加密,并将加密后的密钥发送给服务器

6.服务器使用私钥对客户端发送的加密密钥进行解密,得到对称密钥。

7.服务器和客户端使用对称密钥进行加密和解密数据传输。

HTTP/1.1 和 HTTP/2.0 有什么区别?page 17

2.0采用I/O多路复用,以及二进制帧,头部压缩。

什么是 WebSocket?⼀般⽤来做什么?

websocket基于tcp的全双工协议,允许client和server简历持久连接,并且可以实时双向数据传输。一般用来做视频弹幕,实时消息推送以及实时游戏对战。

WebSocket 和 HTTP 有什么区别?(百度)page 1

websocket提供全双工通信,允许client和server同时发送和接受数据。而http是单向请求响应协议。websocket协议前缀是ws,而http就是前缀。websocket的头部信息量小开销比http小。

WebSocket 的⼯作过程是什么样的?

连接初始化,服务器响应,数据传输,保持链接,连接关闭。初始化阶段client向server发送一个特殊的http请求。server响应阶段他会发送http switching protocols响应。保持连接时可以通过发送心跳包维持回话。关闭连接任一方可以发送一个关闭帧。

SSE 与 WebSocket 该如何选择?

sse基于标准的http工作,而websocket是独立的协议。sse仅支持单向通信,websocket支持全双工通信。websocket适合需要双向通信应用如在线游戏,而sse适合单向数据流,如推送通知、实时新闻报道。

PING 命令的作⽤是什么?

用于测试从一个网络设备到另一个设备的连通性。通过icmp回声请求报文实现。他会显式目标主机的域名和IP地址以及rtt。

PING 命令的⼯作原理是什么

基于icmp协议使用echo Request和echo reply报文实现。

DNS 是什么?解决了什么问题?

DNS是域名解析系统,解决了域名和IP地址的映射问题。

DNS 能解析端⼝吗?

不解析,端口号属于传输层的范畴,DNS操作于应用层。

DNS 服务器有哪些?

DNS有4类服务器,根DNS服务器,顶级域名服务器,权威服务器,本地服务器。根管理顶级域名服务器地址信息,处理对顶级域名服务器的查询请求。顶级域名服务器管理com org net的域名信息。权威服务器管理他负责的域名的DNS记录。本地服务器由ISP维护,作为client和更高级别DNS服务器之间的中介。

DNS 劫持了解吗?如何应对

DNS劫持通过修改DNS服务器解析结果,使用户访问的域名指向错误的IP地址,从而引导到恶意网站。应对方式有网络层面和应用层面的方法。网络层面使用多个域名,主域名受到攻击使用备用域名,另外可以修改DNS服务器改为更可靠的公共DNS服务。应用层面使用HTTP DNS替代传统的local DNS,通过HTTP协议直接请求专门DNS解析服务,绕过本地ISP DNS服务。

IP 协议的作⽤是什么?

IP(Internet Protocol,网际协议) 是 TCP/IP 协议中最重要的协议之一,属于网络层的协议,主要作用是定义数据包的格式、对数据包进行路由和寻址,以便它们可以跨网络传播并到达正确的目的地。

什么是 IP 地址?IP 寻址如何⼯作?

IP地址是互联网协议地址,有IPv4和ipv6,在网络通信中每个数据包有源IP地址和目的IP地址,他们帮助网络设备正确路由数据包

IPv4 和 IPv6 有什么区别?

ipv4使用32位地址,已不足以满足全球需求。ipv6使用128位地址,理论上提供了几乎无限的IP地址。另外ipv6不需要NAT来允许多个设备共享一个公共IP地址。

操作系统

操作系统的调度算法了解吗?

先来先服务算法,最短作业优先算法,高响应比优先算法,高响应比就是说权衡等待时间和要求服务时间,时间片轮转算法。

算法

加权轮询算法实现

加权轮询算法用于负载均衡。权重越高的服务器越有可能被选中。

实现⼀个死锁

Thread t1先获取Synchronized resource1,等待100毫秒,然后获取Synchronized resource2,t2反过来这就产生了死锁。

⽣产者和消费者实现

生产者方法while等待队列的size不为满,wait方法,然后生产一个值,执行notify方法。消费者方法while等待队列的size不为空,wait方法,然后消费一个值,执行notify方法。

数据结构

数组 vs 链表

数组的插入和删除的时间复杂度是O(n),而链表插入删除时间复杂度是O(1)。

栈的应⽤场景

表达式求值,以及函数调用的管理,如虚拟机栈。

队列的分类、应⽤场景

普通队列,用于广度优先算法。双端队列,用于滑动窗口问题。循环队列用于环形缓冲区。优先队列,用于任务调度如CPU任务调度。

哈希表应⽤场景

哈希表可用于快速查找与缓存,如Memcached以及数据库索引。哈希表用于键值对存储如HashMap。哈希表用于内存地址映射,页表使用哈希表映射虚拟地址到物理内存地址。

布隆过滤器应⽤场景

布隆过滤器时一种空间效率极高的概率型数据结构,用于判断一个element是否属于一个集合,但可能错误认为一个不存在的element存在于集合中,假阳性,误判率取决于哈希函数的选择以及位数组的大小,但不会将存在的元素误判为不存在。应用场景有垃圾邮件过滤,以及缓存系统中的热点数据识别,避免频繁访问MySQL。

SQL分组

group by将查询结果按照一个或多个值分组,然后对每个分组进行聚合计算,如SUM

,AVG,COUNT,也可以使用HAVING过滤分组结果。

SQL连接

在两个表之间建立关系。inner join,返回两个表符合连接on条件的记录。不加修饰符的join默认就是inner join。