性能优化-数据库,JVM, 秒杀场景
数据库基本原理
数据库架构
连接器:为每个链接分配专属空间, 一般通过连接池来减少资源开销。
语法分析 :抽象语法树, 分析 SQL 语法是否正确。
语义分析优化:SQL 进行优化, 利用索引进行优化。
执行引擎:根据优化后语法树生成执行计划(索引类型,处理行数,潜在利用的索引)并执行。
PrepareStatement 预编译:可以先行完成 SQL 预处理, 有助于性能提升, 另外就是防止 SQL 注入。
B+树
排序树,一个块存储多个数据,非叶子节点还存放指针,使用指针完成索引, 叶子节点存放数据, 通过 4 级结构能访问几千万记录。
问题在于 数据块离散存储, 每次根据索引进行查找还是要进行多次读写。 性能较差。
下图,将键 1–7 链接到数据值 d1-d7。 链接列表(红色)允许快速有序遍历
聚簇索引: 数据库记录和索引存储在一起。 MySQL 主键主键就是聚簇索引。
非聚簇索引: 叶子节点记录的就不是数据行记录,而是聚簇索引,也就是主键, 通过非聚簇索引找到主键索引,再通过主键索引找到行记录的过程也被称作回表。
合理添加使用索引: 增加索引后会生成对应的 B+树,再 insert,update, delete 记录时, 都需要更新 B+树,性能会有一定影响。 删除不必要的索引。
数据库事务(ACID)
原子性 (Atomicity)
隔离性(IsoLation)
持久性(Durability)
一致性(Consistency)
数据库事务日志
事务日志文件会记录更新前的数据记录,然后再更新数据库中的记录
UNDO:取消本次操作的方法,按照此方法回滚。
REDO:重复本次操作的方法
JVM 架构原理及垃圾回收
JVM 组成架构
执行引擎对应 CPU, 运行期数据区对应内存, 就类似一个虚拟机器一样。
Java 程序启动:启动 jvm 进程, 加载 Class 类, 通过类加载器,放到运行期数据区方法区;
JVM 创建用户主线程, 并为主线程分配 Java 堆栈, 为每个线程分配程序计数寄存器;
线程启动后检查程序计数器存储的指令, 到方法区取出指令;
指令通过执行引擎执行, 执行引擎转化为本地执行指令。
如果执行时需要创建对象, 在堆中存放, 针对这个对象的引用放到 Java 栈中。
Java 字节码文件
字节码执行流程
解释执行和编译执行。
字节码翻译过程
类加载器的双亲委托模型
低层次的当前类加载器,不 能覆盖更高层次类加载器已 经加载的类。
自定义类加载器:
隔离加载类
扩展加载源
字节码加密
运行时数据区
堆 &栈
方法区 &程序计数器
Java(线程)栈
线程工作内存 & volatile
对象引用存放 Java 栈,对象实例创建在堆中, 方法区存放类静态变量及代码。
Java 运行环境
Java 源码通过 java 编译器,编译成 Java 字节码, 通过类加载器加载到方法区,字节码交给解释器和即时编译器执行,编译成本地代码,交予本地操作系统执行。
Java 垃圾回收
JVM 垃圾回收就是将 JVM 堆中的已经不再被使用的对象清理掉,释放宝贵的内存资源
内存回收的三种方法:
清理
压缩
复制
内存回收分代回收:
新生代
老年代
内存回收算法:
串行回收
并行回收
并发回收 CMS
G1 回收
Java 启动参数
标准参数,所有的 JVM 实现都必须实现这些参数的功能,而且向后兼容
运行模式 -server,-client
类加载路径 -cp,-classpath
运行调试 –verbose
系统变量 –D
非标准参数, 默认 jvm 实现这些参数,但不保证所有 JVM 实现都实现,且不保证向后兼容
-Xms 初始堆大小
-Xmx 最大堆大小
-Xmn 新生代大小
-Xss 线程堆栈大小
非 Stable 参数, 此类参数各个 jvm 实现会有所不同,将来可能会随时取消
-XX:-UseConcMarkSweepGC 启用 CMS 垃圾回收
Java 性能诊断工具
基本工具:JPS ,JSTAT,JMAP,JSTACK
集成工具: JConsole,JVisualV
Java 代码优化
合理并谨慎使用多线程
使用场景(I/O 阻塞,多 CPU 并发)
资源争用与同步问题
java.util.concurrent
启动线程数 = [任务执行时间 / (任务执行时间 - IO 等待时间)] * CPU 内核数
可以看到如果属于 CPU 密集型应用,不宜启动过多线程。 多了反而容易竞争,影响执行效率, 如果任务需要等待磁盘操作,网络响应,可以通过增加线程提高效率
竞态条件与临界区
在临界区中使用适当的同步就可以避免竞态条件。
Java 线程安全
方法局部变量
局部变量存储在线程自己的栈中。
方法局部的对象引用
如果在某个方法中创建的对象不会逃逸出该方法,那么它就是线程安全的。
对象成员变量
对象成员存储在堆上。如果两个线程同时更新同一个对象的同一个成员,那这个代码就不是 线程安全的。
Web Servlet 单实例多线程共享, 需要增加锁保证线程安全。
ThreadLocal
实现:
Thread 里有自己的 ThreadLocalMap, ThreadLocalMap 中存放这 Thread 的信息。
Java 内存泄露
容易产生内存泄露部分:
长生命周期对象
静态容器
缓存
合理使用线程池:
复用线程或对象池
池管理算法
对象内容清除
合理容器类:
LinkList 和 ArrayList 的区别及适用场景 :根据随机访问和插入删除场景判断选择
HashMap 的算法实现及应用场景
使用 concurrent 包,ConcurrentHashMap 比较 HashMap 是线程安全特性
缩短对象生命周期
减少对象驻留内存的时间
在使用时创建对象,用完释放
创建对象的步骤(静态代码段-静态成员变量-父类构造函数-子类构造函数)
使用 I/O buffer 及 NIO
延迟写与提前读策略
异步无阻塞 IO 通信
优先使用组合代替继承
减少对象耦合
避免太深的继承层次带来的对象创建性能损失
合理使用单例模式
无状态对象
线程安全
计算机的任何问题都可以通过虚拟层(或者中间层)解决
面向接口编程
7 层网络协议
JVM
编程框架
一致性 hash 算法的虚拟化实现
秒杀系统案例
需求分析:
系统涉及要满足业务需求
架构方案:
单独部署,比在原有系统基础上改造更简单,隔离性更好; 通过边缘计算缓解中心服务压力
设计原则
静态化
并发控制,防秒杀器
简化流程
前端优化
交易系统性能优化
二跳页面优化
交易系统优化
应急预案
通过降级,限流, 拒绝服务防止系统异常
参考及引用
架构师训练营作业-李智慧老师相关讲义
Photo by stein egil liland from Pexels
评论