init
41
.gitignore
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
*#
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
*.sw?
|
||||
*~
|
||||
.#*
|
||||
.*.md.html
|
||||
.DS_Store
|
||||
.classpath
|
||||
.factorypath
|
||||
.gradle
|
||||
.idea
|
||||
.metadata
|
||||
.project
|
||||
.recommenders
|
||||
.settings
|
||||
.springBeans
|
||||
/build
|
||||
MANIFEST.MF
|
||||
_site/
|
||||
activemq-data
|
||||
bin
|
||||
build
|
||||
build.log
|
||||
dependency-reduced-pom.xml
|
||||
dump.rdb
|
||||
interpolated*.xml
|
||||
lib/
|
||||
manifest.yml
|
||||
overridedb.*
|
||||
settings.xml
|
||||
target
|
||||
classes
|
||||
out
|
||||
logs
|
||||
transaction-logs
|
||||
.flattened-pom.xml
|
||||
secrets.yml
|
||||
.gradletasknamecache
|
||||
.sts4-cache
|
163
README.md
Normal file
@ -0,0 +1,163 @@
|
||||
# :memo:全栈工程师笔记
|
||||
|
||||
|
||||
|
||||
| ☕️ | 💻 | 💾 | 📟 | :globe_with_meridians: | 🌳 | 🚀 | 📊 | :cd: | :books: |
|
||||
| :----: | :----: | :----: | :----: | :----: | :----: | :----: | :----: | :----: | :----: |
|
||||
| <a href="#coffee-java">Java</a> | <a href="#-前端">前端</a> | <a href="#-数据库">数据库</a> | <a href="#-操作系统">操作系统</a> | <a href="#-网络通信">网络通信</a> | <a href="#-Spring">Spring</a> | <a href="#-分布式">分布式</a> | <a href="#-算法和数据结构">算法和数据结构</a> | <a href="#cd-大数据">大数据</a> | <a href="#books-读书笔记">读书笔记</a> |
|
||||
|
||||
|
||||
|
||||
# :coffee: Java
|
||||
|
||||
1. Java设计模式
|
||||
|
||||
2. Java数据结构
|
||||
|
||||
3. 深入理解Java虚拟机
|
||||
|
||||
4. 并发编程框架disruptor
|
||||
|
||||
5. Java网络编程
|
||||
6. tomcat 调优
|
||||
|
||||
|
||||
|
||||
## 💻 前端
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
## 💾 数据库
|
||||
|
||||
#### 1. Oracle
|
||||
|
||||
|
||||
|
||||
#### 2. MySQL
|
||||
|
||||
+ MySQL 主从复制及读写分离
|
||||
+ MySQL+keepalived 高可用实践方案
|
||||
+ MySQL 分库分表
|
||||
+ 数据库中间件 Mycat
|
||||
|
||||
#### 3. Redis
|
||||
|
||||
+ redis 简介及基本数据结构
|
||||
+ redis 管道模式详解
|
||||
+ redis AOF 和 RDB 持久化策略原理
|
||||
+ redis 哨兵模式
|
||||
+ reids 集群模式
|
||||
+ 缓存击穿、缓存雪崩的解决方案
|
||||
+ redis 管理客户端cachecloud
|
||||
|
||||
#### 4.MongoDB
|
||||
|
||||
+ MongoDB 简介及基本原理
|
||||
+ MongoDB数据类型分析
|
||||
+ MongoDB 聚合、索引及基本执行命令
|
||||
+ MongoDB数据分片、转存及恢复策略
|
||||
|
||||
|
||||
|
||||
## 📟 操作系统
|
||||
|
||||
linux 核心概念、常用命令
|
||||
|
||||
|
||||
|
||||
## 🌐 网络通信
|
||||
|
||||
1. IO 基本概念、NIO、AIO、BIO 深入分析
|
||||
2. 高性能NIO框架Netty
|
||||
|
||||
|
||||
|
||||
## 🌳 Spring
|
||||
|
||||
#### 1.spring 基础
|
||||
|
||||
+ AOP
|
||||
+ IOC
|
||||
+ Spring事务机制、事务的传播与监控
|
||||
+ ......
|
||||
|
||||
#### 2.spring Boot
|
||||
|
||||
+ spring 自动装配原理
|
||||
+ 理解SpringApplication
|
||||
+ Web MVC REST
|
||||
+ WebFlux 核心
|
||||
+ ......
|
||||
|
||||
|
||||
|
||||
## 🚀 分布式
|
||||
|
||||
#### 1. Zookeeper
|
||||
|
||||
+ Zookeeper 简介及原理介绍
|
||||
|
||||
+ Zookeeper 集群搭建
|
||||
+ Zookeeper 分布式锁实现方案
|
||||
+ Zookeeper 集群升级、迁移
|
||||
+ 深入分析 Zookeeper Zab协议及选举机制
|
||||
|
||||
#### 2. Dubbo
|
||||
|
||||
+ Dubbo 管理中心及监控平台安装部署
|
||||
+ Dubbo 负载均衡和服务降级
|
||||
|
||||
#### 3. Spring Cloud
|
||||
|
||||
- Eureka 服务的注册和发现
|
||||
- Eureka 高可用集群搭建
|
||||
- Ribbon 客户端负载均衡 RestTemplate 服务远程调用
|
||||
- OpenFeign 声明式服务调用、服务容错处理
|
||||
- Hystix 服务容错保护、hystrix dashboard 断路器监控、Turbine 断路器聚合监控
|
||||
- Zuul 网关服务
|
||||
- Sleuth + Zipkin 服务链路追踪
|
||||
- Config 分布式配置中心 、集成Bus消息总线实现配置热更新
|
||||
|
||||
#### 4. 消息中间件:Kafka
|
||||
|
||||
+ Kafka 简介及消息处理过程分析
|
||||
|
||||
+ 基于Zookeeper搭建Kafka高可用集群
|
||||
+ Kafka 副本机制以及选举原理剖析
|
||||
|
||||
#### 5. 消息中间件:RabbitMQ
|
||||
|
||||
+ RabbitMQ 简介及消息处理过程分析
|
||||
+ RabbitMQ 消息确认机制
|
||||
+ RabbitMQ 如何保证消息的可靠性投递和防止重复消费
|
||||
|
||||
#### 6. Nginx
|
||||
|
||||
+ Nginx反向代理及负载均衡服务配置实战
|
||||
+ 利用keeplived+Nginx实现Nginx高可用方案
|
||||
+ Nginx动静分离实战
|
||||
|
||||
#### 7. Docker
|
||||
|
||||
+ Docker 简介及基本概念
|
||||
+ Docker常用命令
|
||||
+ kubernetes 简介及集群搭建
|
||||
|
||||
#### 8.分布式解决方案
|
||||
|
||||
+ 全局id生成方案
|
||||
+ 分布式session解决方案
|
||||
+ 分布式事务解决方案实战
|
||||
+ 分布式锁解决方案
|
||||
|
||||
|
||||
|
||||
## 📊 算法和数据结构
|
||||
|
||||
#### 1. 数据结构
|
||||
|
||||
数组、栈、队列、链表、二分搜索树、集合、映射、优先队列、堆、线段树、Trie、并查集、AVL、红黑树、哈希表
|
||||
|
||||
#### 2. 算法
|
193
notes/Java单例设计模式详解.md
Normal file
@ -0,0 +1,193 @@
|
||||
# Java 单例设计模式详解
|
||||
|
||||
参考自慕课网课程:[Java设计模式精讲](https://coding.imooc.com/class/270.html):star::star::star::star::star:
|
||||
|
||||
#### 1、单例模式的饿汉模式(线程安全)
|
||||
|
||||
缺点:在类初始化时候就会初始化对应的单例类,如果单例类没有被用到且单例类初始化复杂,就不应用这种模式
|
||||
|
||||
```java
|
||||
public class HungrySingleton {
|
||||
|
||||
private static final HungrySingleton hungrySingleton;
|
||||
|
||||
private HungrySingleton(){
|
||||
// 如果不加上这个判断,就会被反射攻击
|
||||
if (hungrySingleton!=null){
|
||||
throw new RuntimeException("单例模式禁止反射调用!");
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
hungrySingleton=new HungrySingleton();
|
||||
}
|
||||
|
||||
public static HungrySingleton getInstance(){
|
||||
return hungrySingleton;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 反射攻击
|
||||
public class ReflectAttack {
|
||||
|
||||
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
||||
// 反射攻击
|
||||
Class<HungrySingleton> hungrySingletonClass = HungrySingleton.class;
|
||||
Constructor<HungrySingleton> constructor = hungrySingletonClass.getDeclaredConstructor();
|
||||
// 获取可操作权限
|
||||
constructor.setAccessible(true);
|
||||
HungrySingleton hungrySingleton = constructor.newInstance();
|
||||
HungrySingleton instance = HungrySingleton.getInstance();
|
||||
System.out.println(hungrySingleton==instance);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 2.单例模式的懒汉模式(线程不安全)
|
||||
|
||||
```java
|
||||
public class LazySingleton {
|
||||
|
||||
private static LazySingleton lazySingleton=null;
|
||||
|
||||
private LazySingleton(){
|
||||
if (lazySingleton!=null){
|
||||
throw new RuntimeException("单例模式禁止反射调用!");
|
||||
}
|
||||
}
|
||||
|
||||
// 在多线程下是不安全的
|
||||
public static LazySingleton getInstance(){
|
||||
if (lazySingleton!=null){
|
||||
lazySingleton=new LazySingleton();
|
||||
}
|
||||
return lazySingleton;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 3.单例模式的懒汉模式(采用双重检查锁,线程安全)
|
||||
|
||||
```java
|
||||
public class LazySingleton {
|
||||
|
||||
// 必须要声明为 volatile 防止指令重排序
|
||||
private static volatile LazySingleton lazySingleton = null;
|
||||
|
||||
private LazySingleton() {
|
||||
if (lazySingleton != null) {
|
||||
throw new RuntimeException("单例模式禁止反射调用!");
|
||||
}
|
||||
}
|
||||
|
||||
public static LazySingleton getInstance() {
|
||||
if (lazySingleton == null) {
|
||||
synchronized (LazySingleton.class) {
|
||||
if (lazySingleton == null) {
|
||||
/*
|
||||
new对象过程:
|
||||
1.分配内存给这个对象
|
||||
2.初始化对象
|
||||
3.设置lazyDoubleCheckSingleton 指向刚分配的内存地址
|
||||
上述三步会发生指令重排序
|
||||
*/
|
||||
lazySingleton = new LazySingleton();
|
||||
}
|
||||
}
|
||||
}
|
||||
return lazySingleton;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 4.使用枚举实现单例(推荐 JVM保证下的单例)
|
||||
|
||||
**能够防止反射攻击和序列化对单例的破坏**
|
||||
|
||||
```java
|
||||
// 枚举单例模式
|
||||
public enum EnumInstance {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
public static EnumInstance getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 测试
|
||||
public class Test {
|
||||
|
||||
public static void main(String[] args) {
|
||||
EnumInstance instance0 = EnumInstance.INSTANCE;
|
||||
EnumInstance instance1 = EnumInstance.getInstance();
|
||||
EnumInstance instance2 = EnumInstance.getInstance();
|
||||
System.out.println(instance0 == instance1); // true
|
||||
System.out.println(instance0 == instance2); // true
|
||||
System.out.println(instance1 == instance2); // true
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
public enum EnumInstance {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
private String data;
|
||||
|
||||
// 可以增加对应的方法
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static EnumInstance getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 5.容器式单例(不常用)
|
||||
|
||||
```java
|
||||
public class ContainerSingleton {
|
||||
|
||||
private ContainerSingleton(){
|
||||
}
|
||||
|
||||
private static Map<String,Object> singletonMap = new HashMap<String,Object>();
|
||||
|
||||
public static void putInstance(String key,Object instance){
|
||||
if(StringUtils.isNotBlank(key) && instance != null){
|
||||
if(!singletonMap.containsKey(key)){
|
||||
singletonMap.put(key,instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getInstance(String key){
|
||||
return singletonMap.get(key);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
1507
notes/Oracle和MySQL异同对比.md
Normal file
612
notes/《Java8实战》读书笔记.md
Normal file
@ -0,0 +1,612 @@
|
||||
# 《Java 8 实战》读书笔记
|
||||
## 目录<br/>
|
||||
<a href="#第二部分-函数式数据处理">第二部分 函数式数据处理</a><br/>
|
||||
<a href="#第4章-引入流">第4章 引入流</a><br/>
|
||||
<a href="#1流的基本使用">1.流的基本使用</a><br/>
|
||||
<a href="#2数值流">2.数值流</a><br/>
|
||||
<a href="#3构建流">3.构建流</a><br/>
|
||||
<a href="#第5章-使用流">第5章 使用流</a><br/>
|
||||
<a href="#1中间操作和基本操作">1.中间操作和基本操作</a><br/>
|
||||
<a href="#2flatMap的使用">2.flatMap的使用</a><br/>
|
||||
<a href="#3归约">3.归约</a><br/>
|
||||
<a href="#第6章-用流收集数据">第6章 用流收集数据</a><br/>
|
||||
<a href="#1预定义收集器">1.预定义收集器</a><br/>
|
||||
<a href="#第三部分-高效的Java-8编程">第三部分 高效的Java 8编程</a><br/>
|
||||
<a href="#第12章-新的日期和时间API">第12章 新的日期和时间API</a><br/>
|
||||
<a href="#121-LocalDate、LocalTime、Instant、Duration、period">12.1 LocalDate、LocalTime、Instant、Duration、period</a><br/>
|
||||
<a href="#1使用-LocalDate-和-LocalTime">1.使用 LocalDate 和 LocalTime</a><br/>
|
||||
<a href="#2使用LocalDateTime">2.使用LocalDateTime</a><br/>
|
||||
<a href="#3时间间隔-Duration-或-Period">3.时间间隔 Duration 或 Period</a><br/>
|
||||
<a href="#122-操纵、解析和格式化日期">12.2 操纵、解析和格式化日期</a><br/>
|
||||
<a href="#1操纵日期加减">1.操纵日期加减</a><br/>
|
||||
<a href="#2使用TemporalAdjusters">2.使用TemporalAdjusters</a><br/>
|
||||
<a href="#3日期格式解析">3.日期格式解析</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 第二部分 函数式数据处理
|
||||
|
||||
### 第4章 引入流
|
||||
|
||||
#### 1.流的基本使用
|
||||
|
||||
```java
|
||||
// 基础数据类
|
||||
public class Data {
|
||||
|
||||
public enum Type {MEAT, FISH, OTHER}
|
||||
|
||||
public static List<Dish> menu = Arrays.asList(
|
||||
new Dish("pork", false, 800, Type.MEAT),
|
||||
new Dish("beef", false, 700, Type.MEAT),
|
||||
new Dish("chicken", false, 400, Type.MEAT),
|
||||
new Dish("french fries", true, 530, Type.OTHER),
|
||||
new Dish("rice", true, 350, Type.OTHER),
|
||||
new Dish("season fruit", true, 120, Type.OTHER),
|
||||
new Dish("pizza", true, 550, Type.OTHER),
|
||||
new Dish("prawns", false, 300, Type.FISH),
|
||||
new Dish("salmon", false, 450, Type.FISH));
|
||||
|
||||
static class Dish {
|
||||
private final String name;
|
||||
private final boolean vegetarian;
|
||||
private final int calories;
|
||||
private final Data.Type type;
|
||||
|
||||
public Dish(String name, boolean vegetarian, int calories, Data.Type type) {
|
||||
this.name = name;
|
||||
this.vegetarian = vegetarian;
|
||||
this.calories = calories;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isVegetarian() {
|
||||
return vegetarian;
|
||||
}
|
||||
|
||||
public int getCalories() {
|
||||
return calories;
|
||||
}
|
||||
|
||||
public Data.Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
// 流的使用
|
||||
public class Stream {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> collect = Data.menu.stream().filter(d -> d.getCalories() > 300).map(Data.Dish::getName).limit(3).collect(Collectors.toList());
|
||||
collect.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.数值流
|
||||
|
||||
**映射到数值流(mapToInt)**
|
||||
|
||||
```java
|
||||
public class Stream {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
//得到的是 Stream<Integer>
|
||||
java.util.stream.Stream<Integer> integerStream = Data.menu.stream().map(Data.Dish::getCalories);
|
||||
Integer reduce = integerStream.reduce(0, Integer::sum);
|
||||
System.out.println(reduce); //4200
|
||||
|
||||
//得到的是 IntStream
|
||||
IntStream intStream = Data.menu.stream().mapToInt(Data.Dish::getCalories);
|
||||
int sum = intStream.sum();
|
||||
System.out.println(sum); //4200
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**数值流到映射(boxed)**
|
||||
|
||||
```java
|
||||
public class Stream {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
//得到的是 Stream<Integer>
|
||||
java.util.stream.Stream<Integer> integerStream = Data.menu.stream().map(Data.Dish::getCalories);
|
||||
Integer reduce = integerStream.reduce(0, Integer::sum);
|
||||
System.out.println(reduce); //4200
|
||||
|
||||
//得到的是 IntStream
|
||||
IntStream intStream = Data.menu.stream().mapToInt(Data.Dish::getCalories);
|
||||
java.util.stream.Stream<Integer> boxed = intStream.boxed();
|
||||
Integer reduceSum = boxed.reduce(0, Integer::sum);
|
||||
System.out.println(reduceSum); //4200
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 收集器对流的消耗
|
||||
public class Stream {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
//得到的是 Stream<Integer>
|
||||
java.util.stream.Stream<Integer> integerStream = Data.menu.stream().map(Data.Dish::getCalories);
|
||||
Integer reduce = integerStream.reduce(0, Integer::sum);
|
||||
System.out.println(reduce); //4200
|
||||
|
||||
//得到的是 IntStream
|
||||
IntStream intStream = Data.menu.stream().mapToInt(Data.Dish::getCalories);
|
||||
int sum = intStream.sum();
|
||||
// 下面这行报错:IllegalStateException: stream has already been operated upon or closed
|
||||
// 因为sum() 是一个终止流方法,会消耗掉流,所以调用 intStream.boxed() 方法时候会报错
|
||||
java.util.stream.Stream<Integer> boxed = intStream.boxed();
|
||||
Integer reduceSum = boxed.reduce(0, Integer::sum);
|
||||
System.out.println(sum); //4200
|
||||
System.out.println(reduceSum); //4200
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.构建流
|
||||
|
||||
**1.由值创建流**
|
||||
|
||||
```java
|
||||
public class StreamCreate {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 由值创建流
|
||||
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
|
||||
stream.map(String::toUpperCase).forEach(System.out::println);
|
||||
|
||||
// 创建空流
|
||||
Stream<Object> empty = Stream.empty();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**2.由数组创建流**
|
||||
|
||||
```java
|
||||
public class StreamCreate {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
int[] numbers = {2, 3, 5, 7, 11, 13};
|
||||
int sum = Arrays.stream(numbers).sum();
|
||||
System.out.println(sum); //41
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3.由文件创建流**
|
||||
|
||||
```java
|
||||
public class StreamCreate {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
try (Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
|
||||
Set<String> collect = lines.flatMap(line -> Arrays.stream(line.split("")))
|
||||
.distinct()
|
||||
.collect(Collectors.toSet());
|
||||
collect.forEach(System.out::print);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**4.由函数生成流**
|
||||
|
||||
Stream API提供了两个静态方法来从函数生成流: **Stream.iterate**和**Stream.generate**。这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。由iterate和generate产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!一般来说,应该使用limit(n)来对这种流加以限制,以避免打印无穷多个值。
|
||||
|
||||
- **iterate**方法接受一个初始值(在这里是0),还有一个依次应用在每个产生的新值上的Lambda(UnaryOperator\<T>类型)。
|
||||
- **generate**方法也可让你按需生成一个无限流。但generate不是依次对每个新生成的值应用函数的。它接受一个Supplier\<T>类型的Lambda提供新的值。
|
||||
|
||||
```java
|
||||
public class StreamCreate {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 迭代
|
||||
Stream.iterate(0, n -> n + 2)
|
||||
.limit(10)
|
||||
.forEach(System.out::println);
|
||||
|
||||
// 生成
|
||||
Stream.generate(Math::random)
|
||||
.limit(5)
|
||||
.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第5章 使用流
|
||||
|
||||
你可以把Java 8的流看作花哨又懒惰的数据集迭代器。它们支持两种类型的操作:中间操作和终端操作。中间操作可以链接起来,**将一个流转换为另一个流**。这些操作不会消耗流,其目的是建立一个流水线。与此相反,**终端操作会消耗流**,以产生一个最终结果,例如返回流中的最大元素。
|
||||
|
||||
#### 1.中间操作和基本操作
|
||||
|
||||
**表5-1 中间操作和终端操作**
|
||||
|
||||
| 操作 | 类型 | 返回类型 | 使用的类型/函数式接口 | 函数描述符 |
|
||||
| --------- | ----------------- | ------------ | ---------------------- | --------------- |
|
||||
| filter | 中间 | Stream<T> | Predicate\<T> | T -> boolean |
|
||||
| distinct | 中间(有状态-无界) | Stream<T> | | |
|
||||
| skin | 中间(有状态-有界) | Stream<T> | long | |
|
||||
| limit | 中间(有状态-有界) | Stream<T> | long | |
|
||||
| map | 中间 | Stream<T> | Function<T,R> | T -> R |
|
||||
| flatMap | 中间 | Stream<T> | Function<T,Stream\<R>> | T -> Stream\<R> |
|
||||
| sorted | 中间(有状态-无界) | Stream<T> | Comparator\<T> | (T , T) -> int |
|
||||
| anyMatch | 终端 | boolean | Predicate\<T> | T -> boolean |
|
||||
| noneMatch | 终端 | boolean | Predicate\<T> | T -> boolean |
|
||||
| allMatch | 终端 | boolean | Predicate\<T> | T -> boolean |
|
||||
| findAny | 终端 | Optional\<T> | | |
|
||||
| findFirst | 终端 | Optional\<T> | | |
|
||||
| forEach | 终端 | void | Cosumer\<T> | T -> void |
|
||||
| collect | 终端 | R | collector<T, A, R> | |
|
||||
| reduce | 终端 | Optional\<T> | BinaryOperator\<T> | (T , T) -> T |
|
||||
| count | 终端 | long | | |
|
||||
|
||||
#### 2.flatMap的使用
|
||||
|
||||
给 定 单 词 列 表["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d"]。
|
||||
|
||||
**Arrays.stream()的方法可以接受一个数组并产生一个流 。**
|
||||
|
||||
```java
|
||||
public class Stream {
|
||||
|
||||
public static void main(String[] args) {
|
||||
String[] strings = {"Hello", "World"};
|
||||
|
||||
// 如下图 5.5
|
||||
List<String[]> list01 = Arrays.stream(strings)
|
||||
.map(s -> s.split(""))
|
||||
.distinct().collect(Collectors.toList());
|
||||
|
||||
// 使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。 如下图5.6
|
||||
List<String> list02 = Arrays.stream(strings)
|
||||
.map(s -> s.split(""))
|
||||
.flatMap(Arrays::stream)
|
||||
.distinct().collect(Collectors.toList());
|
||||
|
||||
//每个单词转换成一个字母数组,然后把每个数组变成了一个独立的流。
|
||||
List<java.util.stream.Stream<String>> list03 = Arrays.stream(strings)
|
||||
.map(s -> s.split(""))
|
||||
.map(Arrays::stream)
|
||||
.distinct().collect(Collectors.toList());
|
||||
|
||||
list01.forEach(System.out::println);
|
||||
list02.forEach(System.out::println);
|
||||
list03.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/LearningNotes/blob/master/pictures/flatmap1.png"/> </div></br>
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/LearningNotes/blob/master/pictures/flatmap2.png"/> </div></br>
|
||||
|
||||
#### 3.归约
|
||||
|
||||
```java
|
||||
public class Stream {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);
|
||||
Integer reduce = integers.stream().reduce(0, (a, b) -> a + b);
|
||||
System.out.println(reduce);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 第6章 用流收集数据
|
||||
|
||||
#### 1.预定义收集器
|
||||
|
||||
预定义收集器主要指 Collectors类提供的工厂方法(例如groupingBy)创建的收集器。它们主要提供了三大功能:
|
||||
|
||||
- 将流元素归约和汇总为一个值
|
||||
- 元素分组
|
||||
- 元素分区
|
||||
|
||||
**表6.1 Collectors 类的静态工厂方法**
|
||||
|
||||
| 工厂方法 | 返回类型 | 用于 |
|
||||
| ----------------- | --------------------- | ------------------------------------------------------------ |
|
||||
| toList | List\<T> | 把流中所有项目收集到一个List |
|
||||
| toSet | Set\<T> | 把流中所有项目收集到一个Set,删除重复项 |
|
||||
| toCollection | Collection\<T> | 把流中所有项目收集到给定的供应源创建的集合 |
|
||||
| counting | Long | 计算流中元素的个数 |
|
||||
| summingInt | Integer | 对流中项目的一个整数属性求和 |
|
||||
| averagingInt | Double | 计算流中项目Integer属性的平均值 |
|
||||
| summarizingInt | IntSummaryStatistics | 收集关于流中项目Integer属性的统计值,例如最大、最小、总和与平均值 |
|
||||
| joining | String | 连接对流中每个项目调用toString方法所生成的字符串 |
|
||||
| maxBy | Optional\<T> | 查找最大元素的Optional,或如果流为空则为Optional.empty() |
|
||||
| minBy | Optional\<T> | 查找最小元素的Optional,或如果流为空则为Optional.empty() |
|
||||
| reducing | 规约操作产生的类型 | 从一个作为累加器的初始值开启,利用BinaryOperator与流中的元素逐个结合,从而将流归约为单个值 |
|
||||
| collectingAndThen | 转换返回返回的类型 | 包裹另一个收集器,对其结果应用转换函数 |
|
||||
| groupingBy | Map<K,List\<T>> | 根据项目的一个属性的值对流中项目作分组,并将其属性值作为结果Map的键 |
|
||||
| partitionBy | Map<Boolean,List\<T>> | 根据对流中每个项目应用谓词的结果来对项目进行区分 |
|
||||
|
||||
```java
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
public class Stream {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// toList
|
||||
List<Data.Dish> dishList = Data.menu.stream().collect(toList());
|
||||
|
||||
// toSet
|
||||
Set<Data.Dish> dishSet = Data.menu.stream().collect(Collectors.toSet());
|
||||
|
||||
// toCollection
|
||||
ArrayList<Data.Dish> arrayList = Data.menu.stream()
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
|
||||
// counting
|
||||
long count = Data.menu.stream().count();
|
||||
// summingInt 可以用中间操作等价
|
||||
Integer sum01 = Data.menu.stream()
|
||||
.collect(Collectors.summingInt(Data.Dish::getCalories));
|
||||
Integer sum02 = Data.menu.stream()
|
||||
.mapToInt(Data.Dish::getCalories).sum();
|
||||
|
||||
// averagingInt
|
||||
Double average = Data.menu.stream()
|
||||
.collect(Collectors.averagingInt(Data.Dish::getCalories));
|
||||
|
||||
// summarizingInt
|
||||
IntSummaryStatistics statistics = Data.menu.stream()
|
||||
.collect(Collectors.summarizingInt(Data.Dish::getCalories));
|
||||
statistics.getAverage();
|
||||
statistics.getMax();
|
||||
|
||||
// joining
|
||||
String joining = Data.menu.stream().map(Data.Dish::getName)
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
// maxBy
|
||||
Optional<Data.Dish> max = Data.menu.stream()
|
||||
.max(Comparator.comparingInt(Data.Dish::getCalories));
|
||||
|
||||
// minBy
|
||||
Optional<Data.Dish> min = Data.menu.stream()
|
||||
.min(Comparator.comparingInt(Data.Dish::getCalories));
|
||||
|
||||
// reducing
|
||||
Integer reduce01 = Data.menu.stream().map(Data.Dish::getCalories)
|
||||
.reduce(0, Integer::sum);
|
||||
Integer reduce02 = Data.menu.stream().map(Data.Dish::getCalories)
|
||||
.reduce(0, Integer::sum);
|
||||
|
||||
// collectingAndThen
|
||||
Integer size = Data.menu.stream().collect(collectingAndThen(toList(), List::size));
|
||||
|
||||
// groupingBy
|
||||
Map<Data.Type, List<Data.Dish>> typeListMap = Data.menu.stream()
|
||||
.collect(groupingBy(Data.Dish::getType));
|
||||
|
||||
// partitionBy
|
||||
Map<Boolean, List<Data.Dish>> booleanListMap = Data.menu.stream()
|
||||
.collect(partitioningBy(Data.Dish::isVegetarian));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 第三部分 高效的Java 8编程
|
||||
|
||||
### 第12章 新的日期和时间API
|
||||
|
||||
#### 12.1 LocalDate、LocalTime、Instant、Duration、period
|
||||
|
||||
##### 1.使用 LocalDate 和 LocalTime
|
||||
|
||||
```java
|
||||
public class NewDateApi {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 创建日期
|
||||
LocalDate date = LocalDate.of(2018, 10, 8);
|
||||
int year = date.getYear(); // 2018
|
||||
Month month = date.getMonth(); // OCTOBER
|
||||
int value = month.getValue(); // 10
|
||||
int dayOfMonth = date.getDayOfMonth(); // 8
|
||||
int i = date.lengthOfMonth(); // 31
|
||||
|
||||
// 获取当前日期
|
||||
LocalDate now = LocalDate.now();
|
||||
|
||||
//创建时间
|
||||
LocalTime time = LocalTime.of(12, 13, 32);
|
||||
int hour = time.getHour();
|
||||
int minute = time.getMinute();
|
||||
int second = time.getSecond();
|
||||
|
||||
// 通过解析创建日期和时间
|
||||
// the text to parse such as "2007-12-03", not null
|
||||
LocalDate localDate = LocalDate.parse("2019-03-12");
|
||||
// the text to parse such as "10:15:30", not null
|
||||
LocalTime localTime = LocalTime.parse("12:21:45");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### 2.使用LocalDateTime
|
||||
|
||||
```java
|
||||
public class NewDateApi {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
LocalDate date = LocalDate.of(2014, Month.MARCH, 18);
|
||||
LocalTime time = LocalTime.of(13, 45, 20);
|
||||
|
||||
LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20);
|
||||
|
||||
LocalDateTime dt2 = LocalDateTime.of(date, time);
|
||||
LocalDateTime dt3 = date.atTime(13, 45, 20);
|
||||
LocalDateTime dt4 = date.atTime(time);
|
||||
LocalDateTime dt5 = time.atDate(date);
|
||||
|
||||
LocalDate localDate = dt1.toLocalDate();
|
||||
LocalTime localTime = dt1.toLocalTime();
|
||||
|
||||
boolean equal = dt1.isEqual(dt2); // true
|
||||
boolean equals = date.equals(localDate); // true
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### 3.时间间隔 Duration 或 Period
|
||||
|
||||
```java
|
||||
// 计算时间间隔
|
||||
public class NewDateApi {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
LocalDate date01 = LocalDate.of(2014, Month.MARCH, 18);
|
||||
LocalDate date02 = LocalDate.of(2012, Month.MARCH, 23);
|
||||
LocalTime time01 = LocalTime.of(13, 45, 20);
|
||||
LocalTime time02 = LocalTime.of(13, 12, 35);
|
||||
|
||||
// 计算日期间隔
|
||||
Period between01 = Period.between(date01, date02);
|
||||
|
||||
// 计算时间间隔
|
||||
Duration between02 = Duration.between(time01, time02);
|
||||
|
||||
// 间隔时间可能为正值 也可能为负值 可以用 isNegative 判断
|
||||
System.out.println(between01.getDays());
|
||||
System.out.println(between01.getYears());
|
||||
System.out.println(between02.getSeconds());
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```java
|
||||
// 创建时间间隔
|
||||
public class NewDateApi {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 创建 Duration 和 Period 对象
|
||||
Duration duration = Duration.ofMinutes(3);
|
||||
Duration duration1 = Duration.of(3, ChronoUnit.MINUTES);
|
||||
|
||||
Period tenDays = Period.ofDays(10);
|
||||
Period threeWeeks = Period.ofWeeks(3);
|
||||
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 12.2 操纵、解析和格式化日期
|
||||
|
||||
##### 1.操纵日期加减
|
||||
|
||||
```java
|
||||
public class NewDateApi {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 以直接修改方式操作 LocalDate
|
||||
LocalDate date1 = LocalDate.of(2014, 3, 18);
|
||||
LocalDate date2 = date1.withYear(2011);
|
||||
LocalDate date3 = date2.withDayOfMonth(25);
|
||||
LocalDate date4 = date3.with(ChronoField.MONTH_OF_YEAR, 9);
|
||||
|
||||
// 以相对的方式操作 LocalDate
|
||||
LocalDate date5 = date1.plusWeeks(1);
|
||||
LocalDate date6 = date1.minusYears(3);
|
||||
LocalDate date7 = date1.plus(6, ChronoUnit.MONTHS);
|
||||
|
||||
|
||||
// 操作 LocalDate
|
||||
LocalTime time = LocalTime.of(12, 3, 18);
|
||||
LocalTime time1 = time.withHour(3);
|
||||
LocalTime time2 = time.plus(3, ChronoUnit.HOURS);
|
||||
System.out.println(time2.getHour());
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### 2.使用TemporalAdjusters
|
||||
|
||||
```java
|
||||
import java.time.*;
|
||||
import static java.time.temporal.TemporalAdjusters.lastDayOfMonth;
|
||||
import static java.time.temporal.TemporalAdjusters.nextOrSame;
|
||||
|
||||
public class NewDateApi {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
LocalDate date1 = LocalDate.of(2014, 3, 18);
|
||||
LocalDate date2 = date1.with(nextOrSame(DayOfWeek.SUNDAY));
|
||||
LocalDate date3 = date2.with(lastDayOfMonth());
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/LearningNotes/blob/master/pictures/TemporalAdjuster类中的工厂方法.png"/> </div></br>
|
||||
|
||||
|
||||
|
||||
##### 3.日期格式解析
|
||||
|
||||
```java
|
||||
public class NewDateApi {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 使用内置格式解析
|
||||
LocalDate date = LocalDate.of(2014, 3, 18);
|
||||
String s1 = date.format(DateTimeFormatter.ISO_LOCAL_DATE); //2014-03-18
|
||||
|
||||
|
||||
// 使用自定义格式解析
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
|
||||
LocalDate date1 = LocalDate.of(2014, 3, 18);
|
||||
String formattedDate = date1.format(formatter);
|
||||
LocalDate date2 = LocalDate.parse(formattedDate, formatter);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
1672
notes/《Linux就该这么学》读书笔记.md
Normal file
1096
notes/《RabbitMQ实战指南》读书笔记.md
Normal file
726
notes/《Redis开发与运维》读书笔记.md
Normal file
@ -0,0 +1,726 @@
|
||||
# 《Redis开发与运维》读书笔记
|
||||
|
||||
## 目录<br/>
|
||||
<a href="#第二章-API的理解与使用">第二章 API的理解与使用</a><br/>
|
||||
<a href="#21-预备">2.1 预备</a><br/>
|
||||
<a href="#211-全局命令">2.1.1 全局命令</a><br/>
|
||||
<a href="#212-数据结构和内部编码">2.1.2 数据结构和内部编码</a><br/>
|
||||
<a href="#213-单线程架构">2.1.3 单线程架构</a><br/>
|
||||
<a href="#22-字符串">2.2 字符串</a><br/>
|
||||
<a href="#23-哈希">2.3 哈希</a><br/>
|
||||
<a href="#24-列表">2.4 列表</a><br/>
|
||||
<a href="#25-集合">2.5 集合</a><br/>
|
||||
<a href="#26-有序集合">2.6 有序集合</a><br/>
|
||||
<a href="#27-键管理">2.7 键管理</a><br/>
|
||||
<a href="#271-单个键管理">2.7.1 单个键管理</a><br/>
|
||||
<a href="#1键重命名">1.键重命名 </a><br/>
|
||||
<a href="#2-随机返回键">2. 随机返回键 </a><br/>
|
||||
<a href="#3键过期">3.键过期</a><br/>
|
||||
<a href="#272-键遍历">2.7.2 键遍历</a><br/>
|
||||
<a href="#1-全量键遍历">1. 全量键遍历</a><br/>
|
||||
<a href="#2-渐进式遍历">2. 渐进式遍历</a><br/>
|
||||
<a href="#273-数据库管理">2.7.3 数据库管理</a><br/>
|
||||
<a href="#1切换数据库">1.切换数据库</a><br/>
|
||||
<a href="#2flushdb/flushall">2.flushdb/flushall </a><br/>
|
||||
<a href="#第三章-小功能-大用处">第三章 小功能 大用处</a><br/>
|
||||
<a href="#31-慢查询分析">3.1 慢查询分析</a><br/>
|
||||
<a href="#311-慢查询的两个配置参数">3.1.1 慢查询的两个配置参数</a><br/>
|
||||
<a href="#32-redis-shell">3.2 redis shell</a><br/>
|
||||
<a href="#321-redis-cli">3.2.1 redis-cli</a><br/>
|
||||
<a href="#322-redis-server">3.2.2 redis-server</a><br/>
|
||||
<a href="#323-redis-benchmark">3.2.3 redis-benchmark</a><br/>
|
||||
<a href="#33-Pipeline">3.3 Pipeline</a><br/>
|
||||
<a href="#34-事务与Lua">3.4 事务与Lua</a><br/>
|
||||
<a href="#35-Bitmaps">3.5 Bitmaps</a><br/>
|
||||
<a href="#36-HyperLogLog">3.6 HyperLogLog</a><br/>
|
||||
<a href="#37-发布订阅">3.7 发布订阅</a><br/>
|
||||
<a href="#38-GEO">3.8 GEO</a><br/>
|
||||
<a href="#第四章-客户端">第四章 客户端</a><br/>
|
||||
<a href="#44-客户端管理">4.4 客户端管理</a><br/>
|
||||
<a href="#441-客户端API">4.4.1 客户端API</a><br/>
|
||||
<a href="#442-客户端相关配置">4.4.2 客户端相关配置</a><br/>
|
||||
<a href="#443-客户端统计片段">4.4.3 客户端统计片段</a><br/>
|
||||
<a href="#第五章-持久化">第五章 持久化</a><br/>
|
||||
<a href="#51-RDB">5.1 RDB</a><br/>
|
||||
<a href="#52-AOF">5.2 AOF</a><br/>
|
||||
<a href="#第六章-复制">第六章 复制</a><br/>
|
||||
<a href="#61-配置">6.1 配置</a><br/>
|
||||
<a href="#611-建立复制">6.1.1 建立复制</a><br/>
|
||||
<a href="#612-断开复制">6.1.2 断开复制</a><br/>
|
||||
<a href="#613-传输延迟">6.1.3 传输延迟</a><br/>
|
||||
<a href="#第十章-集群">第十章 集群</a><br/>
|
||||
<a href="#101-数据分区">10.1 数据分区</a><br/>
|
||||
<a href="#102-搭建集群">10.2 搭建集群</a><br/>
|
||||
<a href="#104--集群扩容">10.4 集群扩容</a><br/>
|
||||
<a href="#1042-扩容集群">10.4.2 扩容集群</a><br/>
|
||||
<a href="#1043-收缩集群">10.4.3 收缩集群</a><br/>
|
||||
<a href="#附[CacheCloud-GitHub]https//githubcom/sohutv/cachecloud">附:[CacheCloud GitHub](https://github.com/sohutv/cachecloud)</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
## 第二章 API的理解与使用
|
||||
|
||||
### 2.1 预备
|
||||
|
||||
#### 2.1.1 全局命令
|
||||
|
||||
1. 查看所有键: **keys \***
|
||||
|
||||
2. 查看键总数:**dbsize**
|
||||
|
||||
3. 检查键是否存在:**exists key**
|
||||
|
||||
4. 删除键:**del key [key ...]** 支持删除多个键
|
||||
|
||||
5. 键过期:**expire key seconds**
|
||||
|
||||
ttl命令会返回键的剩余过期时间, 它有3种返回值:
|
||||
|
||||
- 大于等于0的整数: 键剩余的过期时间。
|
||||
- -1: 键没设置过期时间。
|
||||
- -2: 键不存在
|
||||
|
||||
6. 键的数据结构 **type key**
|
||||
|
||||
#### 2.1.2 数据结构和内部编码
|
||||
|
||||
type命令实际返回的就是当前键的数据结构类型, 它们分别是:**string**(字符串) 、 **hash**(哈希) 、 **list**(列表) 、 **set**(集合) 、 **zset**(有序集合)
|
||||
|
||||
#### 2.1.3 单线程架构
|
||||
|
||||
1. 纯内存访问, Redis将所有数据放在内存中, 内存的响应时长大约为100纳秒, 这是Redis达到每秒万级别访问的重要基础。
|
||||
2. 非阻塞I/O, Redis使用epoll作为I/O多路复用技术的实现, 再加上Redis自身的事件处理模型将epoll中的连接、 读写、 关闭都转换为事件, 不在网络I/O上浪费过多的时间, 如图2-6所示。
|
||||
3. 单线程避免了线程切换和竞态产生的消耗。
|
||||
|
||||
### 2.2 字符串
|
||||
|
||||
| 作用 | 格式 | 参数或示例 |
|
||||
| ---------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| 设置值 | set key value \[ex seconds]\[px milliseconds][nx\|xx] setnx setex | ex seconds: 为键设置秒级过期时间。 <br/>px milliseconds: 为键设置毫秒级过期时间。<br/>nx: 键必须不存在, 才可以设置成功, 用于添加。<br/>xx: 与nx相反, 键必须存在, 才可以设置成功, 用于更新。 |
|
||||
| 获取值 | get key | r如果获取的键不存在 ,则返回nil(空) |
|
||||
| 批量设置 | mset key value [key value ...] | mset a 1 b 2 c 3 d 4 |
|
||||
| 批量获取值 | mget key [key ...] | mget a b c d |
|
||||
| 计数 | incr key decr key incrby key increment(指定数值自增)<br/>decrby key decrement(指定数值自减)<br/>incrbyfloat key increment (浮点数自增) | 值不是整数, 返回错误。 值是整数, 返回自增或自减后的结果。<br/>键不存在,创建键,并按照值为0自增或自减, 返回结果为1。 |
|
||||
| 追加值 | append key value | 向字符串的默认追加值 |
|
||||
| 字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 |
|
||||
| 设置并返回原值 | getset key value | |
|
||||
| 设置指定位置的租字符串 | setrange key offeset value | |
|
||||
| 获取部分字符串 | getrange key start end | |
|
||||
|
||||
### 2.3 哈希
|
||||
|
||||
| 作用 | 格式 | 参数或示例 |
|
||||
| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| 设置值 | hset key field value | hset user:1 name tom<br/>hset user:1 age 12 |
|
||||
| 获取值 | hget key field | hget user:1 name |
|
||||
| 删除field | hdel key field [field ...] | |
|
||||
| 计算field个数 | hlen key | |
|
||||
| 批量设置或获取field-value | hmget key field [field]<br/>hmset key field value [field value...] | hmset user:1 name mike age 12 city tianjin<br/>hmget user:1 name city |
|
||||
| 判断field是否存在 | hexists key field | |
|
||||
| 获取所有field | hkeys key | |
|
||||
| 获取所有value | hvals key | |
|
||||
| 获取所有的filed-value | hgetall key | 如果哈希元素个数比较多, 会存在阻塞Redis的可能。<br/>获取全部 可以使用hscan命令, 该命令会渐进式遍历哈希类型 |
|
||||
| 计数 | hincrby key field<br/>hincrbyfloat key field | |
|
||||
|
||||
### 2.4 列表
|
||||
|
||||
| 作用 | 格式 | 参数或示例 |
|
||||
| -------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| 增 | 左侧插入:lpush key value [value ...] <br/>右侧插入:rpush key value [value ...] <br/>某个指定元素前后插入:linsert key before\|after pivot value | |
|
||||
| 查 | 获取指定范围内的元素列表:lrange key start end <br/>获取列表指定索引下标的元素:lindex key index <br/>获取列表指定长度:llen key | lrange listkey 0 -1 |
|
||||
| 删 | 从列表左侧弹出元素:lpop key <br/>从列表右侧弹出元素:rpop key <br/>删除指定元素:lrem key count value <br/>截取列表:ltrim key start end | count>0, 从左到右, 删除最多count个元素。<br/>count<0, 从右到左, 删除最多count绝对值个元素。<br/>count=0, 删除所有 |
|
||||
| 改 | 修改指定索引下标的元素:lset key index newValue | |
|
||||
| 阻塞操作 | blpop key [key ...] timeout <br/>brpop key [key ...] timeout | key[key...]: 多个列表的键。 timeout: 阻塞时间\|等待时间(单位: 秒) |
|
||||
|
||||
|
||||
|
||||
### 2.5 集合
|
||||
|
||||
集合(set) 类型也是用来保存多个的字符串元素, 但和列表类型不一样的是, **集合中不允许有重复元素**, 并且集合中的元素是无序的, **不能通过索引下标获取元素**。
|
||||
|
||||
**集合内操作**:
|
||||
|
||||
| 作用 | 格式 | 参数或示例 |
|
||||
| -------------------- | ------------------------------ | ----------------------------------------- |
|
||||
| 添加元素 | sadd key element [element ...] | 返回结果为添加成功的元素个数 |
|
||||
| 删除元素 | srem key element [element ...] | 返回结果为成功删除的元素个数 |
|
||||
| 计算元素个数 | scard key | |
|
||||
| 判断元素是否在集合中 | sismember key element | |
|
||||
| 随机返回 | srandmember key [count] | 随机从集合返回指定个数元素,count 默认为1 |
|
||||
| 从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素,spop 会 |
|
||||
| 获取集合中所有元素 | smembers key | 可用sscan 代替 |
|
||||
|
||||
**集合间操作**:
|
||||
|
||||
| 作用 | 格式 |
|
||||
| ---------------------------- | ------------------------------------------------------------ |
|
||||
| 求多个集合的交集 | sinter key [key ...] |
|
||||
| 求多个集合的并集 | suinon key [key ...] |
|
||||
| 求多个集合的差集 | sdiff key [key ...] |
|
||||
| 将交集、并集、差集的结果保存 | sinterstore destination key [key ...] <br/>suionstore destination key [key ...]<br/>sdiffstore destination key [key ...] |
|
||||
|
||||
### 2.6 有序集合
|
||||
|
||||
有序集合中的元素可以排序。 但是它和列表使用索引下标作为排序依据不同的是, 它给每个元素设置一个分数(score) 作为排序的依据。
|
||||
|
||||
**集合内操作**:
|
||||
|
||||
| 作用 | 格式 | 参数或示例 |
|
||||
| ------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| 添加成员 | zadd key score member [score member ...] | nx: member必须不存在, 才可设置成功, 用于添加。<br> xx: member必须存在, 才可以设置成功, 用于更新。<br/>ch: 返回此次操作后, 有序集合元素和分数发生变化的个数<br/>incr: 对score做增加, 相当于后面介绍的zincrby。 |
|
||||
| 计算成员个数 | zcard key | |
|
||||
| 计算某个成员的分数 | zscore key member | |
|
||||
| 计算某个成员的排名 | zrank key member zrevrank key member | zrank是从分数从低到高返回排名, zrevrank反之。 |
|
||||
| 删除成员 | zrem key member [member ...] | |
|
||||
| 增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom |
|
||||
| 返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange是从低到高返回, zrevrange反之。 |
|
||||
| 返回指定分数范围内的成员 | zrangebyscore key min max \[withscores][limit offset count] <br/>zrevrangebyscore key max min \[withscores][limit offset count] | 其中zrangebyscore按照分数从低到高返回, zrevrangebyscore反之。 [limit offset count]选项可以限制输出的起始位置和个数: 同时min和max还支持开区间(小括号) 和闭区间(中括号) , -inf和+inf分别代表无限小和无限大 |
|
||||
| 删除指定排名内的升序元素 | zremrangerank key start end | |
|
||||
| 删除指定分数范围的成员 | zremrangebyscore key min max | |
|
||||
|
||||
**集合间操作**:
|
||||
|
||||
| 作用 | 格式 |
|
||||
| ---- | ------------------------------------------------------------ |
|
||||
| 交集 | zinterstore destination numkeys key \[key ...] [weights weight [weight ...]] \[aggregate sum\|min\|max] |
|
||||
| 并集 | zunionstore destination numkeys key \[key ...] [weights weight [weight ...]] \[aggregate sum\|min\|max] |
|
||||
|
||||
- destination: 交集计算结果保存到这个键。
|
||||
- numkeys: 需要做交集计算键的个数。
|
||||
- key[key...]: 需要做交集计算的键。
|
||||
- weights weight[weight...]: 每个键的权重, 在做交集计算时, 每个键中的每个member会将自己分数乘以这个权重, 每个键的权重默认是1。
|
||||
- aggregate sum|min|max: 计算成员交集后, 分值可以按照sum(和) 、min(最小值) 、 max(最大值) 做汇总, 默认值是sum。
|
||||
|
||||
### 2.7 键管理
|
||||
|
||||
#### 2.7.1 单个键管理
|
||||
|
||||
##### 1.键重命名
|
||||
|
||||
**rename key newkey**
|
||||
|
||||
为了防止被强行rename, Redis提供了renamenx命令, 确保只有newKey不存在时候才被覆盖。
|
||||
|
||||
##### 2. 随机返回键
|
||||
|
||||
**random key**
|
||||
|
||||
##### 3.键过期
|
||||
|
||||
- expire key seconds: 键在seconds秒后过期。
|
||||
- expireat key timestamp: 键在秒级时间戳timestamp后过期。
|
||||
- pexpire key milliseconds: 键在milliseconds毫秒后过期。
|
||||
- pexpireat key milliseconds-timestamp键在毫秒级时间戳timestamp后过期
|
||||
|
||||
注意:
|
||||
|
||||
1. 如果expire key的键不存在, 返回结果为0
|
||||
2. 如果设置过期时间为负值, 键会立即被删除, 犹如使用del命令一样
|
||||
3. persist key t命令可以将键的过期时间清除
|
||||
4. 对于字符串类型键, 执行set命令会去掉过期时间, 这个问题很容易在开发中被忽视
|
||||
5. Redis不支持二级数据结构(例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置
|
||||
6. setex命令作为set+expire的组合, 不但是原子执行, 同时减少了一次网络通讯的时间
|
||||
|
||||
#### 2.7.2 键遍历
|
||||
|
||||
##### 1. 全量键遍历
|
||||
|
||||
**keys pattern**
|
||||
|
||||
##### 2. 渐进式遍历
|
||||
|
||||
scan cursor \[match pattern] \[count number]
|
||||
|
||||
- cursor是必需参数, 实际上cursor是一个游标, 第一次遍历从0开始, 每次scan遍历完都会返回当前游标的值, 直到游标值为0, 表示遍历结束。
|
||||
- match pattern是可选参数, 它的作用的是做模式的匹配, 这点和keys的模式匹配很像。
|
||||
- count number是可选参数, 它的作用是表明每次要遍历的键个数, 默认值是10, 此参数可以适当增大。
|
||||
|
||||
#### 2.7.3 数据库管理
|
||||
|
||||
##### 1.切换数据库
|
||||
|
||||
**select dbIndex**
|
||||
|
||||
##### 2.flushdb/flushall
|
||||
|
||||
flushdb/flushall命令用于清除数据库, 两者的区别的是flushdb只清除当前数据库, flushall会清除所有数据库。
|
||||
|
||||
|
||||
|
||||
## 第三章 小功能 大用处
|
||||
|
||||
### 3.1 慢查询分析
|
||||
|
||||
#### 3.1.1 慢查询的两个配置参数
|
||||
|
||||
```shell
|
||||
# 设置慢查询阈值 耗时高于阈值的操作将被记录
|
||||
config set slowlog-log-slower-than 20000
|
||||
# 设置慢查询维护日志的记录长度
|
||||
config set slowlog-max-len 1000
|
||||
# 将配置持久化到本地配置文件
|
||||
config rewrite
|
||||
|
||||
# 获取慢查询日志
|
||||
slowlog get [n]
|
||||
# 获取慢查询日志当前的长度
|
||||
slowlog len
|
||||
# 慢查询日志重置
|
||||
slowlog reset
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3.2 redis shell
|
||||
|
||||
#### 3.2.1 redis-cli
|
||||
|
||||
```shell
|
||||
# 1. -r (repeat)选项代表将执行多次命令
|
||||
redis-cli -r 3 ping
|
||||
|
||||
# 2. -i (interval) 选项代表每隔几秒执行一次命令
|
||||
redis-cli -r 5 -i 1 ping
|
||||
|
||||
# 3. -x 选项代表从标准输入(stdin)读取数据作为redis-cli 的最后一个参数
|
||||
echo "world" | redis-cli -x set hello
|
||||
|
||||
# 4. --salve 选项是把当前客户端模拟成当前Redis节点的从节点,可以用来获取当前Redis节点的更新操作
|
||||
redis-cli --salve
|
||||
|
||||
# 5. --stat 选项可以实时获取Redis的重要统计信息
|
||||
redis-cli --stat
|
||||
|
||||
# --no-raw 选项要求命令的返回结果必须是原始的格式
|
||||
# --raw 返回格式化之后的结果
|
||||
$ redis-cli set hello "你好"
|
||||
$ redis-cli get hello
|
||||
"\xe4\xbd\xa0\xe5\xa5\xbd"
|
||||
$redis-cli --no-raw get hello
|
||||
"\xe4\xbd\xa0\xe5\xa5\xbd"
|
||||
$redis-cli --raw get hello
|
||||
你好
|
||||
```
|
||||
|
||||
#### 3.2.2 redis-server
|
||||
|
||||
```shell
|
||||
# --test-memory 选项用来检测当前操作系统能否稳定地分配指定容量给Redis
|
||||
redis-server --test-memory 1024
|
||||
```
|
||||
|
||||
#### 3.2.3 redis-benchmark
|
||||
|
||||
```shell
|
||||
# 1. -c (client) 选项代表客户端的并发数量(默认是50)
|
||||
# 2. -n (num)选项代表客户端请求总量(默认是100000)
|
||||
redis-benchmark -c 100 -n 20000 代表100个客户端同时请求Redis,一共执行20000次
|
||||
|
||||
# 3.-q 选项仅仅显示 redis-benchmark 的 requests per second 信息
|
||||
redis-benchmark -c 100 -n 20000 -g
|
||||
|
||||
# 4.-r 执行 redis-benchmark 的时候插入更多随机的键
|
||||
redis-benchmark -c 100 -n 20000 -r 10000
|
||||
# -r 选项会在key,count键上加一个12位的后缀,-r 10000 代表只对后四位做随机处理(-r 不是随机数的个数)
|
||||
|
||||
# 5.-P选项代表每个请求pipeline的数据量(默认为1)
|
||||
|
||||
# 6.-k<boolean>
|
||||
# -k 选项代表客户端是否使用keepalive,1为使用,0为不使用,默认值为 1。
|
||||
|
||||
# 7.-t 选项可以对指定命令进行基准测试
|
||||
redis-benchmark -t get,set -q
|
||||
|
||||
# 8.--csv 选项会将结果按照csv格式输出,便于后续处理
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 3.3 Pipeline
|
||||
|
||||
原生批量命令与Pipeline对比 :
|
||||
|
||||
- 原生批量命令是原子的, Pipeline是非原子的。
|
||||
- 原生批量命令是一个命令对应多个key, Pipeline支持多个命令。
|
||||
- 原生批量命令是Redis服务端支持实现的, 而Pipeline需要服务端和客户端的共同实现。
|
||||
|
||||
|
||||
|
||||
### 3.4 事务与Lua
|
||||
|
||||
1. **multi**命令代表事务开始, **exec**命令代表事务结束 ,如果要停止事务的执行, 可以使用**discard**命令代替exec命令即可。
|
||||
2. **Redis并不支持回滚功能** 。
|
||||
3. 有些应用场景需要在事务之前, 确保事务中的key没有被其他客户端修改过, 才执行事务, 否则不执行(类似乐观锁) , Redis提供了**watch**命令来解决这类问题 。
|
||||
|
||||
|
||||
|
||||
### 3.5 Bitmaps
|
||||
|
||||
```shell
|
||||
# 1.设置值
|
||||
setbit key offset value
|
||||
|
||||
# 2.获取值
|
||||
getbit key offset
|
||||
|
||||
# 3.获取BitMaps指定范围值为1的个数
|
||||
bitcount [start][end]
|
||||
|
||||
# 4.Bitmaps 间的运算
|
||||
bitop op destkey key[key...]
|
||||
# op 可以为 and (交集)、or(并集)、not(非)、xor(异或)操作
|
||||
|
||||
# 计算Bitmaps中第一个值为targetBit的偏移量
|
||||
bitpos key tartgetBit [start] [end]
|
||||
```
|
||||
|
||||
### 3.6 HyperLogLog
|
||||
|
||||
通过HyperLogLog可以利用极小的内存空间完成独立总数的统计 。
|
||||
|
||||
```shell
|
||||
# 1.添加
|
||||
pfadd key element [element …]
|
||||
|
||||
# 2.计算独立用户数
|
||||
pfcount key [key …]
|
||||
|
||||
# 3.合并
|
||||
pfmerge destkey sourcekey [sourcekey ...]
|
||||
```
|
||||
|
||||
### 3.7 发布订阅
|
||||
|
||||
```shell
|
||||
# 1. 发布消息
|
||||
publish channel message
|
||||
|
||||
# 2.订阅消息
|
||||
subscribe channel [channel ...]
|
||||
|
||||
# 3.取消订阅
|
||||
unsubscribe [channel [channel ...]]
|
||||
|
||||
# 4.按照模式订阅和取消订阅
|
||||
psubscribe pattern [pattern...]
|
||||
punsubscribe [pattern [pattern ...]]
|
||||
|
||||
# 查看活跃的频道(指当前频道至少有一个订阅者)
|
||||
pubsub channels [pattern]
|
||||
|
||||
# 查看频道订阅数
|
||||
pubsub numsub [channel ...]
|
||||
|
||||
# 查看模式订阅数
|
||||
pubsub numpat
|
||||
```
|
||||
|
||||
### 3.8 GEO
|
||||
|
||||
```
|
||||
# 1.增加地理位置信息
|
||||
geoadd key longitude latitude member [longitude latitude member ...]
|
||||
geoadd cities:locations 116.28 39.55 beijing
|
||||
|
||||
# 2.获取地理位置信息
|
||||
geopos key member [member ...]
|
||||
geopos cities:locations tianjin
|
||||
|
||||
# 删除地理位置信息
|
||||
zrem key member
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 第四章 客户端
|
||||
|
||||
### 4.4 客户端管理
|
||||
|
||||
#### 4.4.1 客户端API
|
||||
|
||||
**1.client list**
|
||||
|
||||
```shell
|
||||
127.0.0.1:6379> client list
|
||||
id=1610 addr=10.0.2.2:58879 fd=9 name= age=2169 idle=1590 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=scan
|
||||
id=1612 addr=10.0.2.2:59560 fd=10 name=heibairuiwen age=1642 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
|
||||
```
|
||||
|
||||
| 参数 | 含义 |
|
||||
| --------- | ------------------------------------------------------------ |
|
||||
| id | 客户端连接id |
|
||||
| addr | 客户端连接IP和端口 |
|
||||
| fd | socket 的文件描述符 |
|
||||
| name | 客户端连接名<br/>client setName 设置当前客户端名字;<br/>client getName 获取当前客户端名字 |
|
||||
| age | 客户端连接存活时间 |
|
||||
| idle | 客户端连接空闲时间 |
|
||||
| flags | 客户端类型类型标识 |
|
||||
| db | 当前客户端正在使用的数据库索引下标 |
|
||||
| sub/psub | 当前客户端订阅的频道或者模式 |
|
||||
| multi | 当前事务中已执行命令个数 |
|
||||
| qbuf | 输入缓冲区总容量<br/>输入缓冲区会根据内容动态调整,但大小不能超过1G |
|
||||
| qbuf-free | 输入缓冲区剩余容量 |
|
||||
| obl | 输出固定缓冲区的长度 |
|
||||
| oll | 输出动态缓冲区列表长度 |
|
||||
| omem | 固定缓冲区和动态缓冲区使用的容量。输出缓冲区的容量控制:<br/>client-output-buffer-limit \<class> \<hard limit> \<soft limit> \<soft seconds><br/>\<class>: 客户端类型分为三种。normal: 普通客户端;slave: slave客户端, 用于复制;pubsub: 发布订阅客户端。<br/>\<hard limit>: 如果客户端使用的输出缓冲区大于\<hard limit>, 客户端会被立即关闭。<br/>\<soft limit>和\<soft seconds>: 如果客户端使用的输出缓冲区超过了\<soft limit>并且持续了\<soft limit>秒, 客户端会被立即关闭。<br/>示例:client-output-buffer-limit normal 20mb 10mb 120 |
|
||||
| events | 文件描述符事作件(r/w): r 和 w 分别代表客户端套接字可读和可写 |
|
||||
| cmd | 当前客户端最后一次执行的命令,不包含参数 |
|
||||
**2.客户端的限制maxclients和timeout**
|
||||
|
||||
Redis提供了maxclients参数来限制最大客户端连接数, 一旦连接数超过maxclients, 新的连接将被拒绝。 maxclients默认值是10000, 可以通过info clients来查询当前Redis的连接数。
|
||||
|
||||
可以通过config set maxclients对最大客户端连接数进行动态设置。
|
||||
|
||||
**3.client kill**
|
||||
|
||||
client kill ip:port 此命令用于杀掉指定IP地址和端口的客户端。
|
||||
|
||||
**4.client pause**
|
||||
|
||||
client pause timeout(毫秒) client pause命令用于阻塞客户端timeout毫秒数, 在此期间客户端连接将被阻塞。
|
||||
|
||||
**5.monitor**
|
||||
|
||||
monitor命令用于监控Redis正在执行的命令。monitor命令能够监听其他客户端正在执行的命令, 并记录了详细的时间戳。
|
||||
|
||||
#### 4.4.2 客户端相关配置
|
||||
|
||||
1. **timeout**: 检测客户端空闲连接的超时时间, 一旦idle时间达到了timeout, 客户端将会被关闭, 如果设置为0就不进行检测。
|
||||
2. **tcp-keepalive**: 检测TCP连接活性的周期, 默认值为0, 也就是不进行检测, 如果需要设置, 建议为60, 那么Redis会每隔60秒对它创建的TCP连接进行活性检测, 防止大量死连接占用系统资源。
|
||||
3. **tcp-backlog**: TCP三次握手后, 会将接受的连接放入队列中, tcpbacklog就是队列的大小, 它在Redis中的默认值是511。
|
||||
|
||||
#### 4.4.3 客户端统计片段
|
||||
|
||||
**info clients**
|
||||
|
||||
1. **connected_clients**: 代表当前Redis节点的客户端连接数, 需要重点监控, 一旦超过maxclients, 新的客户端连接将被拒绝。
|
||||
2. **client_longest_output_list**: 当前所有输出缓冲区中队列对象个数的最大值。
|
||||
3. **client_biggest_input_buf**: 当前所有输入缓冲区中占用的最大容量。
|
||||
4. **blocked_clients**: 正在执行阻塞命令(例如blpop、 brpop、brpoplpush) 的客户端个数。
|
||||
|
||||
|
||||
|
||||
## 第五章 持久化
|
||||
|
||||
### 5.1 RDB
|
||||
|
||||
RDB 持久化是把当前进程数据生成快照保存到硬盘的过程。触发RBD持久化的过程分为手动触发和自动触发。
|
||||
|
||||
**手动触发**:
|
||||
|
||||
```shell
|
||||
# 手动触发(阻塞)
|
||||
save
|
||||
# 手动触发(非阻塞)
|
||||
bgsave
|
||||
```
|
||||
|
||||
**自动触发**:
|
||||
|
||||
1. 使用save相关配置, 如“save m n”。 表示m秒内数据集存在n次修改时, 自动触发bgsave。
|
||||
2. 如果从节点执行全量复制操作, 主节点自动执行bgsave生成RDB文件并发送给从节点。
|
||||
3. 执行debug reload命令重新加载Redis时, 也会自动触发save操作。
|
||||
4. 默认情况下执行shutdown命令时, 如果没有开启AOF持久化功能则自动执行bgsave。
|
||||
|
||||
**保存**:
|
||||
|
||||
RDB文件保存在dir配置指定的目录下, 文件名通过**dbfilename**配置指定。 可以通过执行**config set dir{newDir}**和**config setdbfilename{newFileName}**运行期动态执行, 当下次运行时RDB文件会保存到新目录。
|
||||
|
||||
**缺点**:
|
||||
|
||||
无法做到实时持久化/秒级持久化,重量级操作,频繁执行成本高
|
||||
|
||||
### 5.2 AOF
|
||||
|
||||
AOF 持久化以独立日志的方式记录每次写命令,重启时在重新执行AOF文件中的命令达到恢复数据的目的。
|
||||
|
||||
1. 开启AOF功能需要设置配置: **appendonly yes**, 默认不开启。
|
||||
2. AOF文件名通过**appendfilename**配置设置, 默认文件名是appendonly.aof。
|
||||
3. 保存路径同RDB持久化方式一致, 通过dir配置指定。
|
||||
|
||||
**1. AOF缓冲区同步文件策略**:
|
||||
|
||||
| 可配置值 | 说明 |
|
||||
| -------------- | ------------------------------------------------------------ |
|
||||
| always | 命令写入aof_buf后调用系统fsyn操作同步到AOF文件,fsync 完成后线程返回 |
|
||||
| everysec(常用) | 每秒执行一次 |
|
||||
| no | 同步由操作系统控制 |
|
||||
|
||||
**2. AOF重写过程**:
|
||||
|
||||
**手动触发**:直接调用**bgrewriteaof**命令
|
||||
|
||||
**自动触发**:根据**auto-aof-rewrite-min-size**和**auto-aof-rewrite-percentage**参数确定自动触发时机。
|
||||
|
||||
- auto-aof-rewrite-min-size: 表示运行AOF重写时文件最小体积, 默认为64MB。
|
||||
- auto-aof-rewrite-percentage: 代表当前AOF文件空间(aof_current_size) 和上一次重写后AOF文件空间(aof_base_size) 的比值。
|
||||
|
||||
**3. 错误修复**
|
||||
|
||||
对于错误格式的AOF文件, 先进行备份, 然后采用**redis-check-aof--fix**命令进行修复, 修复后使用diff-u对比数据的差异, 找出丢失的数据, 有些可以人工修改补全。
|
||||
|
||||
|
||||
|
||||
## 第六章 复制
|
||||
|
||||
### 6.1 配置
|
||||
|
||||
#### 6.1.1 建立复制
|
||||
|
||||
三种方式:
|
||||
|
||||
1. 在配置文件中加入slaveof {masterHost} {masterPort}随Redis启动生效。
|
||||
2. 在redis-server启动命令后加入--slaveof {masterHost} {masterPort}生效。
|
||||
3. 直接使用命令: slaveof {masterHost} {masterPort}生效。
|
||||
|
||||
```shell
|
||||
127.0.0.1:6380>slaveof 127.0.0.1 6379
|
||||
```
|
||||
|
||||
#### 6.1.2 断开复制
|
||||
|
||||
在从节点执行**slaveof no one**来断开与主节点复制关系。 从节点断开复制后**并不会抛弃原有数据**, 只是无法再获取主节点上的数
|
||||
据变化。
|
||||
|
||||
通过slaveof命令还可以实现切主操作, 所谓切主是指把当前从节点对主节点的复制切换到另一个主节点。 执行slaveof{newMasterIp} {newMasterPort}命令即可。**会清除原有数据**。
|
||||
|
||||
注:**默认情况下, 从节点使用slave-read-only=yes配置为只读模式**。
|
||||
|
||||
#### 6.1.3 传输延迟
|
||||
|
||||
Redis为我们提供了**repl-disable-tcp-nodelay**参数用于控制是否关闭TCP_NODELAY, 默认关闭, 说明如下:
|
||||
|
||||
- 当关闭时, 主节点产生的命令数据无论大小都会及时地发送给从节点, 这样主从之间延迟会变小, 但增加了网络带宽的消耗。 适用于主从之间的网络环境良好的场景, 如同机架或同机房部署。
|
||||
- 当开启时, 主节点会合并较小的TCP数据包从而节省带宽。 默认发送时间间隔取决于Linux的内核, 一般默认为40毫秒。 这种配置节省了带宽但增大主从之间的延迟。 适用于主从网络环境复杂或带宽紧张的场景, 如跨机房部署。
|
||||
|
||||
|
||||
|
||||
## 第十章 集群
|
||||
|
||||
### 10.1 数据分区
|
||||
|
||||
Redis Cluser采用虚拟槽分区, 所有的键根据哈希函数映射到0~16383整数槽内, 计算公式: slot=CRC16(key) &16383。 每一个节点负责维护一部分槽以及槽所映射的键值数据。
|
||||
|
||||
### 10.2 搭建集群
|
||||
|
||||
**用redis-trib.rb搭建集群** :
|
||||
|
||||
**1.Ruby环境准备**
|
||||
|
||||
```shell
|
||||
# 安装Ruby:
|
||||
wget https:// cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
|
||||
tar xvf ruby-2.3.1.tar.gz
|
||||
cd ruby-2.3.1
|
||||
./configure -prefix=/usr/local/ruby
|
||||
make
|
||||
make install
|
||||
cd /usr/local/ruby
|
||||
sudo cp bin/ruby /usr/local/bin
|
||||
sudo cp bin/gem /usr/local/bin
|
||||
|
||||
# 安装rubygem redis依赖:
|
||||
wget http:// rubygems.org/downloads/redis-3.3.0.gem
|
||||
gem install -l redis-3.3.0.gem
|
||||
gem list --check redis gem
|
||||
|
||||
# 安装redis-trib.rb:
|
||||
sudo cp /{redis_home}/src/redis-trib.rb /usr/local/bin
|
||||
|
||||
# 执行确认
|
||||
# redis-trib.rb
|
||||
```
|
||||
|
||||
**2.节点配置**
|
||||
|
||||
```shell
|
||||
#节点端口
|
||||
port 6379
|
||||
# 开启集群模式
|
||||
cluster-enabled yes
|
||||
# 节点超时时间, 单位毫秒
|
||||
cluster-node-timeout 15000
|
||||
# 集群内部配置文件
|
||||
cluster-config-file "nodes-6379.conf"
|
||||
```
|
||||
|
||||
```shell
|
||||
redis-server conf/redis-6481.conf
|
||||
redis-server conf/redis-6482.conf
|
||||
redis-server conf/redis-6483.conf
|
||||
redis-server conf/redis-6484.conf
|
||||
redis-server conf/redis-6485.conf
|
||||
redis-server conf/redis-6486.conf
|
||||
```
|
||||
|
||||
**3.创建集群**
|
||||
|
||||
```shell
|
||||
redis-trib.rb create --replicas 1 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483
|
||||
127.0.0.1:6484 127.0.0.1:6485 127.0.0.1:6486
|
||||
```
|
||||
|
||||
--replicas参数指定集群中每个主节点配备几个从节点, 这里设置为1。
|
||||
|
||||
**4.集群完整性检查**
|
||||
|
||||
集群完整性指所有的槽都分配到存活的主节点上, 只要16384个槽中有一个没有分配给节点则表示集群不完整。 可以使用redis-trib.rb check命令检测之前创建的两个集群是否成功, check命令只需要给出集群中任意一个节点地址就可以完成整个集群的检查工作 .
|
||||
|
||||
```shell
|
||||
redis-trib.rb check 127.0.0.1:6379
|
||||
redis-trib.rb check 127.0.0.1:6481
|
||||
```
|
||||
|
||||
### 10.4 集群扩容
|
||||
|
||||
#### 10.4.2 扩容集群
|
||||
|
||||
**步骤:**
|
||||
|
||||
```shell
|
||||
# 准备新的节点
|
||||
redis-server conf/redis-6385.conf
|
||||
redis-server conf/redis-6386.conf
|
||||
|
||||
# 加入集群
|
||||
redis-trib.rb add-node 127.0.0.1:6385 127.0.0.1:6379
|
||||
redis-trib.rb add-node 127.0.0.1:6386 127.0.0.1:6379
|
||||
|
||||
# 槽重新分片
|
||||
redis-trib.rb reshard 127.0.0.1:6379
|
||||
|
||||
#要把节点6386作为6385的从节点, 从而保证整个集群的高可用。
|
||||
127.0.0.1:6386>cluster replicate 1a205dd8b2819a00dd1e8b6be40a8e2abe77b756
|
||||
|
||||
#查看情况验证
|
||||
cluster nodes
|
||||
```
|
||||
|
||||
槽重新分片:
|
||||
|
||||
```shell
|
||||
redis-trib.rb reshard host:port --from <arg> --to <arg> --slots <arg> --yes --timeout
|
||||
<arg> --pipeline <arg>
|
||||
```
|
||||
|
||||
参数说明:
|
||||
|
||||
- host: port: 必传参数, 集群内任意节点地址, 用来获取整个集群信息。
|
||||
- --from: 制定源节点的id, 如果有多个源节点, 使用逗号分隔, 如果是all源节点变为集群内所有主节点, 在迁移过程中提示用户输入。
|
||||
- --to: 需要迁移的目标节点的id, 目标节点只能填写一个, 在迁移过程中提示用户输入。
|
||||
- --slots: 需要迁移槽的总数量, 在迁移过程中提示用户输入。
|
||||
- --yes: 当打印出reshard执行计划时, 是否需要用户输入yes确认后再执行reshard。
|
||||
- --timeout: 控制每次migrate操作的超时时间, 默认为60000毫秒。
|
||||
|
||||
#### 10.4.3 收缩集群
|
||||
|
||||
下线节点:
|
||||
|
||||
```shell
|
||||
redistrib.rb del-node{host: port}{downNodeId}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 附:[CacheCloud GitHub](https://github.com/sohutv/cachecloud)
|
||||
|
3663
notes/《javascript高级程序设计》读书笔记.md
Normal file
2272
notes/《实战Java高并发程序设计》读书笔记.md
Normal file
3222
notes/《深入理解ES6》读书笔记.md
Normal file
254
notes/《深入理解Java虚拟机》读书笔记.md
Normal file
@ -0,0 +1,254 @@
|
||||
# 《深入理解Java虚拟机》读书笔记
|
||||
## 目录<br/>
|
||||
<a href="#第二章-java内存区域与内存溢出异常">第二章 java内存区域与内存溢出异常</a><br/>
|
||||
<a href="#22-运行时数据区域">2.2 运行时数据区域</a><br/>
|
||||
<a href="#221--程序计数器">2.2.1 程序计数器</a><br/>
|
||||
<a href="#222--Java虚拟机栈">2.2.2 Java虚拟机栈</a><br/>
|
||||
<a href="#223-本地方法栈">2.2.3 本地方法栈</a><br/>
|
||||
<a href="#224-Java堆">2.2.4 Java堆</a><br/>
|
||||
<a href="#225-方法区">2.2.5 方法区</a><br/>
|
||||
<a href="#第四章-虚拟机性能监控与故障处理工具">第四章 虚拟机性能监控与故障处理工具</a><br/>
|
||||
<a href="#421-JDK命令行工具">4.2.1 JDK命令行工具</a><br/>
|
||||
<a href="#421-jps虚拟机进程状况工具">4.2.1 jps:虚拟机进程状况工具 </a><br/>
|
||||
<a href="#422-jstat虚拟机统计信息监视工具">4.2.2 jstat:虚拟机统计信息监视工具</a><br/>
|
||||
<a href="#1-gc-参数返回结果">1. gc 参数返回结果</a><br/>
|
||||
<a href="#2-class-参数返回结果">2. class 参数返回结果</a><br/>
|
||||
<a href="#3-gcmetacapacity-参数返回结果">3. gcmetacapacity 参数返回结果</a><br/>
|
||||
<a href="#423-jinfoJava配置信息工具">4.2.3 jinfo:Java配置信息工具</a><br/>
|
||||
<a href="#424-jmapJava内存映像工具">4.2.4 jmap:Java内存映像工具 </a><br/>
|
||||
<a href="#425-jhat虚拟机堆转储快照分析工具">4.2.5 jhat:虚拟机堆转储快照分析工具 </a><br/>
|
||||
<a href="#426-jstackJava堆栈跟踪工具">4.2.6 jstack:Java堆栈跟踪工具 </a><br/>
|
||||
<a href="#43-JDK的可视化工具">4.3 JDK的可视化工具 </a><br/>
|
||||
<a href="#第十二章-Java内存模型">第十二章 Java内存模型</a><br/>
|
||||
## 正文<br/>
|
||||
|
||||
|
||||
|
||||
## 第二章 java内存区域与内存溢出异常
|
||||
|
||||
### 2.2 运行时数据区域
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/LearningNotes/blob/master/pictures/java虚拟机运行时数据区.png"/> </div></br>
|
||||
|
||||
|
||||
|
||||
#### 2.2.1 程序计数器
|
||||
|
||||
**当前线程所执行的字节码的行号指示器。**
|
||||
|
||||
- 为了线程切换后能够恢复到正确地执行位置,每个线程都需要有一个独立的程序计数器;
|
||||
- 如果正在执行java方法,计数器记录的是正在执行的虚拟机字节码指令的地址,如果执行的是Native方法,则计数器值为空;
|
||||
- 此内存区域是唯一一个在java虚拟机规范中没有规定任何outOfMemoryError情况的区域。
|
||||
|
||||
|
||||
|
||||
#### 2.2.2 Java虚拟机栈
|
||||
|
||||
虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出入口等信息。每一个方法从调用直到执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。
|
||||
|
||||
- 局部变量表存放了编译器可知的各种基本数据类型、对象引用和returnAddress类型(指向一条字节码指令的地址)。
|
||||
- 在java虚拟机规范中,对这个区域规定了两种异常:
|
||||
- 如果线程请求的栈深入大于虚拟机所允许的深度,将抛出StackOverflowError异常;
|
||||
- 如果虚拟机栈扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
|
||||
|
||||
|
||||
|
||||
#### 2.2.3 本地方法栈
|
||||
|
||||
与虚拟机栈类似,针对Native 方法服务。
|
||||
|
||||
|
||||
|
||||
#### 2.2.4 Java堆
|
||||
|
||||
此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。Java 堆可以处于物理上不连续的内存空间中,只要是逻辑上是连续的即可。如果堆中没有内存完成实例分配,并且堆也无法在扩展时,将会抛出OutOfMemoryError异常。
|
||||
|
||||
|
||||
|
||||
#### 2.2.5 方法区
|
||||
|
||||
方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。
|
||||
|
||||
- 当方法区无法满足内存分配的需求时,将会抛出OutOfMemoryError异常。
|
||||
- **运行时常量池**是方法区的一部分,Class文件中出了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存在。
|
||||
|
||||
|
||||
|
||||
## 第四章 虚拟机性能监控与故障处理工具
|
||||
|
||||
部分内容参考自博客:[JDK的命令行工具](https://blog.csdn.net/qq_31156277/article/details/80035236)
|
||||
|
||||
### 4.2.1 JDK命令行工具
|
||||
|
||||
#### 4.2.1 jps:虚拟机进程状况工具
|
||||
|
||||
命令格式: jps \[options] [hostid]
|
||||
|
||||
| 参数 | 描述 |
|
||||
| ---- | ------------------------------------------------ |
|
||||
| -l | 输出主类的全名,如果是执行的jar,则输出jar的路径 |
|
||||
| -v | 输出JVM启动时的JVM参数 |
|
||||
| -m | 输出虚拟机进程启动时传递给主类main() 函数的参数 |
|
||||
|
||||
#### 4.2.2 jstat:虚拟机统计信息监视工具
|
||||
|
||||
命令格式:jstat [options] vmid [interval \[s|ms][count]] ]
|
||||
|
||||
命令格式:jstat \[-命令选项]\[vmid]\[间隔时间/毫秒][查询次数]
|
||||
|
||||
| 参数 | 功能作用 |
|
||||
| ----------------- | ------------------------------------------------------------ |
|
||||
| -class | 监视类装载、卸载数量,以及总空间和装载耗时等 |
|
||||
| -gc | 监视堆中 eden、 survivor 、老年代、元空间等空间大小和已使用情况,GC次数、耗时等 |
|
||||
| -gcmetacapacity | 元空间 |
|
||||
| -gcutil | 与gc 类似,但是注重的是占比情况 |
|
||||
| -printcompilation | 输出已经被JIT重新编译的方法 |
|
||||
| -gcoldcapacity | 老年代统计信息 |
|
||||
| -gcnew | 新生代 |
|
||||
| -gccause | 与-gcutil相似,但是会输出上一次GC的原因 |
|
||||
|
||||
##### 1. gc 参数返回结果
|
||||
|
||||
使用-gc 命令; 为了能够更加直观,在程序中设置了VM相关参数; 然后运行、查看结果并分析。
|
||||
|
||||
| 参数 | 解析 |
|
||||
| ---- | -------------------- |
|
||||
| S0C | surivor(s0)区域大小 |
|
||||
| s1c | s1区大小 |
|
||||
| S0U | S0的使用大小 |
|
||||
| S1U | S1的使用大小 |
|
||||
| EC | eden可以使用的大小 |
|
||||
| EU | eden已经使用 |
|
||||
| OC | 老年代可以使用的大小 |
|
||||
| OU | 老年代已经使用的带下 |
|
||||
| MC | 元空间可以使用的大小 |
|
||||
| MU | 元空间已经使用的大小 |
|
||||
| CCSC | 压缩类空间大小 |
|
||||
| CCSU | 压缩类已经使用大小 |
|
||||
| YGC | 年轻代垃圾回收次数 |
|
||||
| YGCT | 年轻代垃圾回收总耗时 |
|
||||
| FGC | 老年代垃圾回收次数 |
|
||||
| FGCT | 老年代垃圾回收总耗时 |
|
||||
| GCT | 垃圾回收消耗总时间 |
|
||||
|
||||
##### 2. class 参数返回结果
|
||||
|
||||
-class 参数; 监视类装载、卸载数量、总空间以及装载所耗费的时间。
|
||||
|
||||
| 参数 | 解析 |
|
||||
| -------- | --------------- |
|
||||
| Loaded | 加载class的数量 |
|
||||
| Bytes | 占用空间大小 |
|
||||
| Unloaded | 未加载数量 |
|
||||
| Bytes | 未加载占用空间 |
|
||||
| Time | 时间 |
|
||||
|
||||
##### 3. gcmetacapacity 参数返回结果
|
||||
|
||||
元数据空间统计(-gcmetacapacity)
|
||||
|
||||
| 参数 | 描述 |
|
||||
| ----- | ---------------------- |
|
||||
| MCMN | 最小元数据容量 |
|
||||
| MCMX | 最大元数据容量 |
|
||||
| MC | 当前元数据空间大小 |
|
||||
| CCSMN | 最小压缩类空间大小 |
|
||||
| CCSMX | 最大压缩类空间大小 |
|
||||
| CCSC | 当前压缩类空间大小 |
|
||||
| YGC | 年轻代垃圾回收次数 |
|
||||
| FGC | 老年代垃圾回收次数 |
|
||||
| FGCT | 老年代垃圾回收消耗时间 |
|
||||
| GCT | 垃圾回收消耗总时间 |
|
||||
|
||||
#### 4.2.3 jinfo:Java配置信息工具
|
||||
|
||||
命令格式:jinfo [option] ipd
|
||||
|
||||
在控制台输入 jinfo ,则会提示相关命令参数,可借助提示执行相关命令。
|
||||
|
||||
```shell
|
||||
-flag <name> to print the value of the named VM flag
|
||||
-flag [+|-]<name> to enable or disable the named VM flag
|
||||
-flag <name>=<value> to set the named VM flag to the given value
|
||||
-flags to print VM flags
|
||||
-sysprops to print Java system properties
|
||||
<no option> to print both of the above
|
||||
```
|
||||
|
||||
#### 4.2.4 jmap:Java内存映像工具
|
||||
|
||||
`jmap`(JVM Memory Map for java)命令用于生成`堆转储快照`; 当然还可以使用`-XX:HeapDumpOnOutOfMemoryError` 参数,可以让虚拟机在OOM异常之后自动生产dump文件。
|
||||
|
||||
| 选项 | 作用 |
|
||||
| ------------- | ------------------------------------------------------------ |
|
||||
| -dump | 生成Java堆转储快照。 格式 -dump:[live,]format=b,file=< filename >,其中live子参数说明是否只dump出存活的对象 |
|
||||
| finalizerinfo | 显示F-Queue中等待Finalizer线程执行finalize方法的对象 |
|
||||
| -heap | 显示java堆详细信息,如使用哪种收集器、参数配置、分代状况等 |
|
||||
| -histo | 显示堆中对象统计信息,包括类,实例数量、合计容量 |
|
||||
| -F | 当虚拟机进程对 -dump选项没有响应时,可使用这个选项强制生成dump快照 |
|
||||
|
||||
#### 4.2.5 jhat:虚拟机堆转储快照分析工具
|
||||
|
||||
可用 VisualVM 或者其他可视化分析工具代替
|
||||
|
||||
#### 4.2.6 jstack:Java堆栈跟踪工具
|
||||
|
||||
jstack [ option ] vmid
|
||||
|
||||
| 参数 | 作用 |
|
||||
| ---- | -------------------------------------------- |
|
||||
| -F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
|
||||
| -l | 除堆栈外,显示关于锁的附加信息 |
|
||||
| -m | 如果调用本地方法的话,可以显示 c/C++的堆栈 |
|
||||
|
||||
### 4.3 JDK的可视化工具
|
||||
|
||||
官方工具1: JConsole(Java监视与管理控制台)
|
||||
|
||||
官方工具2: VisualVM(多合一故障处理工具)
|
||||
|
||||
IDEA下推荐插件:JProfiler
|
||||
|
||||
|
||||
|
||||
## 第十二章 Java内存模型
|
||||
|
||||
<div align="center"> <img src="https://github.com/heibaiying/LearningNotes/blob/master/pictures/java内存模型.png"/> </div></br>
|
||||
|
||||
关于主内存与工作内存之间具体的交互协议,即一个变量如何从主内存拷贝到工作内存、 如何从工作内存同步回主内存之类的实现细节,Java内存模型中定义了以下8种操作来完成 :
|
||||
|
||||
1. **lock(锁定)**:作用于主内存的变量,它把一个变量标识为一条线程独占的状态。
|
||||
2. **unlock(解锁)**:作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
|
||||
3. **read(读取)**:作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用。
|
||||
4. **load(载入)**:作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
|
||||
5. **use(使用)**:作用于工作内存的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作。
|
||||
6. **assign(赋值)**:作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
|
||||
7. **store(存储)**:作用于工作内存的变量,它把工作内存中一个变量的值传送到主内存中,以便随后的write操作使用。
|
||||
8. **write(写入)**:作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。
|
||||
|
||||
|
||||
|
||||
**Java内存模型还规定了在执行上述8种基本操作时必须满足如下规则:**
|
||||
|
||||
1. 不允许read和load、 store和write操作之一单独出现,即不允许一个变量从主内存读取了但工作内存不接受,或者从工作内存发起回写了但主内存不接受的情况出现。
|
||||
2. 不允许一个线程丢弃它的最近的assign操作,即变量在工作内存中改变了之后必须把该变化同步回主内存。
|
||||
3. 不允许一个线程无原因地(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存中。
|
||||
4. 一个新的变量只能在主内存中“诞生”,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量,换句话说,就是对一个变量实施use、 store操作之前,必须先执行过了assign和load操作。
|
||||
5. 一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。
|
||||
6. 如果对一个变量执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始化变量的值。
|
||||
7. 如果一个变量事先没有被lock操作锁定,那就不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定住的变量。
|
||||
8. 对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、 write操作)
|
||||
|
||||
|
||||
|
||||
**先行发生原则:**
|
||||
|
||||
1. **程序次序规则**(Program Order Rule):在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。 准确地说,应该是控制流顺序而不是程序代码顺序,因为要考虑分支、 循环等结构。
|
||||
2. **管程锁定规则**(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作。 这里必须强调的是同一个锁,而“后面”是指时间上的先后顺序。
|
||||
3. **volatile变量规则**(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读操作,这里的“后面”同样是指时间上的先后顺序。
|
||||
4. **线程启动规则**(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。
|
||||
5. **线程终止规则**(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread.join()方法结束、 Thread.isAlive()的返回值等手段检测到线程已经终止执行。
|
||||
6. **线程中断规则**(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。
|
||||
7. **对象终结规则**(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。
|
||||
8. **传递性**(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。
|
||||
|
BIN
pictures/Collection.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
pictures/Executors.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
pictures/Node节点.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
pictures/RabbitMQ模型架构.png
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
pictures/TemporalAdjuster类中的工厂方法.png
Normal file
After Width: | Height: | Size: 153 KiB |
BIN
pictures/abstractCollection.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
pictures/direct-exchange.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
pictures/flatmap1.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
pictures/flatmap2.png
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
pictures/java内存模型.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
pictures/java虚拟机运行时数据区.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
pictures/java集合类图.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
pictures/js原型1.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
pictures/js原型2.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
pictures/js对象属性.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
pictures/kafka主题和分区.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
pictures/kafka多消费者组.png
Normal file
After Width: | Height: | Size: 187 KiB |
BIN
pictures/kafka消费者.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
pictures/kafka生产组件图.png
Normal file
After Width: | Height: | Size: 239 KiB |
BIN
pictures/kafka集群复制.png
Normal file
After Width: | Height: | Size: 203 KiB |
BIN
pictures/memcached 存储结构.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
pictures/msm相关依赖.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
pictures/topic-exchange.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
pictures/事件流.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
pictures/偏移量.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
pictures/原子包.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
pictures/合理线程池数量.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
pictures/大数据技术栈.xmind
Normal file
BIN
pictures/客户区大小.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
pictures/延迟队列.png
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
pictures/文件属性信息.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
pictures/文件权限的字符与数字表示.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
pictures/消息队列运转过程.png
Normal file
After Width: | Height: | Size: 200 KiB |
BIN
pictures/滚动大小.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
pictures/设备文件名称.png
Normal file
After Width: | Height: | Size: 19 KiB |