MySQL 的锁机制
并发事务访问相同记录的情况大致可以分为三种:读读、读写和写写。读读的情况并不会产生并发问题,真正会产生问题的是并发的读写或者写写。并发的写写可能会产生脏写和丢失更新的问题,由于这类问题比较严重,数据库会通过加锁的方式阻塞并发的修改,因此在任何隔离级别下数据库都不会出现脏写和丢失更新的情况,但是在应用层面,还是有可能出现逻辑意义上的丢失更新。
并发事务访问相同记录的情况大致可以分为三种:读读、读写和写写。读读的情况并不会产生并发问题,真正会产生问题的是并发的读写或者写写。并发的写写可能会产生脏写和丢失更新的问题,由于这类问题比较严重,数据库会通过加锁的方式阻塞并发的修改,因此在任何隔离级别下数据库都不会出现脏写和丢失更新的情况,但是在应用层面,还是有可能出现逻辑意义上的丢失更新。
对于使用 InnoDB 存储引擎的数据库表来说,它们聚集索引的行记录中都包含两个必要的隐藏列:DB_TRX_ID(trx_id)和 DB_ROLL_PTR(roll_pointer)。每次事务对某条记录进行修改时,都会把该事务的事务 ID 赋值给 trx_id 隐藏列,同时还会把该条记录的旧版本写入到 undo log 中,而这个 roll_pointer 就相当于一个指针,通过它可以找到该记录修改前的信息。
MySQL 的隔离性是通过锁机制来保证的,而原子性、一致性和持久性则是通过数据库的预写式日志(Write-Ahead Logging,WAL),具体来说是 redo log 和 undo log 来保证的。
一般情况下,Java 源代码需要经过前端编译器(比如 javac)编译成 class 文件,然后再通过解释器解释执行。但是对于一些热点代码(频繁调用的方法或代码块),虚拟机为了提高它们的执行效率,会在运行期将这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器就是即时编译器(Just In Time Compiler,JIT),比如 HotSpot 中的 C1、C2 编译器。
根据 InnoDB 的行记录格式和数据页结构,我们已经知道了数据页之间组成了一个双向链表,而每个数据页中的行记录又会按照主键值从小到大的顺序组成一个单向链表。在某一页中查找指定主键值的记录可以根据页目录使用二分法快速定位到行记录所在分组的槽,然后遍历该分组的行记录即可。
InnoDB 有很多页类型,数据页的页类型为 B-tree Node,存放的是表中行的实际数据。InnoDB 的数据页由 7 部分组成,其中文件头(File Header)、页头(Page Header)和文件尾(File Trailer)的大小是固定的,分别为 38、56 和 8 个字节,这些空间用来标记该页的一些信息。行记录(User Records)、Free Space(空闲空间)和页目录(Page Directory)的大小是动态的,这些空间为实际的行记录存储空间。
InnoDB 中最小的存储单位为页,默认每页的大小为 16 KB。页中的数据是按行进行存放的,每页中存放的行记录最少为 2 行,最多为 16 KB / 2 - 200
行,也就是 7992 行。
我们知道,Session 是由服务端生成的,在生成 Session 的同时还会生成一个唯一的 Session Id。一般的 Web 应用会在服务器内存当中开辟一部分内存用来存储 Session,同时这个 Session Id 会以 Cookie 的形式发送给浏览器,浏览器会将它保存到本地磁盘当中,在下次请求同一个域时会一并提交。然而当系统架构升级为分布式架构或者微服务架构时,作为流量入口的网关会通过负载均衡策略将用户的请求转发到不同的 Web 容器当中,这就可能会出现用户登录系统后,再次发送请求则被告知没有登录的情况。此时就需要某种机制来确保所有的 Web 容器能够共享用户的 Session 信息。
像 Tomcat、Jetty、Undertow 等这些 Web 容器(也可以看作是 Servlet 容器),它们既可以运行动态程序(Servlet/JSP),又可以处理静态资源,但是一般情况下,它们处理静态资源的能力与 Apache、Nginx 等这些 HTTP Server 相比要弱很多,因此在很多系统中会采用动静分离的设计,即使用 HTTP Server 作为入口网关,所有的静态资源由 HTTP Server 直接处理,而其他的动态内容则转发给 Servlet 容器。
Dubbo 良好的扩展性得益于它的 SPI 机制,在 Dubbo 中,几乎所有的功能组件都是基于 SPI 机制实现的。由于 Java SPI 机制存在一些问题,且无法满足 Dubbo 的需求,于是 Dubbo SPI 就在 Java SPI 的思想上做了改进,形成了一套自己的配置规范和特性。