1.领取活动
2.首次成功领取活动,发送MQ消息
3.执行抽奖doDrawExec
4.结果落库
5.发送MQ,触发发奖流程
抽象抽奖过程doDrawExec
1.获取抽奖策略
2.校验抽奖策略是否已经初始化到内存
3.获取不在抽奖范围内的列表,包括:奖品库存为空、风控策略、临时调整等
4.执行抽奖算法
5.包装中奖结果
索引、主从读写分离、集群、分库分表、sql、锁、参数调优、表结构
1.偶尔很慢
(1)数据库在刷新脏页
(2)遇到锁
(3)sql
2.一直很慢
(1)没有用上索引或者索引失效
(2)有索引可能走全表扫描
system>const>eq_ref>ref>range>index>ALL
type:表示MySQL在表中找到所需行的方式,或者叫访问类型
- type=ALL,全表扫描,MySQL遍历全表来找到匹配行
- type=index,索引全扫描
- type=range,索引范围扫描
- type=eq_ref,唯一索引
- type=NULL,MySQL不用访问表或者索引,直接就能够得到结果(性能最好)
possible_keys: 表示查询可能使用的索引
key: 实际使用的索引
key_len: 使用索引字段的长度
rows: 扫描行的数量
Extra:
- using index:覆盖索引,不回表
- using where:回表查询
- using filesort:需要额外的排序,不能通过索引得到排序结果
主从延迟
解决方式
关键业务读写都走主库,非关键还是读写分离
事务失效
- @Transactional注解应用到非public方法(除非特殊配置,例如使用AspectJ 静态织入实现 AOP)
- 自调用,因为@Transactional是基于动态代理实现的
- 异常在代码中被你自己try catch了
- 异常类型不正确,默认只支持RuntimeException和Error,不支持检查异常
- 事务传播配置不符合业务逻辑
垃圾回收和排查
FGC过于频繁:隔几个小时甚至几天才执行一次
YGC耗时过长:几十或者上百毫秒
- 针对大对象或者长生命周期对象导致的FGC,可通过 jmap -histo 命令并结合dump堆内存文件作进一步分析,需要先定位到可疑对象。
用jstack排查
- 先执行top,找到CPU占用比较高的进程
- jstack 进程id > show.txt
- 找到进程中CPU占用比较高的线程,线程id转为16进制
- 到show.txt文件中根据线程id查看线程的具体状态即可
top -c 命令找出当前进程的运行列表
按一下 P 可以按照CPU使用率进行排序
使用命令 top -Hp 2609 找出这个进程下面的线程,继续按P排序
2854是十进制的,我们需要转换为十六进制,转换结果:b26
YGC采用复制算法
3. YGC的触发时机
1、新对象会先尝试在栈上分配,如果不行则尝试在TLAB分配,否则再看是否满足大对象条件要在老年代分配,最后才考虑在Eden区申请空间。
上述回收器均采用复制算法,都是独占式的,执行期间都会Stop The World.
此外,老年代如果采用的是CMS回收器,为了减少CMS Remark阶段的耗时,也有可能会触发一次YGC,这里不作展开
可作为YGC时GC Root的对象包括以下几种:
1、虚拟机栈中引用的对象
2、方法区中静态属性、常量引用的对象
3、本地方法栈中引用的对象
4、被Synchronized锁持有的对象
5、记录当前被加载类的SystemDictionary
6、记录字符串常量引用的StringTable
7、存在跨代引用的对象
8、和GC Root处于同一CardTable的对象
卡表,划分成页512字节,1字节标记脏页
另外需要注意的是,针对下图中跨代引用的情况,老年代的对象A也必须作为GC Root的一部分,但是如果每次YGC时都去扫描老年代,肯定存在效率问题。在HotSpot JVM,引入卡表(Card Table)来对跨代引用的标记进行加速。
Eden区是如何加速内存分配的?
HotSpot虚拟机使用了两种技术来加快内存分配。分别是bump-the-pointer和TLAB(Thread Local Allocation Buffers)。
标记清除算法(内存碎片)
标记复制
标记整理
一般当线程在这个时间点上状态是可以确定的,如确定 GC Root 的信息等,可以使 JVM 开始安全地 GC。Safe Point 主要指的是以下特定位置:
- 循环的末尾
- 方法返回前
- 调用方法的 call 之后
- 抛出异常的位置 另外需要注意的是由于新生代的特点(大部分对象经过 Minor GC后会消亡), Minor GC 用的是复制算法,而在老生代由于对象比较多,占用的空间较大,使用复制算法会有较大开销(复制算法在对象存活率较高时要进行多次复制操作,同时浪费一半空间)所以根据老生代特点,在老年代进行的 GC 一般采用的是标记整理法来进行回收。
1:2
初始标记(STW)
并发标记(并行)时间长
并发预清理
重新标记(STW)
以及并发清除(浮动垃圾)
如果设置地太高很容易导致在 CMS 运行期间预留的内存无法满足程序要求,会导致 Concurrent Mode Failure 失败,这时会启用 Serial Old 收集器来重新进行老年代的收集,而我们知道 Serial Old 收集器是单线程收集器,这样就会导致 STW 更长了。
g1
YGC:根扫描、更新&&处理RSet、复制对象
线程是 CPU 调度的最小单位
- RUNNING:能接受新任务,并处理阻塞队列中的任务
- SHUTDOWN:不接受新任务,但是可以处理阻塞队列中的任务
- STOP:不接受新任务,并且不处理阻塞队列中的任务,并且还打断正在运行任务的线程,就是直接撂担子不干了!
- TIDYING:所有任务都终止,并且工作线程也为0,处于关闭之前的状态
- TERMINATED:已关闭。
而像 Tomcat 这种业务场景,大部分情况下是需要大量 I/O 处理的情况就做了一些定制,修改了原生线程池的实现,使得在队列没满的时候,可以创建线程至最大线程数。
CPU密集 CPU核心数
IO密集 增大线程数 2倍