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:需要额外的排序,不能通过索引得到排序结果

    主从延迟
    解决方式
    关键业务读写都走主库,非关键还是读写分离

    事务失效

    1. @Transactional注解应用到非public方法(除非特殊配置,例如使用AspectJ 静态织入实现 AOP)
    2. 自调用,因为@Transactional是基于动态代理实现的
    3. 异常在代码中被你自己try catch了
    4. 异常类型不正确,默认只支持RuntimeException和Error,不支持检查异常
    5. 事务传播配置不符合业务逻辑

    垃圾回收和排查
    FGC过于频繁:隔几个小时甚至几天才执行一次
    YGC耗时过长:几十或者上百毫秒

    • 针对大对象或者长生命周期对象导致的FGC,可通过 jmap -histo 命令并结合dump堆内存文件作进一步分析,需要先定位到可疑对象。

    用jstack排查

    1. 先执行top,找到CPU占用比较高的进程
    2. jstack 进程id > show.txt
    3. 找到进程中CPU占用比较高的线程,线程id转为16进制
    4. 到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倍