Skip to content

Commit

Permalink
modify jvm mds
Browse files Browse the repository at this point in the history
  • Loading branch information
h2pl committed Apr 9, 2023
1 parent 0de9813 commit 88e6f70
Show file tree
Hide file tree
Showing 45 changed files with 234 additions and 203 deletions.
4 changes: 2 additions & 2 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,11 @@ todo
* [深入理解JVM虚拟机:JNDI,OSGI,Tomcat类加载器实现](docs/java/jvm/深入理解JVM虚拟机:JNDI,OSGI,Tomcat类加载器实现.md)
* [深入了解JVM虚拟机:Java的编译期优化与运行期优化](docs/java/jvm/深入理解JVM虚拟机:Java的编译期优化与运行期优化.md)
* [深入理解JVM虚拟机:JVM监控工具与诊断实践](docs/java/jvm/深入理解JVM虚拟机:JVM监控工具与诊断实践.md)
* [深入理解JVM虚拟机:JVM常用参数以及调优实践](docs/java/jvm/深入理解JVM虚拟机:JVM常用参数以及调优实践.md)
* [深入理解JVM虚拟机:JVM常用参数以及调优实践](docs/java/jvm/temp/深入理解JVM虚拟机:JVM常用参数以及调优实践.md)
* [深入理解JVM虚拟机:Java内存异常原理与实践](docs/java/jvm/深入理解JVM虚拟机:Java内存异常原理与实践.md)
* [深入理解JVM虚拟机:JVM性能管理神器VisualVM介绍与实战](docs/java/jvm/深入理解JVM虚拟机:JVM性能管理神器VisualVM介绍与实战.md)
* [深入理解JVM虚拟机:再谈四种引用及GC实践](docs/java/jvm/深入理解JVM虚拟机:再谈四种引用及GC实践.md)
* [深入理解JVM虚拟机:GC调优思路与常用工具](docs/java/jvm/深入理解JVM虚拟机:GC调优思路与常用工具.md)
* [深入理解JVM虚拟机:GC调优思路与常用工具](docs/java/jvm/temp/深入理解JVM虚拟机:GC调优思路与常用工具.md)

### Java网络编程

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@

对于虚拟机内核来讲,只要将标志位设为虚拟机状态,则可以直接在CPU上执行大部分的指令,不需要虚拟化软件在中间转述,除非遇到特别敏感的指令,才需要将标志位设为物理机内核态运行,这样大大提高了效率。

所以安装虚拟机的时候,务必要将物理CPU的这个标志位打开,是否打开对于Intel可以查看grep "vmx" /proc/cpuinfo,对于AMD可以查看grep "svm" /proc/cpuinfo
所以安装虚拟机的时候,务必要将物理CPU的这个标志位打开,是否打开对于Intel可以查看grep"vmx" /proc/cpuinfo,对于AMD可以查看grep "svm" /proc/cpuinfo

这叫做硬件辅助虚拟化。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Redis的字典由三个基础的数据结构组成。最底层的单位是哈希
union { void *val; uint64_t u64; int64_t s64; } v; // 指向下个哈希表节点,形成链表
struct dictEntry *next; } dictEntry;
````
实际上哈希表节点就是一个单项列表的节点。保存了一下下一个节点的指针。 key 就是节点的键,v是这个节点的值。这个 v 既可以是一个指针,也可以是一个 `uint64_t`或者 `int64_t` 整数。*next 指向下一个节点。
实际上哈希表节点就是一个单项列表的节点。保存了一下下一个节点的指针。 key 就是节点的键,v是这个节点的值。这个 v 既可以是一个指针,也可以是一个`uint64_t`或者`int64_t`整数。*next 指向下一个节点。

