架构师训练营 2 期 Week09 总结
数据库的基本原理和 JVM 虚拟机架构原理
本周学习了数据库和 JVM 相关的知识。因为本人的是.NET 技术栈的。对 Java 的 JVM 不是很了解,不过听了 JVM 的基本架构知识对系统层面架构有了一些认知。
数据库的基本原理
连接器
数据库连接器会为每个连接请求分配一块专用的内存空间用于会话上下文管理。建立连 接对数据库而言相对比较重,需要花费一定的时间,因此应用程序启动的时候,通常会 初始化建立一些数据库连接放在连接池里,这样当处理外部请求执行 SQL 操作的时候, 就不需要花费时间建立连接了。
语法分析器
语义分析与优化器
语义分析与优化器就是要将各种复杂嵌套的 SQL 进行语义等价转化,得到有限几种关系代数计算结构,并利用索引等信息进一步进行优化。
执行计划
Key:索引类型,NULL 无索引
Rows:需要处理的行数
Possible_keys:潜在可以利用的索引
为什么 PrepareStatement 更好
PrepareStatement 会预先提交带占位符的 SQL 到数据库进行预处理,提前生成执行计 划,当给定占位符参数,真正执行 SQL 的时候,执行引擎可以直接执行,效率更好一点。
PrepareStatement 可以防止 SQL 注入攻击。
聚簇索引
聚簇索引的数据库记录和索引存储在一起。 MySQL 数据库的主键就是聚簇索引,主键 ID 和所在的记录行存储在一个 B+树中。
非聚簇索引
非聚簇索引在叶子节点记录的就不是数据行记录,而是聚簇索引,也就是主键。 通过非聚簇索引找到主键索引,再通过主键索引找到行记录的过程也被称作回表。
添加必要的索引优化 SQL 查询性能
在几百万行的数据库中查找一个条记录,如果没有索引,就需要全表扫描,检索所有的 行记录,才能找到需要的记录。
合理使用索引
不要盲目添加索引,尤其在生产环境中
添加索引的 alter 操作会消耗较长的时间(分钟级)
Alter 操作期间,所有数据库的增删改操作全部阻塞,对应用而言,因为连接不能释放,事实 上,查询也被阻塞。
删除不用的索引,避免不必要的增删开销
使用更小的数据类型创建索引
int 4 字节 bigint 8 字节,Timestamp 4 字节 Datetime 8 字节
数据库事务
事务特性 ACID
原子性(Atomicity): 事务要么全部完成,要么全部取消。 如果事务崩溃,状态回到事 务之前(事务回滚)。
隔离性(Isolation): 如果 2 个事务 T1 和 T2 同时运行,事务 T1 和 T2 最终的结果是相同 的,不管 T1 和 T2 谁先结束,隔离性主要依靠锁实现。
持久性(Durability): 一旦事务提交,不管发生什么(崩溃或者出错),数据要保存在数 据库中。
一致性(Consistency): 只有合法的数据(依照关系约束和函数约束)才能写入数据库。
数据库事务日志
进行事务操作时,事务日志文件会记录更新前的数据记录,然后再更新数据库中的记录, 如果全部记录都更新成功,那么事务正常结束,如果过程中某条记录更新失败,那么整 个事务全部回滚,已经更新的记录根据事务日志中记录的数据进行恢复,这样全部数据 都恢复到事务提交前的状态,仍然保持数据一致性。
LSN:一个按时间顺序分配的唯一事务记录日志序列号。
TransID:产生操作的事务 ID。
PageID:被修改的数据在磁盘上的位置。
PrevLSN:同一个事务产生的上一条日志记录的指针。
UNDO:取消本次操作的方法,按照此方法回滚。
REDO:重复本次操作的方法。
JVM 虚拟机原理
JVM 组成架构
Java 是一种跨平台的语言,JVM 屏蔽了底 层系统的不同,为 Java 字节码文件构造了 一个统一的运行环境。
Java 字节码文件
Java 如何实现在不同操作系统、不同硬件平台上 , 都可以不用修改代码就能顺畅地执 行?
计算机领域的任何问题都可以通过增加个中间层(虚拟层)来解决
Java 所有的指令有 200 个左右,一个字节( 8 位)可以存 储 256 种不同的指令信息,一个这样的字节称为字节码( Bytecode )。在代码的执行过程中, JVM 将字节码解释执行, 屏蔽对底层操作系统的依赖, JVM 也可以将字节码编译执行,如果是热点代码,会通过 JIT 动态地编译为机器码,提高执行效率。
字节码执行流程
Java 字节码文件编译过程
类加载器的双亲委托模型
低层次的当前类加载器,不 能覆盖更高层次类加载器已 经加载的类。如果低层次的 类加载器想加载一个未知类, 需要上级类加载器确认,只 有当上级类加载器没有加载 过这个类,也允许加载的时 候,才让当前类加载器加载 这个未知类。
自定义类加载器
隔离加载类:同一个 JVM 中不同组件加 载同一个类的不同版本。
扩展加载源:从网络、数据库等处加载字节码。
字节码加密:加载自定义的加密字节码, 在 ClassLoader 中解密。
堆和栈
堆:每个 JVM 实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放 在这个堆中,并由应用所有的线程共享。
堆栈:JVM 为每个新创建的线程都分配一个堆栈。也就是说,对于一个 Java 程序来说, 它的运行就是通过对堆栈的操作来完成的。
Java 中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配, 也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对 象,而在堆栈中分配的内存只是一个指向这个堆对象的引用而已。
方法区 和 程序计数器
方法区主要存放从磁盘加载进来的类字节码,而在程序运行过程中创建的类实例则存放 在堆里。程序运行的时候,实际上是以线程为单位运行的,当 JVM 进入启动类的 main 方法的时候,就会为应用程序创建一个主线程,main 方法里的代码就会被这个主线程执 行,每个线程有自己的 Java 栈,栈里存放着方法运行期的局部变量。而当前线程执行 到哪一行字节码指令,这个信息则被存放在程序计数寄存器。
Java(线程)栈
所有在方法内定义的基本类型变量,都会被每个运行这个方法的线程放入自己的栈中, 线程的栈彼此隔离,所以这些变量一定是线程安全的。
线程工作内存 & volatile
Java 内存模型规定在多线程情况下,线程操作主内存变量,需要通过线程独有的工作内 存拷贝主内存变量副本来进行。
一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后,保证了不同 线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他 线程来说是立即可见的。
JVM 垃圾回收原理
JVM 垃圾回收就是将 JVM 堆中的已经不再被使用的对象清理掉,释放宝贵的内存资源。
JVM 通过一种可达性分析算法进行垃圾对象的识别,具体过程是:
从线程栈帧中的局部变量,或者是方法区的静态变量出发,将这些变量引用的对象进行标记,然后看这些被标记的对象是否引用了其他对象,继续进行标记,所有被标记过的对象都是被使用的对象,而那些没有被标记的对象就是可回收的垃圾对象了。
进行完标记以后,JVM 就会对垃圾对象占用的内存进行回收,回收主要有三种方法:
清理:将垃圾对象占据的内存清理掉,其实 JVM 并不会真的将这些垃圾内存进行清理,而 是将这些垃圾对象占用的内存空间标记为空闲,记录在一个空闲列表里,当应用程序需要创 建新对象的时候,就从空闲列表中找一段空闲内存分配给这个新对象。
压缩:从堆空间的头部开始,将存活的对象拷贝放在一段连续的内存空间中,那么其余的空 间就是连续的空闲空间。
复制:将堆空间分成两部分,只在其中一部分创建对象,当这个部分空间用完的时候,将标 记过的可用对象复制到另一个空间中。
JVM 分代垃圾回收
JVM 垃圾回收器算法
G1 垃圾回收内存管理机制
版权声明: 本文为 InfoQ 作者【Calvin】的原创文章。
原文链接:【http://xie.infoq.cn/article/916798ab451c6a1c6262e117b】。未经作者许可,禁止转载。
评论