沧海一粟

天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。

0%

redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;
binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。

Redo log不是记录数据页“更新之后的状态”,而是记录这个页 “做了什么改动”。
Binlog有两种模式,statement 格式的话是记sql语句, row格式会记录行的内容,记两条,更新前和更新后都有。(row格式会导致日志变大)

binlog是可以关的,你如果有权限,可以set sql_log_bin=0关掉本线程的binlog日志。 所以只依赖binlog来恢复就靠不住。

innodb B+树主键索引的叶子节点存的是什么,存的是页
B+树的叶子节点是page (页),一个页里面可以存多个行

References

[1] 02 | 日志系统:一条SQL更新语句是如何执行的?
[2] 详细分析MySQL事务日志(redo log和undo log)
[3] 详细分析MySQL的日志(一)
[4] dev.mysql.com/doc/refman/5.7/en/server-logs.html
[5] dev.mysql.com/doc/refman/5.7/en/innodb-redo-log.html

什么是分布式ID

在我们系统数据量不大的时候,单库单表完全可以支撑现有系统,数据再大一点搞个MySQL主从同步读写分离也能对付。
但随着数据日渐增长,主从同步也扛不住了,就需要对数据库进行分库分表,但分库分表后需要有一个唯一ID来标识一条数据,数据库的自增ID显然不能满足需求;特别一点的如订单、优惠券也都需要有唯一ID做标识。此时一个能够生成全局唯一ID的系统是非常必要的。那么这个全局唯一ID就叫分布式ID。

分布式ID需要满足那些条件
全局唯一:必须保证ID是全局性唯一的,基本要求
高性能:高可用低延时,ID生成响应要块,否则反倒会成为瓶颈
高可用:100%的可用性是骗人的,但是也要无限接近于100%的可用性
好接入:要秉着拿来即用的设计原则,在系统设计和实现上要尽可能的简单
趋势递增:最好趋势递增,这个要求就得看具体场景了,一般不严格要求

有哪些生成方式

程序自增ID
关系型数据库自增ID
Redis

UUID

数据库多主模式
号段模式

雪花算法(SnowFlake)
滴滴出品(TinyID)
百度 (Uidgenerator)
美团(Leaf)

阅读全文 »

Java虚拟机是Java平台的基石。它是技术的组成部分,负责硬件和操作系统的独立性,编译代码的小巧大小以及保护用户免受恶意程序侵害的能力。

Java虚拟机是抽象的计算机。像真正的计算机一样,它具有指令集,并在运行时操作各种内存区域。使用虚拟机实现编程语言是相当普遍的。最知名的虚拟机可能是UCSD Pascal的P代码计算机。

Java虚拟机对Java编程语言一无所知,仅对特定的二进制格式(class文件格式)一无所知。一个class文件包含的Java虚拟机指令(或字节码)和符号表,以及其它辅助信息。

为了安全起见,Java虚拟机对class文件中的代码施加了严格的语法和结构约束。但是,classJava虚拟机可以托管任何可以用有效文件表示的功能的语言。受通用的,独立于机器的平台的吸引,其他语言的实现者可以将Java虚拟机用作其语言的交付工具。

References

https://docs.oracle.com/javase/specs/index.html
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
https://github.com/deephacks/awesome-jvm
https://github.com/graalvm/graal-jvmci-8
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javap.html

网络通信中,最底层的就是内核中的网络 I/O 模型了。

随着技术的发展,操作系统内核的网络模型衍生出了五种 I/O 模型,《UNIX 网络编程》一书将这五种 I/O 模型分为 阻塞式 I/O非阻塞式 I/OI/O 复用信号驱动式 I/O异步 I/O。每一种 I/O 模型的出现,都是基于前一种 I/O 模型的优化升级。

阅读全文 »

代码工具:jmh JMH是一个Java工具,用于构建、运行和分析nano/micro/mili/macro基准,这些基准是用Java和其他针对JVM的语言编写的。
Code Tools: jmh JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targetting the JVM.

JMH解决了什么问题

JMH怎么用

  1. 添加对应jar包
  2. 根据实际情况配置参数
  3. 编写代码运行

添加依赖

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.23</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.23</version>
</dependency>
阅读全文 »

Java 性能调优不像是学一门编程语言,无法通过直线式的思维来掌握和应用,它对于工程师的技术广度和深度都有着较高的要求。
互联网时代,一个简单的系统就囊括了应用程序、数据库、容器、操作系统、网络等技术,线上一旦出现性能问题,就可能要你协调多方面组件去进行优化,这就是技术广度;而很多性能问题呢,又隐藏得很深,可能因为一个小小的代码,也可能因为线程池的类型选择错误…可归根结底考验的还是我们对这项技术的了解程度,这就是技术深度。

我们调优的对象不是单一的应用服务,而是错综复杂的系统。应用服务的性能可能与操作系统、网络、数据库等组件相关,所以我们需要储备计算机组成原理、操作系统、网络协议以及数据库等基础知识。具体的性能问题往往还与传输、计算、存储数据等相关,那我们还需要储备数据结构、算法以及数学等基础知识。