通过一个哈希表的数组把各个节点链接起来:
````
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ typedef struct intset {

* `encoding`: 数据编码,表示intset中的每个数据元素用几个字节来存储。它有三种可能的取值:INTSET_ENC_INT16表示每个元素用2个字节存储,INTSET_ENC_INT32表示每个元素用4个字节存储,INTSET_ENC_INT64表示每个元素用8个字节存储。因此,intset中存储的整数最多只能占用64bit。
* `length`: 表示intset中的元素个数。`encoding``length`两个字段构成了intset的头部(header)。
* `contents`: 是一个柔性数组([flexible array member](https://en.wikipedia.org/wiki/Flexible_array_member)),表示intset的header后面紧跟着数据元素。这个数组的总长度(即总字节数)等于`encoding * length`。柔性数组在Redis的很多数据结构的定义中都出现过(例如[sds](http://zhangtielei.com/posts/blog-redis-sds.html), [quicklist](http://zhangtielei.com/posts/blog-redis-quicklist.html), [skiplist](http://zhangtielei.com/posts/blog-redis-skiplist.html)),用于表达一个偏移量。`contents`需要单独为其分配空间,这部分内存不包含在intset结构当中。
* `contents`: 是一个柔性数组([flexible array member](https://en.wikipedia.org/wiki/Flexible_array_member)),表示intset的header后面紧跟着数据元素。这个数组的总长度(即总字节数)等于`encoding * length`。柔性数组在Redis的很多数据结构的定义中都出现过(例如[sds](http://zhangtielei.com/posts/blog-redis-sds.html),[quicklist](http://zhangtielei.com/posts/blog-redis-quicklist.html),[skiplist](http://zhangtielei.com/posts/blog-redis-skiplist.html)),用于表达一个偏移量。`contents`需要单独为其分配空间,这部分内存不包含在intset结构当中。

其中需要注意的是,intset可能会随着数据的添加而改变它的数据编码:

Expand All @@ -97,7 +97,7 @@ typedef struct intset {

在上图中:

* 新创建的intset只有一个header,总共8个字节。其中`encoding` = 2, `length` = 0。
* 新创建的intset只有一个header,总共8个字节。其中`encoding`= 2,`length`= 0。
* 添加13, 5两个元素之后,因为它们是比较小的整数,都能使用2个字节表示,所以`encoding`不变,值还是2。
* 当添加32768的时候,它不再能用2个字节来表示了(2个字节能表达的数据范围是-2<sup>15</sup>~2<sup>15</sup>-1,而32768等于2<sup>15</sup>,超出范围了),因此`encoding`必须升级到INTSET_ENC_INT32(值为4),即用4个字节表示一个元素。
* 在添加每个元素的过程中,intset始终保持从小到大有序。
Expand Down Expand Up @@ -247,11 +247,11 @@ intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {

* `sadd`用于分别向集合`s1``s2`中添加元素。添加的元素既有数字,也有非数字(”a”和”b”)。
* `sismember`用于判断指定的元素是否在集合内存在。
* `sinter`, `sunion``sdiff`分别用于计算集合的交集、并集和差集。
* `sinter`,`sunion``sdiff`分别用于计算集合的交集、并集和差集。

我们前面提到过,set的底层实现,随着元素类型是否是整型以及添加的元素的数目多少,而有所变化。例如,具体到上述命令的执行过程中,集合`s1`的底层数据结构会发生如下变化:

* 在开始执行完`sadd s1 13 5`之后,由于添加的都是比较小的整数,所以`s1`底层是一个intset,其数据编码`encoding` = 2。
* 在开始执行完`sadd s1 13 5`之后,由于添加的都是比较小的整数,所以`s1`底层是一个intset,其数据编码`encoding`= 2。
* 在执行完`sadd s1 32768 10 100000`之后,`s1`底层仍然是一个intset,但其数据编码`encoding`从2升级到了4。
* 在执行完`sadd s1 a b`之后,由于添加的元素不再是数字,`s1`底层的实现会转成一个dict。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ ziplist的数据结构组成是本文要讨论的重点。实际上,ziplist还
* `<entry>`: 表示真正存放数据的数据项,长度不定。一个数据项(entry)也有它自己的内部结构,这个稍后再解释。
* `<zlend>`: ziplist最后1个字节,是一个结束标记,值固定等于255。

上面的定义中还值得注意的一点是:`<zlbytes>`, `<zltail>`, `<zllen>`既然占据多个字节,那么在存储的时候就有大端(big endian)和小端(little endian)的区别。ziplist采取的是小端模式来存储,这在下面我们介绍具体例子的时候还会再详细解释。
上面的定义中还值得注意的一点是:`<zlbytes>`,`<zltail>`,`<zllen>`既然占据多个字节,那么在存储的时候就有大端(big endian)和小端(little endian)的区别。ziplist采取的是小端模式来存储,这在下面我们介绍具体例子的时候还会再详细解释。

我们再来看一下每一个数据项`<entry>`的构成:

Expand Down Expand Up @@ -207,7 +207,7 @@ static unsigned char *__ziplistInsert(unsigned char *zl, unsigned char *p, unsig

* 这个函数是在指定的位置p插入一段新的数据,待插入数据的地址指针是s,长度为slen。插入后形成一个新的数据项,占据原来p的配置,原来位于p位置的数据项以及后面的所有数据项,需要统一向后移动,给新插入的数据项留出空间。参数p指向的是ziplist中某一个数据项的起始位置,或者在向尾端插入的时候,它指向ziplist的结束标记`<zlend>`
* 函数开始先计算出待插入位置前一个数据项的长度`prevlen`。这个长度要存入新插入的数据项的`<prevrawlen>`字段。
* 然后计算当前数据项占用的总字节数`reqlen`,它包含三部分:`<prevrawlen>`, `<len>`和真正的数据。其中的数据部分会通过调用`zipTryEncoding`先来尝试转成整数。
* 然后计算当前数据项占用的总字节数`reqlen`,它包含三部分:`<prevrawlen>`,`<len>`和真正的数据。其中的数据部分会通过调用`zipTryEncoding`先来尝试转成整数。
* 由于插入导致的ziplist对于内存的新增需求,除了待插入数据项占用的`reqlen`之外,还要考虑原来p位置的数据项(现在要排在待插入数据项之后)的`<prevrawlen>`字段的变化。本来它保存的是前一项的总长度,现在变成了保存当前插入的数据项的总长度。这样它的`<prevrawlen>`字段本身需要的存储空间也可能发生变化,这个变化可能是变大也可能是变小。这个变化了多少的值`nextdiff`,是调用`zipPrevLenByteDiff`计算出来的。如果变大了,`nextdiff`是正值,否则是负值。
* 现在很容易算出来插入后新的ziplist需要多少字节了,然后调用`ziplistResize`来重新调整大小。ziplistResize的实现里会调用allocator的`zrealloc`,它有可能会造成数据拷贝。
* 现在额外的空间有了,接下来就是将原来p位置的数据项以及后面的所有数据都向后挪动,并为它设置新的`<prevrawlen>`字段。此外,还可能需要调整ziplist的`<zltail>`字段。
Expand All @@ -223,7 +223,7 @@ hash是Redis中可以用来存储一个对象结构的比较理想的数据类

实际上,hash随着数据的增大,其底层数据结构的实现是会发生变化的,当然存储效率也就不同。在field比较少,各个value值也比较小的时候,hash采用ziplist来实现;而随着field增多和value值增大,hash可能会变成dict来实现。当hash底层变成dict来实现的时候,它的存储效率就没法跟那些序列化方式相比了。

当我们为某个key第一次执行 `hset key field value` 命令的时候,Redis会创建一个hash结构,这个新创建的hash底层就是一个ziplist。
当我们为某个key第一次执行`hset key field value`命令的时候,Redis会创建一个hash结构,这个新创建的hash底层就是一个ziplist。



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ release(){
tryLock(){ SETNX Key 1 Seconds}release(){ DELETE Key}
```

Redis 2.6.12版本后SETNX增加过期时间参数,这样就解决了两条命令无法保证原子性的问题。但是设想下面一个场景: 
Redis 2.6.12版本后SETNX增加过期时间参数,这样就解决了两条命令无法保证原子性的问题。但是设想下面一个场景:

1\. C1成功获取到了锁,之后C1因为GC进入等待或者未知原因导致任务执行过长,最后在锁失效前C1没有主动释放锁

Expand Down Expand Up @@ -167,15 +167,15 @@ release(){
```

Redis 2.6.12后[SET](https://redis.io/commands/set)同样提供了一个NX参数,等同于SETNX命令,官方文档上提醒后面的版本有可能去掉[SETNX](https://redis.io/commands/setnx), [SETEX](https://redis.io/commands/setex), [PSETEX](https://redis.io/commands/psetex),并用SET命令代替,另外一个优化是使用一个自增的唯一UniqId代替时间戳来规避V3.0提到的时钟问题。
Redis 2.6.12后[SET](https://redis.io/commands/set)同样提供了一个NX参数,等同于SETNX命令,官方文档上提醒后面的版本有可能去掉[SETNX](https://redis.io/commands/setnx),[SETEX](https://redis.io/commands/setex),[PSETEX](https://redis.io/commands/psetex),并用SET命令代替,另外一个优化是使用一个自增的唯一UniqId代替时间戳来规避V3.0提到的时钟问题。

这个方案是目前最优的分布式锁方案,但是如果在Redis集群环境下依然存在问题:

由于Redis集群数据同步为异步,假设在Master节点获取到锁后未完成数据同步情况下Master节点crash,此时在新的Master节点依然可以获取锁,所以多个Client同时获取到了锁

### 分布式Redis锁:Redlock

V3.1的版本仅在单实例的场景下是安全的,针对如何实现分布式Redis的锁,国外的分布式专家有过激烈的讨论, antirez提出了分布式锁算法Redlock,在[distlock](https://redis.io/topics/distlock)话题下可以看到对Redlock的详细说明,下面是Redlock算法的一个中文说明(引用)
V3.1的版本仅在单实例的场景下是安全的,针对如何实现分布式Redis的锁,国外的分布式专家有过激烈的讨论,antirez提出了分布式锁算法Redlock,在[distlock](https://redis.io/topics/distlock)话题下可以看到对Redlock的详细说明,下面是Redlock算法的一个中文说明(引用)

假设有N个独立的Redis节点

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
```


Redis给我们提供了两种不同方式的持久化方法:快照(Snapshotting) 和 只追加文件(append-only-file)。
Redis给我们提供了两种不同方式的持久化方法:快照(Snapshotting)和只追加文件(append-only-file)。

(1)名词简介

Expand Down Expand Up @@ -71,17 +71,17 @@ appendonly no #是否使用AOF持久化appendfsync everysec #多久执行一

一、创建快照的方式:

(1)客户端通过向Redis发送`BGSAVE` 命令来创建快照。
(1)客户端通过向Redis发送`BGSAVE`命令来创建快照。

使用BGSAVE的时候,Redis会调用fork来创建一个子进程,然后子进程负责将快照写到硬盘中,而父进程则继续处理命令请求。

使用场景:

如果用户使用了save设置,例如:`save 60 1000` ,那么从Redis最近一次创建快照之后开始计算,当“60秒之内有1000次写入操作”这个条件满足的时候,Redis就会自动触发BGSAVE命令。
如果用户使用了save设置,例如:`save 60 1000`,那么从Redis最近一次创建快照之后开始计算,当“60秒之内有1000次写入操作”这个条件满足的时候,Redis就会自动触发BGSAVE命令。

如果用户使用了多个save设置,那么当任意一个save配置满足条件的时候,Redis都会触发一次BGSAVE命令。

(2)客户端通过向Redis发送`SAVE` 命令来创建快照。
(2)客户端通过向Redis发送`SAVE`命令来创建快照。

接收到SAVE命令的Redis服务器在快照创建完毕之前将不再响应任何其他命令的请求。SAVE命令并不常用,我们通常只在没有足够的内存去执行BGSAVE命令的时候才会使用SAVE命令,或者即使等待持久化操作执行完毕也无所谓的情况下,才会使用这个命令;

Expand Down Expand Up @@ -149,15 +149,15 @@ Redis以每秒同步一次AOF文件的性能和不使用任何持久化特性时

三、重写/压缩AOF文件

随着数据量的增大,AOF的文件可能会很大,这样在每次进行数据恢复的时候就会进行很长的时间,为了解决日益增大的AOF文件,用户可以向Redis发送`BGREWRITEAOF` 命令,这个命令会通过移除AOF文件中的冗余命令来重写AOF文件,是AOF文件的体检变得尽可能的小。
随着数据量的增大,AOF的文件可能会很大,这样在每次进行数据恢复的时候就会进行很长的时间,为了解决日益增大的AOF文件,用户可以向Redis发送`BGREWRITEAOF`命令,这个命令会通过移除AOF文件中的冗余命令来重写AOF文件,是AOF文件的体检变得尽可能的小。

BGREWRITEAOF的工作原理和BGSAVE的原理很像:Redis会创建一个子进程,然后由子进程负责对AOF文件的重写操作。

因为AOF文件重写的时候汇创建子进程,所以快照持久化因为创建子进程而导致的性能和内存占用问题同样会出现在AOF文件重写的 时候。

四、触发重写/压缩AOF文件条件设定

AOF通过设置`auto-aof-rewrite-percentage` 和 `auto-aof-rewrite-min-size` 选项来自动执行BGREWRITEAOF。
AOF通过设置`auto-aof-rewrite-percentage``auto-aof-rewrite-min-size`选项来自动执行BGREWRITEAOF。

其具体含义,通过实例可以看出,如下配置:

Expand All @@ -167,7 +167,7 @@ auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb

表示当前AOF的文件体积大于64MB,并且AOF文件的体积比上一次重写之后的体积变大了至少一倍(100%)的时候,Redis将执行重写BGREWRITEAOF命令。

如果AOF重写执行的过于频繁的话,可以将`auto-aof-rewrite-percentage` 选项的值设置为100以上,这种最偶发就可以让Redis在AOF文件的体积变得更大之后才执行重写操作,不过,这也使得在进行数据恢复的时候执行的时间变得更加长一些。
如果AOF重写执行的过于频繁的话,可以将`auto-aof-rewrite-percentage`选项的值设置为100以上,这种最偶发就可以让Redis在AOF文件的体积变得更大之后才执行重写操作,不过,这也使得在进行数据恢复的时候执行的时间变得更加长一些。

## 验证快照文件和AOF文件

Expand All @@ -179,7 +179,7 @@ redis-check-aofredis-check-dump

他们可以再系统发生故障的时候,检查快照和AOF文件的状态,并对有需要的情况对文件进行修复。

如果用户在运行redis-check-aof命令的时候,指定了`--fix` 参数,那么程序将对AOF文件进行修复。
如果用户在运行redis-check-aof命令的时候,指定了`--fix`参数,那么程序将对AOF文件进行修复。

程序修复AOF文件的方法很简单:他会扫描给定的AOF文件,寻找不正确或者不完整的命令,当发现第一个出现错误命令的时候,程序会删除出错命令以及出错命令之后的所有命令,只保留那些位于出错命令之前的正确命令。大部分情况,被删除的都是AOF文件末尾的不完整的写命令。

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,3 @@
# 目录

* [目录](#目录)
* [重新学习MySQL数据库10:MySQL里的那些日志们](#重新学习mysql数据库10:mysql里的那些日志们)
* [1.MySQL日志文件系统的组成](#1mysql日志文件系统的组成)
* [2.错误日志](#2错误日志)
* [3.InnoDB中的日志](#3innodb中的日志)
* [4- 慢查询日志](#4--慢查询日志)
* [5.二进制日志](#5二进制日志)
* [总结](#总结)


# 目录
* [重新学习MySQL数据库10:MySQL里的那些日志们](#重新学习mysql数据库10:mysql里的那些日志们)
* [1.MySQL日志文件系统的组成](#1mysql日志文件系统的组成)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -905,4 +905,4 @@ title的选择性不足0.0001(精确值为0.00001579),所以实在没有

[5] Codd, E. F. (1970). "A relational model of data for large shared data banks". Communications of the ACM, , Vol. 13, No. 6, pp. 377-387

[6] MySQL5.1参考手册 - [http://dev.mysql.com/doc/refman/5.1/zh/index.html](http://dev.mysql.com/doc/refman/5.1/zh/index.html "http://dev.mysql.com/doc/refman/5.1/zh/index.html")
[6] MySQL5.1参考手册 -[http://dev.mysql.com/doc/refman/5.1/zh/index.html](http://dev.mysql.com/doc/refman/5.1/zh/index.html "http://dev.mysql.com/doc/refman/5.1/zh/index.html")
Loading

0 comments on commit 88e6f70

Please sign in to comment.