自我介绍 、项目经历
数据库:
1. 业务量多少
2. 表结构
用户表,分表
用户活动进度表,乐观锁 version 字段,(并发场景使用kafka消息队列异步处理)
如果要保证消息消费的顺序,可以根据业务key将同一种类型的消息放在一个broker partition中
操作历史记录表 ,全局唯一流水号,操作类型,操作数据 ,创建时间,更新时间
奖励发送表
3. 分库分表怎么设计
瓶颈分析: IO瓶颈 、CPU瓶颈
IO瓶颈:
- 磁盘读io瓶颈,热点数据太多,缓存存不下,每次查询都会产生大量io,查询速率降低—->分库 or 垂直分表
- 网络io瓶颈,请求的数据太多,网络带宽不够,分库
CPU瓶颈:
- sql问题,非索引字段查询等增加cpu计算的操作->sql优化,建立合适的索引 ,在业务service层进行计算
- 单表数据过大,每次扫描的行过多,水平分表
水平分库:系统绝对并发量上来了 ,分表难以解决根本问题,并且还没有明显的垂直业务归属来垂直分库。 库多了 ,cpu 和io的压力自然成倍缓解
- 每个库结构完全一样,每个库的数据都不一样,没有交集,所有库的并集是全部数据
水平分表:系统绝对并发量没有上来,只是数据太多了,影响sql执行效率,增加了cpu负担。表的数据量减少了,sql执行的cpu压力降低,执行效率变高(用户表、抽奖历史记录、用户操作历史记录、奖励发送任务)
- 以某个字段为依据,根据策略将一个表中的数据拆分到多个表中
- 每个表的结构完全一样,数据都不一样没有交集,所有表的并集是全部数据
垂直分库:系统绝对并发量上来了,并且可以根据业务将不同的表拆分到不同的库中
- 以表为依据,按照业务归属不同,将不同的表拆分到不同的库中
- 每个库的结构不一样,表数据也不一样,没有交集。甚至已经可以服务化了
垂直分表:以字段为依据,按照字段的活跃性,将表中的字段拆分到不同的表中(主表和扩展表)
- 系统的绝对并发没上来,表的数据也不多 ,但是字段多,热点数据和非热点数据字段在一起,单行数据所需要的存储空间大,以至于数据库存储的缓存行数减少,查询时产生更多的随机读io
- 每个表的结构不一样,数据不一样,但是至少有一个列有交集,一般是主键,用于关联\]
分库分表工具:
sharding-jdbc
TDDL
MYCAT中间件
分库分表步骤:
根据容量评估分库或者分表个数
选key。。。均匀
分表规则。。。hash等
执行。。。一般双写
扩容问题。。。尽量减少数据的移动
分库分表导致的问题:
非partiton key 的查询问题: 映射法,非partiton key 与partition key有个映射关系 ; 基因法,通过自定义函数,搞出对应关系
4. 索引失效
- 索引列用了函数
- 隐式转换,string 转 int
- 索引列有表达式;
- 模糊查询前% 或者前后%
- 联合索引的第一个索引列不在查询条件
- 查询条件有or关键字,导致前面的所以失效
- is null , is not null
- != , <> 不等于
5. b+树索引
6. 一条update语句的执行过程
mysql的架构分成server层和存储引擎层,server层根据执行计划,向存储引擎层索要数据,存储引擎通过索引一条一条将数据返回给server层,如果该记录所在的页面已经在buffer pool中,直接读取,否则要先加载到buffer pool中。
然后对比要更新的数据和缓存中的数据,如果不一致 ,进行更新操作(mysql_update函数),调用存储引擎更新记录 ha_update_row
然后更新聚簇索引
写undo log,redo log
mtr提交之后,将redo log复制进redo log buff,并且将buffer pool中的数据页记为脏页,加入 flush 链表中。
如果有二级索引跟新,也会记录相应的redo log
记录binlog,等事务最终提交的时候,会将所有的binlog再统一写入到binlog文件中
7. 发生慢查询怎么解决
mysql开启慢查询设置,my.ini文件添加配置
explain 检查sql执行计划
多线程
1. 线程池
threadPoolExecutor类中的构造方法包含了创建线程池的几个核心参数
corePoolSize 核心线程数
maxmumPoolSize 最多线程数
keepAliveTime 存活时间
unit 时间单位
threadFactory 生成线程的工厂
RejectedExecutionHandler 异常处理handler
创建一个线程,如果当前线程数未达到corepoolsize ,直接创建一个新线程执行任务
如果到达core size 未到maxmum size ,进入等待队列
如果等待队列已经满了,且总线程数未到maxmum size ,创建一个新线程执行任务
如果都满了,handler处理报错
超过核心线程数的空闲线程,在keepalivetime到期后会被回收掉
2. 线程状态
Thread.State 枚举中定义了6中线程状态的枚举
NEW :新创建的线程 还没有调用start方法
RUNNABLE : 运行状态 ,(ready就绪和running运行中 两种状态统称为运行)。该对象被调用start()方法,等待cpu调度时间片,进入ready状态RAEDY:就绪状态,在获得cpu时间片后,进入running运行中状态
BLOCKED: 阻塞状态, 线程阻塞于锁
WAITTING :等待状态 ,进入该状态的线程,需要等待其他线程的特定动作,例如通知或中断
time_waitting:超时等待状态,不同于watting,可以在指定的时间之后自行返回
terminated: 终止状态,线程执行完毕
应该还有多线程安全那一块 面试官没找到切入点问
JUC 相关
redis
1. 持久化
jvm
1. full gc排查
fullGC是GC的一种情况,通常伴随着应用长时间的停顿和较高的cpu占用。要排查fullGC,可以通过监控jvm状态、查看gc日志、分析gc日志和排查内存写泄露等方法进行处理。
监控jvm状态: jstat、jconsole、jvisualvm等来监控jvm状态,了解内存使用情况,GC次数 和时间等
查看gc日志: 可以通过启用GC日志来查看fullGC的信息,GC时间、堆内存使用情况、GC原因等。-XX:PrintGC、-XX:PrintGCDetails
分析gc日志: 例如老年代空间不足、永久代空间不足、内存泄露等原因,
排查内存泄露: jmap、jhat等来进行内存泄露分析,定位哪些对象使用了大量的内存,然后进行优化和恢复
- htop 命令 找到对应的java进程
- 检查进程号的gc
jstat -gcutil 72 2000 //每隔2秒 ,pid为72的进程的GC情况
S0 : Heap 上的 Survivor space 0 段已使用空间的百分比 S1 : Heap 上的 Survivor space 1 段已使用空间的百分比 E : Heap 上的 Eden space 段已使用空间的百分比 O : Heap 上的 Old space 段已使用空间的百分比 P : Perm space 已使用空间的百分比 YGC :从程序启动到采样时发生 Young GC 的次数 YGCT : Young GC 所用的时间 ( 单位秒 ) FGC :从程序启动到采样时发生 Full GC 的次数 FGCT : Full GC 所用的时间 ( 单位秒 ) GCT :用于垃圾回收的总时间 ( 单位秒 )
主要观察 FGC这个参数
- 查看在这个进程中消耗cpu最多的线程
top -H -p 72
- dump文件,根据·16进制的进程号去文件查找
jstack 72 > /tmp/dump_file // 将进程号为72的dump文件 输出到/tm;p/dump_file 这个文件
将进程号 80、79 分表装换成16进制 ,在dump文件中搜索 :printf %x 79