如果你们公司做的是 12306 网站,不做系统性能优化就上线,试试看会是什么情况。

一款线上产品如果没有经过性能测试,那它就好比是一颗定时炸弹,你不知道它什么时候会出现问题,你也不清楚它能承受的极限在哪儿。

好的系统性能调优不仅仅可以提高系统的性能,还能为公司节省资源

阅读全文 »

(1) Actor

Actor模型:面向对象原生的并发模型

Actor模型是高性能网络中处理并行任务的一种方法,解决并发问题的利器

Actor模型本质上是一种计算模型,基本的计算单元称为 Actor,在 Actor 模型里,一切都是 Actor,所有的计算都是在 Actor 中执行的,并且 Actor 之间是完全隔离的,不会共享任何变量。

Actor模型解决了 传统编程假设与现代多线程、多CPU架构的现实之间的不匹配问题。

  1. 消息传递的使用避免了锁和阻塞。
  2. Actor能够优化地处理错误情况。

Java 语言本身并不支持 Actor 模型,所以如果你想在 Java 语言里使用 Actor 模型,就需要借助第三方类库,目前能完备地支持 Actor 模型而且比较成熟的类库就是 Akka。

阅读全文 »

Akka is a toolkit and runtime for building highly concurrent, distributed, and fault-tolerant event-driven applications on the JVM. Akka can be used with both Java and Scala. This guide introduces Akka by describing the Java version of the Hello World example. If you prefer to use Akka with Scala, switch to the Akka Quickstart with Scala guide.

Actors are the unit of execution in Akka. The Actor model is an abstraction that makes it easier to write correct concurrent, parallel and distributed systems. The Hello World example illustrates Akka basics. Within 30 minutes, you should be able to download and run the example and use this guide to understand how the example is constructed. This will get your feet wet, and hopefully inspire you to dive deeper into the wonderful sea of Akka!

解决了什么问题

Akka是Actor模型的一种实现
Actor模型解决了 传统编程假设与现代多线程、多CPU架构的现实之间的不匹配问题。

  1. 消息传递的使用避免了锁和阻塞。
  2. Actor能够优化地处理错误情况。

Usage of message passing avoids locking and blocking.
Actors handle error situations gracefully

OOP在构建苛刻需求的分布式系统会遇到的问题

  1. 并发情况下封装导致效率问题和死锁问题。
  2. 并发时共享内存失效(CPU缓存失效导致)导致效率低的问题。
  3. 调用栈导致的后台线程在异常时的通信问题以及后续问题。
  1. The challenge of encapsulation
  2. The illusion of shared memory on modern computer architectures
  3. The illusion of a call stack
阅读全文 »

1
2
java -XX:+PrintFlagsFinal -version | grep HeapSize  # 查看堆内存配置的默认值
jmap -heap pid

常用的JVM配置

-client The client VM is tuned for reducing start-up time and memory footprint. Invoke it by using the -client JVM command-line option.
-server The server VM is designed for maximum program execution speed. Invoke it by using the -server JVM command-line option.

-Dfile.encoding=UTF-8 文件使用UTF-8编码
-Xms2g = -XX:InitialHeapSize=2g = -XX:MinHeapSize=2g 设置初始堆大小为2G
-Xmx2g = -XX:MaxHeapSize=2g 设置最大堆大小为2G
-Xmn1g 设置新生代大小为1G

-XX:MinHeapFreeRatio=
-XX:MaxHeapFreeRatio=
-XX:NewRatio
-XX:NewSize
-XX:MaxNewSize
-XX:+AggressiveHeap



-Xss256k 设置每个线程的堆栈大小,和栈的深度和容器创建的最大线程数有关

-XX:LargePageSizeInBytes=128m 内存页的大小不可设置过大, 会影响Perm的大小
-XX:MaxDirectMemorySize=536870912

-XX:+UseFastAccessorMethods 原始类型的快速优化

-XX:PetenureSizeThreshold= 设置直接被分配到老年代的最大阀值

-XX:+TraceClassLoading 打印类加载信息

配置垃圾回收算法

-XX:+UseG1GC 使用G1垃圾收集器

gc配置

-XX:+PrintGC 用于垃圾收集时的信息打印 -verbosegc (which is equivalent to -XX:+PrintGC) sets the detail level of the log to fine.
-XX:+PrintGCDetails 打印GC详细信息 ets the detail level to finer.
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式 245469.1 )
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+HeapDumpBeforeFullGC FullGC前保存堆栈信息 测试环境使用,线上最好别用,小心磁盘被打满
-XX:+HeapDumpAfterFullGC FullGC后保存堆栈信息 测试环境使用,线上最好别用,小心磁盘被打满

-XX:+DisableExplicitGC 禁止代码中显示调用GC
-Xloggc:filename gc日志保存到指定文件 把filename替换成 /home/admin/gc.log

-XX:+HeapDumpOnOutOfMemoryError OOM后保存堆栈信息
-XX:-OmitStackTraceInFastThrow 当大量抛出同样的异常的后,后面的异常输出将不打印堆栈
-XX:ErrorFile=logs/hs_err_pid%p.log

JIT config

