synchronized和lock API的区别
区分点 | synchronized | lock api |
---|---|---|
来源 | java内置关键字 | java.util.concurrent包 |
获取锁时是否可以响应中断 | 不能响应中断 | 可以,使用lock.lockInterruptibly() |
支持的锁类型 | 非公平锁,有锁升级的过程(无锁->偏向锁->轻量级锁->重量级锁) | 支持非公平锁和公平锁(ReentrantLock lock = new ReentrantLock(boolean fair)) |
加锁和释放 | 自动释放锁 | 手动释放锁,在finally中释放 |
锁信息获取 | 不可获取锁信息 | 可以通过lock.isLocked()查看是否获取到锁 |
获取锁方式 | 阻塞 | 支持阻塞lock.lock()和非阻塞方式获取锁lock.tryLock()、lock.tryLock(timeout) |
实现方式 | 对象头中的监视器 | AQS |
注意:synchronized和lock在获取锁之后都可以响应中断,java的中断Thread.interrupt()方法只有在线程执行如Thread.sleep()、Thread.sleep(timeout)、Thread.join()、Thread.join(timeout)、Object.wait()、Object.wait(timeout)等阻塞时才会抛出InterruptedException异常,其他情况下只会设置中断标志为true。
AQS原理
todo 补充aqs原理
java内存模型分为哪些区域,哪些区域会发生GC,哪些区域会发生OOM?
区域 | 是否发生GC | 是否发生OOM |
---|---|---|
程序计数器 | 否 | 否 |
堆 | 是 | 是 |
虚拟机栈 | 否 | 是 |
本地方法栈 | 否 | 是 |
元空间 | 是 | 是 |
直接内存 | 否 | 是 |
java垃圾收集器和工作原理?
todo 补充具体工作流程
CMS
分代模型:新生代+老年代
工作流程:
初始标记(STW),并发标记,重新标记(STW)、并发清除
问题:
- 使用标记-清除 回收算法产生内存碎片,可能会触发fullgc
- cpu敏感,并发阶段占用cpu资源
- 浮动垃圾:无法处理标记后产生的新垃圾
G1:
分代模型:新生代+老年代
工作流程:
初始标记(STW),并发标记,重新标记(STW)、并发清除
问题:
- 使用标记-清除 回收算法产生内存碎片,可能会触发fullgc
- cpu敏感,并发阶段占用cpu资源
- 浮动垃圾:无法处理标记后产生的新垃圾
MySQL主从同步数据机制?
主从同步流程
- 主库写操作
- 写入binlog
- binlog dump线程发送给从库
- 从库I/O线程接收写入relay log中
- 从库SQL线程重放
复制方式
- 异步复制
主库提交事务后立即返回客户端,不等待从库确认。吞吐量高,但主库故障数据可能丢失。 - 半同步复制
主从提交事务后,等待至少一个从库确认接收binlog才返回。降低了数据丢失风险,吞吐量下降。 - 全同步复制
主库事务需从库全执行完才返回成功。强一致性系统,性能消耗明显。
binlog格式对比差异
格式 | 定义 | 优点 | 缺点 |
---|---|---|---|
statement | 记录sql语句 | 日志量小 | UUID随机函数、时区不一致导致主从数据不一致 |
row | 记录每行数据变更,原始值、新值 | 主从数据一致 | 日志量大 |
mixed | 混合模式,默认statement,涉及到不确定操作时切换到row | 平衡性能和一致性 | - |
undolog、redolog分别是什么含义,用来做什么?
先进行一个总体比较
维度 | undolog | redolog |
---|---|---|
含义 | 用来做事务的回滚 | 用来做事务的持久化和奔溃恢复 |
日志类型 | 逻辑日志 | 物理日志,记录物理页的修改 |
写入时机 | 事务修改数据前 | 事务提交前 |
生命周期 | 异步清理(purge线程) | 固定大小,循环覆盖 |
TODO 补充各自特性
Innodb一级索引和二级索引的区别?
一级索引:又称聚簇索引,叶子节点存储的是数据列,数据行按照主键顺序物理排列。
一级索引的生成:如果显示定义了主键,直接使用主键作为_row_id;如果有唯一索引,则使用唯一索引作为_row_id;否则隐式生成_row_id作为一级索引。
二级索引:又称非聚簇索引,叶子节点存储的主键值。查询时如果使用二级索引,需要回表获取具体的数据行。
Innodb和myisam主键索引的区别?
innodb主键索引即数据文件,物理存储按照主键顺序,如果频繁插入无序主键,会有页分裂和索引调整,性能下降;
myisam所有索引均为非聚簇索引,主键索引和数据文件分开存储,索引文件存储主键值和数据文件地址的偏移量,主键索引需要两次IO(索引-数据文件)。无需维护主键顺序,适合频繁插入无事务的场景。
相比于B树,B+树的优势?
在innodb中,索引和数据的存储单位都是页,innodb中页大小为16K。对于主键索引,非叶子节点存储的是主键值+下层页的地址指针,叶子节点存储的是数据行。
- 假设数据行大小为1K,叶子节点一页16K能存储16行数据记录;
- 主键是bigint类型,8字节;页指针大小为6字节,非叶子节点一页能存储 16*1024/(8+6)字节=1170
- 2层能存储的数据行大小为 1170 * 16= 18720
- 3层能存储的数据行大小为 1170 * 1170 * 16 = 21902400 (2千万)
也就是说,3层的B+树能存储千万的数据,而层数相当于IO次数,B+树能有效减少IO次数。而且叶子节点使用双向链表,范围查询高效。
在 InnoDB 的表空间文件中,约定 page numbe r为 3 的代表主键索引的根页,而在根页偏移量为 64 的地方存放了该 B+ 树的 page level。
间隙锁解决啥问题?
间隙锁解决了可重复读事务隔离级别的幻读问题。如果只有行锁进行事务更新时锁定,如下:
假如只有 (id=2,age=‘1’) 这一行数据。
事务开始
1 select * from user where id > 1 and id < 4;
2 select * from user where id > 1 and id < 4;
事务结束
查询语句1返回了id=2这条数据,在执行语句2之前,别的事务进行了插入
insert into user (id, age) values(3, ‘3’);
语句1和2的执行结果不一致,出现了幻读。此时锁定id=2和 (1,2) 和(2,4)这个范围,让其他插入语句不能对此范围操作,避免事务执行前后不一致。
Sql调优
调优步骤:分析执行计划->优化索引->改写sql->参数调整
执行计划:
- 全表扫描ALL需优化为 ref 或 range
- rows预估扫描行数;
- 额外操作:using filesort(文件排序)、using temporary(临时表)
排查思路:
- 检查是否索引失效或者索引缺失
- 驱动表选择不当多次回表
- 索引使用函数或者隐式类型转化导致索引失效
优化思路:
- 唯一性高的数据优先作为索引;
- 索引覆盖:索引包含查询字段,减少回表;
- 索引下推:在索引中增加条件,减少回表次数;
SQL优化:
- 避免使用select *,减少内存和网络开销;
- 分页优化,增加定位起始id,避免深分页;
- 减少数据扫描:在join前用条件减少数据量;
架构优化:
- 读写分离减少主库压力;
- 增加缓存减少数据库访问频率;
监控手段:
- 启用慢查询日志;
- 实时监控数据库QPS和连接数等指标;
执行计划
执行计划中包含的字段:
- rows(扫描行数):用来判断是否全表扫描或者扫描行数过多;
- possible keys:可能命中的索引
- type:如何查找表数据,一般优化到range到ref就可以,枚举如下:
system:查询系统表,表中仅有一行数据,比如查询information_schema 等系统表。
const:通过主键或唯一索引精确匹配单行。
eq_ref:多表关联时,被驱动表通过主键/唯一索引等值匹配。
ref:非唯一索引等值匹配,可能返回多行。
ref_or_null:类似 ref,但包含 NULL 值过滤。
fulltext:全文索引检索。
index_merge:合并多个索引的结果(OR 条件优化),减少回表次数。
unique_subquery:子查询返回唯一值(主键/唯一索引),等价于 eq_ref。
index_subquery:子查询返回非唯一索引值。
range:索引范围扫描(BETWEEN、IN、LIKE ‘xxx%’)。
index:全索引扫描(扫描整个二级索引),若需回表则退化为 ALL。
all:全表扫描(逐行检查),无索引的 WHERE 条件。
null:表示不会访问到表数据,从索引中就可以获取所需数据。如查询主键索引的主键字段。
- key:sql执行中真正用到的索引
- ref:查询执行时,通过哪些列或常量与索引进行匹配。
核心价值:ref 字段帮助开发者理解查询如何利用索引,识别低效的索引使用场景(如函数、未命中索引)。
优化方向:
确保 ref 显示为列名或常量(而非 NULL)。
多表连接时,检查 ref 是否包含关联字段。
避免在索引列使用函数或隐式类型转换。
可能的取值为:
const:等值查询使用主键或唯一索引的常量值。如select * from user where id=1;仅匹配单行,效率最高。
列名(column):非唯一索引的等值查询或者范围查询。
多列(col1,col2):复合索引的部分列匹配。
函数或表达式:索引列参与函数计算(可能导致索引失效)。
NULL:未使用索引条件(如全表扫描或未命中索引的查询)。
ref 与 key 的区别:
key:实际使用的索引名称,如idx_age;
ref:索引匹配的具体列或常量,如age、const。
- extra:sql执行过程中额外的细节,常见的情况如下:
需要优化的情况:
Using filesort:使用了文件排序;比如sql为:select * from user where created_at > ‘2024-01-01 00:00:00’ order by created_at, age; 但是created_at上有索引,而age上没有,此时如果过滤后的数据大小超过了sort_buffer_size,就会创建一个临时文件进行排序。如果没有超过sort_buffer_size,则会进行内存排序。需要注意的是,这个参数是每个连接独享的,设置得过大可能会浪费内存资源。
Using temporary:需要创建临时表存储中间结果,常见于GROUP BY/DISTINCT/JOIN。优化方案:减少查询不必要的字段;调大临时表内存参数;强制直接使用磁盘临时表(省掉内存临时表转换为磁盘临时表的过程);
Using join buffer:连接操作使用缓存(通常因关联字段无索引),大表关联时未命中索引。
积极提示:
Using index:覆盖索引(查询字段全在索引中,无需回表)。
Using index condition (ICP):索引条件下推(MySQL 5.6+),部分条件在存储引擎层过滤,WHERE 条件包含索引列的非等值过滤。
Using where:存储引擎返回数据后,MySQL 需回表过滤(可能涉及回表操作)。索引未完全覆盖 WHERE 条件。
Select tables optimized away:优化器跳过表扫描(如 COUNT(1) 直接通过索引统计)。比如:SELECT COUNT(id) FROM orders;(id 为主键)
Impossible WHERE:WHERE 条件永远不成立(如 WHERE 1=0),直接返回不查询。
Distinct:MySQL 自动去重(优化 DISTINCT 查询)。
数据库和缓存一致性
todo 待补充
缓存击穿如何解决
查询数据库中没有的数据,加布隆过滤器,提高误判率可以增加位图大小,也可以修改hash函数减少hash冲突。
Redis持久化策略
AOF:
RDB:
AOF+RDB: