Javaguide整合
数据库
讲讲mysql的锁机制(得物)page 1
锁保证隔离性。
表级锁。
行级锁。分为共享锁排他锁,innodb用记录锁,间隙锁,next key锁防止幻读。
死锁。innodb默认启用死锁检测。
如何避免数据库死锁?(数据库出现死锁怎么排查)(数据库死锁问题)(mysql什么情况下会死锁)(怎么解决mysql的死锁)(网易雷火)(小红书,百度,得物,腾讯,美团)page 75
死锁原因:1.事务操作数据时持有互斥锁,并在请求其他资源形成循环等待。如innodb,rr隔离级别下,用非唯一索引select for update,触发gap lock、next key lock,但执行查询sql获取的gap lock不会阻塞,当执行插入sql时,会在间隙获取插入意向锁,插入意向锁与gap lock是冲突的。这时可能会形成死锁。2.索引设计和更新不当。用辅助索引更新数据时,产生额外的间隙锁,两个事务如果分别使用不同索引(或一个用辅助索引,一个用聚簇索引)更新,可能因锁范围重叠相互等待。3.长事务,死锁。
优化方式。1.用唯一索引或主键进行查询和更新。防止重复创建记录。2.固定访问顺序。多个事务涉及多个数据行更新时,按照固定顺序访问数据库记录。3.降低事务占用时间。将长事务拆为小事务,合理设置锁等待超时时间,设置参数innodb lock wait timeout。
锁的兼容矩阵图:
持有锁 \ 请求锁 | GAP | Insert Intention | Record | Next-key |
---|---|---|---|---|
GAP | 兼容 | 冲突 | 兼容 | 兼容 |
Insert Intention | 冲突 | 兼容 | 兼容 | 冲突 |
Record | 兼容 | 兼容 | 冲突 | 冲突 |
Next-key | 兼容 | 冲突 | 冲突 | 冲突 |
备注 横向是已经持有的锁,纵向是正在请求的锁。
MySQL数据库加锁的原则
原则 1:加锁的基本单位是 next-key lock。希望你还记得,next-key lock 是前开后闭区间。
原则 2:查找过程中访问到的对象才会加锁。
优化 1:索引上的等值查询,给唯一索引加锁的时候,next key lock 退化为行锁。
优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next key lock 退化为间隙锁。
给了一条 select sql 问需要几次 io 操作(一次sql执行的时候,要进行几次IO操作)(2000万行的表查一个数据几次io)(得物)page 1
MySQL8之前服务层有查询缓存,select缓存命中则0次io,缓存未命中去引擎层。引擎层在内存有buffer pool,若未命中则进行磁盘io。如果select语句命中索引,则看B+树层数,如为3层,则进行三次io,如果为主键索引则3次已经获取到数据,如为二级索引,索引覆盖则也是三次io,若未覆盖则要回表,四次io。如索引未命中,则进行全表扫描,MySQL顺序读取每个数据页,有多少个页则进行多少次io。
给定一张表 table,其列 A 上有普通索引,问:执行 SELECT * FROM table;大约会产生多少次 IO?(有一个表table有一个A字段,A字段为普通索引,select * from table 大概有几次IO)(贝壳)page 1
select * from table是一次全表扫描,不会用A上的普通索引,IO次数等于表的数据页数。
给定一张表 table,其列 A 上有普通索引,问:执行 SELECT * FROM table where A=1;大约会产生多少次 IO?page 1
索引遍历。B+树索引高度取决于索引页总数和扇出因子。每层要做一次磁盘页读,共需要H次IO。
回表取行。二级索引的叶子节点存的是主键值,定位到数据页后,还要一次IO。如果索引遍历中得到M条满足A=1的记录,则进行M次IO。一条满足A=1的记录回表过程中的IO次数为1的原因:聚簇索引的内部非叶子结点驻留在buffer pool中,不再触发磁盘IO。
select id,A from table where A = xxx,需要经过几次IO,id为主键索引,A为唯一索引(贝壳)page 1
二级索引中叶子节点不仅存储A的值,还隐式把主键id也存入了索引项。因为查询只要拿回id和A两列,而这两列都包含在二级索引的叶子结点中,这是覆盖索引。
假设内存中buffer pool没有索引页,则从磁盘读,B+树索引高度取决于索引页总数和扇出因子。每层要做一次磁盘页读,共需要H次IO。如树高为3,则要3次磁盘IO。
SQL 标准定义了哪些事务隔离级别?(讲讲事务隔离)(饿了么考过,腾讯)page 1
事务是数据库操作单位,保证acid。原子性,全部成功或全部失败。一致性,事务执行前后,必须从一个状态转换到另一个一致状态。隔离性,多事务并发互不干扰。持久性,事务提交数据永久保存。
隔离性问题。脏读,事务读到另一个事务未提交数据。不可重复读,一个事务多次读同一数据结果不一致。幻读,一个事务多次查询,返回记录数变化。
四种隔离级别。读未提交,一个事务未提交的修改,另一个事务就可以读取,存在脏读。读已提交,事务只有在提交后所做的修改,才能被其他事务看到,可能出现不可重复读。可重复读,在开始时创建一个静态数据视图,会出现幻读,要借助范围锁解决。串行化,对同一数据行操作时,加上读写锁。
MySQL默认的隔离级别是什么?(字节)page 47
REPEATABLE-READ(可重复读)。可重复读可以解决 大部分 幻读问题,用快照读和当前读。快照读通过 mvcc 解决了幻读,当前读通过 记录锁+间隙锁 解决了幻读。
为什么互联网公司选择用读已提交(为什么互联网公司选择用RC)
读已提交在发生锁时,范围更小,系统并发吞吐量更高。
对于不可重复读和幻读,可以用幂等设计,乐观锁在业务层进行保证。
可重复读和读已提交的区别(RR和RC的区别)page 1
读已提交允许不可重复读发生,可重复读不允许不可重复读发生。
在MySQL innodb中,可重复读用MVCC和next key锁实现。
解释一下幻读?(字节)什么时候会发生幻读?(58同城)
幻读:在一个事务中,两次查询同一个范围的数据,第二次查询返回了第一次查询中不存在的行,或者第一次查询存在的行在第二次查询中消失了。
发生条件:可重复读隔离级别下,当另一个事务在当前事务的两次查询之间插入新行或删除现有行时。
MySQL innodb在可重复读下默认用间隙锁防止幻读。
可重复读可以解决幻读吗?(有点难)
MVCC可以完全解决幻读吗?(得物)
幻读是两次查询返回结果个数不同, 通过快照读(select)和当前读(select for update,update,delete)解决幻读绝大部分情况 。
MySQL默认隔离级别,可重复读,什么情况不能解决幻读问题?(可重复读怎么发生幻读)(可重复读什么情况不能解决幻读问题)(作业帮)page 47
假设有AB两个事务,A先读取一个id不存在的数据,如id为x。
B插入新id为x的数据并提交事务。
A用update修改id为x的数据,此时,这条记录的trx_id变成A。
A事务读取id为x的数据,发现可以读到,发生了幻读。
数据库是否了解插入缓冲?(滴滴)
在数据库中,插入操作涉及到索引更新。如果索引是非聚集索引,更新会进行随机写入,导致性能瓶颈。插入缓冲将插入操作缓存,减少了随机写入次数。
联合索引使用情况判断。(百度,作业帮,哈啰,猿辅导,快手)
create table myTest2 (a int, b int, c int, KEY suoyin(a, b, c));
EXPLAIN FORMAT=JSON select * from myTest2 where a=3 and b=5 and c=6;
走联合索引
EXPLAIN FORMAT=JSON select * from myTest2 where c=6 and b=5 and a=3;(拼多多)
走索引,MySQL优化器会自动调整顺序。
EXPLAIN FORMAT=JSON select b, a from myTest;(字节,不懂)
不命中索引,主键索引树扫描index。
EXPLAIN FORMAT=JSON SELECT * FROM taotian_scene WHERE a = 1 AND c = 2;(满帮)
会走联合索引,但应该属于 索引截断 ,非主键唯一索引等值扫描ref。 c没有命中
TODO:索引截断:
EXPLAIN FORMAT=JSON SELECT * FROM example_table WHERE a = 3 AND b = 1 AND c > 2;
范围扫描range。 c命中
EXPLAIN FORMAT=JSON SELECT * FROM taotian_scene WHERE b = 1 AND c > 2 AND a = 3;(快手,淘天多次考)
范围扫描range。 c命中,索引下推。
EXPLAIN FORMAT=JSON select * from example_table where A>=0 and B=7;(得物)
A命中,B不命中。
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE age IN (20, 25, 30); (阿里)
范围扫描range,索引下推,命中索引
EXPLAIN FORMAT=JSON select * from myTest2 where b=5 and c=6;
b不命中,c不命中,索引树扫描index
EXPLAIN FORMAT=JSON select * from myTest2 where a=3 and b>5 and c=6;
由于b用到了范围查询,所以只用到了(a,b)联合索引,发生了索引树扫描index, c不走索引
explain format=json select length(a) from taotian_scene where a=1;走不走索引,为什么
走索引,索引用于过滤,函数在过滤之后执行。
explain format = json select * from taotian_scene where a in (1,2,3) and b=3;
a命中,b命中。
EXPLAIN FORMAT=JSON select * from taotian_scene where a=3 order by b asc;(不懂)
a命中,b命中,非主键唯一索引等值扫描ref。
EXPLAIN FORMAT=JSON select * from taotian_scene where a=3 order by c asc;
a命中,c不命中,非主键唯一索引等值扫描ref,Using filesort指定的排序和索引排序不一致。
但如果b的值在a=3时是固定的,则c自然有序,此时排序命中索引c。
EXPLAIN FORMAT=JSON select * from taotian_scene where b=4 order by a asc;
a不命中,b不命中。发生了全表扫描all。
EXPLAIN FORMAT=JSON select * from myTest2 where a=3 and b=5 or b=4 and c=1;
a不命中,b不命中,c不命中。逻辑运算符AND优先级高于OR,发生了全表扫描all。
Type连接类型(1)system:系统表,少量数据,往往不需要进行磁盘IO;(2)const:常量连接;(3)eq_ref:主键索引(primary key)或者非空唯一索引(unique not null)等值扫描;(4)ref:非主键非唯一索引等值扫描;(5)range:范围扫描;(6)index:索引树扫描;(7)ALL:全表扫描(full table scan);
Key实际用到的索引
Ref哪些列或常数与索引一起使用
Extra列 Using filesort按文件排序,一般是在指定的排序和索引排序不一致的情况才会出现。
Using index用了覆盖索引,说白了它表示是否所有获取的列都走了索引。
Using where表示使用了where条件过滤。
Using temporary表示是否使用了临时表,一般多见于order by 和 group by语句。
MySQL可能单查 A 或者单查 B, 也有可能 A、B 一起查。是建两个单独的索引?还是建一个 A和B 的联合索引?(京东)
建立两个单独的索引,单查A,单查B可以命中索引,当A、B一起查时,MySQL会优化使用两个索引,即索引合并。
in和exists区别?
select * from A where cc in (select cc from B);
select * from A where exist (select cc from B where B.cc=A.cc);’
如果表A比B的数据量大,那么IN查询的效率比exists高,因为这时B表如果对cc列加了索引,那么in查询效率高。
同样,如果表A比B小,那么用exists子查询效率会更高,因为可以用到A表中对cc列的索引,而不用从B中进行cc列的查询。
慢查询EXPLAIN FORMAT=JSON分析的时候,type和extra出现什么内容表示不太优?(EXPLAIN FORMAT=JSON中的哪些type和extra值表明查询可能存在性能问题?)(美团)page 1
type列。all是最差的访问方式,MySQL对整张表全表扫描。index虽然比all好,因为扫描的是索引而不是数据行,但如果索引范围大,也会导致高IO负荷。期望看到的是system const eq_ref ref range。
extra列。using temporary表示执行过程要创建临时表,发生在用groupby、orderby、distinct语句时,如果临时表存在磁盘中,性能很差。using filesort表示mysql无法用索引完成排序,要用额外步骤。
select * from x 1、where a = 1 and b = 1 ;2、where b = 1 and c = 1 ;3、where c = 1 and a = 1。怎么建索引?(boss直聘)
建立联合索引ab,bc,ca
以abc为例,在什么情况下,单查询b也能够命中联合索引?(腾讯,得物,字节)
联合查询遵循最左匹配原则,如果查询条件只涉及b,那么MySQL会尝试使用索引前缀部分。如果查询条件是where b=某个值,MySQL会先找a的值,然后在a的值相同下找b的值, 如果a的值与b的值唯一确定,那么查询就可以命中索引 。
MySQL判断哪个查询速度更快?(滴滴)
select * from student where name like “wu%”;
select * from student where name like “%wu”;
select name from student where name like “wu%”;
第三个最快,第一个其次,第二个最慢。
第三个:命中索引。索引范围扫描,用覆盖索引,用where条件过滤。
第一个:命中索引。索引范围扫描,用覆盖索引,用where条件过滤。
第二个:不命中索引。全索引扫描,用覆盖索引,用where条件过滤。
MySQL有哪些常见的索引?(讲讲索引)(讲讲数据库索引)(讲讲MySQL索引)(介绍一下 mysql 的索引吧)(索引是什么)(腾讯,携程)page 47
索引是一种数据结构,可以让数据库查询不必全表扫描。
基本原理。1.哈希表。用哈希函数将key映射到固定位置,适用等值查询。2.有序数组。用二分查找,适用范围查询,但插入删除成本高。3.B+树,在关系型数据库用的是B+树和B树。B+树中叶子节点存储数据或指向数据行。
索引类型。主键索引是基于表中主键字段建立的索引。二级索引是基于非主键字段建立的索引。聚簇索引是将数据实际存储和索引节点放在一起的索引。非聚簇索引是指索引的叶子结点存储的是数据行的主键值,不是实际数据。覆盖索引,当查询所需要的字段都包含在索引内时,就不必回表。
联合索引与最左前缀原则。索引下推,遍历索引时先过滤不满足条件的记录。
索引提高查询效率,可能影响写入性能。在数据量较大要考虑索引的页分裂问题。索引维护,对二级索引可以通过删除再重新添加来“整理”空间。
介绍下B+树的时间和空间复杂度?(腾讯)page 1
查找插入删除时间复杂度是O log n。
空间复杂度O n。
数据库中,怎么选择索引?(MySQL索引怎么建?)(如何建立索引)(怎么使用索引)(索引的建立怎么考虑)(B站,美团,腾讯)page 21
选择索引时,除了考虑查询效率,还有更新的性能影响。在innodb中,唯一索引和普通索引在查询上性能差别不大,因为都是基于B+树进行查找。但在更新场景下,差异明显。
查询效率。普通索引和唯一索引都是从根节点开始顺序查找到叶子结点,定位记录的过程一致。唯一索引查找到匹配记录后直接结束,而普通索引会继续扫描,但因为数据页有很多记录,这个开销可以忽略。
更新效率。1.唯一索引更新或插入时必须检查唯一性约束,要将数据页加载到内存,增加随机io开销。2.普通索引可以利用innodb的change buffer,当目标页不在内存时,更新操作先记录在change buffer中,延后合并到数据页。3.在写多读少的场景如社交平台帖子写入,change buffer的性能好。
结合场景考虑。1.如果业务层已经确保唯一性,用普通索引性能更好。2.但如果每次更新后数据会立即查询,从而触发merge操作,则change buffer的优势会被抵消,要具体分析。
创建索引的原则?(腾讯云智)page 1
建立索引的场景。1.唯一性。2.经常作为where条件,groupby orderby的字段,遵循最左匹配。3.用于distinct的列,利用索引排序特性。4.多表join的连接条件,用index merge。
不需要建索引的情况。1.查询中字段不是用于定位记录如出现在select中。2.字段数据重复性高如性别。3.频繁更新的字段不建议建立过多索引。
MySQL输入select指令后会发生什么?(MySQL查询语句发生了什么)(请描述一条SQL查询语句在MySQL中是如何执行的?)(说说在MySQL中一条查询SQL是如何执行的?)(美团,字节,高德)page 43
连接权限验证。
查询缓存检查。select语句可能命中缓存,但有更新操作会使缓存失效。
解析。1.词法分析。SQL语句分解为token,识别关键字,表名,常量。2.语法分析。构建解析树。3.预处理器检查。验证数据表,字段是否存在。
优化器。转换解析树为执行计划,决定各个表连接顺序,选择合适索引。
执行。调用存储引擎接口,打开表,获取锁,用索引定位数据,将筛选的数据行组成结果集返回客户端。
mysql执行更新语句发生了什么(undolog和binlog写的顺序?)(MySQL 更新语句的执行流程)(字节)page 43
客户端与数据库建立连接后,用TCPIP将SQL语句发送到server层SQL接口。
sql解析。语法检查。
查询优化。根据索引生成查询计划。
执行阶段。1.加锁。2.行定位,根据where定位要更新的行,加载到缓冲池buffer pool。3.生成undolog。更新前,生成undolog。4.生成redolog,标记为prepare状态。5.更新。写入数据页。
提交。执行Commit所有更新才会生效,binlog记录这次更新,将redolog状态变为Commit。
更新过程中如果涉及到二级索引,在后台用change buffer延迟写更新,减少磁盘随机io。
先生成undolog,用于mvcc。事务提交时,写redolog刷盘,保证持久性。最后生成binlog,记录事务变更。
canel是增量更新,如果表结构发生了变化,canel还可以监听的到么,怎么做补偿?(哈啰)
order by索引和非索引的区别?(字节)page 1
有索引的orderby,扫描方式为索引范围扫描,按索引顺序直接输出已排好序的数据,他是顺序IO,explain语句的extra字段为using index。
无索引的orderby,扫描方式是全表扫描加上排序filesort,先把所有行读入内存,再做排序,extra字段为using filesort。
MySQL binlog和redolog写顺序(字节,shopee)page 42
两阶段提交:第一阶段,redolog标记为prepare状态,第二阶段,binlog写入完成后,redolog标记为commit状态。
先写redolog再写binlog。
select from where orderby groupby having执行顺序?(美团)
from:从指定表获取数据。
where:过滤数据。
groupby:对子句的列数据进行分组。
having:过滤分组后的数据。
select:选择列数据。
orderby:列数据进行排序。
假设查询条件为user_id,并且他存在索引。distinct和groupby哪个去重性能更好?(distinct和groupby区别)(阿里)page12
在语义相同,有索引情况下,groupby和distinct都能用索引,效率相同。
在语义相同,没有索引情况下,distinct效率高于groupby。原因是distinct和groupby都会进行分组操作,但groupby可能先进行排序,触发filesort,导致性能下降。但是MySQL8.0取消了隐式排序,两者性能相似。
建数据库有什么原则?(百度,拼多多)
三大范式。
第一范式,所有字段值都是不可分解的原子值。
第二范式,数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关。
第三范式,数据表中的每一列数据都和主键直接相关,而不能间接相关。
第三范式的作用与原理(B站)
消除传递依赖。
表必须满足第一范式,所有字段都是不可分解的原子值,第二范式,表中每一列都和主键相关。第三范式要求每一列都要和主键直接相关。
事务怎么实现的?(拼多多,百度,快手)page 1
MySQL怎么保证原子性?(得物)
事务四大特性的实现原理(京东)
MySQL如何保证不丢失?(快手)
MySQL ACID的一致性依赖什么机制实现?(腾讯)
原子性,事务所有操作要么全部成功要么全部失败,mysql通过undolog实现。
一致性:保证事务执行前后,数据库状态保持一致。通过数据库约束和隔离级别实现。事务有四个隔离级别,读未提交,读已提交,可重复读,串行化。
隔离性:并发的多个事务互不干扰。通过锁和MVCC实现隔离性。
持久性:事务提交,结果将永久保存在数据库。通过redolog保证。
写入 redolog 和 写入数据页的哪个快?(高德地图)
redolog的写入比向数据页写入快得多。因为redolog是顺序写,而数据页是随机写。redolog和数据页都有缓冲区,分别是redolog buffer和buffer pool。
写入 redolog 后如何进行查询怎么查到变化的数据呢?(高德地图)
查询语句select是结合MVCC在buffer pool脏页中查询的,如果未找到则去磁盘找。
如果innodb一个有索引的列很大,不能全部存入内存中的缓冲区,如何通过索引进行查找?(蚂蚁)
按需加载B+树页,从根节点开始,把查找路径上的几个页先加载进来。
MySQL怎么实现数据恢复?(得物)
备份恢复:定期备份数据库。全量备份,增量备份,差异备份。
二进制日志:binlog记录对数据库所有修改操作,通过回放二进制日志,可以恢复到某个时间点的数据。
事务日志:redolog记录了所有未提交的事务操作,用于在数据库崩溃后恢复未提交的事务。
可重复读RR隔离级别下,B事务执行3s后,开启A事务 两个事务都需要对金额进行修改,A事务2s执行完并提交(不一定成功),B事务在A事务提交后10s才执行完并提交,问A能否正常提交事务,最终金额为多少?(两个事务A B都要对同一笔金额进行修改。事务 B 先启动(例如 t=0 开始执行),3 秒后(t=3)启动事务 A。事务 A 用 2 秒完成操作并在 t=5 时提交可能失败,而事务 B 则在事务 A 提交后约 10 秒(t=15)才结束并提交,问A能否正常提交事务,最终金额为多少?)(得物)page 96
最终的金额为A后修改的值,丢失了B的更新,因为RR隔离级别select语句事务读取的值是启动事务时的快照,如果用select for update则是A和B修改的值。
t0:事务1 select value from tb where id =1; t1:事务3 update tb set value=value +1 where id=1; t2:事务3提交。t3:事务2 update tb set value=value+1 where id=1; t4:事务2 select value from tb where id=1;t5:事务2提交。t6:事务1 select value from tb where id =1;t7: 事务1提交。问事务1,事务2读出的value是多少?(美团)page 48
假设可重复读隔离级别,初始value为V。
事务1。一开始获取快照,在可重复读隔离级别下,事务1看到的是开始时的快照,结果为V。
事务3。数据库中实际值为V+1。
事务2。实际值变为V+2,事务2会看到自己事务的更新,返回V+2。
mysql什么时候加行锁,什么时候加间隙锁?(腾讯)page 96
行锁:当用唯一索引进行等值查询且命中时,加行锁。
行锁:读已提交下,innodb不使用间隙锁,所有操作都通过行锁控制并发。
间隙锁:用唯一或非唯一索引进行范围查询。
间隙锁:非唯一索引等值查询
注:三个影响因素:唯一索引,范围查询,数据是否存在。
间隙锁是加在哪个位置的,加一把间隙锁要做哪些步骤(淘天)page 1
间隙锁是在索引记录之间上加的锁,他锁定两个相邻索引值之间的空白区域。
加间隙锁步骤。1.解析查询。innodb根据where确定使用哪个索引,以及用共享S锁或排他X锁。2.设置意向锁。在表上加排他IX锁。3.唯一索引优化,next key锁退化为行锁。否则innodb按索引顺序扫描
行锁的本质是什么?锁的是什么?如果修改的字段上没有索引,行锁还能用吗(58同城,滴滴)
锁定的是索引。
没有索引,会全表扫描,会对所有记录加next key锁,相当于把整个表锁住了。
保证事务的隔离性加的锁是什么锁?(shopee)
共享锁和排他锁:控制读写操作并发访问。共享锁允许多个事务同时读取数据,排他锁在事务修改数据时,其他事务不能同时读取或修改。
意向锁:提高锁效率。如当一个事务要对表某些行加锁,他会先获取表级意向锁,然后再获取行级锁。这样其他事务在获取表级锁可以快速判断是否存在冲突。
记录锁、间隙锁和next key锁:解决幻读问题。
select for update会不会加表锁,什么情况下加表锁?(for update什么时候表锁什么时候行锁?)(腾讯,度小满,百度,淘天多次考)page 126
唯一索引,等值查询,next key锁退化为记录锁。
二级索引,等值查询,二级索引的行锁和主键索引行锁,锁住二级索引记录,再锁住聚簇索引对应的行。优化。非唯一索引等值查询,先定位到值所在的位置,再向右确认无更多匹配,最后一个不满足条件时加间隙锁;同时对找到的记录加记录锁。
范围查询,next key锁。
带limit的删除和更新,加next key锁或行锁。
没有索引或不能走索引,加表锁。
select for update加锁怎么加的?(美团)page 1
InnoDB 加锁的基本单位是 Next-Key锁,左开右闭,执行时,会“动态地”对遍历过程中遇到的每个区间/记录逐一加锁。
加锁原则。1.只有在查找/扫描过程中访问到的对象(间隙 或 record)才会被加锁。2.唯一索引上的等值查询在定位时,Next-Key锁会退化成行锁。3.向右遍历且碰到不满足条件的末端时,Next-Key Lock 会退化为纯间隙锁。
讲讲SELECT * FROM t WHERE id > 9 AND id < 12 ORDER BY id DESC FOR UPDATE;语句怎么加锁 page 1
根据 ORDER BY id DESC,引擎内部先尝试定位 id=12,未命中,则找到其落在的间隙 (10,15);
因为这是一次“等值搜索”退化,锁定纯间隙 (10,15)。
向左遍历锁定其余行,扫描到 id=10、id=5、id=0,分别对区间 (5,10]、(0,5] 施加 Next-Key Lock。
InnoDB有哪几类行锁?
记录锁(Record Lock),锁定单个行记录;间隙锁(Gap Lock),锁定一个范围但不包括记录本身;以及next key锁(Next-key Lock),结合了记录锁和间隙锁,锁定一个范围并包含记录本身,有效防止幻读问题。
Next-Key Lock 临键锁的加锁范围?(难)
左开右闭。对于唯一索引和非唯一索引情况不同,以及等值查询和范围查询也不同。
场景1:唯一索引的等值查询。若查询值存在,退化为记录锁。若查询值不存在,退化为间隙锁。
场景2:非唯一索引或范围查询,next key锁会锁定 满足条件的索引范围及间隙 。
select * from users where name=’Alice’ for update; 1.锁定所有Alice记录。 2.锁定相邻间隙,(前一个索引值,’Alice’],(‘Alice’,后一个索引值)
select * from users where id>5 and id<15 for update; 1.锁定第一个符合条件的记录如id=10以及之前的间隙(5,1]。 2.依次向后锁定,直到不满足条件的第一个记录如id=15,范围为(10,15]。
next key lock临键锁退化为行锁的过程?(两个SQL语句,一个where条件是唯一索引,一个where条件是普通索引,两个查询的加锁方式有什么不同?)(小红书)page 42
分情况。 唯一索引上的等值查询。如果行存在,则退化为行锁。如果不存在,则退化为间隙锁。
非唯一索引上的等值查询。如果行存在,会锁定行以及间隙。如果行不存在,则使用间隙锁。
范围查询。 唯一索引的范围查询。还要分情况,如果是大于等于,那么这个行的next key会退化成记录锁,如果是小于等于,扫描到终止范围查询的行时,该行的索引的next key锁会退化成间隙锁。
如果A是主键,查询语句是”A=0”,此时加的是record锁还是next-key锁?如果A不是主键而是非唯一索引呢?(得物)page 146
执行 select * from xxx where a = 1 for update 会加什么锁?(度小满)
1.主键:行存在加行锁,不存在退化为间隙锁。
2.非唯一索引:
行存在,对符合查询条件的二级索引加next key锁,对第一个不符合查询条件的二级索引加间隙锁。
行不存在,对第一个不符合查询条件的二级索引加间隙锁,这时的间隙锁还要考虑 主键值 ,这个区间是由下一个存在的索引记录的二级索引值和主键值构成的。
select * from user where userid=5 for update,假如userid是索引但是没有5这个数据,锁的是什么?假如没有索引也没有5这个数据,锁的是什么?(在执行SELECT * FROM user WHERE userid=5 FOR UPDATE;时,如果满足条件的记录不存在,那么:如果 userid 是索引,锁定的是什么?如果没有索引,锁定的又是什么?)(快手)page 146
1.间隙锁,比如数据(1, ‘User1’),(2, ‘User2’),(7, ‘User7’),(8, ‘User8’); 锁定(2,7),即此id上一个记录到下一个记录之间的间隙。
2.是记录锁,所有主键的记录锁。
如果A是非唯一索引,表里只有”A=1”和”A=10”两条数据,此时有两条命令:”SELECT * FROM … WHERE A=5 FOR UPDATE”;”SELECT * FROM … WHERE A=6 FOR UPDATE”。请问这两条命令会互斥吗,什么时候互斥、什么时候不互斥?(得物)page 146
不互斥,加间隙锁,两个间隙锁兼容,但如果其中一个事务往间隙锁的区间插入数据,此时是互斥的。
MySQL怎么创建写锁?(得物)
select for update加写锁
insert … on duplicate key update直接更新数据,如果遇到唯一键冲突,会加写锁。
update,delete。
讲一讲热查询,冷查询和对应的解决方案。(百度)
归档:将冷数据归档到低成本存储如hdfs,减少主数据库压力。
异步处理:将冷查询放入消息队列,异步处理,避免阻塞主线程。
定期优化:定期对冷数据进行索引优化和数据清理,提高查询效率。
select 和 update哪个是快照读,哪个是当前读?(百度)
select默认是快照读,update是当前读。
快照读和当前读有什么区别?(美团)page 37
快照读用MVCC,事务启动时生成read view,后续查询基于这个快照。
当前读对数据加锁。有幻读问题,前后两次查询结果集合不一样。innodb为解决rr隔离级别下当前读的幻读,用了next key锁。
MySQL是怎么解决幻读的(淘天)page 1
可重复读隔离级别下:事务启动时建立快照,普通select快照读,后续重复读取给予快照,不会看到其他事务的插入,避免幻读;而当前读如select for update,lock in share mode或DML语句用next key锁锁定范围数据。用MVCC和next key锁的组合,innodb在可重复读级别下很大程度避免了幻读。
order by是怎么实现的(order by是怎么工作的)(拼多多)page 1
有三种实现,全字段排序、rowid排序、索引排序。
实现一,全字段排序,假设where字段建立二级索引。执行流程,初始化sort buffer,存放列,扫描二级索引,取出主键id,根据id回表取出整行,装入sort buffer,扫描完所有满足条件的行后,用快速排序或外部排序对该字段排序。
实现二,rowid排序,当单行长度过大时,MySQL的sort buffer只存放排序字段和主键id,同样扫描二级索引,回表取出排序字段和主键id,排序完成后,再对这些记录再次回表,降低了排序时的内存压力,但多了一次回表。
实现三,索引排序,需要保证索引扫描顺序即为所需排序顺序,这样可以避免file sort。
MySQL双写缓冲区(字节,拼多多)page 37
双写是innodb引擎保证数据页完整性的机制。解决数据页部分写入问题。
原理:当innodb要将数据页写入磁盘时,先将数据页写入到双写缓冲区中,完成后再将这个数据复制到实际数据文件中,当数据页同时在双写缓冲区和数据文件都有一份拷贝时认为写入成功。
可以配置innodb_doublewrite启用。
了解过非关系型数据库吗?(讲讲非关系型数据库)(快手)page 1
非关系型数据库牺牲ACID部分特性,如BASE式最终一致,换取性能和灵活性。
kv存储如Redis,解决复杂数据的存储和低延迟。只保证一致性和隔离性,不保证原子性和持久性。使用场景如会话缓存、排行榜、队列。
文档数据库如MongoDB,解决动态schema问题。使用场景如电商商品、用户画像。
列式存储如HBase、ClickHouse,解决列级别统计的io优化问题。使用场景如离线分析、数据仓库。
全文搜索如ElasticSearch,解决高性能模糊搜索,多维搜索问题。使用场景如搜索引擎。
参考:https://time.geekbang.org/column/article/8377
https://time.geekbang.org/column/article/10301
https://time.geekbang.org/column/article/147946
MySQL中文字符占几个字节,占几个字符(B站)page 1
三个字节。1个字符。
tiny int和small int区别(B站)page 1
tiny int一字节,-128到127。small int -32768到32767。
MySQL int(11)中的11代表什么含义
MySQL int类型不管怎么写都占用4字节,11是显示宽度,最多显示11个字符。
分布式事务,讲讲2PC和TCC(shopee)page 1
两阶段提交。准备阶段,协调者向参与者发送准备提交请求,参与者执行事务,返回是否可以提交响应。提交 阶段,协调者发现如果所有参与者都可以提交才提交。优点保证强一致,缺点协调者单点故障。
tcc。try,锁定资源,如在MySQL中引入锁定字段。confirm。业务逻辑。cancel。解锁资源,注意只能解锁当前tcc事务在try锁定的资源。
参考:https://time.geekbang.org/column/article/491501
有一张表有学生、班级、性别等字段,如何通过一条SQL语句查出各班级分别有多少男生和女生?(得物)page 1
SELECT class, SUM(CASE WHEN gender = ‘男’ THEN 1 ELSE 0 END) AS 男生数, SUM(CASE WHEN gender = ‘女’ THEN 1 ELSE 0 END) AS 女生数 FROM students GROUP BY class;
OLAP和OLTP数据库方面的设计的区别?(字节)
业务系统分为OLTP和OLAP,OLTP是联机交易场景,OLAP是联机分析场景。OLTP是面向交易处理,单笔交易数据量小,但要在短时间内给出结果,场景如购物、缴费、转账。OLAP是基于大数据集计算,场景如生成个人年度账单和企业财务报表。
HTAP
HTAP实现了OLAP和OLTP的融合。
讲讲怎么减少行锁对性能的影响 page 1
控制事务中锁的申请时机。根据两阶段锁协议,如果一个事务要更新多行,尽量把可能锁冲突的放到事务后期执行。让短事务先释放锁,减少长事务影响。
降低死锁检测cpu开销。如果业务确定不会死锁,临时关闭死锁检测innodb deadlock detect。
数据库端并发控制。在服务端或中间件层面进行并发排队管理。在数据进入innodb前,针对热点行的请求进行排队。
数据库设计降低热点冲突。如果业务允许,拆分一行数据为多行降低锁冲突概率。比如顾客在影院购票,给顾客余额扣除电影票价,给影院账户余额增加,高并发活动时可能死锁,可以将账户金额拆分成10条记录,每次更新随机选择一条加值。
讲讲MySQL count很慢怎么办?page 1
count对查询结果每一行计数。myisam将表的总行数存储在磁盘上,innodb不存储总行数,必须遍历。
count 字段。要考虑字段是否为null,且每行要读取字段值。
count 主键ID。要返回id值,会拷贝字段值。
count 1和count *。MySQL在优化器层面对count * 做了优化,不取出字段内容,count *和count 1区别可忽略不计。
如何解决。1.尽量用count *或count 1。2.如果业务场景数据量大,可以考虑设计计数表或用缓存,要注意一致性问题。3.分库分表,增加覆盖索引,如tinyint加速统计。
sql语句中#与$有什么区别(百度)page 1
井号由jdbc的PrepareStatement负责绑定参数值。没有SQL注入风险。
dollar被直接替换成对应的字符串文本,有SQL注入风险。
SQL注入(腾讯)
攻击者通过在应用输入字段中插入恶意SQL代码,操作数据库查询。
如何防止:
用预编译语句:将用户输入作为参数处理。
输入验证。
最小权限原则。
MySQL支持哪些存储引擎,默认使用哪个?
MySQL 支持多种存储引擎,包括 InnoDB(默认且支持事务)、MyISAM、Memory 等,可以通过 SHOW ENGINES; 命令查看所有支持的存储引擎。
MyISAM和InnoDB的区别?page 19
InnoDB 支持 事务 、 行级锁 ,并能在系统崩溃后安全恢复数据,支持外键约束,而 MyISAM 则提供 全文索引 和较高的读取性能,但不支持事务、行级锁和崩溃后的安全恢复,不支持外键约束。
并发事务带来了哪些问题?不可重复读和幻读有什么区别?
可能导致问题如脏读、丢失修改、不可重复读和幻读,这些问题需要通过适当的隔离级别和锁机制来避免,以确保数据库操作的一致性和完整性。
不可重复读是单行数据的变化,幻读是整个结果集的变化。
表级锁和行级锁了解吗?有什么区别?
表级锁会锁定整张表,适合于低并发的写操作,而行级锁锁定具体的数据行,适合高并发的写操作,提高了系统的并发能力。
哪些操作会加表级锁?哪些操作会加⾏级锁?请简单举例说⼀下
alter table会加表级锁,update以及for update会加行级锁。
另外对于普通字段,不是主键,没有索引,在可重复读隔离级别下,是加表锁。
MySQL存储引擎架构了解吗?
MySQL 的存储引擎采用插件式架构,允许不同的表使用不同的存储引擎,支持第三方存储引擎开发,使得数据库可以根据不同的应用场景选择最适合的存储解决方案。
redis
Redis 做分布式缓存,流量很大怎么处理(淘天)
在高流量场景,用Redis集群或主从加哨兵部署分布式缓存。另外可用全局负载均衡SLB和NLB,以及代理层Proxy设计,做读写分离和多租户分流。
调优手段。1.网络优化。调大操作系统文件描述符ulimit - n,避免达到max clients限制。2.内存管理。关注info Memory的mem fragmentation ratio指标,开启active defrag整理内存碎片。
热key。1.热点分散。对单一key拆分成多个小key。读写时轮询分片,最后在应用层合并结果。2.本地缓存。在代理层用LRU本地缓存,由DB定时计算热点集合反馈给代理。
Redis锁有哪些?怎么实现?(滴滴考过,蚂蚁)page 36
redis实现分布式锁删除时应该注意哪些?(得物)
分布式锁怎么加锁,怎么解锁的?(58同城)
1.基于 SET 命令的分布式锁,SET lock_key unique_lock_value NX PX lock_timeout,NX 表示只有在 lock_key 不存在时才设置,PX 表示设置键值对的过期时间(单位:毫秒),lock_key 是锁的键名,unique_lock_value 是锁的唯一值,防止锁被非持有者释放。
有三个关键点 ,首先是原子命令加锁,set key和设置过期时间一个命令完成。
第二点就是设置的值是随机值或者每个客户端唯一的值。
第三点就是设置的值是为了安全释放锁,释放的时候要检查key是否存在,还判断值是否和我指定的值一样。
2.redlock红锁。(多节点容错锁)
3.Redisson库。
Redis分布式锁的原理是什么(贝壳)page 1
加锁。set nx e x,nx仅当lock key不存在时设置。e x设置过期时间。
解锁。避免他人误删除锁,用lua脚本保证读取,条件判断,删除操作原子。用eval命令在Redis内执行,不被中断。
Redis如何保证原子性。1.单线程模型,Redis进程只有一个命令执行线程。2.IO多路复用,Redis用epoll拿到一批就绪socket,但逐个回调,仍是单线程串行。
setnx实现的分布式锁有什么问题?
死锁。如果持有锁的客户端宕机,锁将永远存在,其他客户端无法获取,导致死锁。解决方案,必须设置锁的过期时间,set e x n x。
锁误删。客户端a超时释放后,客户端b获取锁,此时客户端a完成任务后误删客户端b的锁。解决方案,锁的value包含唯一标识,释放时用lua脚本原子校验value是否匹配。
redisson分布式锁的问题是什么?
redisson分布式锁底层使用什么数据结构?
Redis hash存储锁状态,key存储锁名,field存储uuid和thread id,value存储重入次数。
redisson公平锁用到Redis List和z set。List作为请求队列,锁获取失败时,将uuid和thread id,r push到List末尾。释放锁或重试时,用l pop检测自己是否排在队头。z set跟踪超时。z add将每个线程超时时间作为score,成员为uuid和thread id
redisson 是怎么确定锁的拥有者的(美团)
redisson给每个调用者打上唯一标识,即UUID加上线程id实现的。
redis什么底层原理保证可以作为分布式锁?(字节)page 36
单线程模型。redis内部采用单线程事件循环,保证命令顺序执行,set nx ex命令可以保证设置锁是原子性的,只能有一个客户端能成功获得锁。
过期机制。给锁设置ttl,避免死锁问题,通过redis内部定时器机制实现。
Redis什么底层原理保证可以作为高可用的锁?(字节)page 36
哨兵。redis用哨兵机制实现自动故障转移。当主节点故障时,哨兵自动将从节点提升为主节点。
集群模式。采用数据分片。
redlock算法。在多个redis实例同时加锁。
讲讲redlock底层原理。page 12
redlock在N个大于5Redis节点加锁。
多节点加锁。客户端依次向n个Redis实例发送加锁请求。用set nx px命令,以及随机字符串区分不同客户端。
加锁成功。客户端要大于等于n/2+1个节点成功。
时间校验。用初始过期时间减去加锁耗费时间保证执行业务期间不会失效。
失败回滚。加锁失败则向已成功加锁的节点发送释放锁请求。
存在时钟问题。
设计分布式锁需要考虑什么问题?(得物,作业帮多次考)page 1
互斥性:分布式锁基本要求,在redis或Zookeeper设置唯一的value实现。
死锁问题:设置锁的超时时间、看门狗解决。
容错性:依赖于多个节点实现高可用。如redis可以配置主从复制和哨兵机制确保主节点失效,锁服务仍然可用。
可重入:在锁实现中引入线程ID或客户端ID实现。
讲一讲分布式锁的实现?(实现分布式锁有什么途径)(得物,作业帮,美团)page 1
基于数据库实现。创建锁表,通过插入唯一键获取锁,资源标识作为主键。
基于redis实现:1.用redis setnx命令实现锁机制。在键不存在时设置键值返回1,如果键存在返回0。设置过期时间防止死锁。2.redlock。部署n个独立主从隔离的Redis实例,客户端向所有实例发setnx px命令,在超过一半节点上获取成功,则持有锁,并锁的有效期为TTL减去加锁时间。
基于Zookeeper实现:用它的临时顺序节点实现。每个节点尝试创建一个临时顺序节点,判断自己是否是最小节点,如果是则获取锁成功,否则监听前一个节点的删除时间。可靠性高。
基于e t c d实现。用raft共识算法,写操作必须多数节点一致后才返回成功,解决Redis异步复制导致的多主并发持锁的情况,用事务保证原子性。
Redis 分布式锁自动续期如何实现(饿了么)page 1
用守护线程watchdog和锁持有者校验实现。
加锁时启动守护线程,并将锁设置初始过期时间。
周期性续期,守护线程定时,即初始TTL的三分之一,检查锁状态。他首先判断当前客户端是否还持有锁,比较Redis key的值实现。
延长过期时间,用expire命令重置锁的TTL。
业务完成终止续期,应关闭守护线程。
解决并发问题,数据库和分布式锁哪个效率好一点?(拼多多)page 146
数据库锁适用单机或简单事务,强一致性需求,通过行锁、表锁、乐观锁版本号实现,瓶颈是数据库吞吐量,复杂度低,可靠性依赖数据库事务ACID。
分布式锁适用分布式系统跨服务协调,高并发场景,用Redis或Zookeeper实现,瓶颈是中间件性能,复杂度高,需处理锁续期、容错,可靠性依赖中间件高可用。
分布式锁redis有什么应用?(得物)
参考。 防止重复提交:电商系统中,用户可能多次点击提交订单,redis分布式锁可以确保同一时间只有一个请求能处理订单。
资源访问控制:多服务共享资源情况下,它可以确保一个资源同一时间被一个服务访问。
任务调度:在分布式任务调度系统如xxl-job中,他可以确保同一任务不会被多个节点同时执行。
秒杀系统:他可以防止超卖现象,确保库存正确扣减。
redis分布式锁可重入吗?(得物,58同城,百度)
默认redis分布式锁是不可重入的。
实现方法:可以参考Reentrantlock使用线程标识。获取锁时,除了设置锁的键值对,还可以将线程的唯一标识如线程ID存储在锁的值中,每次获取锁时,检查当前线程标识是否与锁的值匹配。
设计锁的时候如何考虑公平性和可重入性?(阿里)
讲一讲雪花算法和时钟回拨(百度)page 36
雪花算法,是有序的吗?(字节)page 36
雪花算法是用于生成分布式唯一ID的算法。思想是将一个64位ID分成多个部分。
时间戳41位,精确到毫秒。机器ID10位。序列号12位。
时钟回拨问题:由于网络延迟或系统时间同步问题,可能导致雪花算法生成ID出现重复。
解决方案:等待机制:当检测到时钟回拨时,可以简单等待时钟追上当前时间再继续生成。
预留ID区间:在设计时预留一部分ID区间,处理时钟回拨情况。
使用逻辑时钟:即使物理时钟回拨,逻辑时钟仍然单调递增。
雪花算法不是有序的,有时钟回拨问题,id可能重复。
雪花算法的时钟回拨问题 page 1
解决方案:等待机制:当检测到时钟回拨时,可以简单等待时钟追上当前时间再继续生成。
预留ID区间:在设计时预留一部分ID区间,处理时钟回拨情况。
使用逻辑时钟:即使物理时钟回拨,逻辑时钟仍然单调递增。
雪花算法由符号位,时间戳,机器号和序号组成,怎么保证全局有序性?时间戳只会增大不会减小,序号可以保证一毫秒内的有序性。那你同一毫秒内机器号大的生成序号小,最后总大小反而大怎么办?(雪花算法如何通过时间戳、机器号和序号来保证生成的全局唯一、有序的ID?假设在同一毫秒内,不同机器生成的ID中,机器号大的节点可能得到较小的序号,导致整体ID数值更大,这种情况如何确保全局顺序不出错?)(字节)page 157
在同一毫秒内,虽然是按机器号与序号共同排序,但他不会破坏全局有序性。雪花算法他只能保证按字段组合的有序性,在同一毫秒,不同机器之间的序号根本就不能比,不保证他们之间的顺序,能保证的只能是同一毫秒内,同一机器内按序号排序。
分布式锁一个线程锁过期了另一个线程拿到了怎么办?如果是宕机导致过期,又该怎么办?(Redis加了锁之后服务挂了,别的实例拿不到锁了,怎么办)(小红书)page 39
如果分布式锁到期了,没有完成锁内部逻辑调用怎么办?(得物)
用看门狗对锁进行续约。
设置锁成功后,启动一个看门狗,每隔一段时间如10秒检测业务是否处理完成,检测依据是判断分布式锁key是否还存在,如果不存在进行续约。
如果宕机,这时候看门狗就不能工作,也就不能续期,那么在默认的配置下最长 30s 的时间后,这个锁就自动释放了。
Redis分布式锁的key超时被删除了任务还没完成怎么解决?(元戎启行)page 1
首先用看门狗机制,在锁仍持有时,客户端定期向Redis发续期命令,将锁TTL延长。如果客户端宕机,则续期也会中断。
其次如果任务本身非常耗时,则应拆分为多阶段独立加锁的小事务。
如果获取分布式锁后,业务执行过程中抛异常了怎么办(蚂蚁)page 1
用try finally,finally块中执行释放锁。
在catch块中捕获业务异常并记录日志。
为分布式锁设置合理过期时间,以及看门狗机制,即使应用崩溃,Redis锁在过期后自动释放。
对于分布式锁后,什么情况下,业务逻辑时间比过期时间短,但是仍然会用到过期时间(蚂蚁)page 1
防止死锁。如果发生了两个线程相互持有并等待对方的分布式锁,则需要过期时间保证释放。
redis分布式锁为什么会出现a线程释放b线程锁的情况。(作业帮)page 35
锁过期。一个线程获得锁,业务执行时间超过TTL。此时另一个线程重新获得锁,如前一个线程业务结束后调用释放锁,就会发生。
缺少唯一标识。释放锁应先校验锁的持有者。
非原子操作。校验锁的持有者和删除锁应该为原子操作。
redisson的看门狗机制怎么实现的?(字节)page 47
默认锁定时间和续期。客户端通过redisson获取分布式锁时,默认锁超时时间是30秒,只要持有锁的线程仍在运行,redisson会自动续期。
后台看门狗线程。redisson内部启动后台定时任务,用scheduled ExecutorService实现,定期检查所有正在持有的锁。
原子续期。用lua脚本续期。续期前判断当前锁持有者是否为当前客户端。
redisson分布式锁的工作原理(shopee)
Redisson分布式锁有什么问题? page 1
看门狗隐患和redlock算法的不安全性。
问题一,如果客户端整个实例崩溃或者线程崩溃,看门狗无法续约,锁会在ttl到期后自动释放。
问题二,续约失败场景。如果Redis连接断开,watchdog续约命令会超时失败,锁到期释放。解决方案是简单重试。
问题三,redlock算法不安全。比如gc暂停的案例。客户端1在获取锁过程中gc暂停很久导致锁过期,而锁被客户端2抢到,客户端1随后接收到过期前的加锁成功响应,结果两个客户端都认为自己持有了锁。
redisson可重入怎么实现的?(字节)page 47
锁数据结构。线程第一次获取锁时,redisson在redis创建一个锁key,value是json,记录了当前持有锁的线程标识(为客户端唯一id加上线程id)以及重入计数。
获取锁逻辑。当一个线程请求锁,redisson执行lua脚本,如果锁不存在,用类似setnx原子操作创建锁,设置重入计数为1。如果锁存在,检查存储在锁中的线程标识是否与当前线程匹配。
红锁加锁5台机器成功加锁3个,但其中的主节点宕机,又恢复然后变成从节点,现在只剩两个机器有红锁,其他线程来获取不能加锁怎么办?红锁该如何释放?(使用 Redlock 实现分布式锁时,在 5 台机器上尝试加锁,正常情况需要至少 3 台成功持锁。但实际中,只有 3 台机器加锁成功,其中一台是主节点,该主节点宕机后恢复,却变成了从节点,导致现在只有 2 台机器实际持有锁。其结果是其他线程无法获取锁。此时该如何释放已有的 Redlock 锁?)(腾讯)page 1
其他线程来可以加锁,因为主节点宕机恢复变成从节点,此时之前的锁失效,加锁的机器变为2台,其他的线程可以获取剩下的3个机器的锁。
Redis 为什么能做分布式锁?(猿辅导)
在redis中,setnx可以实现分布式锁,因为redis单线程模型和原子操作特性。单线程模型确保同一时间只有一个命令会执行。而原子操作setnx保证设置锁时不被其他操作打断。
String的应用场景有哪些?(滴滴考过)
缓存 。我们可以把一些频繁访问的数据,如用户信息、商品详情,存储在redis中。 计数器 。比如,我们可以用redis String来记录网站的访问量,点赞数,评论数等,redis的incr和decr命令可以很方便实现计数功能。 分布式锁 。我们可以用redis String实现分布式锁,确保在分布式系统中, 同一时间只有一个客户端访问一个资源,setnx命令可以实现这个功能。 session存储 。比如在web应用中,我们可以把用户session信息存储在redis中,实现session的共享。 限流。我们可以用redis String实现api限流功能,防止某个api过度调用,可以用incrml 记录某个api调用次数。
redis的使用场景有哪些(满帮)page 1
缓存。redis缓存频繁访问的数据,减少数据库负载。比如缓存用户会话数据,热门文章。
会话存储。在分布式系统中,redis string存储用户会话信息,保证用户在不同服务器切换会话状态一致性。
消息队列:redis发布订阅可以实现简单消息队列。
计数器:redis原子操作适合做计数器。
排行榜:z set可以实现。
分布式锁:redis hash在分布式系统中保证对资源的互斥访问。
setnx的底层原理是什么?(京东,满帮,蚂蚁)page 146
原子性通过单线程模型实现,Redis基于单线程的事件循环处理命令,并通过哈希表操作,原子性检查键是否存在。
什么是延迟双删?延迟双删可能会导致什么问题?(京东)
延迟双删是在更新数据库数据时,先删除缓存中的数据,然后更新数据库数据,最后在一段时间(通常是主从同步时间)后,再次删除缓存中的数据。
可能带来的问题: 延迟时间难以设定:主从同步时间受网络和负载影响,难以准确预估。若太短则有旧数据残留;太长则增加系统响应时间。
删除操作可能失败:两次都可能因为网络抖动或宕机失败,需配合重试机制(如消息队列)保证可靠。
系统复杂性增加:需引入延迟任务(如定时器、消息队列)实现第二次删除。
redis集群如何同步数据的?(美团)page 35
用主从复制,以及gossip协议维护节点状态。
分布式数据分片。redis集群将数据分为16384个槽,用一致性哈希算法实现数据和节点之间映射关系。
主从复制。1.主节点写,将命令异步发送给从节点。2.从节点重新连接时,主节点生成rdb发送给从节点。3.主从间有复制积压缓冲区,从节点下次重新连接可以从缓冲区获取数据。
redis lua脚本原理?(百度)page 147
Redis采用单线程处理命令,lua脚本执行期间独占整个线程,所有操作原子执行。
执行流程:通过eval或script load加载脚本,redis计算脚本SHA1摘要。脚本首次执行后会被缓存,后续通过SHA1调用。redis将lua脚本编译为字节码执行。
Redis如何判断数据是否过期呢?(得物)
用一个名为过期字典的结构来追踪键的过期时间,该字典存储在每个Redis数据库实例中,保存键到对应过期时间(UNIX时间戳,毫秒精度)的映射。
向 1G 的 Redis 陆续写入 2G 的数据、会发生什么?(猿辅导)page 144
redis的key没有过期,会不会被回收?(得物)
redis内存淘汰策略讲讲。(redis 的内存淘汰机制)(作业帮多次考,shopee,美团)
Redis内存淘汰机制了解吗?(或者问Redis怎么只存储部分热点数据?)
常用的缓存淘汰算法有哪些?(58同城)
当maxmemory达到上限时,又有新写入请求,必须淘汰旧数据腾空间。
淘汰策略。1.不淘汰,noeviction,写请求直接返回错误,不推荐用于缓存场景。2.仅对设置过期时间的key淘汰,volatile-random,随机删除;volatile-ttl,删除最近将要过期的;volatile-l r u,基于LRU;volatile-l f u,基于LFU。3.对所有key淘汰,allkeys-random,随机删除;allkeys-l r u和l f u。
LRU算法。逻辑上维护链表,看数据最后一次使用到发生淘汰的时间长短,但Redis不会移动整个链表,且在淘汰时,Redis随机抽样N个,选出一个最久未访问的一条删除。
LFU算法,看一定时间内数据被使用的次数,对象越冷门越容易被淘汰。
首选allkeys-l r u,对冷热数据识别最优。
LRU怎么设计?时间复杂度?(腾讯)
用双向链表和哈希表。
get(key),put(key,value)时间复杂度为O 1。
双向链表维护数据访问顺序,头部表示最近使用的数据,尾部表示最久未使用的数据。
哈希表快速查找链表节点。
lru(LRU,最近最少使用)和lfu(LFU,最近最不经常用)的原理,lru和lfu可以有什么优化(腾讯)page 157
lru:数据最近被访问过,将来被访问可能性较大。实现:hashmap与双向链表,hashmap用于o1查找,双向链表维持元素顺序。优化:1.并发环境引入并发链表和分段锁。2.分段lru:2q算法,将缓存分为两部分,一个存初次放入的短期数据,一个存经过时间考验的长期数据。防止缓存污染问题。
lfu:如果数据被访问频率低,将来被访问的概率也较小。实现:计数器。优化:1.o1复杂度:用频率桶,将所有访问频率相同数据放到一个桶,维护桶之间顺序。2.惰性更新:不是每次访问都立即更新频率,而是将更新延迟到一定时机批量处理。
LRU为什么要双向链表?能不能是队列?(快手)
存储在双向链表中的数据,可能要把这个数据删除,删除节点要获取前驱节点,如果不是双向链表,就得从头遍历。
可以是队列,但是删除时间复杂度高。
过期数据的删除 策略 了解吗?(得物,shopee)page 1
说一下redis的过期机制(腾讯音乐)page 1
过期删除两种策略。被动删除,在客户端执行get hget等操作时,Redis检查key是否过期。如过期则删除,当做不存在返回。
定期删除。Redis默认每100毫秒执行一次过期循环,从带过期设置的key随机采样默认20个,检查是否过期,过期则删除。
redis持久化有多少种丢数据的可能?linux会丢数据吗?(携程)page 147
RDB:周期性快照,若两次快照之间宕机,最后一次快照后的修改丢失。如配置为save 60 1000(60秒内1000次修改触发快照)若在59秒崩溃,则这59秒的写入丢失。
RDB依赖fork的子进程生成快照,如子进程崩溃则丢失。
AOF:写缓冲未同步:AOF默认每秒同步appendfsync everysec。
Linux丢失数据:内核缓存未刷盘:redis的RDB或AOF写入先进入Linux的page cache,再由内核异步刷盘。如果系统崩溃,缓冲区未刷盘的数据会丢失。应通过fsync配置刷盘策略。
redis AOF持久化方式有哪些?(作业帮)
三种策略:
always:每个写命令都会立即写入AOF文件,并同步磁盘。最安全,性能开销最大。
everysec:每秒将AOF缓冲区内容写入文件并同步到磁盘。是默认的策略。
no:写命令会写入AOF文件,不会主动同步到磁盘,由操作系统决定何时同步。
RDB和AOF区别?(58同城,shopee多次考)
存储方式:RDB是快照存储方式。AOF是日志存储方式,记录redis收到所有写命令。
持久化频率:RDB可以通过save规则触发快照生成,如save 900 1表示在900秒内 至少有1个键 发生变化时触发快照。AOF默认每秒fsync一次。
AOF重写了解吗?(命令重写是什么)(shopee,百度)page 1
AOF你觉得怎么优化?(shopee)
AOF文件过大怎么办?(作业帮)
AOF的写回策略?(拼多多)
通过在 Redis 服务器运行期间读取当前数据库中的键值对并创建一个更紧凑的新 AOF 文件来完成的,同时使用 AOF 重写缓冲区 捕获在这个过程中发生的所有写操作,确保数据的完整性和一致性,最终用新的 AOF 文件替换旧的文件以减少空间占用。新的AOF文件更小是因为如多次Set值的命令,AOF重写会优化成只有一个Set值命令,保存最终的值。
redis宕机选哪个持久化方式?(腾讯)
数据一致性要求较高:用AOF。优先使用everysec选项。
恢复速度要求较高:用RDB。如果允许分钟级别丢失可以用RDB
在实际中,通常会结合用RDB和AOF,可以定期执行RDB,在AOF中记录所有写操作。
redis集群模式下如果节点宕机,如何进行的收缩和扩容(字节)page 34
收缩。1.清理故障节点。要在所有剩余节点执行cluster forget命令,移除故障节点。2.槽重分配。如果故障节点是master,要迁移他负责的槽到其他master节点上,用–cluster reshard命令调整。
扩容。1.节点添加,cluster meet将新节点加入到集群。2.槽重分配–cluster reshard。
redis怎么设置过期时间(字节)
用expire key seconds命令,将key的过期时间设置为seconds。
或者写入时直接指定过期时间,set key value ex seconds。
什么是bigkey?怎么解决?(大key)(作业帮,阿里)page 34
一个键(key)所对应的值(value)占用的内存非常大,例如,当一个字符串类型的值超过 10 KB 或者一个复合类型的值包含超过 5000 个元素时,这样的键可以被视为 bigkey。
识别bigkey:用memory usage查看某个键的内存占用情况。通过scan命令结合memory usage可以扫描整个数据库,找出bigkey。
解决方法:拆分bigkey:将bigkey拆分为多个小键,比如将集合拆分成多个小集合。对长列表分页。
删除bigkey:用unlink命令,异步删除。
开启lazy-free:让redis 采用异步方式删除key。
压测什么指标可以认为这是一个Redis bigkey问题
P95、P99、P999延迟异常拉升。
bigkey的缺点是什么?
内存占用高,bigkey也会 占用大量内存空间 ,导致内存碎片增加,影响redis性能,对于bigkey的操作 可能导致redis实例阻塞 ,比如del命令删除一个bigkey可能导致redis实例一段时间内无法响应其他客户端请求。
redis bigkey怎么解决?
对bigkey进行 拆分 ,如将有数万成员的hash key拆分为多个hash key,并保证每个key成员数量在合理范围。对过期数据进行 定期清理 。
针对redis的热数据你会怎么处理?(针对redis的热key你会怎么处理?)(热key的危害及解决方法)(字节)page 152
热key是访问频率很高的key。有两种情况。1.在高并发场景如秒杀、热搜。2.资源分配不平衡导致访问热点。
解决方案。1.把热key拆分,实现流量分流。2.用多级缓存设计,增加本地缓存减少目标节点访问。用额外的实例接收Redis 客户端的数据上报,对不同服务实例访问相同的key时,将相同key的访问上报到同一个worker实例,实例检测到热key,将热key信息推送给业务实例,写入本地缓存。3.对访问频率高的节点扩容,用负载均衡方式分散流量。4.在Redis客户端和Redis实例之间引入代理层。当对key进行访问时,在Redis实例进行key访问频率统计,如判定为热key,则用推或拉模式,将热key缓存到代理层中。
如果只有1%的key是热key,其他的不是热key。热key的请求量是非热key的几十倍。那么用主从的方式怎么去平衡成本(腾讯)
为什么热key影响redis的背后底层原理(比如能抗住1000w qps的redis为什么会被100w 热k给打挂掉)(字节)
什么是热key?(作业帮)
指某个key 被请求的频率很高 ,比如一个redis实例总qps为1万,而其中一个key的qps达到了7千。
以最近最少使用算法LRU(Least Recently Used)为基础,经过高效的排序以及统计算法识别出当前实例的热点Key。
多级缓存,怎么解决热点数据问题?(淘天)page 1
利用本地缓存。仅对真正热点key设置较长TTL,用旁路缓存模式。
Redis。对只读或读多写少热点数据,在Redis构造多个副本,映射到不同槽上。具体用随机前缀法进行映射,在代码里维护映射关系。
写场景热点。多副本维护一致性差。1.升级Redis节点配置,用更大内存,网络带宽和cpu。2.专门部署热key组,把热key单独路由到该组。
redis如何确定一个key是热key(小红书,阿里)
参考。 监控访问频率。用redis的monitor命令或slowlog命令,监控key访问频率。
自定义统计脚本。可以编写脚本定期扫描redis的key,统计每个key的访问次数,通过阈值判断是否为热点key。
记录每个请求,定时把收集到的数据上报,然后由一个统一的服务进行聚合计算。
redis的lru机制。lru可以帮助识别最近最少使用的key。
结合业务逻辑。某些key可能天生就是热点key,比如用户登录token,热门商品信息比如百亿补贴商品。
对于特别火的热key,要如何存储?(腾讯)
如何解决热key问题?(作业帮多次考,拼多多,阿里)
创建热key的多个 副本 ,并把他们 分不到不同的数据分片 ,哈希槽,中。
如果一个数据已经在redis中,突然成为热点key,该怎么办?(京东,网易)
数据分片。如果热点key数据量较大,可以将数据分片存储到多个redis实例中。
本地缓存。对于热点数据,可以在应用层引入本地缓存,将数据缓存在应用服务器内存中。
限流和降级。如果热点key访问量过大,可以在应用层进行限流或降级。比如使用令牌桶算法限制热点key访问频率。
布隆过滤器原理介绍一下。(得物,58同城,腾讯,TME,淘天)page 34
布隆过滤器由初始值为0的位图数组和n个哈希函数构成。先对数据用n个哈希函数做哈希计算,得到n个哈希值,然后对位图数组长度取模,将对应位置设置为1。
插入元素。将元素用n个哈希函数计算哈希值,取模计算索引,将索引对应的比特位置为1。
查询元素。同样用n个哈希函数对元素哈希,取模计算索引,检查n个位置是否全部为1,如至少一个位置为0,则一定不存在,如果n个位置全为1,则可能存在。
假如数据库更新,删除了几个数据,布隆过滤器怎么处理?(布隆过滤器怎么解决删除操作的局限性)(饿了么)page 1
原始的布隆过滤器不支持删除,解决方案有计数型过滤器和布谷鸟过滤器。
方案一,计数型过滤器。将原来每个位用计数器代替,元素插入时对k个哈希位置的计数器分别加一,删除时则对这k个计数器分别减一。查询时,只要检查计数器是否全部非零。每个计数器用4个比特。
方案二,布谷鸟过滤器。数据结构由一个哈希表组成,每个桶可以容纳4个短哈希值。插入元素时,先计算两个候选索引h1和h2,如果有一个桶有空位则存入短哈希值,否则随机从一个桶中删除已有短哈希,插入新哈希值并将踢出的短哈希迁移到另一个候选索引。删除操作也是计算两个候选索引,再两个索引中查找并删除短哈希值。查询时也是查两个候选索引,但匹配短哈希值后要访问原始数据验证。布谷鸟过滤器插入方面性能差,当表接近满时,插入要多次踢出并重新定位元素。
缓存穿透,你所说的布隆过滤器只能防止一些可识别的非法请求,假设攻击者获取到合法的查询请求再进行大量请求呢?(美团)
用限流策略和验证码校验。
方案一,限制访问频率,可按请求来源IP、用户、接口维度限流。1.IP限流,用NGINX、网关实现,用令牌桶、漏桶算法。2.用户限流,用token限制频率。3.接口全局限流,设置最大QPS。
方案二,验证码校验,检测到短时间多次请求时,触发验证码机制,如滑块、点击验证码。
怎么对百万级别的订单数据进行去重?兼容空间成本(美团)
使用布隆过滤器有什么缺点?(58同城)
误判率。布隆过滤器可能误判一个元素存在于集合中。应为多个元素可能映射到同一个位上,可以通过增加位数组大小和哈希函数数量。
无法删除元素。布隆过滤器不支持删除。一旦一个元素插入,它对应的位置为1,但这些位可能被其他元素共享。
布隆过滤器位图数组怎么保证空间能够充分利用?(58同城)
压缩技术:对于稀疏位图,可以用压缩技术如Run-Length-Encoding减少存储空间。压缩技术通过编码连续的0或1减少位图的存储。例如,连续的10个0可以编码为0, 10,从而减少存储空间。
多级位图:在某些场景,可以用多级位图优化空间利用率。多级位图通过将大位图划分为多个小位图,减少内存占用。
布隆过滤器应用场景(淘天)page 45
减少缓存系统重复查询。布隆过滤器用于判断某个数据是否已被缓存。
大数据去重。对于大量可能重复数据,布隆过滤器能在空间占用上优化,避免直接用内存存储所有数据。
反垃圾邮件。布隆过滤器可用来存已知的垃圾邮件地址、诈骗电话号码,从而在接收到新邮件快速判断。
参考:https://help.aliyun.com/zh/redis/developer-reference/tairbloom-command?spm=a2c4g.11186623.0.0.3fbb35fanQgDQH#90f9f481e3wlk
redis内存溢出了,现在有少量bigkey,大量普通key,你怎么 设计 内存淘汰方案?(百度)page 147
紧急止损:立即配置maxmemory和allkeys-lru策略,触发自动淘汰。对bigkey,如果不是热key,执行异步删除unlink。
中期优化:拆分bigkey,设置TTL。启用lazy free,降低删除阻塞风险。
长期规划:引入集群分片,将数据分布到多个节点,通过一致性哈希将数据拆分到多个实例。
什么是一致性哈希算法?(腾讯)page 148
解决分布式系统负载均衡问题。传统哈希算法的问题:当节点数量发生变化时,几乎所有键值都重新映射到不同节点上,这导致大量数据迁移。而一致性哈希算法将对象和服务器都映射到同一个圆环上,使得在增加删除节点时,只有部分数据会重分配。
具体,构造一个虚拟的环,如0到2^32-1的整数范围。然后每个节点用哈希函数计算数值,并根据这个值在环中确定位置;同样,对象也被哈希到环上某个位置。之后,按顺时针方向找最近节点作为对象目标存储位置。
redis有一个bigkey你怎么删掉?(shopee)page 148
redis hash里有很多数据,但是都要删,可以直接删除吗?(拼多多)
分批删除:直接删除一个大key可能会导致redis阻塞,因此可以采用分批删除,通过scan命令遍历key的子集,逐个删除。
用unlink命令:redis 4.0引入了unlink命令,他和del命令类似,但不会阻塞redis,unlink会将key从内存中移除,并在后台异步删除。
public void deleteBigKey(Jedis jedis, String key) {
long cursor = 0;
do {
ScanResult<String> scanResult = jedis.scan(String.valueOf(cursor), new ScanParams().match(key + "*").count(100));
List<String> keys = scanResult.getResult();
if (!keys.isEmpty()) {
jedis.del(keys.toArray(new String[0]));
}
cursor = Long.parseLong(scanResult.getCursor());
} while (cursor != 0);
}
public void unlinkBigKey(Jedis jedis, String key) {
jedis.unlink(key);
}
mset如果有一个点出现差错怎么实现事物回滚(mset命令如果发生异常怎么实现回滚)(百度)
在业务层做补偿,先读旧值,然后执行mset,发生异常则补偿。
Map<String,String>oldVal=jedis.mget(keys);
try{
redis.mset(kvMap);
}catch(Exception e){
if(!oldVal.isEmpty()){
jedis.mset(oldVal);
}
throw e;
}
Redis lua脚本对多个槽的key set会报错怎么办?(百度)
方案一,把这些key绑定到同一个槽中,在key名称中用大括号,作为哈希标签。
方案二,拆分脚本,每次操作同一个槽的一组key。
redis怎么给hash一个key单独设置过期时间(用友)
主动删除:用z set存储,key是hash中的字段,value是过期时间。定期扫描删除。
被动删除:在hash的字段中,添加过期时间,访问时再判断是否过期。
注:在redis7.4中,支持对hash的key设置单独过期时间,hexpire命令。参考:https://redis.io/docs/latest/commands/hexpire/
如何设计一个排行榜?
排行榜除了z set还有没有其他实现方式?(字节)
1.使用数据库的ORDER BY语句:这是最直接的方式,适用于数据量较小、需求简单的场景。例如,可以通过SQL的ORDER BY关键字对数据进行排序。这种方法的好处是简单易行,不需要额外的技术栈,但缺点是在数据量大或业务复杂时,对数据库的性能消耗较大,可能导致响应时间延长。
2.使用Redis的z set:z set是Redis中一种支持按分数排序元素的数据结构,非常适合处理排行榜场景。通过z set,你可以轻松地进行元素的增加、删除、更新,并能快速获取排行榜的上位元素或者某个用户的排名和分数。使用Redis还可以处理高并发场景,保持高性能的同时确保数据的实时性。z set常见的命令有zadd,zrange,zrevrange和zrank.
怎么解决大量key集中过期问题(怎么解决缓存雪崩问题)
通过设置随机的过期时间来分散 key 过期的时刻,或启用 Redis 4.0 引入的 lazy-free 特性,使用子线程异步释放内存,避免主线程阻塞,从而提高处理客户端请求的响应速度。
用什么方式能快速统计网页的访问量(快手)
使用HyberLogLog统计页面UV怎么做?
首先通过 PFADD 命令将每个访问页面的用户ID加入到HyperLogLog中,例如使用PFADD PAGE_1:UV USER1 USER2 … USERn来添加用户。然后,使用 PFCOUNT PAGE_1:UV命令统计并返回该页面的估计UV数。PAGE_1:UV是HyberLogLog的名字。HyberLogLog本质是概率计算。
注:统计访问量有三种选型。一,redis hash,用户访问,用hset命令,key是uri,field是用户ID或随机标识,value设置为1。二,redis bitmap,setbit命令,key是uri,field是用户ID,value设置为1。三,概率算法PFADD。
redis的RDB持久化指令BGSAVE操作是fork一个子进程进行持久化的,为什么不创建一个子线程完成持久化?(Redis rdb持久化bgsave创建一个子进程为什么不创建一个子线程?)(字节)page 51
内存隔离。fork的子进程有父进程内存空间副本(写时复制),持久化不会受到父进程干扰。如果用子线程,线程共享内存,数据结构需加锁。
线程安全。redis本身是单线程模型,如果用多线程持久化,违背简单的设计理念。
RDB持久化过程中数据发生了改变怎么办?(腾讯)page 1
写时复制。在bgsave命令fork子进程后,子进程与父进程共享同一块内存的页表映射,当父进程对某块内存页修改后,操作系统会将该页复制给子进程,再由子进程写入rdb。
RDB 写时复制原理(百度)page 1
bgsave命令用fork创建子进程在后台写RDB,fork之后父子进程共享同一物理内存页,主线程在快照期间仍可正常处理写命令,当要修改内存时,操作系统先复制出该页,子进程继续读取旧数据,保证RDB一致。
Redis限流(字节)
分布式缓存常见的技术选型方案有哪些?
包括 Redis 和 Memcached。
redis支持五种数据结构,memcached只支持hash。
redis支持宕机恢复和持久化,memcached不支持持久化。
说一下Redis和Memcached的区别和共同点
都是高性能的基于内存的缓存系统,共同点包括基于内存存储、高性能和具有过期策略;
而区别主要在于Redis支持更丰富的数据类型、 数据持久化、 灾难恢复、 集群模式以及多种高级功能如发布订阅、Lua 脚本和事务处理,而Memcached结构简单,主要支持基本的键值存储。
缓存数据处理流程是怎样的?
读。首先检查缓存中是否存在用户请求的数据,如果存在则直接返回;如果缓存中不存在,则查询数据库,若数据库中存在该数据则将其更新到缓存并返回给用户,如果数据库也不存在该数据,则返回空结果。
写。有多重策略,比如旁路缓存策略。写直接更新数据库,并删除缓存。
为什么要用Redis?为什么要用缓存?
用缓存,特别是使用 Redis,主要是为了提升系统的性能和并发能力,通过缓存高频访问的数据至内存中,可以显著减少对数据库的直接访问, 加快数据访问速度,并大幅提高系统处理请求的能力 。
Redis除了做缓存,还能做什么?
分布式锁,setnx命令。 与Lua结合限流,redis的string可以用来统计网页访问数量。 消息队列。stream数据类型。
Redis可以做消息队列吗?
虽然 Redis 支持使用发布/订阅模式和新引入的 Stream 数据结构来实现消息队列的功能,并具有消息持久化的能力,但由于面对专业消息队列系统(如 Kafka、RocketMQ)所具备的高级特性和稳定性,Redis 在处理大规模消息传输时的消息丢失和堆积问题可能不是最佳选择,因此一般不推荐用 Redis 来做主要的消息队列系统。
String还是Hash存储对象数据更好呢?(存储对象时,到底用 String + json 还是用 Hash 呢?)
如果你需要经常访问或修改对象中的某些字段,或者对象字段频繁变动,Hash类型更为合适,因为它支持部分读写而且可以节省网络流量;而如果对象不经常变动且系统对内存和性能要求较高,String类型更优,因为它在存储整个序列化对象时更加节省内存。对于如购物车这类频繁变动的应用,推荐使用Hash存储。String比如缓存静态数据或配置数据更优。
使用Set实现抽奖系统需要用到什么命令?
SADD命令。用于将参与者添加到抽奖池中。
SPOP。随机从抽奖池中抽取一个或多个参与者。
SCARD。获取抽奖池中参与者数量,可以用于检查抽奖池是否为空。
使用Bitmap统计活跃用户怎么做?
首先要为每天的用户活动创建一个Bitmap。每个Bitmap以日期为key,用户ID作为offset。如果用户在某天活跃,就将相应的位设置为1。例如,使用SETBIT 20210308 1 1命令表示用户ID为1的用户在2021年3月8日活跃。为了统计一段时间内的活跃用户数,可以使用BITOP命令进行位运算(例如,使用AND或OR运算)合并多天的数据,然后用BITCOUNT命令统计结果中位为1的数量,得到总的活跃用户数。SETBIT key offset value
Redis单线程模型了解吗?
单线程模型优势。 避免线程切换上下文开销。 简化编程模型。减少死锁问题。 事件驱动模型。redis使用事件驱动模型,通过epoll,kqueue机制处理网络io,实现高效异步操作。
redis 6.0引入多线程IO,用于处理网络读写操作,命令的执行仍然是单线程。
Redis6.0之前为什么不使用多线程?
因为单线程编程简单易维护,Redis性能瓶颈通常不在CPU上而在内存和网络I/O上,且多线程可能带来死锁和线程上下文切换等问题,这些都可能反而降低性能。
Redis之后为何引入了多线程?
提高网络IO读写性能,解决了Redis的网络性能瓶颈,同时保持了命令执行的单线程顺序,避免了多线程可能引发的 线程安全 问题,需要在配置文件中显式启用和设置适当的线程数量来激活这一功能。
Redis给缓存设置过期时间有啥用?
管理内存资源和控制数据的时效性,这样可以自动清理不再需要的数据,防止内存溢出,并且在业务上支持时间敏感的数据处理,如短信验证码和用户session等。
怎么保证Redis挂掉之后再重启数据可以进行恢复?
使用Redis的两种持久化机制:RDB(快照),定期保存数据状态的快照到磁盘;或者AOF(只追加文件),记录每个写操作指令到磁盘,重启时通过重放这些指令来恢复数据。
什么是RDB持久化?
是Redis的一种数据备份方法,通过创建数据的快照来保存数据在特定时间点的状态,这些快照文件可以用于数据恢复、备份或在不同服务器间同步数据状态。
RDB创建快照时会阻塞主线程吗?
使用SAVE命令创建RDB快照会阻塞主线程,而使用BGSAVE命令则由子线程执行,不会阻塞主线程。BGSAVE全称是BackgroundSave。
什么是AOF持久化?
AOF(Append Only File)持久化方式在Redis中通过将每一个写操作命令追加到AOF文件来保证数据的持久性,提供了比快照持久化更好的实时性,并可以通过配置appendfsync来控制数据同步到硬盘的频率。
AOF日志是如何实现的?
通过在每个写命令执行后记录操作到日志文件中来实现数据持久化,这种方法避免了语法检查的开销并允许命令无阻塞地执行,尽管这样做可能在发生故障时导致最近的数据修改丢失。
Redis4.0对持久化做了什么优化?
通过引入 RDB 和 AOF 的混合持久化方式(可通过配置 aof-use-rdb-preamble 开启),在 AOF 重写过程中使用 RDB 文件内容作为 AOF 文件的开头,这样结合了 RDB 的快速加载和 AOF 的数据完整性保障。
如何使用Redis事务?
可以使用 MULTI 命令开启一个事务,随后输入多个命令进行队列存储,通过 EXEC 命令执行所有队列中的命令,或者使用 DISCARD 来取消事务中的所有命令。此外,WATCH 命令可以用来监听键的变化,如果在执行事务前这些键被修改了,执行 EXEC 时事务将被中断并返回失败。
Redis支持原子性吗?page 32
支持,并发原子性。
虽然能保证一系列命令顺序执行且不被其他命中断,但因为它不支持回滚机制,因此不满令足传统意义上的事务原子性,如果事务中的某条命令执行失败,已执行的命令不会回滚。
如何解决Redis事务的缺陷?
虽然使用 Lua 脚本可以在 Redis 中原子性地执行多条命令,减少网络开销并防止命令间的干扰,但它仍然 不支持 事务中的回滚。
如何发现bigkey?
可以使用 redis-cli –bigkeys 命令来扫描并发现占用内存最大的键,或者通过分析 RDB 文件来识别存储大量数据的键,利用工具如 redis-rdb-tools 或 rdb_bigkeys 进行更深入的分析。bigkey会导致客户端超时阻塞,另外会导致集群在slot分片均匀下,出现数据和查询倾斜的情况。
Java基础
Java代码是怎么运行的?(编写Java程序到运行经历了什么)(JIT怎么回事,Java代码从写完到运行发生了什么)(淘天)page 1
编译阶段。用javac将源码编译成字节码。字节码由一系列以1字节为单位的opcode和操作数组成。
类加载。jvm启动后,用类加载子系统classloader完成以下步骤。1.加载。读取二进制字节码流,生成class对象。2.验证。保证字节码语法规范,保证jvm安全。3.准备。为类静态变量分配内存设置默认初始值。4.解析。将常量池符号引用替换为直接引用。5.初始化。执行类init方法,给静态变量初始值。
运行时数据区分为方法区、堆、方法栈、本地方法栈、程序计数器。
执行引擎。jvm不执行字节码,要翻译成机器码,有两种方式。1.解释器,逐条读取字节码,翻译并执行,不产生机器码缓存。2.及时编译j i t。将热点方法批量翻译成本地机器码缓存。hotspot提供C1 client、c2 server、graal编译器,c1简单,c2编译时间长,适合追求性能,graal是新一代的编译器。hotspot用分层编译,先用c1编译热点方法,再对其中更热点的代码用c2深度优化。
常量池具体在JVM什么位置?(高德地图)page 1
常量池分为类文件常量池、运行时常量池、字符串常量池。
类文件常量池是Java class文件constant pool表,有各种符号引用如类名、方法名、字段名,和字面量字符串和数字常量。这时编译期生成的数据结构。
运行时常量池,每个加载到JVM的类或接口在方法区有自己的运行时常量池,他是类文件常量池运行时副本。即运行时常量池分配在方法区。运行时常量池中的符号引用会在类链接时被解析成直接引用即内存地址。
字符串常量池,他不属于类文件常量池。在jdk 1.6中分配在永久代,jdk 1.7开始,字符串常量池迁移到堆。
注:运行时常量池里的字符串条目只是对字符串常量池中对象的引用,字符串常量池才是真正存放字符串实例的地方。
变量存在JVM的哪里?(常量可以存放在特定位置,那变量现在是存在JVM哪里)(高德地图)page 1
变量分为成员变量、静态变量和局部变量。
成员变量是对象状态的一部分,随对象在堆中分配而存储,即每当用new创建对象时,成员变量分配在Java堆内存中。成员变量的生命周期取决于对象。
静态变量是类级别的变量,存储在JVM方法区,静态变量生命周期与类的加载周期一致。
局部变量定义在方法内部或代码块,存储在当前线程的Java栈帧中。每次方法调用创建新的栈帧存储局部变量,不同线程有独立栈空间。原始类型的局部变量直接存储在栈中;对象类型的局部变量存储对象引用地址在栈上,实际对象存储在堆上。对象类型的局部变量的生命周期与方法一致,方法执行完毕,局部变量清理。而实际对象的生命周期由GC负责,在不可达之后回收。
jvm为什么要区分堆和栈
存取速度。栈的分配释放速度快,他只移动栈顶指针,适合存放短生命周期的小数据,如基本类型,对象引用。堆的分配较为复杂,要维护空闲块、处理碎片,还要和gc配合。
线程安全。栈他每个线程是有单独的栈,天然线程安全。堆是所有线程共享的区域,对象被多个线程访问时,需要同步机制保证可见性和一致性。
JVM的永久代中会发生垃圾回收吗
永久代会发生垃圾回收Full GC,清理没有被类加载器引用的元数据和常量池。元空间也会发生Full GC。
为什么String不是基本数据类型?(东方财富)
复杂性和功能:String提供了许多方法操作字符串,如substring,indexOf等,这些方法是在String类内部实现的。
常量池:Java对字符串有特殊优化,如字符串常量池。当创建相同内容字符串时,Java只是将他们指向内存中同一个对象。
String不可变的好处是什么(Java String为什么是不可变的?)(滴滴)page 1
安全:String对象不可被修改,在多线程环境是安全的。
常量池:Java对字符串有特殊优化,如字符串常量池。当创建相同内容字符串时,Java只是将他们指向内存中同一个对象。
concurrentModificationexception是在什么情况下抛出的?(小红书,网易)
当一个线程正在遍历集合时,另一个线程对集合进行了修改,就会抛出这个异常。
如果让你实现concurrentModificationexception的代码,你会怎么做?(小红书)
在遍历集合时,记录当前的 修改次数 。每次迭代时,检查当前的修改次数是否与记录的修改次数一致。如果不一致,说明在遍历过程中集合被修改了,此时抛出 ConcurrentModificationException 。
为什么hashmap在链表元素超过8时转化为红黑树?(网易,百度)page 1
因为8个元素时红黑树是4层,超过后红黑树还是4层,而链表平均查找次数大于红黑树,所以在这个时候发生转化。
hashmap什么时候红黑树变链表?(快手)page 1
在hashmap扩容时,如果一个桶的节点数小于等于6,则将红黑树转化为链表。
hashmap桶节点个数大于8链表变红黑树,桶节点个数小于等于6红黑树变链表,为什么阈值不一样?(快手)page 1
阈值不一样,导致中间有一个空挡区间,主要是两个原因。原因一,防止阈值来回抖动,如果树化和反树化都用一个值,则桶中节点数正好在阈值附近反复增删时,就会不停在链表和红黑树之间转换,开销大。原因二,红黑树的删除对节点数目有最小要求。
hashmap会有什么样的并发问题?(满帮)page 1
并发导致数据丢失,两个线程同时往一个桶链表头插法put元素,由于没有同步,A线程修改指针时,B线程会把自己的节点当head覆盖,导致A线程的节点再也访问不到。
并发扩容进入死循环。假设旧表中a桶上有链表,A指向B,B指向null。而线程T1 T2同时进入resize,都拿到旧表,都要把这条链表重新哈希到新表,在jdk 1.7中采用头插法,会发生链表成环。
自己设计一个哈希表的类,假设内存大小固定为1M,那么要怎么做。怎么在内存有限的情况下保证扩展性和安全性(腾讯)
由于Java hashmap采用数组,链表和红黑树方式存储key,指针的开销较大,我们可以用两个数组存储键和值,用线性探测法而不是拉链法节省内存。
对象自身开销,对象头包含mark word8字节,类指针4字节,对齐填充4字节。引用字段keys,values 8字节。基本类型字段size、capacity、threshhold、salt、loadFactor共20字节。总共44字节。对齐填充后48字节。
两个int数组的开销。他们的对象头分别占用16字节,总共是32字节加上8字节乘以capacity。
因此总的内存是80字节加上8字节乘以capacity。1MB可以存储十万级别的key。
HashMap,把a:1,b:2、、、z:26存入hashmap流程,扩容几次(菜鸟)
初始状态,内部数组为空,第一次put时才真正分配,长度为默认初始容量16。
插入流程,计算key的哈希值,用与运算定位到下标,如果该bucket为空,直接插入,否则遍历链表或红黑树末尾插入。size加一,并检查size是否大于阈值,若是则触发扩容。
第一次扩容,在插入第13个元素时,触发从16到32的扩容。第二次扩容,在插入第25个元素时,触发从32到64的扩容。总共扩容2次。
ConcurrentHashMap,HashTable,HashMap的各自实现细节,不同之处,具体应用场景(美团)page 46
hashmap:数组链表红黑树。hashtable:数组链表,没有红黑树。concurrenthashmap:数组链表红黑树。
不同:hashmap线程不安全,hashtable线程安全但性能较低,concurrenthashmap线程安全且性能好。
具体应用场景:1.hashmap用于单线程环境或用外部锁缓存数据。2.hashtable不推荐使用。3.(1)concurrenthashmap用于并发缓存。(2)concurrenthashmap用于限流器,记录用户访问时间戳,根据时间窗口决定是否放行。
红黑树什么样的?(腾讯)
每个节点是红色或黑色。根节点是黑色,叶子节点NIL节点是黑色。红色节点子节点必须是黑色的。从任意节点到其余每个叶子的路径包含相同数目的黑色节点。
hashmap为什么用红黑树不用avl树?为什么不用B+树,为什么不用跳表?(百度,腾讯)page 148
为什么hashmap用红黑树不用跳表,不用B+树?(得物)
红黑树和平衡二叉树avl的区别?(58同城)page 22
红黑树和B+树区别?page 1
红黑树和跳表区别?page 1
介绍下B+树的时间和空间复杂度?(腾讯)
红黑树vsAVL树:AVL树严格平衡,查询效率高,红黑树近似平衡,插入删除效率更高。而hashmap需要频繁插入删除节点,红黑树性能更优。
红黑树vsB+树:B+树多叉树结构,节点存储多个键,适合磁盘io减少寻道次数,用于数据库。红黑树二叉结构,内存操作更高效。
红黑树vs跳表:红黑树最差查找插入删除时间复杂度为log n,而跳表最差为O n,不如红黑树。
讲讲红黑树(红黑树的特点)(淘天)page 1
红黑树是一种平衡二叉查找树,他是为了解决普通二叉查找树在数据更新过程中,复杂度退化的问题产生的。红黑树的高度近似Log n,且它是近似平衡,插入删除查找时间复杂度都是log n。
Mysql 数据页之间是如何关联的?(京东)
非叶子节点的数据页之间用指针关联,叶子节点的数据页用双向链表,双向指针关联。
为什么选择区分度大的字段作为索引,从数据结构层面解答(为什么Mysql建立索引选择区分度大的字段?)(贝壳)page 1
在B+树上建立索引,如果该列值的区分度小,比如只有10个不同的值,每个叶子key要指向大量行。而如果该列值的区分度大,比如有10万个不同的值,叶子层就能容纳更多不同的key,每个指针对应更少的行。
redis z set listpack和跳表的区别?什么时候用哪一种?redis为什么用跳表不用红黑树?(阿里)page 74
1.listpack是紧凑的列表结构,元素之间通过指针或偏移量连接,插入删除查找时间复杂度为O n。
跳表是有序的数据结构,通过多级索引实现快速查找,查找插入删除时间复杂度为O log n。
2.listpack在元素少的时候用,多的用跳表。
3.跳表实现简单。跳表的zrange时间复杂度和红黑树相当,而且实现更简单,红黑树需要平衡调整。
二叉树遍历方式,怎么保存到文件里和重新构造出来?(腾讯)
前序遍历,中序遍历,后序遍历,层序遍历。
用前序遍历,空节点用null表示。
红黑树最多容忍左右高度差是多少?(携程)
红黑树从根节点到任意叶子节点的最长路径不会超过最短路径的两倍。红黑树的左右子树高度差最多为2倍。
hashmap的优缺点?(携程)
hashmap的键key可以为null吗?(58同城)
优点。 快速查找,hashmap通过哈希函数将键映射到数组索引位置,平均时间复杂度为O 1。 动态扩容。hashmap可以根据需要自动调整容量。 灵活性。支持null键和null值。
缺点。 线程不安全。hashmap不是线程安全的,多线程环境下使用需要同步机制。
哈希冲突。虽然哈希函数设计得当可以减少冲突,但极端情况下仍可能发生哈希冲突。
用final关键字new了一个hashmap,能往这个hashmap里put数值吗?为什么(百度)
可以put。
因为final是这个引用不能再指向另一个对象或被重写,但是使用final声明的对象内部状态可以改变。
Java的基本数据类型都有哪些?(滴滴考过,腾讯)
short,int, long, double, float, boolean, char和 byte,八种
Java最小操作的数据单位(拼多多)
比特,位运算。
String str1=“abc”和String str2=new String(“abc”)他们是一样的吗? (Java 为什么直接赋值的字符串(例如 “abc”)和用 new 创建的字符串(new String(“abc”))不是同一个对象?)(滴滴考过,百度)page 148
str1如果常量池存在”abc”,不创建对象;如果不存在”abc”,在常量池创建1个对象。
str2先检查常量池”abc”,如果不存在,创建对象。
无论常量池是否存在,都会在堆中创建一个新string对象,值指向常量池中”abc”。
即str1创建0或1个,str2创建1或2个。
在jdk1.8中,一个长度为10的字符串,占用多少字节的内存?(百度)page 1
一个类有两个字段,short和boolean,建立对象占多大内存空间?(得物)page 1
占用64字节,需要考虑内存对齐。
String对象(24字节): 对象头Mark Word:8字节。 类指针:4字节。(指向方法区) value数组引用:4字节。 hash值:4字节。 对齐填充:4字节。
char[]数组对象(40字节): 对象头Mark Word:8字节。 类型指针:4字节。 数组长度:4字节。 实际字符数据:20字节(10个char,每个2字节)。 对齐填充:4字节。
这个类占用16字节。 对象头Mark Word:8字节。 类指针:4字节。 字段占用 3 字节(short 2 字节,boolean 1 字节)。 填充了 1 字节,使总大小对齐到 8 字节的倍数。
如何设计一个不可变类?如果成员变量是集合类型的怎么保证不可变?(快手)page 46
类申明为final:保证不能被继承,防止子类覆盖方法改变行为。
所有字段声明为private final。
不提供setter方法。
在构造函数中初始化所有字段。
返回字段时用防御性复制:如果字段是可变对象,返回时应该返回副本,防止外部代码通过修改副本影响源对象。
public final class ImmutableClass {
private final int id;
private final String name;
private final List<String> items;
public ImmutableClass(int id, String name, List<String> items) {
this.id = id;
this.name = name;
this.items = new ArrayList<>(items); // 防御性复制
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public List<String> getItems() {
return Collections.unmodifiableList(items); // 返回不可修改的列表
}
}
Exception和Error区别?(滴滴考过)
Exception是程序可以处理的异常,分受检异常和不受检。Error是程序无法处理的严重问题。受检异常,如IOException,ClassNotFoundException等,需要写catch代码,否则编译不通过,和非受检异常,如NullPointerException,ArrayIndexOutOfBoundsException等。Error错误类,不应通过代码处理,如OutOfMemoryError,StackOverflowError等。
HashMap和Hashtable的区别?(浪潮考过)
HashMap是非线程安全,允许null键;而Hashtable是线程安全的,不允许null键。但现代Java推荐用HashMap和ConcurrentHashMap。
HashMap的长度为什么是2的幂次方?(或者讲讲HashMap的扩容机制吧,饿了么考过)(度小满,美团)page 50
hashmap扩容为什么乘两倍(腾讯,美团)
可通过位运算(n-1)&hash代替mod计算索引,效率更高。扩容会对哈希表的长度扩展为2倍,而新的数组位置的计算仍然是hash&(n-1),而n-1的二进制表示就是高位多了个1,这时看hash在高位对应的值,如果为0则元素的位置不变,如果为1则数组的位置在扩容之后的那一部分, 因此扩容后比较均匀 , 但是hash的均匀情况取决于hashCode方法和扰动函数。
另外,扩容后不需要重新计算索引,如果原来的哈希值对应的位是0则索引不变,1则索引右移原先容量个元素位置。
hashmap扩容,如果元素个数是100个,那么这个时候容量应该是256。
hashmap在 jdk 1.7和1.8扩容机制有什么不同?(百度)page 1
节点插入方式。jdk 1.7扩容时将旧数组每个链表节点拆分,重新计算在新数组位置,用头插法插入新链表中。jdk 1.8是尾插法。
jdk 1.8在链表长度达阈值后转为红黑树。
hashmap扩容时能执行put,get方法吗?(百度)page 100
hashmap扩容是原地扩容还是复制到一个新数组?为什么?(百度)
hashmap的哪些操作,在并发中可能会出现什么样的错误的结果?(快手)
可以执行。但扩容时,所有现有元素需重新散列并放到新的桶数组中。如果在过程中put 或get会碰到问题。
put:在扩容期间调用put,新加入数据直接放到新数组里。但因为旧的数据还没有完全移到新桶数组中,所以可能出现, 你刚放入的数据暂时无法通过get获取到 ,扩容完成后才能访问。
get:在扩容时,根据当前键值计算临时位置找数据,只有key没有变动,还能找到value。
为什么hashmap的扩容因子是0.75(百度)
考虑性能和容量的平衡。
Java的hashmap和redis的hash的区别?(字节)
java怎么解决hash冲突,hash扩容复杂度多少?redis对扩容做了优化你知道吗?(字节考过)page 1
java通过拉链法或重哈希解决冲突。扩容是元素个数超过容量与负载因子乘积时,会发生的操作,扩容原有的元素要被重新计算哈希并重新放入新的桶中,平均复杂度是O n,最差情况下复杂度也是O n。
redis的扩容优化 渐进式扩容,redis数据迁移分布在多次操作中,扩容时,redis维护两个哈希表,分别是旧表和新表。每次对redis的读写时,都会从旧表中迁移一部分数据到新表。
redis渐进式哈希怎么实现?为什么起一个线程集中重新哈希?(说说渐进式哈希,redis底层怎么扩容的?)(百度,小红书)page 1
分步调整哈希表大小:当哈希表需要扩容或缩容时,不是一次性将所有元素重新哈希到新的表中,而是逐步进行。每次插入或删除元素时,只处理一部分元素的重新哈希。
双哈希表结构:通常使用两个哈希表,一个用于当前数据,另一个用于新数据。随着时间的推移,逐步将数据从旧表迁移到新表。
为什么起一个线程:通过启动一个专门的线程来处理重新哈希,可以更好地控制资源使用,避免阻塞主线程。
线程的生命周期和状态?(美团考过,得物)
新建、就绪、阻塞、等待、定时等待、终止。
当一个线程对象被创建但没有调用start方法时,处于新建状态。
当调用start方法后,线程进入就绪状态,什么时候运行取决于线程调度器。
当线程试图获取锁但失败时,进入阻塞态。
线程可以通过调用wait方法进入等待态,直到其他线程调用notify方法唤醒它。
调用sleep(long millis)方法时,进入 定时等待 ,线程在指定时间后自动醒来。 当线程执行完成或因异常退出时,进入终止态,线程生命周期结束。 线程状态转换可以通过 状态转换图 表示。
Java运行线程的几种方式?(创建线程的方式?)(京东,得物,腾讯)page 1
继承thread类,重写run方法创建线程。
实现Runnable接口,并重写run方法创建线程。
通过callable接口并使用future获取线程执行结果。
通过executor service创建线程池管理多个线程。
操作系统的线程和Java的线程有什么区别和联系?(字节,拼多多)
关系:Java线程和操作系统线程是同源的,但是具体实现和调度机制不同。Java线程通过JVM线程模型和操作系统线程进行映射。早期jdk1.2之前的线程依赖用户空间的线程实现,M:N模型,而jdk1.2开始JVM线程由操作系统线程支持,1:1模型。
线程模型区别:操作系统线程模型可以是用户空间线程,内核线程或者是两者结合的混合模式。JVM线程在不同平台上会根据操作系统实现方式采取不同的映射。
Java 线程状态的切换过程(讲讲Java线程状态切换)(淘天)page 1
new到Runnable:调用thread.start方法,创建操作系统线程。
Runnable到blocked:线程尝试进入synchronized,未获取到进入blocked。
blocked到Runnable:获得锁,等待cpu调度。
Runnable到waiting:调用wait方法、调用无参数的join方法、调用lockSupport.park方法。
Runnable到time_waiting:调用带时间参数的sleep方法、调用带时间参数的wait方法、调用带时间参数的join方法、调用parkNanos方法。
waiting或time_waiting到Runnable:调用notify方法、调用lockSupport.unpark方法、join目标线程结束、超时到期。
Runnable到terminated:run方法返回。或调用interrupt方法。分为两种情况。1.java线程实际是阻塞在io上时,操作系统线程也是阻塞状态,只能用异常响应中断。2.如果Runnable线程没有阻塞,确实在运行,那么接收到interrupt方法后,只能靠isInterrupted方法主动检测,检测的是中断标志位。
Java线程池怎么实现的?(得物)
实现依赖于juc包中的ThreadPoolExecutor类。
线程池核心组件:
ExecutorService接口:定义了线程池基本操作,如提交任务,关闭线程池等。
ThreadPoolExecutor类,提供线程池具体实现。当提交一个任务时,如果线程数小于核心数,创建线程执行任务。任务队列未满,添加到队列。当任务队列满,且线程数小于最大线程数,创建线程执行任务。当线程数达最大线程数,拒绝任务。
BlockingQueue接口,存储待执行任务队列。
ThreadFactory接口,创建新线程。
定时线程池和spring中定时任务注解是单机的还是多个服务器的(贝壳)
Java线程池是单机的,ScheduledThreadPoolExecutor仅在JVM进程内调度。Spring的Scheduled注解也是单机的,Spring背后用threadPoolTaskScheduler触发带注解的方法。
jdk有没有提供销毁核心线程节约资源的方法?(快手)
有。
allowCoreThreadTimeOut设置核心线程是否可以超时终止。
如何设计一个动态线程池,支持运行时修改参数?(快手)page 143
动态修改核心和最大线程数。threadpoolExecutor原生提供setCorePoolSize和setMaxPoolSize方法,可以在运行时调整核心和最大线程数。
动态修改队列大小。jdk的阻塞队列容量是构造时固定的,它的capacity字段被声明为final,不能改变。如果要动态调整队列容量,有两种方案。1.自定义可变队列。2.重建线程池,先暂停旧线程池,采用优雅停机,然后用新的队列容量创建新的threadpoolExecutor并替换。
动态修改拒绝策略。threadPoolExecutor提供了方法,可以在运行时替换拒绝策略
核心线程数、最大线程数、阻塞队列大小、拒绝策略,这些参数应该放在比如ZooKeeper中间件中。
线程池的应用场景(去哪儿)
在处理大量客户端请求时,适合用线程池,比如电商场景,会不断收到用户下单、查询商品的请求,用线程池就能复用线程。
还有就是异步任务处理,比如在一个系统中,有些任务不需要马上得到结果,如发送邮件、短信通知,可以把这些任务提交到线程池异步执行。
另外,在定时任务和周期性任务也会用到线程池。如系统每天凌晨定时数据备份,比如每隔一段时间进行数据统计分析。
给你一个线程池,corePoolSize = 10, maximumPoolSize = 30, workQueue.size() = 100, 问第几个任务过来的时候,线程数量能够扩大到最大的数量30(蚂蚁)
当第111个任务到来时,队列已达到100上限。当第130个任务到来时,线程池线程数量扩大到最大值30。
为什么线程池先放阻塞队列再放非核心线程?(快手)
因为创建和销毁线程是有开销的,阻塞队列可以减少这种开销。
线程池核心线程数可以设置为0吗?(腾讯)
可以。
线程池参数,核心线程10,最大线程20,消息队列100,会如何执行?(得物)
当任务数量<=核心线程数,所有任务都由核心线程直接处理。
当任务数量>核心线程数,<=核心线程数+消息队列容量,核心线程会处理部分任务,剩下的放入消息队列中等待处理。
当任务数量>核心线程数+消息队列容量时,线程池会创建新的线程处理任务,直到线程总数达到最大线程数,如果任务数量>最大线程数+消息队列容量总和,新任务会被拒绝执行。
线程池拿到任务是新建一个线程还是直接使用空闲的线程?(京东)
如果有空闲线程,直接将任务分配给空闲线程。如果没有空闲线程,且当前线程数未达到线程池最大线程数,线程池创建新线程处理任务。
线程池核心线程数量怎么确定?(百度)page 55
线程池参数考量 有没有一个既定的公式(字节)page 55
IO密集型:线程数配置为核心数乘以2。
CPU密集型:线程数等于核心数。
混合型:(线程等待时间与线程CPU时间之比+1)乘以核心数。
调优。根据上述理论计算出一个初步线程数,然后经过压测验证。用线程平均执行时间和线程平均整体时间衡量性能,当线程数增加时,线程平均执行时间会逐渐增加,而线程平均整体时间则先减少后增加,找到这个拐点就是最佳核心线程数量。
2000qps,响应时间100ms,四核,I/O密集型,怎么设置核心线程数(网易)page 156
线程数=cpu核数/(1-阻塞系数)。阻塞系数是线程在等待io所占的比例,假设这里为0.9。核心线程数=40。2000qps,100ms的响应时间, 意味着系统要同时处理200个并发请求 ,线程池应结合200设计最大线程数。
线程池怎么判断一个线程是否空闲?谁来判断?(百度)
线程状态监控:线程池通过ThreadPoolExecutor类中的Worker对象管理线程,用一个AtomicInteger变量记录线程执行状态runState和线程数量workCnt。keepAliveTime定义了线程空闲时间。
threadpoolExecutor判断。
线程池的队列一般会使用哪些(美团)
ArrayBlockingQueue,有界数组队列,用可重入锁保证线程安全。
linkedBlockingQueue,链表阻塞队列,默认无界,可指定容量。threadPoolExecutor的默认队列就是未指定容量的linkedBlockingQueue。
synchronousQueue,没有缓冲队列,每个插入操作必须等待移除操作
线程池阻塞队列大小如何设置(美团)page 1
cpu核心数。先估算线程池大小,对cpu密集任务,线程数设置为核心数或核心数加一;对io密集任务,用公式,线程数等于cpu核心数乘以(1加上平均等待时间除以平均执行时间)。得出合适线程数后,结合压测以及实验选择最合适的线程数。
吞吐量和延迟。参考排队论的little定律,系统平均并发数L等于请求到达率乘以请求在系统停留平均时间。根据历史峰值QPS和目标的响应时延,可以估算系统需要支持的并发任务数,这个数字可以决定阻塞队列大小。如峰值QPS1000,期望99%请求在100毫秒内处理,则平均并发数为100,此时队列长度应该为100。
如果核心线程、最大线程都在工作,队列也满了,这时候来了个任务,此时会发生什么?(阿里)
会触发线程池的拒绝策略。 默认的拒绝策略是AbortPolicy,抛出RejectExcutionException。也可以自定义拒绝策略,比如用CallerRunsPolicy,让调用线程自己执行任务。或者用DiscardPolicy直接丢弃任务。
线程池回收线程的过程讲一下。(阿里)
线程池执行完毕:线程执行完一个任务后,不会立即销毁,而是回到线程池等待下一个任务。
空闲线程检测:线程池会通过定时任务ScheduledThreadPoolExecutor检查空闲线程数量,如果某个线程keepAliveTime内没有执行任务,会被标记为可回收,也考虑allowCoreThreadTimeOut参数。
线程终止。被标记为可回收的线程最终会被终止,通过调用线程interrupt方法实现,线程捕获到中断信号并终止执行。
注:参考 https://juejin.cn/post/6922069411981426702#heading-4
线程池什么时候会创建非核心线程,使用 LinkedBlockingQueue 会有什么问题(腾讯)
当任务队列已满,且线程数小于最大线程数时,线程池会创建新的非核心线程处理任务。
LinkedBlockingQueue默认是无界队列,它可以无限增长。可能导致内存耗尽。
线程池开始创建的时候,线程数是等于核心线程数吗?(哈啰)
线程池初始化时,不会立即创建所有核心线程。初始状态,线程数为0,当有任务提交时,会创建新线程,但不会超过核心线程数。
线程池如何终止?停止后线程会停吗?使用stop后立即返回了吗?(腾讯)page 1
终止方式:shutdown和shutdownNow。shutdown是优雅关闭,不接受新任务,但会等当前执行任务完成。
shutdownNow立即关闭线程池,停止所有执行任务。
线程池停止后线程状态:shutdown后会继续执行。
shutdownNow不保证所有线程立即终止,取决于他们是否响应中断。
stop方法:已被废弃。他会导致线程立即终止,可能导致资源泄露。
线程池有多个线程竞争同一个资源怎么办?没抢到资源的线程处于什么状态?(腾讯)page 149
同步机制: 1.用synchronized,线程进入blocked阻塞状态; 2.用Reentrantlock,线程进入waiting或timed_waiting状态。
为什么用线程池不用进程池?多线程能否完全取代多进程?(腾讯) page 164
在多核CPU可以让多个进程并发的场景下,为什么还需要线程?(滴滴)page 149
线程共享进程内存空间,进程之间内存空间独立。
线程上下文切换更快。
多线程能否取代多进程 :
多进程:
稳定。子进程崩溃不会影响主进程和其他子进程。
缺点是开销大,且操作系统有进程数限制,在内存和CPU限制下,进程过多,操作系统的调度也会受影响。
多线程:任何一个线程崩溃都可能导致整个进程崩溃,因为所有线程共享内存。
从编程语言角度考虑,Java和go都是一个进程,不方便再创建进程,而C语言或C++语言可以用fork方法创建进程。
注:这个问题比较开放,而且Windows,apache都有用过多进程,参考 https://liaoxuefeng.com/books/python/process-thread/process-vs-thread/
Linux查看进程号状态命令(阿里国际)
ps -p $pid -o pid,user,stat,cmd
cat /proc/$pid/status
死锁怎么解决?(美团)page 43
预防死锁:资源有序分配,保证所有线程按照相同顺序请求资源;资源分级:将资源分级,高级别资源优先分配。
检测死锁:资源分配图,图节点代表一个线程或资源,边代表资源请求或占用关系,如果图中存在环,则说明系统存在死锁。
避免死锁:银行家算法。
解除死锁:资源抢占,强制释放某些线程资源;线程终止,终止某些线程打破死锁。
死锁产生的必要条件:互斥、请求与保持、不可抢占、循环等待。
解决方案。1.破坏请求与保持。一次性申请所有所需资源。2.破坏不可抢占条件。用juc包下的lock,如Reentrantlock的trylock方法,让线程在申请不到锁时主动释放已持有资源。3.破坏循环等待。采用固定顺序加锁。但应该根据具体场景选择合适方案,因为每个方案的时间复杂度不同,破坏请求与保持就比破坏循环等待时间复杂度高。
如何预防死锁?(拼多多)
破坏死锁产生的必要条件即可。
必要条件:互斥、请求与保持、不可抢占和循环等待。
死锁的发生具体场景举个例子?(字节)page 53
房间的锁上锁不需要钥匙,早上上班把钥匙落在床上,出门把钥匙锁在里面,要拿到钥匙就必须先打开房门,要打开房门必须先拿到钥匙。
HashMap的原理?以及不同情况下的复杂度?(字节,小鹏考过,58同城,快手)page 149
hashmap是基于哈希表的数据结构,通过哈希函数将键映射到一个索引位置,实现插入删除查找操作。如果发生哈希冲突,则采用拉链法或开放地址法。 复杂度。平均情况插入查找删除是O 1,最坏情况,链表,插入查找删除是O n,如果一个桶内是红黑树则插入查找删除复杂度是O log n。
hashmap和红黑树的使用场景(腾讯)
hashmap插入删除查询时间复杂度O 1,红黑树插入删除查询时间复杂度O log n。
hashmap的使用场景是本地缓存,结合双向链表实现LRU缓存。红黑树的使用场景是排行榜,按照按分数排序,TreeMap自定义比较器。
hashmap jdk 1.7和1.8区别(美团)page 1
jdk1.7只用链表处理哈希冲突,jdk1.8用链表和红黑树,在链表长度小于等于8时用链表,大于8时用红黑树。
jdk1.7扩容把所有key value重新哈希到新数组,且遍历时采用头插法,会导致原链表翻转。jdk1.8扩容用尾插法能保留原顺序。
hashmap的get方法的流程是什么?(阿里)
计算哈希值。调用get方法时,会传入一个key,这个key会传递给哈希函数,计算哈希值。
确定桶的位置。计算的哈希值会通过一个取模操作确定key应该存储在哈希表哪个桶bucket中。这个桶就是数组的索引位置。
查找桶中的链表或红黑树。找到对应的桶后,要在桶中查找key,如果桶只有一个元素,那么直接比较key即可。如果桶中有多个元素,需要遍历整个链表或红黑树,逐一比较key,直到找到匹配的key。
如果找到了匹配key就返回对应的value,如果找不到则返回null。
hashmap源码中,计算hash值为什么有一个高16位和低16位异或的过程?(百度考过,蚂蚁)page 149
当数组长度很短时,只有低位数的哈希值能参与索引运算。而让高16位参与运算可以更好均匀散列,减少碰撞。
讲讲锁的分类(快手)page 1
从多个维度划分:
按阻塞方式:分为阻塞锁如synchronized,非阻塞锁如CAS。
按公平性:公平锁如Reentrantlock(true),非公平锁如synchronized。
按可重入性:可重入锁如synchronized。
按读写分离:读写锁优化读多写少场景,如ReentrantReadWriteLock。
按作用范围:如本地锁synchronized与分布式锁Redis SETNX,Zookeeper临时有序节点。
公平锁与非公平锁分别用在什么具体场景(美团)page 150
公平锁:1.订单系统,用公平锁,可确保请求按队列顺序被执行。2.关键资源:多租户系统,数据库连接必须按请求顺序分配。
非公平锁:高并发业务,如秒杀场景,更看重吞吐量和响应速度。
锁的分类
当无法判断锁住的代码会执行多久时,用互斥锁。
当能确定被锁住的代码执行时间很短时,用自旋锁。他用C A S在用户态完成加锁和解锁。加锁失败的线程会忙等待,他不是一直执行C A S函数,而是和cpu紧密配合,用cpu提供的pause指令,减少循环等待。
当能明确区分读和写两种场景,用读写锁。读写锁可以倾向于读线程,也可以倾向于写线程。为避免倾向性导致读写线程饿死,可以用队列把请求锁的线程排队,按照先来后到顺序加锁,此时读线程仍然可以并发,但不能插队到写线程前。Java的ReentrantReadWriteLock读写锁就支持这种公平读写锁。注意,在读写锁中,如果先获取读锁,再获取写锁,这是锁的升级,ReadWriteLock不支持这个操作,会导致线程阻塞。但在ReadWriteLock中,锁的降级是允许的,即先获取写锁,再获取读锁。
参考:https://time.geekbang.org/column/article/234548
stampedlock
stampedlock支持写锁、悲观读锁和乐观读。其中写锁、悲观读锁的语义和ReadWriteLock是类似的。不同的是,stampedlock的写锁和悲观读锁操作成功后,返回一个stamp,然后解锁要传入stamp。
stampedlock不支持重入,另外,他的悲观读锁和写锁不支持条件变量。
最后,不要对正在自旋等待的stampedlock的线程调用Interrupt方法,否则导致CPU忙等。如果需要在等待时支持中断,要换成readlock Interruptibly方法和writelock Interruptibly方法。
对象锁和类锁的区别?(百度考过)
对象锁时针对某个具体对象实例的锁,类锁是针对整个类的锁。 对象锁可以通过Synchronized关键字修饰实例方法实现。 类锁通过Synchronized关键字修饰静态方法实现。
synchronized锁普通方法和静态方法互斥吗?(腾讯)
不互斥。静态方法是锁定类 对象 ,普通方法锁定对象 实例 。
synchronized为什么是不公平锁?(百度,腾讯)page 58
内部实现:synchronized基于JVM的Monitor机制,当线程尝试获取锁时,如果锁已经被占用,线程会被放入一个等待队列中。但是如果锁没有占用,线程可以直接获取,这样JVM就不用去等待队列唤醒线程,减少调度开销。
偏向锁与自旋锁。为提高性能,JVM采用偏向锁、自旋锁优化,可能导致饥饿。
synchronized锁的内容抛出异常会发生什么?(度小满)page 1
锁在异常抛出时自动释放。
为什么hashmap写操作不是线程安全的?
内部结构。hashmap内部结构是基于数据和链表或红黑树实现的,当我们进行写操作时,比如插入或删除元素,hashmap需要对数据和链表进行操作。这些操作不是原子的,他们不能在多线程环境下保证线程安全。
并发问题。在多线程环境同时对同一个hashmap进行写操作可能会导致以下问题:数据覆盖,两个线程同时插入同一个位置的元素,可能导致其中一个线程的插入操作被覆盖。 链表成环。在扩容过程中,如果多个线程同时进行rehash,可能导致链表成环。
为了避免这些问题,java提供了线程安全的Concurrenthashmap,他通过分段锁和cas保证线程安全。
什么是序列化,什么是反序列化?(美团考过)
序列化就是将对象转化为字节流以便保存到文件,而反序列化是字节流恢复到对象。
讲一讲concurrentHashmap的底层实现结构?(讲一下concurrenthashmap的原理?)(携程,淘天)page 27
concurrenthashmap怎么实现线程安全的?(快手多次考,美团,满帮)page 27
分段锁机制。concurrenthashmap在jdk1.7中采用分段锁,每个segment类似一个小的hashmap,内部维护一个 hashentry 数组。这样的好处是,不同的线程可以同时访问不同segment,提高并发度。
cas加synchronized。在jdk1.8中,concurrenthashmap的实现发生变化。放弃了分段锁,转而使用cas操作和synchronized保证线程安全。具体来说,进行插入操作时,首先会通过 cas 操作更新节点,如果失败则使用 synchronized 对 链表头结点 进行加锁,然后进行插入操作。
扩容机制。concurrenthashmap支持并发扩容。每个线程负责迁移一部分桶bucket,加快扩容速度。
concurrenthashmap有了CAS之后,为什么还要用synchronized做同步?
先无锁尝试,用cas处理简单场景,当cas无法解决如操作链表或树,用细粒度synchronized保证线程安全。
concurrenthashmap在JDK1.7中size怎么计算的?和JDK1.8有什么区别?(concurrenthashmap size方法的实现?)(滴滴多次考)page 1
JDK1.7分段计数:每个segment维护一个计数器,计算size要将所有segment计数器累加。可能性能瓶颈,因为每次计算size要锁定所有segment。
JDK1.8:直接返回baseCount和counterCells的总和计算总大小。baseCount是基础计数器,counterCells是数组,在并发下分离计数器的更新操作,减少锁。
public int size() {//JDK 1.7
int size = 0;
for (Segment<K,V> segment : segments) {
size += segment.count;
}
return size;
}
public int size() {//JDK 1.8
long n = baseCount;
CounterCell[] as = counterCells;
if (as != null) {
for (CounterCell a : as) {
if (a != null)
n += a.value;
}
}
return (n < 0) ? Integer.MAX_VALUE : (int)n;
}
concurrenthashmap的put的流程?(快手)
计算哈希值:根据键计算。
定位桶:根据哈希值找到桶,如果桶为空直接插入键值对。
加锁:如果桶不为空,加锁。
插入或更新:找键是否存在,存在则更新,不存在插入新的键值对。
扩容 :如果负载因子超过阈值,concurrenthashmap会自动扩容。
Java中支持并发的数据结构有哪些?(腾讯)
concurrenthashmap:将整个哈希表分成多个段,每个段类似一个独立的哈希表。每个段都有一个锁,因此多线程可以访问不同的段。
copyOnWriteArrayList:用于读多写少的列表。
concurrentLinkedQueue:无界队列。基于链表,可以用于生产者消费者模型。
BlockingQueue接口以及实现类:如LinkedBlockingQueue,在多线程环境下提供阻塞操作。
arrayBlockingQueue插入满了会发生什么?怎么保证多线程情况下安全?(腾讯)
尝试插入新元素会阻塞。
put方法会阻塞,直到有空间。offer方法会立即返回false,不阻塞。add方法会抛出IllegalStateException。
保证线程安全:
Reentrantlock:内部用了它保证插入删除的线程安全。
condition:notEmpty,notFull实现阻塞和唤醒。
原子:计数器和索引操作用原子操作。
程序计数器是线程安全的吗?(小红书)
是线程安全的,在jvm中每个线程都有自己的程序计数器,不会被多个线程共享。jvm线程模型保证每个线程在执行时有自己的执行上下文。
java程序启动慢的原因有哪些?怎么优化?(小红书)page 1
类加载和初始化。 java程序启动时,要加载大量类文件并进行初始化,很耗时间。可以通过延迟初始化策略,将类初始化推迟到第一次使用时。
jit编译。 java程序运行时,jit编译器会对热点代码进行编译,可能导致启动时间延迟。可以调整jvm参数,tiered stop at level,减少编译层次。使用aot,提前将代码编译成机器码。
NoClassDefFoundError 和 ClassNotFoundException 有什么区别?(恒生)
no class define:发生在编译阶段。代码中引用了某个类,但在当前项目中没有定义这个类。
MyClass myObj = new MyClass();但在项目中没有定义MyClass类。
class not found:发生在运行时,jvm无法找到指定类文件。
java -cp . MyClass,jvm在当前路径下找不到MyClass.class文件。
类加载过程,实际中类加载会遇到哪些问题?(京东)
类找不到,class not found exception。jvm尝试加载一个类时,如果找不到类的定义,就会抛出异常。
类版本冲突,linkage error。当jvm尝试加载一个类时,如果类的定义和之前加载的类版本不兼容,就会抛出linkage error。
静态变量什么时候赋值?(携程)page 157
类加载时。1.加载类文件。2.验证规范。3.准备内存,这时为静态变量分配内存,设置默认值。4.解析引用。5.初始化static块,静态变量赋值。
不同的class loader可以加载同名class吗?他们是同一个对象吗?(小红书)
可以加载同名的class,每个classloader有自己的命名空间,即使两个classloader加载了同名class,他们实际上是两个不同的class对象。
class是怎么加载的?类加载过程?(描述一下JVM加载class文件的原理机制)(讲讲类的生命周期)(小红书,得物,百度,字节,拼多多,腾讯)page 1
加载类文件。类加载器根据类的全限定名读取类的二进制数据,并转换为方法区的数据结构。
验证规范。确保加载的类符合jvm规范。 准备内存。为类的静态变量分配内存,设置默认初始值。 解析引用。将类接口字段和方法的符号引用替换为直接引用。 初始化static块。执行类的静态初始化块和静态变量的赋值操作。
类加载机制有什么用,写代码哪些场景用到?(字节)page 1
作用。1.动态加载。只在需要时才将类文件加载到内存。2.命名空间隔离。每个类加载器维护独立命名空间,同一券限定类名的类如由不同加载器加载,被当成不同的类型。
具体场景。我在自己实现的简单RPC框架中用到了,用jdk JavaCompiler api在运行时将字符串形式Java源码编译成字节码,并用loadclass将生成的字节码加载到jvm,再用反射实例化并注入,实现对远程服务调用。
说下Java集合的继承结构(Java常用的集合类)(腾讯,转转)
顶层接口有Collection和Map。
Collection有List、Set、Queue、Deque接口,实现有ArrayList、LinkedList、HashSet、TreeSet、PriorityQueue。
Map实现有HashMap、TreeMap。
synchronized是怎么用字节码表达的?虚拟机怎么支持它的?(小红书)
monitorenter,monitorexist获取对象监视器锁。
如何进行字节码增强?(小红书)
使用aspectj,可以在编译或加载时织入代码。
步骤:定义增强逻辑,比如在某个方法调用前后插入日志记录。 选择增强方式,比如用aspectj。 写增强代码,实现字节码修改。
aop和aspectj区别?(美团)
aop是运行时增强,不操作字节码;aspectj是编译时增强。
ArrayList删除元素后是否会缩容?(百度)page 1
不会。
Java Set去重的原理是什么?(哈啰)
hashSet:用哈希表存储元素,通过元素的hashcode方法和equals方法判断重复。先计算哈希值如果哈希值相同,再调用equals方法比较。
TreeSet:用红黑树存储元素,通过元素自然顺序或比较器判断是否重复。如果两个元素比较结果是0,则相同。
treeset可以存null值吗,为什么?Concurrenthashmap可以存null值吗,为什么?HashMap为什么可以存入null值(阿里)page 1
ConcurrentHashMap的value可以为null吗?为什么hashmap的可以(网易)page 1
treeset不允许存储null值,原因是treeset的排序机制:用comparable接口或Comparator接口比较元素。如果允许存储null值,在比较时导致nullPointerException,因为null不能和任何对象比较。
ConcurrentHashMap 不允许存储 null 的键或值。这样做是为了避免在并发环境中出现二义性问题:当 get() 返回 null 时,无法区分是因为该键不存在,还是该键所映射的值为 null。同时,这也帮助实现了更高效和安全的并发操作。
HashMap 在设计时就允许 null 值和一个 null 键,因为它内部存储的数据结构(基于数组加链表/红黑树的实现)并不依赖于值的非空性。对于键,HashMap 通过对 null 进行特殊处理(将其 hash 值默认为 0),保证能够正确定位位置。
说一说自己对synchronized关键字的理解
确保多个线程对资源访问的同步性,通过锁机制保证同一时刻只有一个线程执行它修饰的方法或代码。
ArrayList和LinkedList区别?(58同城)
linkedlist是单链表还是双链表?(58同城)
ArrayList基于动态数组,支持快速随机访问,但是删除插入较慢。
LinkedList基于双向链表,头尾插入删除快,但是随机访问慢。
说说ArrayList的扩容机制吧(度小满,阿里)
涉及动态调整底层存储数组大小。默认大小为0,在第一次添加元素时,设置容量为10,当超过时1.5倍扩容,Arrays.copyOf方法完成拷贝到新数组,ensureCapacity方法可预先加ArrayList容量。
当需要扩容时,ArrayList会创建一个新的数组,容量为计算出的新容量。 然后将旧数组中的元素复制到新数组中。 最后,旧数组会被垃圾回收。
为避免频繁扩容,可以在创建ArrayList时预估容量,指定初始容量。
进程,线程,协程区别?(作业帮,shopee)page 1
进程:是操作系统资源分配和调度基本单位,每个进程有独立的内存空间和系统资源。进程间完全独立,进程间通信方式有管道,共享内存,消息队列。
线程:是进程内的执行单元,一个进程至少包含一个线程,多个线程共享进程内存空间和系统资源,但每个线程拥有自己的栈、程序计数器。需要注意线程安全问题。适用于多线程服务器等。
协程:用户态轻量级线程,由程序员控制调度,可以在任意位置挂起和恢复。适合要高并发任务,如网络爬虫。缺点是协程需要开发者自己管理调度。
为什么会有协程 page 1
在高并发网络服务器场景中,瓶颈在并行的io,包括网络读写和磁盘读写。
但操作系统提供的io成本高,大量线程的堆栈和TLS和上下文开销大。
为解决性能问题,出现了异步io如libevent,对epoll进行跨平台封装,虽然高效,但可读性和维护性差。
协程的价值是保持同步阻塞的写法,且切换是在用户态,栈大小可配置、去掉了TLS。
父进程加锁了,然后fork一个子进程,子进程还能访问吗(腾讯)
取决于锁的类型,如果是互斥锁,他不会在子进程中生效,访问不到。如果是文件锁,对子进程可见,可以访问,但要等待锁释放。如果是共享内存锁,对子进程可见,但也要等待锁释放。
说一下协程,适合什么场景(淘天)
适合网络服务的开发,但他需要用户程序管理切换,适合结合编程语言或三方库进行协程调度,如go语言。
协程一定好吗(拼多多)
协程要程序员管理调度,增加复杂度,线程和进程调度是操作系统负责。
进程调度和线程调度区别?(字节,蚂蚁)
资源分配方式不同:进程调度:分配文件描述符等资源,开销更大;
线程调度:只要切换线程上下文,如寄存器状态,栈指针等,开销较小。
进程如何切换的?(腾讯,字节)
进程切换的触发条件:
时间片用完:操作系统为每个进程分配一个时间片。
IO操作:进程进入等待态。
系统调用。
中断:如硬中断。
切换步骤:
保存当前进程上下文:程序计数器,寄存器状态,内存管理信息。在内核态操作。
选择下一个执行的进程:调度算法。先来先服务,短作业优先,时间片轮转。
恢复下一个进程的上下文。
想提升吞吐量,用哪种进程调度算法更好(字节)
吞吐量是单位时间内系统完成作业数,短作业优先更好,他能最大化单位时间完成作业数。
通过什么信号通知进程切换到下一个进程?(蚂蚁)
SIGALRM,SIGCHLD。
注:SIGALRM,Timer signal from alarm(2)。SIGCHLD,Child stopped or terminated。
虚拟线程怎么置换内存?(腾讯)
JVM会根据需要将线程的内存状态保存到堆中,并在需要时恢复。
讲一讲volatile底层原理?(得物多次考,shopee,哈啰,美团)page 1
volatile语义。volatile保证变量对所有线程可见。volatile保证有序性,对volatile变量写操作编译器和cpu禁止前后相关的指令重排序。
jmm中的volatile。对volatile变量写入操作与之后的读操作之间存在happens Before关系。内存屏障,他是一种cpu指令,读屏障写屏障分别保证读写操作顺序,现代cpu用专门指令实现,如x86中的mfence、lfence、sfence等。
cpu缓存一致性。当多线程运行在不同cpu核心时,各核之间缓存可能不一致,volatile用总线嗅探和缓存一致MESI协议保证一致。
volatile关键字作用,为什么jvm会指令重排序,我说指令重排序加快运行速率,为什么可以加快?(volatile 关键字的作用是什么?)(为什么 JVM 会进行指令重排序?)(指令重排序如何提升执行效率?)(为什么会指令重排序、为什么要禁止指令重排序)(转转)page 1
作用。保证可见性,多线程环境下,普通变量写可能先在工作内存即线程缓存中执行,但未刷新到主内存,导致其他线程读到过期值。volatile保证立即刷新到主存。jmm定义对volatile变量写相当于在写后执行释放内存屏障,对volatile读相当于在读前获取内存屏障。
为什么。编译器在将Java字节码编译成机器码时,用公用表达式消除、寄存器分配、延迟写入优化,用指令重排序减少cpu流水线冲突和内存访问次数。以及jit也会进行内联、逃逸分析和指令重排序。
为什么加快。1.流水线并行,将相互独立的指令提前执行,减少流水线停顿,提升每个时钟周期完成的微操作数量。2.内存访问优化。将多个主存访问聚合,减少缓存未命中的等待时间。
讲讲Java内存模型(阿里)
他描述多线程环境下,线程怎么和内存交互。
Java内存模型将内存分为主内存和工作内存,主内存所有线程共享,工作内存线程私有,线程在工作内存操作变量,通过特定指令同步到主内存。
volatile保证变量可见性。
happens-before原则,保证有序性。
什么是泛化调用(百度)
TODO:file:///E:/download/23%E4%B8%A8%E5%A6%82%E4%BD%95%E5%9C%A8%E6%B2%A1%E6%9C%89%E6%8E%A5%E5%8F%A3%E7%9A%84%E6%83%85%E5%86%B5%E4%B8%8B%E8%BF%9B%E8%A1%8CRPC%E8%B0%83%E7%94%A8%EF%BC%9F.pdf
单处理器有可见性问题吗?(字节)page 150
有可见性问题。
编译器处理器优化:即使在单处理器,编译器和cpu会对代码优化和指令重排序,可能打破代码预设的执行顺序,导致一个线程对共享变量的修改在另一个线程中未能按预期可见。
线程切换本地缓存:多线程在单处理器运行时,由于线程各自使用的寄存器,可能导致共享变量最新值没有及时刷新到主存。
i++是线程安全的吗?有什么解决办法?(得物)
不是线程安全。
可以用synchronized关键字或者用AtomicInteger类,提供原子性自增操作。
讲一讲synchronized关键字的底层原理(重要)(得物,字节)page 53
synchronized底层实现依赖于Monitor和对象头mark word,用字节码中Monitor enter Monitor exist指令实现同步控制。jvm为提升性能,引入三种不同锁偏向锁、轻量级锁、重量级锁实现。
偏向锁。假设一个对象在生命周期内只会被一个线程访问。他用c a s在对象头mark word记录持有锁线程id。
轻量级锁,实现流程。1.复制对象头中的mark word到一个displaced header保存状态。2.用c a s在对象头中设置轻量级锁标记。3.如果c a s成功,则当前线程获得了锁,若失败,则竞争激烈,升级为重量级锁。
重量级锁。用操作系统互斥量实现同步。
jvm会根据锁竞争程度调整锁实现策略,会有锁升级。也有锁降级,当jvm进入安全点时,会检查闲置的Monitor,并尝试降级。
原子类的定义、原理和使用场景
原子类用无锁方案解决原子性问题。原理是用硬件C A S指令,C A S包含三个参数,共享变量内存地址A,比较的值B,和共享变量的新值C;且只有内存中地址A的值等于B时,才能将地址A中的值更新为新值C。
原子类包括原子化的基本类型、原子化的对象引用类型、原子化数组、原子化对象属性更新器和原子化累加器。
使用场景:当互斥锁的性能无法满足需求时,无锁的原子操作更为合适。他适合解决针对一个共享变量的互斥问题,如果需要解决多个变量的原子性问题,建议用互斥锁方案。
JDK1.6之后的synchronized关键字底层做了哪些优化?(重要)
引入偏向锁、轻量级锁、自旋锁等技术。减少synchronized锁操作的开销。
偏向锁减少无锁竞争的开销。当一个线程首次获得锁,JVM会将锁标记为偏向这个线程,下次他来时直接授予,不需要CAS。
轻量级锁通过CAS获取锁。
自旋锁是非阻塞机制。获取锁失败不会立即阻塞,而是忙等一段时间。
锁消除是jit编译器优化。如果发现同步块的锁是多余的,他会自动消除这个锁。锁粗化是反向优化,jit将多个连续的同步块合并为一个更大同步块。
说说自旋锁和互斥锁(字节)
自旋锁:一个线程尝试获取已经被另一个线程占用的自旋锁时,线程持续循环,不休眠。用忙等实现。
互斥锁:一个线程尝试获取时,线程被阻塞,进入休眠,等锁释放。用操作系统同步原语pthread_mutex_lock实现。
synchronized和volatile的区别(重要)
volatile保证变量可见性,synchronized除保证可见性外,还保证原子性,能对代码进行同步控制。
synchronized和Reentrantlock的区别(synchronized和reentrantlock的区别)(重要)(得物,快手,美团)reentrantlock page 1
Java中的锁有了解吗?(58同城)page 1
synchronized是Java语言级别内置锁,用synchronized修饰方法或代码块时,jvm在编译后字节码中通过Monitor enter和Monitor exist实现锁,自动获取和释放。Reentrantlock属于juc包中锁实现,显式调用lock,unlock获取释放锁。
功能。1.公平性。Reentrantlock可以选择是否公平,synchronized默认是不公平的。2.中断响应。Reentrantlock提供了lock interruptibly方法,使线程等待锁可以对中断做出响应。3.条件变量。Reentrantlock允许newcondition创建条件变量,替代wait notify notifyAll。4.可重入。Reentrantlock和synchronized都可重入。
性能,synchronized和Reentrantlock性能不相上下。
Java里面并发编程需要注意什么(美团)
原子性,一组操作要不全都执行,要不全都不执行。可以用synchronized和juc中的如AtomicInteger实现。
可见性,一个线程修改了共享变量的值,其他线程能立刻看到这个修改。volatile和synchronized保证可见性。
有序性,多线程环境编译器和处理器可能会重新排序代码来优化执行性能。可以用synchronized和lock保证有序性。
Reentrantlock的可重入的原理是什么?(京东,腾讯)page 66
Reentrantlock基于AQS。可重入性:同一个线程在持有锁的情况下再次调用 lock() 不会被阻塞,而是简单地增加内部计数器;相应地,每次调用 unlock() 都会将计数器减 1。
数据结构。1.同步状态(state)。用 volatile 修饰的 int 类型变量 state,state == 0 表示锁空闲,state > 0 表示锁已被占用。2.独占所有者标识。维护了一个指向当前持有锁的线程引用exclusiveOwnerThread。3.CLH 队列与节点(Node)。未获取到锁的线程会封装为 Node 节点,用CAS方式加入 AQS 内部的等待队列。队列是一个 FIFO 双向链表,头结点是一个哨兵节点,不和任何线程关联,唤醒机制也是通过检查队列中的节点来实现。
底层方法。1.lock方法用Sync实现完成。如果状态为0,则通过CAS将state由0变1,获取成功。若CAS失败,则调用acquire(1)方法,自旋或排队等待。acquire(1)内部调用重写的tryAcquire获取锁。2.unlock方法,调用release(1)方法,调用tryRelease方法,检查持有锁,减少重入计数,若状态减少到0,则清空持有者引用,唤醒等待队列中的下一个线程。
reentrantlock公平和非公平的实现原理是什么?(京东,阿里,快手)page 150
公平锁:1.队列优先:公平锁尝试获取锁之前,先用hasQueuedPredecessors方法检查当前等待队列是否有其他线程排队,如存在排队线程,则不抢占。2.tryAcquire:如果状态为0(锁未被持有),先判断队列是否有优于自己的线程;只有没有先行线程,才通过cas获取锁。如果自己已持有锁,增加状态值。
非公平锁:1.抢占:获取锁时直接用cas,不管其他线程。2.tryAcquire:先用cas将状态改为锁定;若失败则进入AQS等待队列排队。
AQS为什么用双向链表?(得物,场景题)page 164
参考。
存储在双向链表中的线程,可能这个线程出现异常不要竞争锁,这时要把这个节点删除,删除节点要获取前驱节点,如果不是双向链表,就得从头遍历。
新加入链表的线程,在进入阻塞队列前,要判断前驱节点状态, 只有前驱节点是sign状态才阻塞当前线程 ,这里涉及查找前驱节点。如果前驱节点不是sign,当前线程不应该阻塞。
对应于acquireQueued,shouldParkAfterFailedAcquire方法。jdk1.8
AQS队列为空时线程加入队列发生什么?(百度)page 150
参考。
如果是公平锁,AQS队列为空,用CAS尝试获取锁,如果没有获取到,则自旋入阻塞队列,如果他是队头,再CAS尝试获取锁,如果获取失败,挂起当前线程。
注:AQS源码解析 https://javadoop.com/post/AbstractQueuedSynchronizer
AQS和CAS区别?(得物)
AQS。作用。构建锁和同步的框架,提供了基于FIFO队列的阻塞锁和相关的同步器。
实现方式。AQS通过维护一个状态变量和一个等待队列实现线程阻塞和唤醒。
CAS。作用。在多线程环境下实现无锁并发。通过比较内存中值和期望值,如果相等则更新为新值,否则不更新。
实现方式。CAS由硬件指令支持,比如x86架构的cmpxchg指令,Java的atomic包提供了基于CAS的原子类,比如AtomicInteger。
synchronized锁升级过程和实现原理?什么情况下会达到重量级锁?(百度,得物,满帮)
synchronized怎么膨胀的?(得物)
偏向锁:当第一个线程访问同步块时,JVM会将对象头中的mark word设置为偏向锁模式,并将线程ID记录在当中。如果后续线程再次访问同步块,会直接进入,不需加锁。
轻量级锁。如果有其他线程尝试获取,升级为轻量级锁。JVM在当前线程栈帧创建lock record,将对象头的mark word复制到lock record中,尝试通过 CAS 将对象头的 mark word 替换为指向lock record的指针。
重量级锁。如果CAS失败,会膨胀为重量级锁。JVM调用操作系统互斥量mutex实现锁,线程会进入阻塞态,等待操作系统调度。
两个方法都被synchronized修饰,其中一个调用另一个可以成功吗?(小鹏)
如果这两个方法属于同一个对象,那么调用是可以成功的,因为synchronized是可重入锁,同一个线程再次请求同一把锁不会造成死锁。
如果属于同一个类,但是一个方法是static方法,一个是普通方法,那么也可以成功,类锁和对象锁不冲突。
线程池两个线程可以拿到同一个任务吗?是怎么保证不拿到同一个的?锁是怎么实现的?是什么锁?锁机制是什么?/锁的原理是什么?(京东)
不会,线程池通过线程安全的任务队列如BlockingQueue保证不重复。队列内部用Reentrantlock、cas保证并发安全。
Reentrantlock底层原理怎么做的?(携程,得物)
Reentrantlock是java中的一种可重入锁,允许一个线程多次获取同一个锁。比synchronized提供了更灵活的锁定机制,支持公平锁。
底层实现。Reentrantlock依赖于AQS框架。aqs是一个抽象类,提供了双向链表队列,用于管理等待获取锁的线程。Reentrantlock内部维护了sync类,这个类继承自aqs,并实现了具体的锁定逻辑。
加锁 过程。当一个线程调用lock方法时, 如果锁没有被其他线程持有 ,该线程会立即获得锁,并 将aqs的state字段加1 ,state表示当前锁的被重入的次数。
如果锁已经被其他线程持有 ,当前线程会被封装成一个node节点,并 加入到aqs的等待队列 中。
当持有锁的线程释放锁 ,调用unlock方法时, aqs的state字段会减1 。如果state字段减到0,表示锁已经被完全释放,aqs会从队列中 唤醒一个等待的线程 ,尝试让他获取锁。
synchronized和Reentrantlock哪个性能更好?(小红书)page 150
在java6及以后的版本中,synchronized和Reentrantlock的性能差距已经很小。低并发时synchronized可能更优,而高并发下Reentrantlock的灵活性如条件变量、可中断会有更高吞吐量。实际性能则应看场景:
若只需基础同步,优先用synchronized。
若需超时、公平锁,则用Reentrantlock,性能不应该是唯一因素,和场景适配更重要。
ThreadLocal有什么用?
解决了多线程环境下,每个线程要有自己的 独立变量副本 的问题,确保每个线程可以访问自己的。
ThreadLocalMap被谁引用?(蚂蚁)
被Thread对象引用。
ThreadLocal的原理了解吗?(用友,快手,美团)page 1
threadlocal不直接存数据,作为一个访问接口。每个线程调用threadlocal get set remove 方法时,实际操作的是当前线程内部维护的threadlocalmap的数据结构。具体,每个thread对象内部有threadlocals属性。调用如set方法,检查threadlocals初始化,如未初始化,调用createmap创建新的threadlocalmap将threadlocal作为key,把变量存入map中。key是弱引用,如果外部没有threadlocal对象强引用,则在下一次gc,回收这个key。value是强引用。threadlocalmap生命周期和线程一致,要在业务逻辑结束调用remove方法,避免内存泄漏。
看过threadlocal源码吗,如果让你设计ThreadLocal,你会怎么设计(字节)page 1
Java每个线程有私有的threadlocalmap,他是threadlocal的静态内部类,底层用entry数组保存数据,key是弱引用,value是强引用。
自定义threadlocal设计。针对内存泄漏,我们思路是把线程作为弱引用,放在weakHashMap中。但weakHashMap线程不安全,因此用Collections synchronizedMap包装,当线程执行完毕且没有其他地方引用Thread对象时,GC将回收线程对象,并清理weakHashMap中的key value。同时,我们也自定义remove方法,从map中直接删除key value。
什么情况下一个线程会长期未结束?(阿里)page 1
等待。线程处于等待或定时等待状态,没有对应的notify方法唤醒。
阻塞blocked。等待io阻塞调用,或获取锁。
ThreadLocal一般是用来存什么东西的?有什么具体的场景可以举例吗?(百度)page 1
为每个线程存储自己的独立变量副本。
具体场景如日志链路追踪,在网关或拦截器拦截到一次http请求时,用UUID生成唯一的Trace id,下游Service、RPC调用时,可以直接getTraceid,可以关联同一个请求的所有日志。
ThreadLocal内存泄漏问题是怎么导致的?(得物,字节)
ThreadLocal的key和value分别放的什么数据?(京东)
threadlocal使用时的注意事项(快手多次考)
ThreadLocal变量在ThreadLocalMap中以Entry形式存储,而Entry的key是ThreadLocal实例的弱引用,value是强引用。当ThreadLocal实例被回收时,由于Entry的key是弱引用,key会被自动回收,但value仍然强引用,无法被回收。
弱引用对象不会阻止垃圾回收器回收他所引用的对象。只要强引用存在,垃圾回收器就不会回收被引用的对象。
怎么避免泄漏。 用完ThreadLocal后,显式调用remove方法。
ThreadLocal为什么value是强引用?(美团)page 104
数据可达性。threadlocal设计目的就是让每个线程有自己的变量副本。如果value是弱引用,当gc时,可能在不恰当时机把本该由线程持有的局部变量回收。
ThreadLocal为什么key是弱引用?(腾讯音乐,淘天)page 50
防止threadlocal内存泄漏,因为key在应用代码中没有引用链,而value存在,即thread->ThreadLocalMap->Entry->Value,如果key是强引用,则会内存泄漏,因为Entry没有显式持有key,但显式持有了value。
threadlocal既然弱引用是不是可以不remove?是不是value弱引用就可以不用remove?(淘天)page 1
要remove,要remove。
讲讲四大引用(字节)page 53
强引用。new创建对象得到的引用。
软引用。在内存不足时回收,要和引用队列一起使用。
弱引用。对象在下一次GC时回收。
虚引用。不决定对象生命周期。
lambda表达式为什么使用作用域内局部变量时,提示必须为有效final?(携程)
Lambda 在编译后,底层被转换为匿名内部类或 invokedynamic 调用;而匿名内部类只能访问显式或语义上的 final 局部变量。
java类编译的过程(美团)
分析。将源代码字符流拆分为token,根据Java语法规则,将token组织成抽象语法树,构建符号表。将抽象语法树转化为三地址码中间表示。
生成。将中间表示转化为jvm指令集的字节码,生成class文件。
基本类型和包装类型的区别?(58同城)
成员变量包装类型不赋值就是null,而基本类型有 默认值 且不是null。
基本类型都是在栈中吗(网易)page 156
取决于变量的声明位置。基本类型局部变量存在栈中;而基本类型作为对象的字段或全局变量存在,对象在堆中。
除了clone还有哪些方式可以对对象进行深拷贝?
使用第三方库,如 apache commons lang 库的serialization utils的clone方法,这个方法是通过序列化和反序列化实现深拷贝的。 使用json序列化 ,将对象转化为json字符串,然后再将json字符串反序列化为新对象,适合跨平台传输场景。
参考:https://juejin.cn/post/6844903693100417038
为什么重写equals方法必须重写hashCode方法?(重写equals要不要改写 hashcode )(得物,美团,菜鸟)page 151
如果两个对象通过equals方法判断是相等的,那么他们的hashcode的值也必须相同,这是java规范中的一个基本原则。 如果不重写hashcode,默认的方法会根据对象的内存地址生成哈希码,这会导致两个逻辑相等的对象产生不同的哈希码。 hashmap,hashset依赖于hashcode确定对象的存储位置,如果hashcode不一致,可能导致逻辑上相等的对象被存储在不同的桶中,从而无法正确查找。
Integer a=128, Integer b=128, a==b?(满帮)
等于等于比较的是对象引用地址是否相同。
Integer的缓存范围是负128到127,128超出范围,JVM会new一个新的对象,因此这是两个不同的对象,结果为false。
==和equals的区别?(腾讯,快手)
==用于比较基本类型的值或者引用的地址。equals用于比较两个对象的内容或值。
浅拷贝和深拷贝区别了解吗?什么是引用拷贝?(腾讯)
浅拷贝会创建一个新的对象,但是内部的引用类型属性仍然是原对象和拷贝对象共享的同一个引用地址;
深拷贝则会递归地复制对象及其所有内部对象,使得原对象和拷贝对象完全独立;
引用拷贝是将对象的引用赋值给另一个变量,这两个变量指向同一个对象。
自动装箱与拆箱了解吗?原理是什么?(腾讯)
装箱是将基本类型用他们对应的引用类型包装起来,发生在编译阶段。拆箱是将包装类型转换为基本类型。自动拆箱可引发NPE问题。比如一个包装类型Integer是null,那么他去执行intValue会报NPE异常。
装箱原理是valueOf方法,拆箱原理是intValue方法。
装箱拆箱怎么去优化性能(网易)page 151
避免不必要的装箱拆箱。用具体类型或泛型,避免将值类型转化为object类型。
用泛型替代object类型。在集合类中,用list
缓存:如某个值在多处被装箱,提前将他装箱缓存。
接口和抽象类有什么共同点和区别?(美团)
接口是完全抽象的类,只包含方法的声明,没有实现,没有方法体。它用来定义行为规范,一个类可以实现多个接口。
抽象类是不能被实例化的类,可以包含抽象方法和具体方法。抽象类作为其他类的基类,一个类只能继承一个抽象类。
字段:接口字段默认是常量,抽象类可以是常量也可以是变量。
// 接口示例
public interface Animal {
void makeSound(); // 默认是public abstract
}
// 抽象类示例
public abstract class Vehicle {
protected String brand;
public Vehicle(String brand) {
this.brand = brand;
}
public abstract void start(); // 抽象方法
public void stop() { // 具体方法
System.out.println("Vehicle stopped.");
}
}
// 实现接口的类
public class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
// 继承抽象类的类
public class Car extends Vehicle {
public Car(String brand) {
super(brand);
}
@Override
public void start() {
System.out.println("Car started.");
}
}
重载和重写有什么区别?(快手)
重载就是同一个类中多个同名方法根据不同的传参执行不同的逻辑处理。重写是子类对父类方法的重新改造,外部样子不变,内部逻辑变化。
java中把long转为int,溢出后的int值为多少(贝壳)
直接截取long的值的低32位作为结果。它的结果是原始值对2的32次方取余,如果结果大于等于2的31次方,则再减去2的32次方得到负数。
fail fast和fail safe(快手)
他们是迭代器在并发修改集合时的两种行为。
fail fast是在检测到集合被并发修改时,立即抛出并发修改异常,比如ArrayList和hashmap,非并发安全。
fail safe是并发修改时不抛异常,而是创建原集合快照,在快照上迭代,并发安全。
什么是字节码?采用字节码的好处是什么?
JVM可以理解的代码就是字节码,解决了传统解释性语言执行效率低的问题,同时保留了解释性语言可移植的特点。
为什么不是全部使用AheadOfTimeCompilation呢?
这和Java语言的动态特性有关。
为什么Java语言解释与编译并存?
因为Java程序需要经过先编译,后解释两个步骤。
成员变量与局部变量区别?
成员变量属于类的,局部变量是在代码块或方法中定义的。
静态方法和实例方法有什么不同?
调用静态方法时,可以用类名.方法名,实例方法最好用对象.方法名。
什么是可变长参数?
本质上基于数组实现,是Java的一个语法糖。
包装类型的缓存机制了解吗?
Byte、Short、IntegerLong这四种包装类默认创建了数值【-128,217】的缓存数据,Character创建了数值在【0,127】的缓存数据。Boolean直接返回True或False。
为什么浮点数有精度丢失的风险?
因为计算机在表示一个浮点数时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断。
如何解决浮点数运算的精度丢失问题?
BigDecimal可以实现浮点数的计算不会丢失精度。
超过Long的数据应该如何表示?
BigInteger内部使用int 【】数组来存储任意大小的整型数据。
如果一个类没有声明构造方法,能正确执行吗?
可以,因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。
面对对象三大特征?
封装、继承(extends)、多态。
Object类的方法有哪些?
hashCode方法,用于返回对象的哈希码。equals用于比较两个对象的内存地址是否相等。toString返回类的名字加上实例的哈希码的16进制字符串。
为什么要有hashCode?
hashCode在Java中提供一种高效的方式确定对象存储位置,尤其在用HashSet时,它通过减少equals方法调用次数提高性能。
String,StringBuilder,StringBuffer 的区别?
String是不可变的,每次修改都创建新对象。StringBuilder是可变的,且非线程安全,适合单线程。StringBuffer是可变的且线程安全。
String为什么是不可变的?
因为内部字符数组用final关键字修饰且设为私有,没有提供公共方法修改数组内容,同时String自身被声明为final,防止通过继承改变不可变性质。
字符串拼接用+还是StringBuilder?
简单的字符串拼接用+,在循环中拼接用StringBuilder提高性能。
字符串常量池的作用了解吗?
字符串常量池是JVM为了提升性能和减少内存消耗针对String类专门开辟的一块区域。在堆中的字符串常量池 驻留 字符串的引用。
intern方法有什么作用?
将指定的字符串对象的引用保存在字符串常量池中。
String类型的变量和常量做+运算时发生了什么?
若都是常量,则将其在编译期间合并存入常量池;若包含非常量,则运行时通过StringBuilder拼接,导致额外堆内存分配。
在开发过程中处理字符串的场景比较多,常用处理字符串的方法有哪些?
字符串拼接StringBuilder,字符串分割split方法,字符串替换replace方法。
Throwable类常用的方法有哪些?
getMessage方法返回异常发生时的简要描述。
try-catch-finally中的finally的代码一定会执行吗?
不一定,如finally之前虚拟机被终止运行,则不会执行。
如何使用try-with-resources代替try-catch-finally?
try-with-resources 语法可以简化这个过程,你只需要将实现了 AutoCloseable 接口的资源声明在 try 语句中,这样资源会在 try 块结束时自动关闭,无需显式在 finally 块中关闭它们。
异常使用有哪些需要注意的地方?
不要把异常定义为静态变量,因为这样会导致异常栈信息错乱。每次抛出异常时,应该创建一个新的异常实例。
反射的应用场景有哪些?
主要用于框架开发,如Spring和Mybatis,它允许这些框架动态创建对象,调用方法访问属性和操作注解,从而简化开发过程。
什么是SPI?
专门提供给服务提供者Server或者扩展框架功能的开发者使用的接口。
SPI和API有什么区别?
api是从服务提供方角度出发,接口由被调方定义,spi是从服务调用方角度出发,接口由服务调用方定义。
SPI的优缺点?
能提高接口设计的灵活性,但需要遍历加载所有实现类,不能做到按需加载。
Java IO流了解吗?
分为字节流和字符流,都继承自四个核心抽象类,InputStream和OutputStream用于字节流,Reader和Writer用于字符流。
IO流为什么要分为字节流和字符流呢?
字符流避免在处理文本数据时可能出现编码错误和乱码问题。
说说List Set Queue Map四者之间的区别?
List元素是有序可重复的,Set元素是无序不可重复的,Queue元素是有序的可重复的,Map的key是无序的不可重复的,value是无序的可重复的。
如何选用集合?
根据数据结构需求和特性决定,使用Map实现键值对存储,使用HashMap实现高效访问,TreeMap进行排序,ConcurrentMap实现线程安全。
ArrayList和Vector区别?
ArrayList是线程不安全的,Vector是线程安全的。
comparable和Comparator的区别
comparable定义对象的自然排序顺序,实现compareTo方法,一个类只能有一个自然排序顺序。 Comparator定义对象的定制排序顺序,实现compare方法,可以为一个类定义多个不同的排序顺序。
无序性和不可重复性的含义是什么?
无序性指元素在集合中不按特定顺序排列,按哈希决定位置,不可重复性指集合不存在相同元素。
比较HashSet LinkedHashSet TreeSet 三者的异同
HashSet LinkedHashSet和TreeSet是Set接口实现,HashSet基于HashMap实现,不保证顺序;LinkedHashSet基于LinkedHashMap实现,保证元素插入顺序;TreeSet基于红黑树实现,元素自然有序。
Queue和Deque区别?
Queue是单端队列,只能一端插入元素,另一端删除元素。Deque是双端队列,两端均可以插入删除元素。
ArrayDeque与LinkedList的区别?
ArrayDeque基于动态数组实现,不支持null,性能优于LinkedList;LinkedList基于链表实现,支持null。小技巧,集合的名字只要以Array开头就是动态数组实现。
说一说PriorityQueue
PriorityQueue利用了二叉堆的数据结构来实现。
HashMap和TreeMap区别
HashMap元素无序,TreeMap基于红黑树实现,可根据键自然顺序或自定义比较器排序元素,同时支持集合导航功能。
HashSet如何检查重复?
首先通过对象hashCode方法计算哈希值来确定存储位置,若已有对象,则通过equals方法判断是否真正相同,以保证元素唯一性。
HashMap的底层实现
底层数据结构式数组+链表或者红黑树,数组的每个元素是一个桶,每个桶可以存储一个链表或红黑树。当链表长度超过阈值8时,链表会转化为红黑树。 hashmap使用哈希函数计算键的哈希值,然后将哈希值映射到数组的索引位置。 hashmap的初始容量是16,负载因子是0.75,当元素个数超过负载时,会触发扩容,扩容时,数组大小翻倍,并所有元素会重新计算哈希值并分配到新的桶中。 红黑树。当链表长度超过8时,链表会转化为红黑树,红黑树是自平衡的二叉查找树,查找插入删除时间复杂度都是O log n,红黑树的引入是为了解决链表过长导致的性能问题。
HashMap多线程操作导致死循环问题
在多线程中,HashMap并发修改和重哈希可能导致结构性修改异常,如形成闭环的链表,更推荐使用ConcurrentHashMap。
ConcurrentHashMap和Hashtable的区别
ConcurrentHashMap在JDK1.8中使用 分段锁和细粒度的锁策略 以及链表转红黑树优化。而Hashtable通过 方法级synchronized 实现线程安全,但是效率低。
JDK1.7和1.8的ConcurrentHashMap实现有什么不同?page 1
JDK1.7的通过使用 分段锁 实现线程安全,每个分段是独立的哈希表,而1.8使用 节点锁 (Node+CAS和synchronized)提高锁粒度,在碰撞多时把链表转为红黑树,提高并发性能。CAS是比较并交换。
程序计数器为什么是私有的?
保证线程在切换执行时能准确恢复到各自的执行位置,避免多线程执行相互干扰。
虚拟机栈和本地方法栈为什么是私有的?
确保线程局部变量隔离。
并发和并行的区别?
并发是两个及以上作业在同一时间段内执行。并行是两个及以上作业在同一时刻运行。
为什么要使用多线程呢?
提高程序在单核和多核CPU上执行效率。
使用多线程可能带来什么问题?
内存泄漏、死锁、线程不安全。
什么是上下文切换?
CPU从一个线程切换到另一个线程的过程。
什么是线程死锁?如何避免死锁?
多个线程因争夺资源造成相互等待的现象。避免:保证线程获取资源顺序一致或超时机制。银行家算法,关键是确保进入安全状态。
sleep方法和wait方法对比
sleep暂停线程执行,不释放锁,wait用于进程间通信,会释放锁,要通过notify唤醒。
为什么wait方法不定义在Thread中?
wait方法是与对象的监视器锁直接相关的操作,wait操作对象并释放当前线程,因此定义在Object中,sleep是线程级别操作定义在Thread中。
可以直接调用Thread类的run方法吗?
不能,应用start方法启动多线程,因为直接调用run方法会在当前线程执行,而不是新建一个线程执行。
如何保证变量的可见性?
volatile可保证变量的可见性,即每次读取变量时从主存中获取最新值,但不保证线程安全,原子性。
如何禁止指令重排序?
volatile关键字。通过内存屏障禁止指令重排序,确保多线程环境下对象的安全发布。
volatile可以保证原子性吗?
不保证,如i++操作。
JVM
为什么分为年轻代和老年代?(拼多多)
存在假设,大部分Java对象只存活一小段时间,而活下来的小部分Java对象则存活很长一段时间。
对象在Java堆中的分配与回收。(说说Java对象分配规则)(滴滴考过)page 1
Java堆内存结构。新生代,包含Eden区,新创建对象在这分配,频繁Minor gc。Survivor区,存经过一次gc存活的对象。老年代,存生命周期较长的对象。元空间,存储类元数据。
对象分配。默认在新生代Eden区,用指针碰撞算法。大对象直接进入老年代,当对象在新生代年龄达到阈值晋升到老年代。为提高分配效率,jvm为每个线程在Eden区划分一块内存TLAB,用于新生代对象分配。
垃圾回收算法。新生代用复制算法,老年代用标记清除或标记整理算法。
新生代用的什么垃圾回收算法(得物)page 1
新生代用复制算法,老年代用标记清除或标记整理算法。
新生代进入老年代方式有哪些?(百度)
年龄阈值。当对象年龄来到阈值,在下一次GC晋升到老年代。
大小阈值。JVM允许大对象直接在老年代分配。
Java垃圾回收啥时候失败?(阿里)
内存泄漏:对象被错误的持有引用, 垃圾回收器无法回收这些对象。
CMS并发模式失败:如果并发标记阶段耗时过长,会导致并发模式失败,触发Full GC,可以调整CMS回收器参数解决。
频繁Full GC是什么原因?频繁Full GC怎么解决?(如何降低Full GC频率)(58同城,高德地图)page 12
fullgc原因。1.堆内存空间不足。2.老年代积累了大量存活对象。
解决方法。1.降低大对象创建。业务中会从数据库中加载包含大量字段的大对象,容易直接分配到老年代,将大对象拆解:第一次只加载关键字段,后续需要再进行延迟加载。2.适当增加堆空间。设置初始堆和最大堆大小。但要注意,堆内存增大后,虽能降低gc频率,但同时累积的对象也会更多,一旦fullgc发生,回收时间也会变长。要结合实验评估。3.监控。用print gc details参数开启gc日志,找到内存分配和对象存活瓶颈,针对性调整,如调整新生代老年代比例、设置晋升阈值。
线上full GC问题怎么排查(腾讯音乐)page 1
首先用print gc detail查看full gc频率和时长;
然后用dump看内存中哪些对象多,这些可能是引起full gc的原因;
如果堆很大或是生产环境,可以开启jmc飞行一段时间,查看这期间的数据定位问题。
假设现在还有挺多内存,有什么情况还会频繁fullgc?(在内存充足的情况下,哪些因素会导致频繁发生Full GC)page 1
内存碎片。大对象直接分配或晋升到老年代要大块连续内存,老年代碎片化,触发fullgc。
元空间。类加载泄漏,元空间异常增长。
解释JVM中Minor GC(小型垃圾收集)、Major GC(主要垃圾收集)和Full GC(完全垃圾收集)之间的区别。是什么触发了每种类型的垃圾收集?(Minor GC、Major GC和Full GC区别?)(滴滴考过)page 110
Minor GC作用在年轻代(Elden+Survivor),触发条件是Enden区分配失败,使用复制算法。
Major GC作用在老年代,触发条件是老年代空间不足,用标记清除如CMS算法。
Full GC作用在整个堆+方法区,触发条件是老年代或方法区空间不足,用标记-清除-整理算法。
JVM为什么进行分代管理?(腾讯)
JVM堆为什么要分为新生代和老年代(美团)
为了提高垃圾回收的效率。将内存划分为新生代老年代,JVM针对不同对象采取不同回收算法。
年轻代用复制算法,老年代用标记清除或标记整理算法。
内部抽象类存放在JVM哪个内存区域?(字节)page 1
内部抽象类在JVM中作为类的一部分处理的。类信息包括抽象类存在方法区。
public class OuterClass {
// 内部抽象类
abstract static class InnerAbstractClass {
abstract void abstractMethod();
}
}
// 使用内部抽象类
public class Main {
public static void main(String[] args) {
// 内部抽象类不能直接实例化
// OuterClass.InnerAbstractClass inner = new OuterClass.InnerAbstractClass(); // 编译错误
// 可以通过继承内部抽象类来使用
class ConcreteInnerClass extends OuterClass.InnerAbstractClass {
@Override
void abstractMethod() {
System.out.println("Implemented abstract method");
}
}
ConcreteInnerClass concreteInner = new ConcreteInnerClass();
concreteInner.abstractMethod();
}
}
虚拟机如何处理热点代码?jit缓存在内存什么位置?(小红书)page 151
jvm会通过代码执行频率识别热点代码,然后将其编译成本地机器码,并缓存起来。
jit编译后的代码在 代码缓存区域code cache ,通常位于堆外内存。
讲一讲对象的创建过程(说说Java对象创建过程)(shopee)page 110
在new一个对象时,jvm做了什么?(阿里,得物)page 110
1.类加载。在代码中使用一个类时,jvm会检查这个类是否已加载,如果没有,jvm通过类加载器加载类,读取类字节码,转化为jvm内部的class对象。
2.分配内存。接下来,jvm为对象分配内存。内存分配的位置取决于对象的大小和jvm内存管理策略,通常,对象被分配到堆中,但也有可能被分配到栈中,比如一些小的生命周期短的对象。
3.初始化。然后jvm会进行初始化操作,包括调用类的构造方法,并按照声明的顺序初始化类的成员变量。
4.对象引用。最后jvm会返回这个对象的引用,这个引用可以被赋值给一个变量。
内存分配的两种方式?给对象分配内存的方式?(讲讲Java堆内存分配两种方式)(得物)page 1
指针碰撞。连续内存分配,当分配新对象时,jvm将当前空闲区起始指针赋值给这个对象,并将指针向后移动对象大小距离。用于年轻代Eden区,因为Eden区用复制算法,空间连续。也用于TLAB线程本地分配缓冲区。
空闲列表分配。分散内存分配,不要求内存空间连续,维护空闲块列表,当要分配对象时,jvm遍历列表,找大小合适空闲块。用于老年代,存在内存碎片。
对象在什么时候被回收?(得物)
JVM如何判断一个对象是否可以被回收?(腾讯)page 104
对象回收条件:不可达。不被任何GC ROOT引用链可达。GC ROOT包括:虚拟机栈的局部变量引用、方法区类静态属性的引用、方法区常量如字符串常量池的引用、本地方法栈Native方法的引用。
回收时机:由GC算法和垃圾回收器决定。
分代回收策略:新生代:Minor GC会频繁回收。老年代:Major GC频率较低。
不同垃圾回收器:CMS、ZGC、G1。
jvm 跨代引用怎么办?(字节,美团)page 60
跨代引用是老年代持有年轻代对象引用,在年轻代gc时,不对这些跨代引用特殊处理,无法把年轻代被引用对象当做根对象。如果在Minor gc时扫描整个老年代找年轻代引用,则违背了分代回收初衷。
解决方案。1.记忆集。记录所有跨代引用对象,年轻代垃圾回收时只扫描记忆集,但还是有集合可能很大缺点。2.cart table。将老年代内存按固定大小512字节划分成块,用一个字节记录每块内存是否存在指向年轻代引用,发生写操作时,记录跨代引用。3.写屏障。对象被赋值时,检查被写入值是否位于年轻代,引用该值对象是否位于老年代,记录跨代引用。
优化。1.用弱引用、软引用。2.调优jvm参数,pretenure size threshold,调整大对象直接分配到老年代。
如果有一段代码进不了安全点怎么办?(美团)page 51
为什么进不了。1.无分支循环,jit会优化代码,会删除循环中的安全点检查。2.jni本地代码,jvm无法在代码中插入安全点。
解决。1.thread interrupted方法和yield触发安全点检查。2.调整jvm参数,use counted loop safepoints。也可以用print safepointstatistics监测。
什么时候需要打破双亲委派?(得物)page 1
自定义类加载器:如框架中,实现类隔离和热部署,需要打破双亲委派。
加载特定路径的类。
模块化系统:Java9引入,为实现模块的隔离和动态加载,需要打破双亲委派。
讲讲双亲委派模型(腾讯,淘天)
当一个类加载器收到类加载请求时,先不回自己尝试加载这个类,而是将这个请求委派给父类完成, 每一层都是这样,因此所有的类加载请求最终被传递到最顶层类加载器,只有当父加载器无法加载时,子加载器才会自己尝试。
JVM在执行 Java 程序的过程中会把它管理的内存划分成哪些数据区域?(jvm的基本结构)(jvm的组成)(淘天)page 1
JVM内存划分(饿了么)
划分为程序计数器、虚拟机栈、本地方法栈、堆、方法区以及直接内存(在JDK 1.8及之后版本)。本地方法栈专门处理本地非Java语言函数调用的栈。区就是共享区域,方法区保存与类相关的全局信息。堆,方法区,虚拟机栈和本地方法栈可能产生OOMError。
创建对象一定分配在堆里吗,了解过逃逸分析和栈上分配吗? page 1
不是所有对象都在堆分配,逃逸分析,判断对象引用是否逃逸出方法和线程,依据是对象是否赋值给某个堆中对象的字段或静态变量,或者对象是否作为方法调用的调用者。hotspot虚拟机并没有采用在栈上分配,而是用标量替换方式,即将一个对象拆解为若干局部变量,直接存放在栈中或者寄存器中。
JVM如何使用栈处理方法调用和返回?
每次方法调用时,一个新的栈帧被压入栈顶,包含方法的参数、局部变量等信息。方法返回时,栈帧被弹出,控制流回到调用方法的位置。
Java堆的分代结构
Java堆按照分代垃圾回收算法分为新生代(Young Generation)和老年代(Old Generation)。新生代用于存放新创建的对象,老年代用于存放存活时间较长的对象。新生代占三分之一,老年代占三分之二。
JVM方法区的概念和作用
Java虚拟机方法区是一块用于存储类信息、常量、静态变量、即时编译器编译后的代码等数据的逻辑区域。
永久代与元空间的关系
JDK 8之前使用永久代来实现方法区,JDK 8及以后使用元空间替代永久代,元空间使用本地内存而不再受限于固定大小上限,解决了永久代大小不可调节和垃圾回收效率低的问题。方法区是抽象的概念,永久代是以前的实现方法,元空间是现在的实现方法。
方法区的内存管理
方法区相比堆区,垃圾回收行为相对稀少,主要集中在对常量池的回收和对无用类的卸载。元空间的优势包括允许动态调整大小、提高了类的加载和卸载效率。
符号引用与直接引用的区别
符号引用是在编译时期能够使用的名字来表示某个目标的符号,而直接引用是直接指向目标的指针、句柄或偏移量等直接定位方式。
NIO与DirectByteBuffer你了解吗?
JDK1.4引入的NIO中的DirectByteBuffer允许Java程序 直接操作直接内存 ,通过Native函数库分配内存,避免了在Java堆和Native堆之间 复制数据 的开销。与传统的堆内缓冲区相比,DirectByteBuffer直接映射到本地内存,适合大数据量的高性能IO操作。
对象的内存布局
对象头,如哈希码、GC信息;实例数据,存储对象的字段值;对齐填充,保证对象起始地址是8字节的倍数。
讨论对象如何从JVM的新生代晋升到老年代
对象在新生代中经过多次Minor GC仍然存活时,会被晋升至老年代。
JVM如何处理新对象的内存分配,以及Eden区分配失败会发生什么?
JVM首先在Eden区为新对象分配内存。当Eden区分配失败时,会触发Minor GC以释放空间。如果GC后Eden区仍无法满足内存需求,较大的对象可能直接在老年代分配。
描述垃圾回收期间“Stop-The-World”事件的过程
停止所有的工作线程,遍历整个堆内存查找和回收不再使用的对象。
标记-清扫算法和复制垃圾收集算法的优缺点
标记-清扫,在进行GC时标记所有存活的对象,然后清除未标记的对象,产生内存碎片。复制算法,将内存分为两部分,每次只使用一部分,内存利用率低。
类加载器有哪些类型
启动类加载器,加载Java核心库;扩展类加载器,ext目录下的jar包;应用程序类加载器,面向用户的加载器。
Tomcat容器是如何使用自定义类加载器来实现类隔离的?
tomcat的类加载器层次结构。首先有个common类加载器,加载所有应用程序共享的类;然后是catalina类加载器,加载服务器内部类;接着是shared类加载器,加载所有应用程序共享的类;最后是web应用程序类加载器,加载特定应用程序的类。
类加载器隔离机制。如果一个应用程序试图加载一个类,tomcat会首先检查该应用程序的类加载器,如果没有才会向上查找父类加载器。
计算机基础
内存持续上升,该如何排查问题?(遇到内存泄露有什么排查方式)(内存泄漏的排查思路?)(内存泄漏、原因、解决办法)(Java内存泄漏怎么排查?)(阿里)page 33
问题定位。1.监控报警,用APM如普罗米修斯和grafana,观测jvm heap,native内存,gc频次,响应时长,load average。2.分层排查。先在系统层面判断机器资源如内存、io瓶颈。然后在进程层面,如Java进程,用pid stat命令看rss(常驻集合大小,非交换区内存用K字节),vsz(虚拟地址大小,虚拟内存用K字节),线程树。然后在jvm层面看堆新生代,老年代,元空间使用情况,以及gc日志。应用层面看线程栈、对象分配热点。
常用工具。1.linux层面。top,看进程线程cpu内存实时占用。vmstat看整体内存,swap,io统计。pid stat -p $pid -r -t 1 5细粒度监控线程进程内存(RSS,vsz)和上下文切换。2.jdk工具。j stat -gcutil $pid 1s,监控gc次数。j stack获取线程快照,排查死锁。j map -heap $pid 看堆分代容量。j map -dump:live,format=b,file=heap.hprof $pid 生成heap dump,用MAT离线分析。
典型的流程。1.初步确认。top/ps 确认 Java 进程内存确实持续上涨且无释放。2.GC 日志分析。开启 -Xlog:gc*:file=gc.log:time,uptime(或旧版 -XX:+PrintGCDetails)观察 Old GC 频率和停顿时长。3.实时指标监控。用 jstat -gcutil 验证 Young/Old/Metaspace 使用趋势。4.线程层面排查。top -Hp
看堆内存溢出的时候会看那些指标?page 1
jvm堆指标。用jstat -gcutil,看s0 s1 u使用率,s0 s1c容量,eden old metaspace使用率,以及ygc fgc的次数和耗时。用jmap -histo[:live] $pid 看实例数量和总大小。
linux查性能瓶颈的命令,查内存的命令,查磁盘的繁忙程度的命令(蚂蚁)
cpu用top观察load average负载。内存用free - h看总内存,已用内存,空闲内存。磁盘io用iostat - x 1 5看% util利用率,await平均等待时间。
讲讲什么是load average(讲讲什么是系统负载)page 1
load average是linux系统衡量整体系统负载状态指标,用三个数值描述系统在最近1分钟5分钟15分钟时间区间内平均有多少个进程处于可运行状态或等待io状态。
含义。1分钟load average为2.5,表示过去1分钟内,平均2.5个进程同时处于可运行或等待io状态。在单核系统,load为1表示刚好满载。多核应该除以核数评估。用top命令。
如何优化接口时间?(如何优化接口响应时间?)(B站)page 16
多线程性能调优。合理设置线程池大小,结合具体机器做实验,从N+1,2N两个公式计算大概的线程数量,之后经过压测,往“增大线程数量”和“减少线程数量”优化,观察线程平均执行时间和线程平均整体时间以及整体处理时间变化。
设计模式调优。比如原型模式和享元模式。1.原型模式是用克隆已有对象创建新实例,而不是new。可以避免耗时的构造过程。2.享元模式对对象中可以共享的内部状态优化,将这些数据提取出来形成共享对象,减少内存占用。比如Java字符串常量池就体现了享元模式。
数据库性能调优。1.避免死锁。(1)优化SQL。用慢查询日志和EXPLAIN FORMAT=JSON定位瓶颈,针对具体场景,设计和调整索引,如经常更新的记录用主键更新,避免用辅助索引更新,减少死锁风险。(2)事务和锁管理。长事务容易死锁,拆解长事务,针对死锁,设置合理的锁等待超时参数innodb lock wait timeout。写事务代码时,保证操作遵循固定顺序。(3)隔离级别。对允许脏读幻读场景,用读已提交减少间隙锁,2.索引优化。(1)对主键索引用自增字段,保证数据顺序写入,减少页分裂和内存碎片。对于辅助索引要考虑能否索引覆盖,避免回表。注意联合索引顺序。(2)索引失效。重写SQL或使用hint比如force index强制使用索引。
做过MySQL调优吗?(MySQL 调优)(MySQL优化)(得物,作业帮多次考)page 35
说一下MySQL常用优化方式(恒生,帆软,滴滴考过,快手)page 35
mysql 的索引调优(淘天)
数据库性能调优。1.避免死锁。(1)优化SQL。用慢查询日志和EXPLAIN FORMAT=JSON定位瓶颈,针对具体场景,设计和调整索引,如经常更新的记录用主键更新,避免用辅助索引更新,减少死锁风险。(2)事务和锁管理。长事务容易死锁,拆解长事务,针对死锁,设置合理的锁等待超时参数innodb lock wait timeout。写事务代码时,保证操作遵循固定顺序。(3)隔离级别。对允许脏读幻读场景,用读已提交减少间隙锁。2.索引优化。(1)对主键索引用自增字段,保证数据顺序写入,减少页分裂和内存碎片。对于辅助索引要考虑能否索引覆盖,避免回表。注意联合索引顺序。(2)索引失效。重写SQL或使用hint比如force index强制使用索引。
MySQL某个表数据量较小,希望它驱动大数据量的表的时候,怎去优化性能?(京东)
MySQL默认用嵌套循环连接算法,由内外两个循环构成,分别从两个表中顺序取数据。外层循环称为外表,内层循环称为内表,因为这个算法是从遍历外表开始,因此外表也被称为驱动表。
嵌套循环算法分为S N L J,B N J和I L J。
S N L J的过程是先遍历外表,取一条记录r 1,遍历内表,对于内表每条记录与r 1做join并输出结果。重复上面的步骤。
b n j是对s n l j的优化。在遍历外表时,不再只取一条记录,而是取一个批次记录,加载到内存,减少了磁盘io。
i l j在b n j上使用索引。先遍历外表,取一个批次记录r i,用连接键和r i确定对内表索引的扫描范围,再用索引得到若干数据记录s j。将r i和s j每条记录做join。
为优化性能,首先内表中要在连接键上建立索引,其次用explain查看优化器的驱动表是否选择正确。
讲讲MySQL 的索引调优(淘天)
定位问题,打开慢查询日志,找到响应时间最长的SQL。
用explain找到type 为ALL即全表扫描,或extra为filesort。
检查表结构,确认字段类型,索引。
设计索引,用select,orderby的字段进行组合。
创建索引后再运行explain,检查rows,type,extra是否改善。
如何预估接口上线后的QPS page 1
首先,理解业务模型和场景。每个业务请求会消耗不同的系统资源,通过分析生产流量、业务比例、时间段,得出每个业务类型的流量特征。业务比例设计要贴合真实生产环境。如果没有生产流量,可以用历史数据预测流量。
其次进行基准性能测试。看单个业务在理想条件下最大TPS。测试时注意不同业务类型的最大承载能力。具体性能测试方案,用jmeter负责发压,普罗米修斯做监控。发压线程递增策略,采取阶梯递增方式,观察TPS。
然后是压力测试和混合场景设计。根据实际业务比例,设计混合性能场景。要关注TPS、响应时间、错误率指标。
计算QPS。基于每个业务TPS,结合比例,估算总的QPS。
优化。在实际测试中,要观察TPS和响应时间的变化,判断瓶颈出现的时机。
参考:https://time.geekbang.org/column/article/178068,https://time.geekbang.org/column/article/178080,https://time.geekbang.org/column/article/181916,https://time.geekbang.org/column/article/182912(最重要),https://time.geekbang.org/column/article/187963,https://time.geekbang.org/column/article/189010,https://time.geekbang.org/column/article/190684
从输入URL到页面展示到底发生了什么?(字节考过,shopee)page 1
浏览器输入一个url的全流程(腾讯多次考)page 1
URL解析,提取出协议,如http,https,主机名,端口号,路径,查询参数。
DNS解析,浏览器检查本地缓存是否有该域名IP地址,如果没有向dns服务器发送查询请求。
TCP连接,用IP地址和端口号三次握手。
发送HTTP请求,构造http请求报文,包括请求行,请求头,请求体,发送到服务器。
server处理请求,根据请求路径和方法处理请求,生成响应。
并返回HTTP报文,
浏览器解析页面,如果是HTML页面,会构建dom树,根据css和js渲染。
连接结束,四次挥手。
ping一个网址中间的全过程(阿里巴巴)page 1
icmp协议。
应用层:操作系统调用ping程序。
DNS解析:将网址转化为IP地址。向DNS服务器发送查询请求,获得目标主机IP地址。
网络层:操作系统生成ICMP echo request数据包,设置源IP目的IP。
数据链路层:数据包封装成以太网帧,包含源MAC和目的MAC,目的MAC通过ARP协议获取。
物理层。
目标主机处理:生成ICMP echo reply。
源主机接收:ping程序显示往返时间RTT。
HTTP状态码有哪些?(滴滴考过)
301和302区别?(美团)
1开头,正在处理请求;2开头,请求成功;3开头,重定向;4开头,客户端错误,请求包含语法错误或无法完成;5开头,服务器处理请求时发生错误。301是永久重定向,302是临时重定向。
404是请求的资源不存在。
500服务器内部错误。
浏览器发生302跳转背后的逻辑?(携程)page 152
302是临时重定向。当服务器接收到一个请求时,如果发现请求的资源已经临时移动到另一个位置,服务器会返回一个302状态码,并在响应头中包含一个新的 location 字段,指向资源的新地址。
浏览器在接收到302状态码后,会 自动发起一个新请求 ,访问location字段中指定的url,这个过程对用户透明,需要注意的是:1.如果原请求时POST,浏览器会转化为GET请求,这也是在需要保留原请求方法时http1.1引入307状态码的原因。2.重定向请求不会保留原请求体,但会携带cookie。
HTTP和HTTPS的区别?(科大字节考过,腾讯)page 1
协议 安全性 。http是超文本传输协议,是一种明文传输协议,数据在传输过程中是未加密的,容易被窃听和篡改。https在http基础上加入了ssl tls协议,数据在传输过程中是加密的,安全性更高。
端口号 。http默认使用80端口进行通信。https默认使用443端口进行通信。
证书 。https需要使用ssl证书验证服务器身份,http则不需要证书。
性能。由于https需要进行加密解密操作,会带来一定性能开销,相比http会稍微慢一些。
http和socket区别?(美团)page 1
协议层次不同:http是应用层协议,基于TCP ip协议栈,用于浏览器和服务器之间通信。
socket是传输层和应用层之间的抽象层,他是TCP ip的封装,提供了编程接口。
数据传输方式:http是请求响应模式,数据格式是文本或json。
socket数据可以是二进制或文本。
应用场景:http适用于web应用。
socket适用于即时通讯,在线游戏。
https加密的原理是什么(https的加密过程)(小红书,得物,腾讯)page 136
讲一讲ssl/tls的连接过程,加密解密的时机和方法,CA证书的验证方法(shopee)page 136
讲一讲https握手的过程(腾讯多次考)page 136
HTTPS 的安全性体现在什么⽅⾯?(本质还是在问 HTTPS 原理)page 136
HTTPS如何防范中间人攻击?(百度)page 136
HTTPS如何保证自身秘钥安全性?(得物)page 136
https本质是http与tls协议组合。
加密方式。对称加密加密解密用相同密钥,处理速度快,缺点是如何安全分发密钥。非对称加密,用公开公钥和只有持有者才能使用的私钥。公钥加密数据,私钥才能解密。
数字证书和CA。数字证书包含网站公钥、拥有者信息、证书有效期及颁发机构。证书由权威机构CA签发,用CA私钥签名,用户端用CA公钥验证签名。形成信任链,由root CA做最后保证。
https握手过程。1.客户端发送client Hello,含tls协议版本,加密套件列表,随机数。2.服务器响应server Hello,发送协商版本,加密算法和自己的随机数,以及自己的数字证书。3.客户端用CA公钥验证证书有效性。然后生成pre master secret随机数,用服务器公钥加密发回服务器。双方借助自己发送和接收到的随机数以及pre master secret算出相同对称会话密钥。4.切换加密模式,双方发送change cipher spec消息,从此开始,双方用协商好的对称加密通信。
防止重放和篡改。协议中引入timestamp和nonce随机数保证唯一性。
https为什么既要非对称加密,又要对称加密?(腾讯)
他们有自己的优势和不足。
非对称加密安全性高,公钥公开,私钥保密,但是他计算复杂度很高。
对称加密计算效率高,但是他密钥管理复杂。
可以用非对称进行初始密钥交换,后续数据用对称进行加密。
TCP与UDP区别。(科大考过,shopee多次考)page 1
TCP面向连接,三次握手,UDP无连接。
数据传输方式,TCP面向字节流,传输过程中分段、排序和重组,接收方不必关心数据边界。UDP面向数据报,保留边界。
可靠性。TCP提供可靠传输,用序列号、确认应答、重传机制、流量控制、拥塞控制,保证可靠。udp没有可靠性。
包头设计。TCP包含很多信息,如序列号、确认号、窗口大小、校验和,首部开销20~60字节。UDP仅包含源端口号、目的端口号、长度和校验和,首部开销8字节。
UDP支持广播。
TCP MSS 跟IP的MTU有啥区别(shopee)
mtu是IP报文,包含头部的最大长度,mss是tcp有效载荷,不含头部的最大长度。
TCP优化(如何提升TCP三次握手的性能)(腾讯)page 1
提高三次握手性能,有三个方案,客户端优化、服务端优化和绕过三次握手。
方案一,客户端优化。控制syn重试次数,调整linux net ipv4 tcp syn retries参数,默认他为6次,退避计时为1到64秒,共127秒,在内网或对时延敏感场景,可适当降低重试次数,加快失败返回应用。
方案二,服务端优化。syn有半连接队列,可以调整tcp max syn backlog调整队列大小。其次可以打开linux的syn cookies功能,他可以在不用半连接队列下成功建立连接,服务器根据当前状态计算出一个值,放在服务器即将发出的syn ack报文中,当客户端返回ack时,取出该值验证,如合法则连接建立成功。可以调整linux的tcp syn cookies参数为1,当队列溢出时启用这个方式。
方案三,绕过三次握手。使用谷歌提出的tcp fast open。第一次握手时,客户端正常进行三次握手,并在syn中请求tfo功能,服务器在syn ack中返回cookie。后续连接客户端在第1个syn报文中携带上次的cookie和应用层的数据,服务器验证后即可直接交给应用处理,节省一个rtt。可以调整linux的tcp fast open参数为3,开启全功能支持。
为什么TCP4次挥手之后要等待2MSL,2这个倍数是怎么确定的,为什么不是3,不是1.5等(tcp为什么四次挥手后等待2msl,而不是3msl,1.5msl?)(腾讯,字节)page 103
首先确保最后ack被对方接受。主动关闭方发送最后一个ack后进入time wait状态,根据rfc规定,time wait就是等待足够时间确认对方收到连接终止的确认报文。假设这个ack丢失,那么对方会重传fin,time wait要保证主动方还能收到对方重传的fin并再次ack,从而关闭连接。保证发送方ack完全消失在网络中,以及保证对方重传的fin也在网络中失效,这两个步骤的叠加刚好2msl。tcp只允许对fin和ack的一次重传,如果一直重做fin,ack等对方确认,tcp关闭就会变成一个无限循环。
TCP 四次挥手等待2MSL,现代网络发展中,这个还是固定的2MSL吗?(字节)
2MSL在协议规范层面,至今没有改变。但不同从操作系统的MSL具体时间是由各自实现决定的。
TCP端口连接的上限是多少?(得物)page 11
单个服务端进程:受限于TCP四元组(源IP,源端口,目标IP,目标端口)的可能组合数。服务端IP和端口在连接中固定,变化的是客户端IP和端口。客户端IP最多为2^32,端口最多为2^16,总共最多2^48。
同时也受限于内存大小,每个TCP连接占用一个4KB内存。
单个服务器:可以监听多个端口,总共2^48x2^16,非常大,但是也受限于内存。
Linux 的fork和clone在系统调用的时候有什么区别?(阿里)page 1
在glibc中,fork是对clone方法调用的参数组合,flag形参为SIGCHLD,即子进程退出时给父进程发SIGCHLD,且不共享任何资源。
内核操作的不同。1.clone的flags形参决定在copy_process中对虚拟内存、文件表等的处理策略,fork没有。2.程序的启动点。fork子进程沿用父进程当前栈和指针,从fork返回点继续执行。clone则要提供child stack,内核返回后用户态手动往新栈里压入函数指针和参数,由start_thread方法跳到用户指定的形参fn(arg)执行。
a = fork() b=fork() print(a,b) 这个最后产生几个进程,打印的内容是什么(字节)
产生4个进程,父进程产生子进程1,父进程和子进程1分别产生子进程2和子进程3。父进程的值分别为两个pid,子进程1的a的值为0,b的值为一个pid。子进程2的a的值子进程1的pid,b的值为0。子进程3的ab两个值都为0。
Linux命令了解吗?(讲讲linux常用5个命令)(Linux的常用命令有哪些)(常用 linux命令)(腾讯,美团)page 1
grep管道,比如grep -Rni –color=auto ‘ERROR’ /var/log/myapp/,在日志中定位异常。
kill,kill -9强制关闭进程。
tail -f,实时跟踪日志,如tail -n 100 -f /var/log/nginx/access.log。
top命令,展示cpu load average。
nohup命令,当前交互命令行退出的时候,程序还在运行。程序如果要不霸占交互命令行,最后加一个与符号。如nohup command > out.file 2>&1 &,即将标准输出和错误输出合并到out.file中。
linux命令查找文件内出现频率topK的电话号码(shopee)
grep -oP ‘1[3-9]\d{9}’ logs.txt | sort | uniq -c | sort -rn | head -n 10
-o只输出匹配部分,-P用正则表达式,sort全局排序,uniq -c对相邻相同的号码进行合并,并在行首输出出现次数,sort -rn按数字-n倒序-r排序,把高频放前面,head -n 10只取前10行。
我服务挂了,重启显示端口占用,啥情况,排查一下(服务挂掉重启时提示端口被占用,该如何排查原因?)(腾讯)page 1
进程未退出。服务异常退出,仍存在僵尸进程或孤儿进程,用lsof -i :<端口号>查看是否有该进程运行。端口号>
僵尸进程怎么解决?(腾讯)page 1
僵尸进程是子进程已终止,父进程未调用wait waitpid获取子进程退出状态,这些进程处于exit zombie状态,子进程PCB仍保留在系统进程表。
及时回收子进程。用waitpid检查子进程状态。
注册sigchld信号处理函数。子进程退出,该信号处理函数会被调用。
双重fork技巧。第一次fork创建子进程,子进程再fork创建孙子进程,父进程直接退出,孙子进程会由init接管,负责回收。
TCP半连接队列是做什么用的?(百度,腾讯多次考)page 1
tcp三次握手第三次失败会怎样?(字节)page 1
管理连接建立过程的机制。
半连接队列存储在TCP三次握手中收到的syn请求。
客户端发送syn请求,服务端收到后,回复syn-ack,客户端收到后,发送ack确认,服务端收到后,将该连接从半连接队列移到全连接队列。
防止syn泛洪攻击。限制队列大小,防止客户端发送大量syn,不进行后续ack确认。
可以调整net.ipv4.tcp_max_syn_backlog改变服务器抗攻击能力。
TCP连接一个没有服务的端口,会发生什么?(腾讯)page 1
连接请求会被拒绝:当客户端尝试连接一个没有服务的端口时,服务器会返回一个RST复位包,表示该端口没有监听服务,连接请求被拒绝。
错误码:在TCP/IP协议中,返回Connection Refused。
TCP UDP能监听相同端口吗(腾讯)page 58
能监听相同端口。TCP和UDP虽然都用端口号标识接口,但他们属于不同的传输层协议,端口号空间是独立的。
HTTP如何保存用户状态?(字节考过)
HTTP通过session和cookie保存用户状态,session保存在server,相对更安全,可通过client的cookie存放seesionID或URL重写实现用户状态跟踪。cookie存在client中,存在被篡改风险。
restful常用请求方式哪些是幂等的?(京东)
包括GET,PUT,DELETE,而POST通常不是幂等的,每次请求可能在服务器创建一个新资源。
网络七层,大体说一下视频通话的时候数据在网络中的流转过程?(哈啰)
应用层:视频通话应用如腾讯会议在这一层生成视频音频数据。应用层负责数据格式化和打包。
表示层:数据编码和加密。
会话层:建立管理和终止会话连接。
传输层:UDP数据传输。
网络层:用IP协议,根据路由表发送到目标地址。
数据链路层:数据包封装成帧,用MAC地址进行通信。
物理层:通过光纤或无线信号通信,数据在这一层被转换为比特流通过物理介质传输。
为什么TCP三次握手?而不是两次或四次?(滴滴考过,58同城,腾讯,叮咚买菜)page 137
1.避免历史连接。防止 两次握手 ,旧SYN包到达服务器,服务器直接进入ESTABLISHED状态,但客户端已放弃,浪费服务器资源。
2.同步双方初始序列号。客户端的syn报文需要服务端回一个ack应答报文,服务端发送初始序列号给客户端时,依然也要得到客户端回答响应,这样两次交互,才能保证双方初始序列号被可靠的同步。 而第二步和第三步可以优化成一步 ,就成了三次握手。避免资源浪费。如果只有两次握手,如果客户端syn报文在网络中阻塞,客户端没有收到ack报文,他就会重复发送syn,由于没有第三次握手,服务端不清楚客户端是否收到了自己回复的ack报文,所以服务端每收到一个syn只能先主动建立一个连接。
介绍一下TCP三次握手page 19
讲一下TCP三次握手四次挥手(shopee)
TCP三次握手四次挥手的状态?(腾讯多次考)
客户端向服务器发送SYN报文,随机生成初始序列号x,客户端进入syn sent状态。
服务端向客户端回复syn ack,syn是服务器自己的随机序列号y,ack为x+1,服务器进入syn received状态。
客户端发送ack,序列号为y+1,进入established状态。
介绍一下TCP四次挥手。page 19
讲一下tcp四次挥手?(作业帮)
客户端调用close后,发送fin给服务端,客户端进入fin wait 1状态。
服务端发送ack,服务端进入close wait状态,客户端收到ack进入fin wait2状态。服务器可能还有数据发送。
服务器数据发送完后,服务端也发送fin给客户端,服务器进入last ack状态。
客户端发送ack给服务器,进入time wait状态,持续2MSL,保证ack能到达服务器。
TCP三次握手过程TCP的ACK包为什么是随机开始?(字节)page 55
防止预测攻击。随机的初始序列号使攻击者难以预测。
保证连接唯一。避免旧数据包混淆。
TCP如果发送了一半的数据,剩下的那一半数据在第二次TCP连接的时候怎么确保不和第一次的重复?(腾讯)
序列号:TCP用序列号标识每个字节的数据。每个TCP连接都会有初始序列号ISN,后续的数据包会根据这个序列号递增。
重传机制:如果第一次连接的数据没有完全发送成功,TCP会进行重传。重传的数据包会带有正确的序列号,避免和第二次连接重复。
TCP close_wait过多什么原因?(字节,腾讯多次考,拼多多)page 164
服务端没有及时调用close关闭套接字。
原因:1.程序没有调用close(),或异常没有处理。 2.连接未释放导致文件描述符泄漏。 3.应用收到对方FIN包后,业务卡在某个耗时操作如长事务、死锁,无法及时调用close()。
解决;1.检查代码,确认分支包括异常关闭连接,用finally或defer。 2.排查阻塞操作,如db长查询,死锁。 3.用netstat -antp | grep CLOSE_WAIT统计CLOSE_WAIT连接。结合lsof -p pid查看进程未释放的文件描述符。
注:close_wait发生在第2次挥手。
TCP time_wait过多什么原因?怎么解决?(58同城,shopee,腾讯多次考)page 152
1.HTTP没有使用长连接。
2.HTTP长连接超时。
3.HTTP长连接请求数量达上限。
4.TCP参数配置不当。tcp tw reuse参数没有正确配置,导致TIME WAIT状态的连接无法被重用。
解决:1.修改tcp tw reuse参数。调整tcp max tw buckets限制TIME WAIT连接数量。
2.用长连接代替短连接。
3.在应用层实现连接池。
注:TCP里面是先close wait再time wait。time wait发生在第4次挥手。
短连接和长连接在防火墙场景下用哪个比较好?(腾讯)page 164
长连接是更好的选择。能减少频繁建连的开销,降低防火墙连接建立表的压力,适合高并发场景。但需注意两点:一是通过心跳机制维持连接活性,避免防火墙超时断开;二是配合连接池和防火墙调优,避免连接耗尽。
TCP time_wait状态连接怎么快速回收?(腾讯)
调整linux系统参数sysctl.conf。
net.ipv4.tcp_tw_reuse=1,允许time_wait状态的socket重新用于新连接。
net.ipv4.tcp_tw_recycle=1,启用TCP时间戳,加速超时状态的回收。
TCP time_wait发生在四次挥手的哪个阶段?(腾讯)page 153
客户端第四次挥手阶段。
第二次握手传回了ACK,为什么还要传回SYN?
既告知Client自己的初始序列号,也确认收到Client的SYN包。
TCP为什么要四次挥手。(滴滴考过,B站)page 21
全双工协议,两个方向通信独立。客户端通信结束不代表服务端的通信也结束了。
TCP队头阻塞问题(滴滴)
TCP数据包由于网络拥塞,发生延迟或丢失。当队列中某个数据包出现问题时,后续所有数据包都会被阻塞。
解决方案:调整网络参数,避免拥塞。
用高效协议:如QUIC。
TCP队头阻塞问题
使用http并发连接,同时对一个域名发起多个长连接。
用域名分片技术,多开几个域名,这些域名都指向同一台服务器,这样实际长连接数量可以更多。
发起http请求后,如果请求超时,该怎么排查(字节)
客户端排查。先检查linux timeout、socketTimeout、ConnectTimeout的配置。然后用curl -v看能否复现,并检查http的方法、headers、请求体大小。
网络排查。用nsloopup看域名解析时长。检查NGINX、api网关的access日志,看请求是否到达上游,排查是否在中间件层被限流、熔断或路由到不健康节点。
服务端排查。在服务端应用定位到客户端请求的traceid,结合apm监控调用链。
TCP keep alive和http keep alive区别?(腾讯)page 153
TCP keep alive:检测TCP连接存活状态。一段时间没有数据传输时,发送小的探测包确认对方是否仍然在线。默认关闭
HTTP:复用tcp连接。在同一个TCP连接上发送多个HTTP请求和响应。默认开启。
讲讲TCP超时重传(腾讯)page 1
序列号。TCP为每个字节分配唯一序列号,在三次握手协商。接收方使用累计确认方式。
自适应重传定时器。针对丢包,TCP对每个已发送未确认数据启动重传定时器RTO,重传时间由往返时延RTT样本的加权移动平均及方差估算,会动态调整。如定时器超时,则重传数据并将下次超时窗口加倍。
快速重传。当接收方检测到乱序,如收到序号大于期望,会持续发送相同ack,发送方收到三个以上冗余ack,立即重传丢失的包。用sack选择性确认,接收方在TCP头部不仅告诉接收方哪个包已经ack,还告诉接收方哪些数据包也到了但不能确认sack。
讲讲TCP流量控制 page 1
TCP用滑动窗口协议实现。接收方在ack报文中报告剩余可用缓冲区大小rwnd。关键变量。last byte acked,已确认的数据末端位置。last byte sent,已发送但不确认的最大序列号。last byte acked+rwnd,可发送的最远边界。
讲讲TCP拥塞控制 page 1
TCP总发送窗口由c w n d,r w n d最小值决定,c w n d反映网络拥塞情况,r w n d反映接收方能力。
慢启动。连接建立初期,c w n d从1MSS(最大报文长度)开始,每收到一个a c k,c w n d增加一个MSS,指数级增长,直到慢启动阈值ssthresh,65535字节。
拥塞避免。当c w n d大于等于ssthresh,c w n d线性增长。
快速恢复。发生三次重复a c k,将ssthresh设为c w n d的一半,将c w n d设为ssthresh加3。
raft如何保证一致性(腾讯)page 1
强领导者模型。raft全集群同时最多只有一个leader,所有写请求必须通过leader处理。leader负责接收客户端命令,追加日志,复制日志和驱动状态机提交,Follower只是被动接收。
唯一leader选举保证。首先是任期编号,每次选举会递增编号,请求投票RPC和日志复制RPC都携带编号。收到更大任期信息,节点会更新自己的任期并回退成Follower。其次是随机选举超时,在raft中随机有两个含义,首先是Follower等待leader心跳信息超时的时间间隔是随机的,其次当没有候选人赢得过半票数,选举无效,这时需要等待一个随机时间间隔,这也是随机的。最后是多数投票原则,Candidate向全体广播请求投票RPC,赢得大于半数票后当选leader。
日志复制一致性。leader接收客户端写请求后,先创建日志项,包含任期编号,索引,命令,追加到本地日志,然后向Follower发送日志复制RPC,如果本地在prev log index没有prev log term一致的日志,则返回失败,否则删除本地从prev log index加一开始的所有日志,追加leader发来的新日志,更新本地提交index。leader只有在大于半数节点复制后,才更新自己的提交index,并在下一次心跳广播给其他节点,这里节省了一个RTT,即leader提交后没有向其他节点发送确认。
最后,成员变化的一致性。raft用单节点变化,一次变化一个节点,不会同时存在旧配置和新配置两个大多数,保证一致。
注:raft算法动画演示:http://thesecretlivesofdata.com/raft/
参考:https://time.geekbang.org/column/article/204472,https://time.geekbang.org/column/article/205784,https://time.geekbang.org/column/article/206274
你怎么理解重构(美团)page 1
重构是对软件内部结构的改善,目的是不改变软件外部行为前提下,使其更易理解、修改成本更低。
重构的方法是依赖单元测试保驾护航,并用设计模式,采取小步提交的思路,每次改动尽量只涉及一个痛点,便于回滚。
参考:https://time.geekbang.org/column/article/179679
A机器发送报文到B机器途中有哪些可能原因会导致丢包?(腾讯)page 153
物理检查:确认网线、端口状态。
链路层:用ping检查连通性,traceroute追踪路径。
网络层:检查路由表、防火墙规则、MTU设置。
传输层:通过tcpdump抓包,观察重传情况。
应用层:调整缓冲区大小。
流量控制和拥塞控制区别?(腾讯)page 1
流量控制关注是发送方和接收方之间数据传输速率匹配,而拥塞控制关注的是整个网络的负载情况。
流量控制用滑动窗口实现,在传输层实现。
拥塞控制有慢启动,拥塞避免,快速重传,快速恢复。在网络层和传输层实现。
计算机单核单cpu还需要使用多线程吗?(淘天)page 1
单核单cpu环境,多线程能带来并发而不是并行。通过上下文切换,cpu在一个线程等待io阻塞时切换到另一个线程执行。
负载均衡有哪些方法(负载均衡你介绍一下)(百度,腾讯音乐)page 1
轮询。各个节点依次接收请求。
加权轮询。各个节点设置权重,权重大的接收更多请求。
最少连接数。优先调度当前连接数最少的节点。
最少响应时间。动态监测各节点平均响应时间,优先调度延迟最低的节点。
源地址哈希。根据客户端IP计算哈希,用于保持会话粘性。
一致性哈希。用于Redis缓存集群,当节点增减时,只有少量key迁移。
常见限流算法你介绍一下(腾讯音乐)page 1
令牌桶算法,系统以恒定速率往桶中生成令牌,每个请求到来要从桶中取走1个令牌,否则被限流。支持短时间突发,如登录验证码接口场景。
漏桶算法,请求放入队列,以固定速率漏到业务逻辑执行,如果队列满,后续请求被拒绝。特点是输出流量绝对平滑,不支持突发,适合对后端服务出入库、写磁盘、消息中间件等严格要求的场景。
固定窗口。按固定时间如1秒窗口统计请求数,超过阈值拒绝。适合阈值较小的场景,且可容忍偶发毛刺。
滑动窗口。窗口在时间线上滑动,记录每次请求时间戳,统计过去T时间内请求数,比固定窗口更平滑,毛刺更小,适合对稳定性要求高,不能接受窗口毛刺的核心业务。
动态限流。BBR、SLAQ算法。他们实时采集系统指标,动态调整最大并发速率。
说说快速排序
先选取pivot元素,然后将数组划分为小于pivot和大于pivot两部分,然后递归对两部分进行快速排序。平均和最好时间复杂度是O n log n,最坏时间复杂度是n的平方,对应已顺序或逆序情况。
推导一下快速排序时间复杂度(字节)
对长度为n的数组,快速排序选取一个枢轴,一趟划分将将数组分为左右两部分,左侧小于等于枢轴,右侧大于等于枢轴,递归对左右两部分进行排序。划分时间复杂度是O n,如枢轴落在第k个位置,则时间复杂度递归式为T n等于T k加上T n减去k减去1加上O n,如果是平均划分,即k等于二分之n,则T n约等于两倍的T 二分之n加上O n,而主定理给出这种递归的解为O n log n的。若每次选到的是最小或最大值,则T n等于T n减1加上T 0加上O n,展开求和,T n等于O n加O n减1,加到O 1,即为O 二分之n乘以n加1,是O n的平方复杂度。
推导一下归并排序时间复杂度(字节)
对长度为n的数组,递归排序先将数组分为两半,各自长度约为二分之n,然后对这两半分别递归的进行归并排序,然后将两个已排序子数组以O n时间复杂度合并为整体有序数组。则时间复杂度递归式为T n等于T 二分之n加上T二分之n加上O n,主定理给出这种递归的解是O n log n的。
链表做归并的时候需要从中间节点断开,这个相比归并数组会影响时间复杂度吗(字节)
因为链表不支持随机访问,因此归并排序找中间节点用快慢指针需要O n,而数组只需O 1时间复杂度。但对两个子链表递归归并排序,以及最后的合并过程,链表和数组的时间复杂度是一样的。
快速排序的时间复杂度是由什么影响的
受到pivot选取策略和输入分布的影响。如果每次pivot都选到最小或最大元素,会退化为n的平方,为避免最坏情况,可以用随机化pivot和三数取中法。
怎么查服务器建立了多少个 TCP 连接
ss -tn state established | wc -l。-t只显示TCP。-n数字化显示。state established只查看已连接的,wc -l统计行数。
路由器和交换机区别?(拼多多,滴滴,腾讯)
工作层次不同:路由器工作在网络层,负责数据路由选择和转发。交换机工作在数据链路层,负责局域网内数据交换。
功能不同:路由器根据路由表将数据包从一个网络发送到另一个网络,支持多种协议如TCP IP和IPX。交换机在局域网内用MAC地址实现端到端传输。
ARP协议干什么的?(滴滴)
将IP地址解析为MAC地址的协议。
当一台主机要和另一主机通信时,先检查自己ARP缓存,如果缓存没有目标主机MAC地址,发送一个ARP广播,询问目标IP地址的MAC地址。
sftp和ftp区别?(拼多多)
协议类型:ftp用两个通道进行通信,一个用于控制命令,一个用于数据传输。
sftp是ssh协议一部分,通过加密进行传输。
端口:ftp用两个端口,21用于控制命令,20用于数据传输,主动模式。
sftp通常用22。
进程间通信的方式,进程通信(百度,shopee,腾讯多次考,字节,阿里)page 1
管道grep,信号,消息队列,信号量,共享内存。管道用于父子进程之间的通信。信号和信号量不同,比如kill -9给进程发送sigkill信号,结束进程。消息队列比管道更适合频繁的数据传输。信号量实现进程之间的互斥和同步。共享内存速度最快。
假如说开了一块共享内存大概是3个g,有两个进程都把它挂上去了这个共享内存占不占用本机进程的内存空间(两个进程都映射同一块 3 GB 的共享内存,这 3 GB 会分别计入它们各自的内存使用吗?)(腾讯)
在物理内存只占用3g,一份真实的数据页,不翻倍。在虚拟地址空间,每个进程各自保留一段3g的映射区域。用top和ps命令看RSS(常驻集合大小)每个进程占用3g,而PSS(比例集合大小)每个进程占用1.5g,均摊。
进程通信哪种方式最快?(字节)page 153
共享内存。共享内存零拷贝。而管道要在用户空间和内核之间复制数据。消息队列的数据在内核队列中暂存,存在两次拷贝(发送方->内核->接收方)。套接字需经过协议栈,开销大,unix域套接字在本地通信性能接近管道。
进程通信的共享内存如何创建,如何绑定到程序的进程内?(腾讯多次考)page 153
共享内存创建:用ftok函数生成唯一标识共享内存的键值key。然后用shmget创建共享内存标识符。
绑定共享内存到进程:通过shmat(share memory attach)将共享内存连接到进程虚拟地址空间。
注:共享内存本身不提供同步。
两个线程之间怎么通信?线程通信(线程间通信方式)(58同城,腾讯多次考,拼多多)page 1
共享内存通信。1.内置锁。synchronized可以将方法代码块设置为临界区,wait notify notifyAll进入等待状态和唤醒等待线程。2.显式锁,Reentrantlock提供互斥,Condition实现锁上的等待通知机制。
消息传递通信。1.BlockingQueue,生产者线程将数据放入队列,消费者线程从队列取出数据。2.CompetableFuture,处理任务依赖关系以及回调。
并发工具类JUC。CountDownlatch,协调多个线程同步。
Go语言中用Channel进行线程通信。
参考:https://blog.csdn.net/J080624/article/details/87454764
Docker在宿主机眼里是什么?(宿主机如何看待 Docker?)(在主机系统里,Docker 是什么?)(Docker 对宿主操作系统来说是什么?)(字节)page 1
容器就是若干隔离的进程,镜像层映射为宿主文件系统的目录结构,网络与卷则分别映射为虚拟接口和宿主存储。
讲讲写时复制(腾讯)page 1
写时复制是延迟复制策略,读多写少场景下,所有读操作共享同一份数据,只有发生写时才将共享数据拷贝。
不可变对象。java中string在执行替换或装箱时用了写时复制,保证内部数组不被原地修改。
使用场景,在我的简单RPC框架的路由表管理用到,客户端每次调用RPC都读取路由表做负载均衡,服务上下线导致写更新,仅发生在少数情况。采用concurrenthashmap,key为string接口名,value是CopyOnWriteArraySet,Set存储Router对象。对于读操作get直接返回底层无锁只读的Set引用,写操作add和remove在内部先复制当前数组,再执行增删,最后替换引用。Router对象用final定义IP,端口,接口名,重写equals、hashcode,保证在set中去重。
讲讲零拷贝(了解零拷贝吗)(腾讯)page 1
零拷贝用专门系统调用,linux的sendfile splice vmsplice,一次性在内核态完成从磁盘page cache到网络套接字缓冲区的拷贝,如果网络设备支持dma,还能让网卡直接从page cache读数据,没有拷贝。
优势。减少系统调用次数,合并读磁盘和写网络为一次系统调用,用户态内核态切换从4次降低到2次(磁盘到内核,内核到用户,用户到内核,内核到网卡)。
讲讲页表的数据结构?(腾讯)page 1
页表是将虚拟地址到物理地址映射的数据结构,每个进程有独立页表,存储虚拟页号到物理页号的映射。为节省空间,实际系统用多级页表,将虚拟页号高位分成多级索引,形成多叉树结构,只有访问到的分支才分配页表节点,将稀疏的页表压缩到KB级别。但这带来了额外访存开销,现代cpu用TLB缓存常用映射解决这个矛盾。
什么是哈希表(腾讯音乐)page 1
哈希表是用哈希函数将key映射到数组下标,以O 1平均时间完成查找插入删除的数据结构,底层是定长数组,为解决哈希冲突用拉链法和开放地址法,当链表过长或负载因子过高时触发扩容。一次性扩容有大量数据迁移开销,如Redis用两张哈希表,结合每次只迁移一个桶的渐进式rehash,减少开销。
说一下拉链法和线性探测法各自的优缺点?(腾讯音乐)page 1
拉链法删除简单,而线性探测法删除还要做标记,因为会影响前面节点的探测。
说一下插入排序,快速排序,堆排序各自的最优、平均、最坏时间复杂度(腾讯音乐)page 1
插入排序最优O n,平均O n平方,最坏O n平方。快速排序最优O n log n,平均O n log n,最坏O n平方。堆排序最优O n log n,平均O n log n,最坏O n log n。
快排空间复杂度?(百度)page 1
取决于递归深度,最好O log n,平均O log n,最差O n平方。
快排什么时候会达到最坏时间复杂度?(腾讯音乐)page 1
原数组已经有序或已经逆序,每次选出的pivot恰好是子数组的最大值或最小值。
在后台开发中,什么时候使用多进程?什么时候使用多线程?(腾讯)page 1
如NGINX等强调稳定性的服务用多进程,因为每个进程内存空间独立,而线程任意一个线程出错,进程中所有线程会跟着一起崩溃。
Linux为什么采用页式内存管理 page 1
硬件支持。现代处理器都在mmu层面直接提供了分页机制。页目录、页表结构已经由硬件定义好。TLB缓存页表项,加速虚拟地址到物理地址的转换。
内存利用率。分段会产生内存碎片,且linux可以在进程访问页面时才触发缺页异常,从磁盘加载页。
性能优化。TLB使得绝大多数地址转换命中缓存,linux用大页面来减少TLB不命中的开销。
操作系统时间片的话它是分配在什么维度的?比如一个时间片切换的时候,它是有进程的切换还是线程的切换?(拼多多)
时间片的分配是在线程的切换,内核调度维护的就是可运行的线程。
堆和栈的操作系统底层实现(拼多多)
栈在内核中用栈底地址和栈大小两个数据实现,栈是从高地址空间向地地址空间方向增长。
堆在内核中用虚拟页实现,内核用一个结构体记录堆包含的虚拟页。堆从低向高方向增长。
linux访问url,修改文件名的命令(小红书)
wget -O 新文件名 URL
如何防止跨站攻击(如何防止CSRF攻击)(腾讯音乐)page 1
跨域攻击是用户点击第三方页面,携带cookie提交表单,发起修改请求。
方案一,CSRF token。在请求中要求携带服务器随机生成、和会话绑定且不好预测的token,服务器验证后才处理请求。
方案二,用cookie same site属性,把该值设为strict,禁止第三方上下文发送cookie。
方案三,使用请求头中的origin和referer字段,拒绝非本站来源的写操作请求。
系统调用和库函数的区别(腾讯音乐)
系统调用会进入内核态,库函数只在用户态。
哈希算法和加密算法的区别(腾讯)
哈希算法输入任意长度数据,输出固定长度哈希值。本质是单向函数,无法从哈希值反推原始数据。
加密算法将明文用算法和密钥转换为密文,本质是双向可逆,用密钥加密,用密钥解密,有对称和非对称,能回复原始明文。
为什么jwt比cookie更加安全?(美团)
linux一个文件里怎么快速查看到指定字段,比如java字段?(小米)
grep “java” file.txt
为什么不能把server发送的ACK和FIN合并起来,变成三次挥手?
服务器不一定完成了数据发送。
如果第二次挥手server的ACK没有送达Client,会怎样?
client重发FIN。
为什么第四次挥手client要等待2*MSL后才进入closed?
防止server没有收到ACK而重发FIN,确保TCP连接可靠终止。
OSI七层模型是什么?
物理层,数据链路层,网络层,传输层,会话层,表示层, 应用层。
TCP/IP四层模型是什么?
应用层,传输层,网络层,网络接口层。
总结一下每层的协议
应用层,HTTP,DHCP,DNS,FTP;传输层,TCP,UDP;网络层,IP,ARP,ICMP,NAT,RIP,OSPF,BGP。
什么时候选择TCP,UDP?
UDP用于及时通信,TCP用于文件传输,发送邮件,远程登录。
使用TCP,UDP的协议
TCP,HTTP,HTTPS,FTP,SMTP,SSH。
UDP,DHCP,DNS。
HTTP1.0和1.1区别?page 18
1.1支持长连接。支持一个tcp连接发送接收多个http请求。
URI和URL区别?
URI唯一标识一个资源,URL可以用来标识一个资源,而且还指明了如何locate这个资源。
什么是操作系统?
操作系统是管理计算机硬件和软件资源的程序。
什么是系统调用?
系统调用是OS提供给用户程序的接口,允许用户程序在用户态中请求OS系统态的功能。
进程间同步的方式有哪些?
互斥量,信号量。
进程的调度算法有哪些?
先到先服务算法,短作业优先,时间片轮转算法,多级反馈队列算法,优先级调度。
操作系统的内存管理主要是做什么?
内存的分配与回收,地址转换。
操作系统的内存管理机制了解吗?内存管理有几种方式?
页式管理,段式管理,段页式管理。
快表和多级页表,介绍一下
快表式加速虚拟地址到物理地址转换的高速缓存,多级页表是一种内存管理技术。
分页机制和分段机制的共同点和区别?
共同点,都为了提高内存利用率,区别,页大小是固定的, 段的大小不固定。
逻辑地址和物理地址
逻辑地址是操作系统决定,物理地址是内存单元真正的地址。
CPU寻址了解吗?为什么要虚拟地址空间
每个程序都有自己的独立地址空间。
什么是虚拟内存?
允许操作系统用硬盘空间模拟额外RAM。
局部性原理
如果程序中某条指令执行,不久后该指令可能再次执行。一旦程序访问了某个存储单元,不久后附近的存储单元也将被访问。
虚拟内存的实现有哪些?
请求分页管理,请求分段管理,请求段页管理。
页面置换算法有哪些?
FIFO,LRU,最佳页面置换算法。
常用框架 spring & Springboot & docker & k8s 这部分记的不是很熟,要常看
Bean的生命周期了解吗?(bean的生命周期?)(Bean是怎么初始化的)(spring的bean是怎么发现并加载的)(小红书,得物,京东,腾讯)page 161
Spring生命周期了解吗?page 161
实例化。构造函数创建bean对象。
依赖注入。用setter注入属性或依赖。
aware回调。如果bean实现了beanNameAware等接口,spring回调方法。
beanPostProcessor。在初始化前,调用postProcessBeforeInitialization,先对实例做一些加工。
初始化。如实现了InitializingBean,调用afterPropertiesSet。再调用init方法,做自定义的初始化。
BeanPostProcessor。调用postProcessAfterInitialization,做最后的包装。
销毁。单例bean在容器关闭时触发,prototype bean的销毁不由容器管理,交给gc管理。
Bean 作用域及创建/销毁时机。1.Singleton作用域。容器启动时初始化,容器关闭时销毁。2.prototype作用域。每次getbean或注入时初始化,gc进行销毁。3.Request作用域。第一次在该请求内访问bean时初始化,请求结束销毁。4.session作用域,第一次在该回话内访问bean时初始化,会话失效后销毁。5.global session,第一次在该回话内访问bean时初始化,全局会话失效时销毁。
Spring bean的作用域有哪些?page 1
Bean 作用域及创建/销毁时机。1.Singleton作用域。容器启动时初始化,容器关闭时销毁。2.prototype作用域。每次getbean或注入时初始化,gc进行销毁。3.Request作用域。第一次在该请求内访问bean时初始化,请求结束销毁。4.session作用域,第一次在该回话内访问bean时初始化,会话失效后销毁。5.global session,第一次在该回话内访问bean时初始化,全局会话失效时销毁。
怎么搭建spring Boot starter(百度)
新建maven模块,定义group id和artifact id。
在pom中声明SpringBoot自动配置依赖,并指明父项目是SpringBoot starter。
可以创建自动配置类,注入bean,本地测试后,用maven clean install命令将jar包发布至私服。
什么情况下Bean对象会注入失败(腾讯)
多个候选bean歧义。
循环依赖,构造器注入下A依赖B,B依赖A无法 解决。
假设在 Bean 的初始化过程中,你想在对象 new 出来之前去做一个初始化的操作,你建议用什么方法(美团)page 1
BeanPostProcessor是在new之后,如要在new之前初始化,用InstantiationAwareBeanPostProcessor,postProcessBeforeInstantiation方法。
谈谈自己对AOP的理解。(滴滴,快手考过)(Spring AOP 的原理,以及它的代理是在哪个阶段实现的)page 164
AOP发生在bean的生命周期的哪个阶段?(百度)page 154
AOP(面向切面编程)是一种编程范式,用于将横切关注点(如日志、事务管理)从业务逻辑中分离出来,以减少代码重复和降低模块间耦合,Spring AOP 利用动态代理机制实现这一功能。动态代理是基于反射实现的,反射原理是通过动态获取类的信息并操作类对象。
AOP发生在postProcessAfterInit方法,这个方法会生成代理对象。
参考:https://juejin.cn/post/7155884227714613285#heading-9
讲讲Spring AOP原理(淘天)page 1
容器启动阶段,注册后置处理器。容器扫描到aspect注解的类,解析pointcut、before、after,为每个切面生成advisor。创建业务bean后,postprocessAfterInitialization方法检查是否匹配pointcut,如匹配则为其生成代理,a o p发生在这个方法中。
方法调用阶段,客户端执行代理对象的invoke方法,传入method,通知链执行,执行Advice的invoke方法,调用目标方法,返回。
AOP的底层实现?(58同城,满帮,网易)page 1
AOP底层依赖于动态代理。1.jdk动态代理,用Proxy类newProxyInstance生成代理对象,代理对象在方法调用时进入InvocationHandler invoke方法执行增强逻辑和目标方法。缺点必须目标对象实现接口,且只能对接口方法代理。2.cglib动态代理,操作字节码,运行时生成目标类子类。代理子类重写目标类方法,调用增强逻辑,再通过MethodProxy invokeSuper调用父类方法。缺点由于采用继承方式,目标方法如被声明为final则无法被代理。
SpringBoot源码,启动过程讲一下(SpringBoot的启动流程)(字节,阿里巴巴,百度)page 1
入口方法。main方法执行springApplication.run。
springApplication初始化,用springFactoriesLoader扫描meta-inf/spring.factories加载springApplicationRun Listener、ApplicationContextInitializer。
事件发布。SpringBoot发布ApplicationStartingEvent,可以用SpringApplicationRunListener#starting方法拦截这个事件并执行初始化逻辑。
环境准备,创建ApplicationContext前,springApplication准备environment,根据web环境探测算法选择ApplicationContext类型。
bean定义加载。enableAutoConfiguration注解用AutoConfigurationImportSelector自动导入符合条件的自动配置类。
ApplicationContext刷新。ConfigurableApplicationContext.refresh方法执行BeanFactoryPostProcessor、BeanPostProcessor回调,实例化所有单例bean,完成依赖注入。刷新完成后,SpringBoot发布ApplicationPreparedEvent,ApplicationStartedEvent,上下文和自动配置已生效。
web容器启动。
完全启动。容器完全刷新且执行完bean初始化后,SpringBoot发布ApplicationReadyEvent。
结合spring讲讲aop一系列过程(美团)
准备阶段。启动Spring容器,注册特殊的bean Annotation aware aspectj auto proxy creator,然后扫描并注册切面bean。
bean实例化和创建代理。bean实例化的过程:属性注入,bean post processor前置处理,bean初始化回调,后置处理,容器把初始化后的bean引用替换为代理对象,后继容器或其他bean获取到的都是代理。
方法调用过程。首先拦截链,Spring把所有匹配该方法的advice包装并按优先级组成链式调用。然后环绕通知,执行拦截器或者真正的方法。然后如果在目标方法或环绕中抛出异常调用异常通知。然后在目标方法正常返回后调用后置通知。最后执行最终通知。
AOP失效的原因(美团)page 1
自身调用。类的内部方法调用另一个内部方法,两个方法都在切点范围内,但因他们是在一个实例内部直接调用,绕过了代理。
方法不是public。Spring AOP默认只拦截public方法,因为jdk代理基于接口实现,非public方法可以用CGLIB代理。
类或方法被final修饰。jdk动态代理和CGLIB都不能代理final方法。
aop执行顺序问题。多切面之间冲突,执行顺序导致切面逻辑没有触发,比如自定义切面在捕获异常后拦截并屏蔽了异常,后续依赖异常来触发的切面比如事务切面根本感知不到异常,也就不会执行事务的回滚。
在Spring AOP中,关注点和横切关注的区别是什么 page 1
关注点是具体业务逻辑,横切关注点是我们要解耦的和业务无关的模块,如日志记录、事务管理。
同一个类中的方法互相调用时,调用的是原始方法而非代理方法 如何解决?(美团)
把另一个方法放到新的bean中,或者用AOPContext.currentProxy()方法。
反射是如何实现的(淘天)page 1
方法的反射调用即Method invoke,的实现如下。方法的调用是由MethodAccessor接口的invoke方法实现的。MethodAccessor接口有三种实现,本地实现,委派实现和动态实现,本地实现是用Method实例所指向的地址,准备好传入的参数,调用进入目标方法。动态实现是直接生成字节码直接运行。委派层是一个中间层,默认前15次调用是本地实现,第16次会触发动态实现。
aop和ioc的区别是什么?(快手)
参考。 目的。aop是编程范式,目的是将横切关注点和业务逻辑分离。
ioc是设计原则,目的是减少对象之间的耦合。
实现。aop通过动态代理实现,jdk代理或cglib代理。
ioc通过依赖注入DI实现。通过构造函数注入等方式将依赖关系注入对象中。
谈谈自己对Spring Ioc的了解。(讲讲Spring IOC)(滴滴考过,腾讯)page 1
介绍Spring bean的创建方式(京东)
IoC(控制反转)是一种将对象创建和管理的控制权从程序代码转移给外部容器的设计思想,它通过依赖注入来减少代码间的耦合度,提高模块的独立性和可扩展性。 Spring容器是IOC核心,负责创建配置管理对象,容器通过读取配置文件或注解了解哪些对象要被创建和管理。
依赖注入 DI是IOC的实现方式,通过DI,对象依赖关系在运行时由容器动态注入。常见的DI/创建方式 有构造器注入,setter注入,我用构造器比较多,因为可以确保对象创建时拥有必要的依赖。
参考:https://time.geekbang.org/column/article/638222
IOC的优点,为什么不直接new,IOC的具体应用场景有哪些?(美团)
IOC优点:1.降低耦合度,各个组件独立。2.可测试,通过依赖注入,测试时替换实际依赖为mock对象或stub。3.灵活性,用配置文件或注解管理对象依赖关系。
为什么:在代码中硬编码,如果依赖发生变化,要大量修改代码。
具体应用:1.spring框架。IoC容器负责创建、初始化和管理Bean。2.单元测试。用IoC可以将业务组件的依赖替换为Mock实现,从而对单个模块测试。
三级缓存怎么解决循环依赖?(网易,58同城,字节)page 1
循环依赖是指两个或多个类之间相互依赖,形成闭环。
三级缓存是spring框架解决循环依赖的机制。一级缓存singletonobjects存放完全初始化好的单例bean。二级缓存earlysingletonobjects存放早期暴露的bean,也就是还未完全初始化的bean。三级缓存singletonFactories存放bean工厂。
解决循环依赖的过程。创建beanA,尝试从 一级缓存 中获取beanA,没有因此创建,将beanA放入 三级缓存 ,并暴露引用。
注入依赖,创建beanA过程中,需要注入beanB,这时尝试从一级缓存获取beanB,没有因此创建。
创建beanB。创建beanB过程中,需要注入beanA,从 三级缓存 中获取beanA早期引用,并放入 二级缓存 。
完成beanB初始化,beanA继续初始化。在SpringBoot 2.6版本解决了循环依赖问题。
为什么解决循环依赖要有第三级缓存?直接两个不行吗?(快手,网易)page 1
如果只是解决循环依赖问题可以只用两个,第三个是为了延迟代理的创建,不打破bean的生命周期。
如果构造函数内存在循环依赖还能解决循环依赖吗?(两个构造函数内的循环依赖能被解决吗?)(构造函数内的循环依赖能被解决吗?)(shopee)page 156
Bean创建的三步:实例化new,属性注入set,初始化。
构造器注入,比如A(B b),那表明new A的时候,就需要得到B。因此如果A B全是构造器注入,那Spring就不能处理循环依赖。
而一个set注入,一个构造器注入,看情况。
A set注入B,B构造器注入A,成功,因为B注入A时A已经实例化。
A构造器注入B,Bset注入A,失败,因为A构造器未完成(A还没实例化)。Spring是按照字母序创建Bean的,A永远在B前面。
spring和springboot启动方式的区别(小红书)
spring需要配置大量xml或java配置,springboot采用约定优于配置原则,提供自动配置功能。
启动。spring应用要通过applicationContext或WebApplicationContext手动启动,配置servlet容器并加载spring配置文件。 springboot提供了内置servlet容器,研发只要运行一个主类包含springbootApplication注解,就可以启动。
spring boot印象最深刻,最好用的几个地方(美团)
首先是自动配置,只需要引入依赖,SpringBoot能根据类路径jar包自动完成配置。比如加了springboot starter web依赖,会自动配置嵌入式tomcat服务器,还有默认的mvc配置,可以直接写controller,连web.xml都不需要手动配。
然后是springboot的启动器依赖,他们把常用的功能打包到一起,减少自己找依赖的麻烦。
最后是springboot外部化配置,通过application properties设置不同参数,切换开发测试生产环境,这对持续集成和部署很重要。
Springboot核心原理是什么?Springboot容器的key是什么?Springboot容器的value是什么?Springboot容器可以用泛型吗?(腾讯)page 156
核心原理:自动配置:扫描classpath的类和配置文件,自动配置Spring应用的上下文。
起步依赖:预先配置了常用的库和框架。
嵌入式web服务器。
容器的key是名称或ID,value是被管理的Bean实例。
Spring Boot 的核心容器是基于 Spring 框架的,而 Spring 框架本身是支持泛型的。因此,在 Spring Boot 中,无论是 key 还是 value,都可以使用泛型。
可以使用泛型来定义 bean 的类型,在依赖注入时,Spring 会根据泛型类型自动匹配并注入相应的 bean。但由于Java类型擦除,复杂场景可能要Qualifier注解保证注入正确。
参考:https://blog.csdn.net/qq_34598667/article/details/83245753
依赖注入的方式有几种,各是什么
构造器注入,Setter注入,字段注入。
构造器注入,用构造函数传入依赖。Setter注入,用Setter方法。字段注入,在字段上用Autowired注解。
Springboot全局异常处理?(腾讯)page 155
项目中如何处理异常(拼多多)
通过统一的机制处理所有未捕获的异常,避免异常信息直接暴露给用户。
统一异常处理:避免在Controller重复编写异常处理代码。
1.ControllerAdvice注解标记一个类为全局异常处理器。ExceptionHandler注解指定处理特定异常的方法。
2.继承ResponseEntityExceptionHandler重写异常处理方法。
spring怎么知道所有bean创建完的?(快手)page 155
依赖于ApplicationContext的刷新过程,Spring执行refresh方法的步骤如下:
1.加载bean定义:通过注解和配置文件,把bean注册到beanFactory中。
2.实例化非懒加载的单例bean:调用finishBeanFactoryInitialization方法,该方法调用beanFactory的preInstantiateSingletons。该方法遍历所有非懒加载单例bean,创建这些实例。
3.执行SmartInitializingSingleton接口回调。如果某个bean实现了这个接口,它的afterSingletonInstantiated方法会在bean创建完成后调用。
4.发布ContextRefreshEvent事件。调用finishRefresh方法,发布ContextRefreshEvent事件,通过这个事件,应用程序可以知道Spring容器已经初始化完毕。
beanFactory和factoryBean的区别(阿里)page 155
BeanFactory 是 Spring IoC 容器的最底层抽象接口,负责管理、实例化和维护所有 bean 的生命周期以及处理依赖注入等核心功能。
FactoryBean 是一个特殊的接口,允许你在 Spring 容器中以工厂的方式创建复杂的 bean 对象。通过实现 FactoryBean 接口,你可以自定义对象的创建过程,从而封装实例化逻辑。
Spring框架用了哪些设计模式?(Spring框架中都用到了哪些设计模式)(科大考过,58同城)page 155
适配器模式,将一个类接口转换为客户端期望的另一种接口。Spring DispatcherServlet的HandlerAdaper。不同类型的Controller如注解形式、实现Controller接口和Servlet,都通过各自的HandlerAdapter如AnnotationMethodHandlerAdapter适配成统一的handle调用。
策略模式,定义一些算法,把他们封装,使他们可以互换。Spring AOP的两种实现jdk动态代理和CGLIB动态代理。AOPProxy是策略接口,jdkDynamicAOPProxy、cglibAOPProxy实现接口;defaultAOPProxyFactory根据配置动态选用策略。
组合模式,将对象组合成树形结构以表示整体部分层次。Spring缓存管理Cache Manager体系。Composite Cache Manager持有多个子Cache Manager,对外暴露统一的get Cache、get Cache names接口。
装饰器模式,在不改变接口前提下,动态给对象添加额外职责。Spring的transaction Aware Cache Decorator,他为底层Cache添加事务同步支持,在往Cache写数据或删除时,先判断当前线程是否有激活事务,如果有激活的事务,向Spring注册同步回调,这时才会往Cache写或删除数据。
工厂模式,定义一个创建对象的接口,让子类决定实例化哪一个类,使创建延迟到子类。Spring IOC本身bean Factory、Application Context是工厂,Factory bean接口允许用户自定义工厂逻辑,如用静态工厂创建复杂对象。
springboot的jar包与普通Java的jar包有什么区别(百度)page 1
普通jar根据manifest mf中main class指定的入口类,直接加载并运行。
而springboot jar,manifest mf指定的入口是SpringBoot的launcher,先解析内嵌的boot-inf lib,将依赖动态加入到类加载器,再加载带有SpringBootApplication的主类启动Spring容器。
介绍一下SpringBootApplication注解。(spring boot的启动类注解有什么功能)(浪潮,滴滴考过,哈啰,携程)page 1
它封装了 @SpringBootConfiguration、@EnableAutoConfiguration 和 @ComponentScan 三个关键注解,简化了 Spring 应用的初始配置。
@SpringBootConfiguration是Configuration注解的拓展,标识当前类是Spring配置类, 标识一个类可以使用 Spring IoC 容器作为 bean 定义的源。
@EnableAutoConfiguration 告诉 Spring Boot 根据添加的 jar 依赖猜测需要的配置,自动配置你的 Spring 应用,自动装配。
最后,@ComponentScan 使 Spring 自动扫描你的项目中的所有组件(如 @Service, @Controller 等),默认情况下扫描与配置类相同的包和子包。
Gradle与Maven各有什么优劣?(小红书)
构建方式。maven基于xml配置清晰但繁琐,适合中小型项目。gradle使用groovy配置灵活,适合大型项目。
性能。maven依赖解析和构建速度较慢。gradle采用增量构建和缓存机制,性能更好。
拓展性。maven插件丰富,但定制化难度较高。gradle高度可定制,支持自定义任务。
maven的依赖优先级原则?(腾讯)
直接依赖优先于传递依赖。
路径最短优先:如果存在多个传递依赖路径,maven会选择路径最短的。
先声明优先:路径长度相同,maven优先在pom先声明的依赖。
构建工具是如何如何处理重复依赖的?(小红书)
冲突解决。如果两个或多个依赖版本不一致,构建工具会尝试解决冲突,通常根据策略,比如最近原则。
依赖合并。对于相同的依赖项,构建工具会尝试合并他们,减少重复库文件。
依赖排除。构建工具允许开发者显式排除某些依赖。
假如有3个方法,ABC组成嵌套事务,那么C是怎么知道AB开启了事务的?(在 A、B、C 三个嵌套的方法中,方法 C 怎么知道前面的方法(A 和 B)已经开启了事务?)(得物)page 1
在嵌套事务中, 事务上下文 会被传递。方法a开启事务后,事务上下文会被传递给方法b和c。在spring中,事务上下文通过threadlocal传递。
Spring事务什么时候失效?事务失效?(百度,得物多次考,哈啰,网易)page 154
非public方法:Spring事务注解默认作用域public方法。
内部调用:一个类内部,一个方法调用了另一个带有Transactional注解的方法,事务会失效,因为事务通过代理模式实现,内部调用不会经过代理对象。解决:将需要事务控制的方法拆分到外部bean中。
未抛出异常:Spring事务默认遇到runtimeException或error才会回滚,如果方法捕获了异常但没有抛出,事务不会回滚。解决:对于检查型异常,明确在transactional注解指定rollbackFor。
事务传播:如果设置了不合适的事务传播行为,如NOT_SUPPORTED会导致事务失效。
Spring事务的原理是什么?(得物,58同城)page 154
基于a o p代理模式实现的。
a o p代理与事务切面:Spring在启用事务管理后,会为目标对象创建代理。
事务属性解析:当一个被事务切面拦截的方法被调用时,代理通过TrasactionAttributeSource解析该方法上的事务属性,如传播行为、隔离级别。
事务拦截器:解析完事务后,TrasactionInterceptor会根据这些属性调用具体的事务管理器(实现了PlatformTrasactionManager接口,如DataSourceTrasactionManager),在调用方法前开启事务。
参考:https://juejin.cn/post/6844903608224333838#heading-0
mybatis为什么只需要编写接口和mapper.xml(转转)
接口加动态代理。当我们在代码中声明DAO接口,Mapper Proxy动态代理会在运行时为这个接口生成一个代理对象。当在Spring中注入这个接口时,实际拿到的是动态代理实例。调用方法时,代理转交给MyBatis核心去执行SQL。
MyBatis的核心组件:Configuration、SqlSessionFactory、SqlSession、Executor。Configuration加载Mapper xml,SqlSessionFactory是根据Configuration创建的,SqlSession代表一次数据库会话,Executor会执行SQL,处理jdbc连接、预编译、结果集映射,将结果集映射成接口方法的返回类型。
Spring,Spring MVC,SpringBoot,Spring Cloud什么关系?(快手,美团)
spring是轻量级Java开发框架,用于构建企业级应用。提供了依赖注入和面向切面编程等功能,帮助开发者更高效管理对象和业务逻辑。
Spring MVC 是Spring的一个模块,专注于构建MVC架构的Web应用。
SpringBoot是Spring的一个子项目,简化了spring应用的初始搭建和开发过程。通过自动配置和约定大于配置的原则,springboot让开发者可以快速启动一个spring应用。
springcloud是基于springboot的微服务框架,他提供了一系列工具和库,帮助开发者构建和管理分布式系统。springcloud包括服务发现、配置管理、断路器、智能路由、微代理、控制总线等功能。
Component和Bean的区别是什么?(百度)
都用于将对象注册到Spring容器中,但@Component用于 自动扫描 类并注册为bean,而@Bean则用在显式定义在 配置类 中的 方法 ,用于生成具体的bean实例,可以用于无法修改源码的类如第三方库。
参考:https://blog.csdn.net/w605283073/article/details/89221522
SpringBoot的自动配置如何实现的?什么是SpringBoot自动装配?(Spring Boot中自动装配机制的原理)(腾讯,恒生)page 116
在Spring Boot应用里面,只需要在启动类加上SpringBootApplication注解就可以实现自动装配。 SpringBootApplication是一个复合注解,真正实现自动装配的注解是EnableAutoConfiguration。 自动装配的实现主要依靠三个核心关键技术。
- 引入Starter启动依赖组件的时候,这个组件里面必须要包含Configuration配置类,在这个配置类里面通过Bean注解声明需要装配到IOC容器的Bean对象。
- 这个配置类是放在第三方的jar包里面,然后通过SpringBoot中的约定优于配置思想,把这个配置类的全路径放在 classpath:/META-INF/spring.factories 文件中。这样SpringBoot就可以知道第三方jar包里面的配置类的位置,这个步骤主要是用到了Spring里面的SpringFactoriesLoader来完成的。
- SpringBoot拿到所第三方jar包里面声明的配置类以后,再通过Spring提供的 ImportSelector 接口,实现对这些配置类的动态加载。
在我看来,SpringBoot是约定优于配置这一理念下的产物,所以在很多的地方,都会看到这类的思想。它的出现,让开发人员更加聚焦在了业务代码的编写上,而不需要去关心和业务无关的配置。 其实,自动装配的思想,在SpringFramework3.x版本里面的Enable注解,就有了实现的雏形。Enable注解是模块驱动的意思,我们只需要增加某个Enable注解,就自动打开某个功能,而不需要针对这个功能去做Bean的配置,Enable底层也是帮我们去自动完成这个模块相关Bean的注入。 以上,就是我对Spring Boot自动装配机制的理解。
参考:https://juejin.cn/post/7046554366068654094,https://juejin.cn/post/7162568709955911717
nginx和springcloud gateway的区别(腾讯)page 64
部署层次。1.NGINX用于互联网请求进入系统的最外层。他用于处理DNS解析后,将请求分发到虚拟IP后的网关集群,并用lvs和NGINX多级转发保证网络稳定性。2.springcloud gateway自身作为一个微服务存在,并集成注册中心,其作用是作为外层NGINX和后端微服务之间的桥梁,在后端微服务扩容或变化时,gateway可以自动从注册中心获取最新服务节点信息。
动态路由。NGINX路由配置用配置文件手动调整,而gateway内置动态路由,能感知后端微服务扩缩容。
docker怎么处理资源隔离?(腾讯)
docker通过命名空间和控制组实现。
命名空间: 进程隔离:docker用pid命名空间隔离进程。每个容器有自己的进程树。
网络隔离:net命名空间隔离了网络接口、IP地址、端口,每个容器有自己的网络栈。
文件系统隔离:mnt命名空间隔离了文件系统挂载点。
控制组:
资源限制:cgroups允许通过对容器的资源使用进行限制,如CPU内存磁盘IO。
容器化和虚拟化的区别(腾讯)page 52
架构层次。虚拟化基于hypervisor,每个虚拟机包含完整的操作系统。容器化基于容器运行时,每个容器只包含应用和运行时环境,依赖宿主操作系统内核。
Kafka的消费者组重平衡问题介绍下?(介绍一下Rebalance机制)(字节)page 106
重平衡是让所有消费者就订阅主题分区分配达成共识的过程,由协调者负责,协调者的选择基于kafka内部位移主题_consumer_offsets的分区,算法为:1.根据group.id的哈希值与分区数计算分区。2.找到分区的leader所在的broker,这个broker是该消费组的协调者。
解决。1.心跳机制优化。调整session timeout ms和heartbeat interval ms可以设置为6000ms和2000ms。这样可以在消费者真正出现问题前发送3轮心跳。2.消费逻辑优化。max poll interval ms参数默认5分钟,如果消费处理逻辑较重,可将该参数调大。3.gc优化。频繁full gc会导致消费者响应变慢,触发心跳超时,引发不必要的重平衡。
kafka Offset自动提交是怎么实现的?(kafka位移自动提交是怎么实现的?)
enable auto commit参数。每次调用poll拉取之前,客户端会把上次poll返回的所有分区的最新位移提交到broker。然后再重新拉取并处理一批消息。
Canal面临MySQL跨表、多表事务时出现什么行为(Canal 在解析 MySQL 的 binlog 时,遇到一个事务同时修改了多张表。此时,Canal 会如何按表和事务边界输出这些变更?)(淘天)page 1
canal不会原子的把多表事务打包成一个消息,也不保证跨topic跨partition的顺序。
要想在下游实现跨表事务原子同步,1.保证有因果关系的数据如同一业务主键更新序列不乱序,其他的无关联的并行即可。2.在canal和mq的桥接处,用同一个分区键如全局事务id,或binlog中的Commit position做哈希,将同一事务的所有消息强制路由到一个partition。3.消费端在按序消费缓存直到看到canal transaction end,再一次性落库。
讲讲Spring自带的实现非空校验的工具,底层原理(Spring 自带的非空校验工具有哪些?)(字节)page 1
编程式断言Assert.notNull。手动调用断言方法,他用静态方法完成校验,不依赖反射。传入对象为空抛异常。
声明式校验。@NotNull,NotEmpty,NotBlank注解,用反射实现,传入对象为空抛异常。
Spring cache 底层原理,基于什么实现的?(美团)
Spring cache基于注解,cacheable,cacheput配合AOP自动实现缓存。在配置类上添加enablecaching注解,Spring就会用Import Selector引入缓存配置类并启动AOP代理,具体流程如下:1.注册代理处理器,实例化AbstractAdvisorAutoProxyCreator,他在bean创建时根据切面生成代理。2.解析缓存注解,调用SpringCacheAnnotationParser解析注解,处理Cacheable,CacheEvit,Cacheput注解,生成CacheableOperation,CacheEvictOperation等对象。3.缓存切面织入,把CacheOperation织入切面。4.CacheInterceptor拦截执行,调用CacheAspectSupport execute方法执行缓存逻辑。
Spring Cache本身只是一个抽象,不直接实现缓存存储逻辑,提供了CacheManager和Cache接口。
cookie和session区别?他们的用法是什么?(美团)
存储位置。cookie存储在客户端浏览器的,session存储在服务端的。
安全性。cookie因为存储在客户端,相对更容易窃取或篡改,session存储在服务端,相对安全,因为数据不会直接暴漏给客户端。
生命周期。cookie可以设置过期时间,即使浏览器关闭,下次打开仍然有效。session通常在用户关闭浏览器或服务端主动销毁时失效,生命周期较短。
用途。cookie用于存储用户的偏好设置,购物车内容等,适合存储少量,不敏感数据。session用于存储用户登录状态,权限信息等,适合存储敏感,需要安全保护的数据。
实现机制,cookie通过http头信息发送给客户端,客户端每次请求都会带上相应cookie。seesion通常是通过在cookie中存储一个sessionid来实现,服务器根据id查找对应的session数据。
ETCD满足CAP哪两个?(拼多多)
CP,一致性和分区容错,放弃可用。
AP(可用性,分区容错性)的中间件有哪些?CP(一致性,分区容错性)的中间件有哪些?(美团)
AP(可用性,分区容错性):kafka,rocketMQ,Cassandra(nosql数据库),redis cluster,mongodb。
CP(一致性,分区容错性):Zookeeper,ETCD,consul,HBASE。
AutoWired和Resource的区别?(滴滴)
@Autowired 是Spring提供的注解,主要通过类型来自动注入依赖,而在存在多个实现时可配合 @Qualifier 明确指定注入的bean;@Resource 是JSR规范的注解,符合Java EE标准,它默认按名称注入,也可以指定类型来实现注入。
参考:https://www.zhihu.com/question/39356740,https://blog.csdn.net/Weixiaohuai/article/details/120853683
什么是Spring框架?
是一个开源的Java开发框架,它通过提供控制反转(IoC)和面向切面编程(AOP)等核心功能,帮助开发人员提高开发效率。
注入Bean的方式有哪些?你用的是什么?
构造函数注入,setter注入,以及注解注入,spring推荐使用构造函数注入。
Spring管理事务的方式有几种?
编程式事务,通过使用 TransactionTemplate 或 TransactionManager 在代码中手动管理事务;以及声明式事务,更常用且推荐的方法,通常通过在 XML 配置文件中配置或使用基于注解的方式(如 @Transactional),利用 AOP 实现。
Spring事务中有哪些事务传播行为?
PROPAGATION_REQUIRED(加入当前事务或 创建新事务 )、PROPAGATION_REQUIRES_NEW(总是创建新事务并挂起当前事务)、PROPAGATION_NESTED(嵌套事务或等同于 REQUIRED)、PROPAGATION_MANDATORY(必须运行在事务中)、PROPAGATION_SUPPORTS(在事务中则加入,否则非事务执行)、PROPAGATION_NOT_SUPPORTED(总是非事务执行并挂起当前事务)、PROPAGATION_NEVER(必须非事务执行,存在事务则抛出异常),这些行为用于解决不同业务方法间的事务互动问题。PROPAGATION是传播的意思。
Spring事务的隔离级别有几种?
ISOLATION_DEFAULT(使用数据库默认隔离级别)、ISOLATION_READ_UNCOMMITTED(未提交读,可能导致脏读、幻读和不可重复读)、ISOLATION_READ_COMMITTED(已提交读,可防止脏读但幻读和不可重复读可能发生)、ISOLATION_REPEATABLE_READ(可重复读,可防止脏读和不可重复读但幻读可能发生)、ISOLATION_SERIALIZABLE(可串行化,防止脏读、不可重复读和幻读,但性能影响最大)。
Transactional(RollbackFor=Exception.class)注解了解吗?
这个注解表示如果在方法执行过程中抛出任何类型的 Exception(包括运行时异常和非运行时异常),则会触发事务的回滚,保证数据一致性,这样的配置使得事务处理更为灵活且能够应对更广泛的异常情况。
Spring包含的模块有哪些?
如Core Container(核心容器)、AOP(面向切面编程)、Data Access/Integration(数据访问/集成)、Web(包括WebFlux和WebSocket支持)、Messaging(消息传递)和Test(测试支持)。
什么是Spring Bean?
由Spring IoC容器管理的对象,通过配置元数据(如XML文件、注解或Java配置类)告诉IoC容器如何创建和管理这些对象,实现了对象生命周期的全面控制和依赖注入的功能。
将一个类声明为Bean的注解有哪些?
Component,Repository,Service,Controller。
Bean的作用域有哪些?
singleton(单例),prototype(原型),request(每个 HTTP 请求创建一个实例,仅适用于 Web 应用),session(每个 HTTP session 创建一个实例,仅适用于 Web 应用),application/global-session(全局应用级别,仅适用于 Web 应用),以及 websocket(每个 WebSocket 会话创建一个实例,仅适用于 Web 应用)。
单例Bean的线程安全问题了解吗?
主要源于资源竞争,常见的解决办法包括避免使用可变的成员变量,或者使用 ThreadLocal 来保证线程局部变量的安全。大多数无状态的 Bean(如 Dao、Service)本身就是线程安全的。
手写单例模式(美团)page 1
饿汉式。用静态常量。
懒汉式。用synchronized同步。
双重校验锁DCL。
静态内部类。静态内部类持有唯一实例,且只在第一次调用getInstance方法时才会加载。在SingletonHolder类被加载时,并不会触发内部Holder类的加载。jvm类加载的流程,加载、连接、初始化。在SingletonHolder类的clinit方法中,并不包含初始化INSTANCE的逻辑,而Holder类的clinit方法中包含INSTANCE被实例化这一条赋值语句,只有当外部线程首次触发getInstance方法,jvm才会加载并执行Holder的clinit,完成实例创建。
枚举单例。
public class SingletonEager{
private static final SingletonEager INSTANCE=new SingletonEager();
private SingletonEager(){}
public static SingletonEager getInstance(){
return INSTANCE;
}
}
public class SingletonLazy{
private static SingletonLazy instance;
private SingletonLazy(){}
public static synchronized SingletonLazy getInstance(){
if(instance==null){
instance=new SingletonLazy();
}
return instance;
}
}
public class SingletonDCL{
private static volatile SingletonDCL instance;
private SingletonDCL(){}
public static SingletonDCL getInstance(){
if(instance==null){
synchronized(SingletonDCL.class){
if(instance==null){
instance=new SingletonDCL();
}
}
}
return instance;
}
}
public class SinletonHolder{
private SinletonHolder(){}
private static class Holder{
private static final SinletonHolder INSTANCE=new SinletonHolder();
}
public static SinletonHolder getInstance(){
return Holder.INSTANCE;
}
}
public enum SinletonEnum{
INSTANCE;
public void someMethod(){
}
}
Spring AOP和ApectJ AOP有什么区别?
Spring AOP 是基于代理的运行时增强,主要用于较轻量级的AOP实现,而AspectJ 是通过字节码操作进行编译时增强,提供更全面和高效的AOP功能。
AspectJ定义的通知类型有哪些?
包括 Before(前置通知)、After(后置通知)、AfterReturning(返回通知)、AfterThrowing(异常通知)以及 Around(环绕通知)。
多个切面的执行顺序如何控制?
通过使用 @Order 注解或实现 Ordered 接口并重写 getOrder() 方法来控制,其中返回值越小,切面的优先级越高,从而决定执行顺序。@Order(1)这样用。
说说自己对Spring MVC的了解
是一个基于 MVC 设计模式的框架,它通过分离业务逻辑、数据和表示层来提高代码的组织性和可维护性,实现更高效的 Web 应用开发,并且与 Spring 框架的集成提供了强大的后端服务支持。
SpringMVC的核心组件有哪些?
Spring MVC 的核心组件包括 DispatcherServlet(中央处理器)、HandlerMapping(处理器映射器)、HandlerAdapter(处理器适配器)、Handler(请求处理器),以及 ViewResolver(视图解析器),这些组件共同工作以处理客户端的请求和生成适当的响应。
Spring MVC工作原理了解吗?
涉及 DispatcherServlet 作为中心调度器拦截请求,通过 HandlerMapping 查找合适的 Controller,通过 HandlerAdapter 调用 Controller 处理请求并返回 ModelAndView,最后由 ViewResolver 解析视图并通过 DispatcherServlet 返回渲染后的视图给客户端。
统一异常处理怎么做?
推荐使用 @ControllerAdvice 和 @ExceptionHandler 注解来实现统一的异常处理,这允许你在全局或特定的控制器中捕获并处理异常。
如何使用JPA 在数据库中非持久化一个字段?
即不被存储到数据库中,可以通过标记该字段为 transient 或使用 @Transient 注解。
JPA的审计功能是什么?有什么用?
用于自动记录实体对象的创建和修改信息,如创建时间、创建者、最后修改时间和修改者,这有助于跟踪和维护数据库记录的历史变更。
实体之间的关联关系注解有哪些?
包括 @OneToOne(一对一)、@ManyToOne(多对一)、@OneToMany(一对多)和 @ManyToMany(多对多);这些注解允许在实体间定义各种类型的关系。
有哪些控制请求访问权限的方法?
包括使用 permitAll() 允许所有访问,anonymous() 允许未登录的匿名访问,denyAll() 拒绝所有访问,authenticated() 和 fullyAuthenticated() 限制访问只给已认证的用户,以及 hasRole(), hasAnyRole(), hasAuthority(), hasAnyAuthority(), 和 hasIpAddress() 来限制特定角色、权限或 IP 地址的用户访问。
hasRole和hasAuthority有什么区别?
hasRole 和 hasAuthority 主要区别在于命名约定:hasRole 自动添加 ROLE_ 前缀,适用于角色检查,而 hasAuthority 用于权限检查,不添加前缀。设计层面上,角色通常视为权限集合。
如何对密码进行加密?
推荐使用基于 bcrypt 强哈希函数的 PasswordEncoder 实现类来对密码进行安全加密。
如何优雅更换系统使用的加密算法?
推荐使用 DelegatingPasswordEncoder 来管理多种密码加密方案,它允许平滑地过渡和兼容旧的加密算法。
Spring有啥缺点?
在实际应用中,特别是在整合第三方库、启用高级特性(如事务管理和Spring MVC)时,Spring仍需要较多的配置工作。此外,管理这些配置及处理库间的依赖和版本冲突的问题。
为什么要有SpringBoot?
简化Spring开发,减少配置文件。
说说SpringBoot主要优点
包括简化了基于Spring的应用程序的开发和配置过程。它通过提供默认配置(”固执己见的配置”)来减少必需的样板代码和XML配置,从而显著提高开发效率和简化过程。Spring Boot还集成了Spring生态系统中的各种框架,如Spring Security、Spring Data等,提供了嵌入式HTTP服务器(如Tomcat和Jetty)简化了Web应用的开发和测试。此外,它还支持使用命令行接口(CLI)和多种插件来优化构建和部署流程。
什么是SpringBoot Starters?
用于将常用的依赖组合成预配置的集合。这大大简化了项目依赖管理,避免了单独引入多个库和框架的复杂性。例如,使用 spring-boot-starter-web,你可以通过一个简单的依赖项获得开发Web应用或REST服务所需的Spring MVC、Tomcat、Jackson等组件。
SpringBoot支持哪些内嵌Servlet容器?
内置了对Tomcat、Jetty和Undertow这几种流行的Servlet容器的支持,此外,Spring Boot应用也可以被部署到任何兼容Servlet 3.1+的Web容器中。
如何在SpringBoot应用程序中使用Jetty而不是Tomcat?
通过修改项目的构建配置文件来更改容器依赖。在Maven中,你需要在spring-boot-starter-web依赖中排除spring-boot-starter-tomcat,并添加spring-boot-starter-jetty作为依赖。对于Gradle构建系统,你也需执行类似操作:在spring-boot-starter-web中排除Tomcat模块并添加Jetty启动器。<exclusions>
开发RestfulWeb常用的注解是什么?
@RestController 用于定义控制器类;HTTP 请求处理注解如 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping;数据绑定注解如 @RequestParam、@PathVariable、@RequestBody;以及组件定义注解如 @Component、@Service、@Repository、@Controller。
SpringBoot常用的两个配置文件
通过 application.properties 或者 application.yml 对 Spring Boot 程序进行简单的配置。
什么是YAML,YAML的优势在哪里?
用于配置文件的人类可读数据序列化格式,它的优势在于提供了结构化和分层的配置方式,使得配置更加直观。
SpringBoot读取配置的方法有哪些?
使用 @Value 注解读取简单配置,通过 @ConfigurationProperties 注解绑定配置到 Beans,并进行数据校验,以及使用 @PropertySource 注解读取特定的 properties 文件。
SpringBoot加载配置文件优先级了解吗?
位于外部配置目录下的 config/application.yaml 最高,其次是类路径下的 src/main/resources/config/application.yaml,然后是类路径下的 src/main/resources/application.yaml。
常见的Bean映射工具有哪些?
包括 Spring BeanUtils、Apache BeanUtils、MapStruct、ModelMapper、Dozer、Orika 和 JMapper,其中 MapStruct 由于其优良的性能和灵活性,被推荐为较佳选择。
SpringBoot如何监控系统运行情况?
通过集成 Spring Boot Actuator 模块并使用其提供的端点,如 /health,来监控系统的实际运行状况。
SpringBoot如何做请求参数校验?
使用 JSR-303/JSR-380 标准定义的校验注解(如 @NotNull, @Min, @Max, @Email 等)和 Hibernate Validator 扩展的注解,以及在控制器层使用 @Valid 或 @Validated 注解来触发这些校验规则。
SpringBoot如何做到全局异常处理?
通过使用 @RestControllerAdvice 结合 @ExceptionHandler 注解来实现全局异常处理。
SpringBoot如何实现定时任务?
通过在方法上使用 @Scheduled 注解来定义定时任务,同时在启动类上添加 @EnableScheduling 注解来激活这些定时任务的调度。
系统设计
如何使用session-cookie方案进行身份验证?
通常包括用户登录后服务器创建一个 Session 和 Session ID,将 Session ID 存储在用户的 Cookie 中,以后每次用户请求时带上这个 Cookie,服务器通过比较 Cookie 中的 Session ID 和服务器存储的 Session 数据来确认用户身份。
多服务节点session-cookie方案怎么做?
可采用集中式Session管理策略,如使用Redis等分布式缓存系统来统一存储和管理Session数据,从而解决单点故障问题并提高系统的伸缩性和可靠性。
如果没有cookie,session还能用吗?
即使客户端禁用了 Cookie,Session 仍然可以通过将 Session ID 嵌入到 URL 参数中来使用,但这种方式可能会降低安全性和用户体验。
为什么cookie无法防止CSRF攻击?
因为浏览器会自动携带对应域的 Cookie,攻击者可以通过伪造用户在信任站点的请求利用这一特性;而 Token(通常不存于 Cookie 中)需要在每次请求中手动附加到请求头或请求体,不会自动被浏览器携带,这样只有知道 Token 的合法用户才能发起有效请求,从而有效防止 CSRF 攻击。Cross-site request forgery
什么是OAuth2.0?
是一个行业标准的授权协议,主要用于授权第三方应用访问服务器资源,它通过颁发一个有时效性的令牌(Token)实现安全的授权,并广泛应用于第三方登录。
认证和授权的区别是什么?
认证(Authentication)主要是确认用户的身份,例如通过用户名和密码验证用户是否为其声称的人;而授权(Authorization)则是在认证之后发生,用于确定已认证的用户可以访问和操作系统中的哪些资源和功能。
RBAC模型了解吗?
RBAC(Role-Based Access Control)是一种基于角色的访问控制模型,通过将用户分配到具有特定权限集的角色,实现简化的权限管理和授权。
什么是 cookie,作用是什么?
是存储在用户本地终端的小数据文件,主要用于识别用户身份、保存登录信息、用户偏好设置和跟踪用户行为,帮助实现无状态的HTTP协议中的状态维持功能。
如何在项目中使用cookie?
包括创建并发送 Cookie 到客户端通过 HttpServletResponse,使用 @CookieValue 注解来获取请求中特定的 Cookie 值,以及通过 HttpServletRequest 读取所有 Cookie 值,从而有效管理用户的会话状态和偏好设置。
什么是SSO?
SSO(Single Sign-On)是一种允许用户通过一次登录认证后,无需重复登录即可访问多个相关但独立的系统的技术解决方案
为什么需要定时任务?
为它们允许系统自动化执行重要的周期性活动,如数据备份、订单管理、内容更新、定时发布和性能报告等,从而确保业务流程的高效。
单机定时任务选型有哪些?
包括使用Java的Timer类和ScheduledExecutorService类,其中Timer提供简单的任务调度但不支持并发执行,而ScheduledExecutorService提供了并发执行和更灵活的调度选项,包括支持使用Cron表达式;此外,Spring框架的@Scheduled注解提供了便捷的方式来声明定时任务,适合集成到Spring应用中。
分布式系统定时任务选型有哪些?
包括使用Quartz、Elastic-Job、XXL-JOB以及PowerJob等框架,这些框架支持任务在分布式系统中的高可用性、任务分片、动态任务添加、集群管理以及可视化控制,适用于处理复杂和大规模的任务调度需求。
什么是JWT?
JWT(JSON Web Token)是一种基于Token的认证授权机制,它通过一个包含认证信息的JSON对象,生成一个经过编码和签名的字符串,用于身份验证和信息交换,支持无状态的设计,使得服务端不需要存储Session信息,从而提高系统的可用性。
如何基于JWT进行身份验证?如何防止JWT被篡改?
基于JWT进行身份验证涉及服务端生成一个签名的Token返回给用户,用户后续请求时将此Token携带在HTTP请求头中,服务端通过验证Token的签名确认用户身份;为防止JWT被篡改,重要的是保证签名密钥的安全性,确保无法由外部重构有效的签名。
Mybatis
mybatis中#和$符号的区别
#{}是预编译占位符,他防止SQL注入,所有参数被当成值处理;$是字符串直接拼接,会被SQL注入,用户传入的任何内容都字面拼进SQL。
maven中install 和package区别
package产生编译好的二进制包,放在项目target目录下。
install除了在target目录生成包之外,还会把这个包安装到本地仓库,同一机器上其他maven项目可以当做依赖直接引用。
maven怎么管理不同的版本号
项目资深版本用version字段控制。其次,可以集中管理,父pom的dependencyManagement和pluginManagement统一版本。
{}和${}的区别是什么?
${} 用于从 properties 文件中静态替换文本,而 #{} 用作 SQL 的参数占位符,其中 MyBatis 会使用 PreparedStatement 动态绑定参数,保障 SQL 执行时的安全性
xml映射文件中,除了常见的select,insert,update,delete标签之外还有哪些标签?
还包括 resultMap、parameterMap、sql、include、selectKey,以及支持动态 SQL 的标签如 trim、where、set、foreach、if、choose、when、otherwise、bind 等。
Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
Dao 接口工作原理依赖于 JDK 动态代理机制,通过接口全限名和方法名组合为 key 唯一定位和执行 MappedStatement 对应的 SQL,Dao 接口的方法可以进行重载,但重载方法必须具有唯一对应的 SQL 映射,以避免运行时错误。
Mybatis是如何做分页的,分页插件的原理是什么?
通过使用 RowBounds 进行内存分页,直接在 SQL 中书写物理分页语句实现数据库层面的分页,或使用分页插件,后者通过插件接口拦截执行的 SQL,重写 SQL 以添加物理分页参数和语句,从而实现高效的物理分页功能。
简述Mybatis插件运行原理,以及如何写一个插件。
基于 JDK 动态代理,允许开发者通过实现 Interceptor 接口并重写 intercept() 方法来创建自定义插件,这些插件可以拦截并增强 ParameterHandler、ResultSetHandler、StatementHandler、Executor 四种接口的方法,通过在配置文件中注册插件可以将其应用于实际的 SQL 执行流程中。
MyBatis 执⾏批量插⼊,能返回数据库主键列表吗?
能。
Mybatis动态sql是做什么的?
通过在 XML 映射文件中使用特定标签(如 <if>, <choose>, <where>, <foreach>, <bind> 等)允许开发者根据参数动态构建 SQL 语句,利用 OGNL 表达式从参数对象中动态评估条件并拼接 SQL,实现灵活的查询构建。
Mybatis如何将sql执行结果封装成目标对象返回的?都有哪些映射形式?
一是使用 <resultMap> 标签定义列与对象属性之间的映射关系,二是利用 SQL 列的别名功能,将列别名设置为对象属性名,然后通过反射机制创建对象并给属性赋值。
Mybatis能执行一对一一对多的查询吗?有哪些实现方式。
支持一对一、一对多、多对一和多对多的关联查询,实现方式主要有单独查询和嵌套查询两种:单独查询分别查询主对象和关联对象,然后程序中组合;嵌套查询通过 JOIN 将主对象和关联对象一起查询,利用 <resultMap> 中的 <id> 标签指定唯一列来去重,确保数据的准确关联和整合。
Mybatis是否支持延迟加载?
支持 association(一对一)和 collection(一对多)的延迟加载,通过在配置文件中设置 lazyLoadingEnabled=true 来启用。其实现原理是利用 CGLIB 为目标对象创建代理对象,代理对象在首次访问关联对象的方法时触发拦截器方法,从而执行预定义的 SQL 语句加载数据,实现延迟加载的功能。
Mybatis不同的xml映射文件,id是否可以重复?
如果不同的 XML 映射文件配置了不同的 namespace,则这些文件中的 id 可以重复,因为 namespace + id 组合成为唯一标识,用于区分不同映射文件中的同名操作;如果没有配置 namespace,则 id 不能重复,以避免映射定义的相互覆盖。
MyBatis 中如何执⾏批处理?
使⽤ BatchExecutor 完成批处理。
Mybatis有哪些Executor执行器?
三种类型的 Executor 执行器:SimpleExecutor 每次操作后立即关闭 Statement 对象,ReuseExecutor 重用 Statement 对象并缓存以便再次使用,而 BatchExecutor 则是专用于批量更新操作,缓存多个 Statement 对象并批量执行,这些执行器的使用范围都限制在单个 SqlSession 生命周期内。
Mybatis如何指定使用哪个Executor?
通过在配置文件中设置默认的 ExecutorType 或在调用 DefaultSqlSessionFactory 的创建 SqlSession 方法时手动传递 ExecutorType 参数。
Mybatis是否可以映射枚举类?
可以,通过自定义 TypeHandler 实现 setParameter() 和 getResult() 方法,分别处理 Java 类型到 JDBC 类型的转换和从数据库结果集到 Java 类型的转换。
Mybatis映射文件中,如果A标签通过include引用了B标签的内容,B标签能否定义在A标签后面,还是说必须定义在A前面?
被引用的 B 标签可以定义在引用它的 A 标签的前面或后面,因为 MyBatis 在初次解析时会标记未解析的引用,待所有标签解析完毕后会重新解析这些标记为未解析的标签,确保所有依赖关系被正确处理。
简述Mybatis映射文件和Mybatis内部数据结构之间的映射关系。
XML 映射文件的各种标签如 <parameterMap>, <resultMap>, <select>, <insert>, <update>, <delete> 等被解析并封装成相应的内部数据结构,包括 ParameterMap, ResultMap, MappedStatement 等,这些结构存储在 Configuration 对象中,实现 SQL 与 Java 对象之间的映射和管理。
为什么Mybatis被称为半自动的ORM工具?
因为它不像 Hibernate 那样能够完全自动根据对象关系模型处理关联查询,而是需要开发者手动编写 SQL 来实现关联对象和集合的查询,这要求开发者具备一定的 SQL 编写能力。
分布式
RPC是什么?
是一种允许程序调用另一台计算机上的程序或服务的技术,使得远程服务调用就像本地函数调用一样简单。它隐藏了网络编程的复杂性,自动处理通信协议和数据序列化等问题,让开发者能专注于业务逻辑的实现。
RPC的原理是什么?
RPC的原理主要基于客户端和服务端之间的信息交互。过程如下:
客户端调用:服务消费者通过本地接口调用方式尝试执行远程服务。
客户端Stub:也称为代理类,负责将调用的方法、参数等信息序列化成网络传输格式(如RpcRequest),并通过网络发送到服务端。
网络传输:信息通过网络(通常使用TCP/IP协议)传输到服务端,可以使用Socket或高效的网络框架如Netty。
服务端Stub:服务端接收到请求后,进行反序列化,恢复为RpcRequest对象,并据此执行本地方法。
服务端响应:执行完成后,服务端将结果封装并序列化回应(如RpcResponse),再通过网络发送回客户端。
客户端接收:客户端Stub接收响应,反序列化得到最终结果,呈现给服务消费者。
ZooKeeper 特点
顺序一致性: 从同一客户端发起的事务请求,最终将会严格地按照顺序被应用到 ZooKeeper 中去。原子性: 所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的,也就是说,要么整个集群中所有的机器都成功应用了某一个事务,要么都没有应用。单一系统映像: 无论客户端连到哪一个 ZooKeeper 服务器上,其看到的服务端数据模型都是一致的。
ZooKeeper应用场景
命名服务:可以通过 ZooKeeper 的顺序节点生成全局唯一 ID。数据发布/订阅:通过 Watcher 机制 可以很方便地实现数据发布/订阅。当你将数据发布到 ZooKeeper 被监听的节点上,其他机器可通过监听 ZooKeeper 上节点的变化来实现配置的动态更新。分布式锁:通过创建唯一节点获得分布式锁,当获得锁的一方执行完相关代码或者是挂掉之后就释放锁。分布式锁的实现也需要用到 Watcher 机制。
ZooKeeper的一些概念
ZooKeeper 数据模型采用层次化的多叉树形结构,每个节点上都可以存储数据,这些数据可以是数字、字符串或者是二进制序列。并且。每个节点还可以拥有 N 个子节点,最上层是根节点以“/”来代表。每个数据节点在 ZooKeeper 中被称为 znode,它是 ZooKeeper 中数据的最小单元。
znode 分为 4 大类:
持久(PERSISTENT)节点:一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
临时(EPHEMERAL)节点:临时节点的生命周期是与 客户端会话(session) 绑定的,会话消失则节点消失。
持久顺序(PERSISTENT_SEQUENTIAL)节点:除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 /node1/app0000000001、/node1/app0000000002 。
临时顺序(EPHEMERAL_SEQUENTIAL)节点:除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性。
高性能
什么是读写分离?
是一种数据库架构策略,通过将写操作定向到主数据库,而将读操作分发到一个或多个从数据库,从而提升读操作的处理效率并优化整体性能。
读写分离会带来什么问题?
可能导致主库和从库间的数据不一致性问题,常见的解决方法包括强制路由读请求至主库以确保数据一致性,或设计业务流程以适应同步延迟,如延迟关键读操作以等待数据同步。
怎么实现读写分离?
通常涉及部署一台主数据库和多台从数据库,使用主从复制确保数据同步,并通过代理或组件方式如sharding-jdbc来路由读写请求到相应的数据库实例。
什么是分库分表?什么是水平拆分,什么是垂直拆分?
分库指的是将数据分散到多个数据库中以提高性能和扩展性;分表是将一个大表拆分成多个小表,可以是垂直拆分(按列拆分)或水平拆分(按行拆分),其中垂直拆分将表中的某些列分到不同的表中,而水平拆分将表中的行分散到多个表中。
什么情况下需要分库分表?
常在面对单表数据量过大、数据库存储空间和备份时间过长、或应用并发量过大的情况下考虑。
分库分表会带来什么问题?
如跨库join操作困难、事务处理复杂化、需生成分布式唯一ID(雪花算法)等问题。
分库分表有什么推荐方案?
ShardingSphere 是分库分表的优选方案,提供读写分离、分库分表、分布式事务和数据库治理等功能。
分库分表后,数据怎么迁移呢?
通过停机迁移(使用脚本在低峰时段同步数据)或者采用双写策略(同时更新老库和新库以确保数据一致性),对于无法承受停机的系统,还可以使用如Canal等工具进行增量数据迁移。
什么是CDN?
一种通过将静态资源如图片、视频、文档、CSS和JS分布在地理位置分散的服务器上,实现数据就近访问,以加快访问速度和减轻原始服务器负担。
CDN的 工作原理是什么?
将静态资源预热到边缘节点、通过全局负载均衡(GSLB)基于性能和地理位置选择最合适的CDN节点来服务用户请求,以及使用Referer防盗链和时间戳防盗链等机制防止资源盗用。
静态资源是如何缓存到CDN节点中的?
通过预热将源站资源同步到CDN,若无预热,资源请求将回源获取并缓存以提高命中率和降低回源率,资源更新后通过刷新机制确保用户访问最新资源。
如何找到最合适的CDN节点?
通过全局负载均衡(GSLB),基于DNS解析系统,考虑用户请求的地理位置、CDN节点的性能和负载状况,选择并返回性能最佳的CDN地址。
如何防止资源被盗刷?
包括设置Referer防盗链来验证请求来源,使用时间戳防盗链确保URL的时效性和安全性,以及通过IP黑白名单和访问频率限制来进一步保护资源。Guide哥和阿秀就被攻击过。
高可用
什么是高可用?
系统在大部分时间内保持正常运行和服务可用,常用多个9(如99.999%)来表示其稳定性,也可通过功能失败次数与请求总次数之比来衡量。
哪些情况会导致系统不可用?
黑客攻击、硬件故障、用户请求量激增导致服务宕机、代码问题如内存泄漏、关键架构组件如Nginx或数据库失效。
有哪些提高系统可用性的方法?
注重代码质量和进行严格的代码审查,使用集群来减少单点故障,实施限流、超时和重试机制,部署熔断机制,采用异步调用和消息队列来优化性能,使用缓存来减轻数据库压力,以及实施系统监控、灰度发布和定期硬件检查等综合措施。
什么是限流?
对软件系统接收请求的速率进行控制的技术手段,目的是为了防止瞬间高并发请求超过系统处理能力而导致系统崩溃,从而保证系统的稳定性。
常见限流算法有哪些?
固定窗口计数器算法、滑动窗口计数器算法、漏桶算法和令牌桶算法,这些算法通过不同的机制控制数据流入速率,以保护系统免受瞬时大量请求的冲击。
单机限流有哪些方法?
通过使用令牌桶算法实现的Google Guava的RateLimiter工具类,或者使用Bucket4j和Resilience4j等库,这些工具和库提供了平滑突发限流和平滑预热限流等策略。
分布式限流有哪些方法?
通过使用中间件如Sentinel或Redis实现限流逻辑,或在网关层如Spring Cloud Gateway使用RedisRateLimiter或整合Sentinel来进行限流。
什么是超时机制?
一个在设定时间内如果请求未被处理则自动取消并返回错误的机制,主要包括连接超时和读取超时,用于防止系统资源被长时间占用而导致服务瘫痪。
超时时间该如何设置?
应考虑避免系统压力过大和请求堆积,建议将读取超时设为1500ms作为一种普适的起始点,并根据实际服务性能和需求调整,同时连接超时可适当放宽至1000ms至5000ms之间,并可考虑实现超时参数的动态配置以灵活应对不同的系统状态。
什么是重试机制?
通过多次自动重新发送相同请求以应对瞬态或偶然性故障的方法,常与超时机制结合使用,以提高请求的成功率。
重试的次数该如何设置?
通常建议将重试次数设置为3次,并适当设置增加的重试间隔,例如第1次失败后等待1秒,第2次失败后等待2秒,第3次失败后等待3秒。
讲讲重试幂等的概念。
实施超时和重试机制时,必须确保操作的幂等性,即多次执行相同的请求应有相同的结果,避免如重复扣费等问题,通常通过在服务端检查和管理请求标识来实现。