JVM 的 JIT 机制的存在,如果某个函数被调用多次之后,JVM会尝试将其编译为机器码,从而提高执行速度。

在 Java8 之前,HotSpot 集成了两个 JIT,用 C1 和 C2 来完成 JVM 中的即时编译。
到了 Java9,AOT 编译器被引入。AOT 是在程序运行前进行的静态编译,这样就可以避免运行时的编译消耗和内存消耗,且 .class 文件通过 AOT 编译器是可以编译成 .so 的二进制文件的。
Java10,一个新的 JIT 编译器 Graal 被引入。Graal 是一个以 Java 为主要编程语言、面向 Java bytecode 的编译器。与用 C++ 实现的 C1 和 C2 相比,它的模块化更加明显,也更容易维护。Graal 既可以作为动态编译器,在运行时编译热点方法;也可以作为静态编译器,实现 AOT 编译。

1
2
#CompileThreshold must be between 0 and 268435455
-XX:CompileThreshold=128

方法调用计数器:用于统计方法被调用的次数
方法调用计数器的默认阈值在 C1 模式下是 1500 次,在 C2 模式在是 10000 次,可通过 -XX:CompileThreshold 来设定;而在分层编译的情况下,-XX:CompileThreshold 指定的阈值将失效,此时将会根据当前待编译的方法数以及编译线程数来动态调整。

1
-XX:OnStackReplacePercentage=10000

回边计数器:用于统计一个方法中循环体代码执行的次数,

在字节码中遇到控制流向后跳转的指令称为“回边”(Back Edge),该值用于计算是否触发 C1 编译的阈值,在不开启分层编译的情况下,C1 默认为 13995,C2 默认为 10700,可通过 -XX: OnStackReplacePercentage=N 来设置;而在分层编译的情况下,-XX: OnStackReplacePercentage 指定的阈值同样会失效,此时将根据当前待编译的方法数以及编译线程数来动态调整。

在一些循环周期比较长的代码段中,当循环达到回边计数器阈值时,JVM 会认为这段是热点代码,JIT 编译器就会将这段代码编译成机器语言并缓存,在该循环时间段内,会直接将执行代码替换,执行缓存的机器语言。

1
-XX:CICompilerCount=2

经常执行的方法,默认情况下,方法体大小小于 325 字节的都会进行内联,我们可以通过 -XX:MaxFreqInlineSize=N 来设置大小值;
不是经常执行的方法,默认情况下,方法大小小于 35 字节才会进行内联,我们也可以通过 -XX:MaxInlineSize=N 来重置大小值

1
2
3
-XX:+PrintCompilation //在控制台打印编译过程信息
-XX:+UnlockDiagnosticVMOptions //解锁对JVM进行诊断的选项参数。默认是关闭的,开启后支持一些特定参数对JVM进行诊断
-XX:+PrintInlining //将内联方法打印出来
阅读全文 »

多线程设计模式是前人解决并发问题的经验总结,当我们试图解决一个并发问题时,首选方案往往是使用匹配的设计模式,这样能避免走弯路。
大家都熟悉设计模式,所以使用设计模式还能提升方案和代码的可理解性。

避免共享的设计模式
Immutability 模式Copy-on-Write 模式线程本地存储模式 本质上都是为了避免共享,只是实现手段不同而已。

多线程版本IF的设计模式
Guarded Suspension 模式Balking 模式 都可以简单地理解为“多线程版本的 if”,但它们的区别在于前者会等待 if 条件变为真,而后者则不需要等待。

三种最简单的分工模式
Thread-Per-Message模式Worker Thread 模式生产者-消费者模式 是三种最简单实用的多线程分工方法

(1) Immutability Pattern 不变性模式

解决并发问题,其实最简单的办法就是让共享变量只有读操作,而没有写操作。

不变性(Immutability)模式。所谓不变性,简单来讲,就是对象一旦被创建之后,状态就不再发生变化。换句话说,就是变量一旦被赋值,就不允许修改了(没有写操作);没有修改操作,也就是保持了不变性。

Java SDK 里很多类都具备不可变性。例如经常用到的 String 和 Long、Integer、Double 等基础类型的包装类都具备不可变性,这些对象的线程安全性都是靠不可变性来保证的。仔细翻看这些类的声明、属性和方法,你会发现它们都严格遵守不可变类的三点要求:类和属性都是 final 的,所有方法均是只读的。

Java所有的基础类型的包装类都不适合做锁,因为它们内部用到了享元模式,这会导致看上去私有的锁,其实是共有的。

使用 Immutability 模式的注意事项在使用 Immutability 模式的时候,需要注意以下两点:

  1. 对象的所有属性都是 final 的,并不能保证不可变性;
  2. 不可变对象也需要正确发布。

在使用 Immutability 模式的时候一定要确认保持不变性的边界在哪里,是否要求属性对象也具备不可变性。
Foo对象是不变的,但是Foo对象的属性是可以变化的

1
2
3
4
5
6
7
8
9
10
class Foo{
int age=0;
int name="abc";
}
final class Bar {
final Foo foo;
void setAge(int a){
foo.age=a;
}
}

阅读全文 »