Compare commits
42 Commits
c1fc5ac09c
...
9d9b55a2ae
Author | SHA1 | Date | |
---|---|---|---|
|
9d9b55a2ae | ||
|
818ccbca79 | ||
|
fcdbe12190 | ||
|
f3754a92d4 | ||
|
72019c2b09 | ||
|
a19b2744b0 | ||
|
19cb0cd775 | ||
|
372cb240ca | ||
|
954364882e | ||
|
3f2471340c | ||
|
0d8c0c0fb3 | ||
|
f3c36f2916 | ||
|
5678155022 | ||
|
ef93bec410 | ||
|
d993af3421 | ||
|
76b220540b | ||
|
a13c531f35 | ||
|
881bc5c591 | ||
|
22e3ef1efc | ||
|
9153648dba | ||
|
4df51f86e9 | ||
|
6bdc809e2f | ||
|
72c52dba3e | ||
|
893c673f1c | ||
|
2975f2f244 | ||
|
a788fc300d | ||
|
7f877f8e5f | ||
|
8f8d7c102a | ||
|
cf9e96aa6c | ||
|
566c75d5a4 | ||
|
675d97ce1b | ||
|
fac0c7698d | ||
|
be1b2f46ce | ||
|
be6c1faa03 | ||
|
7703240ba3 | ||
|
638181bf70 | ||
|
44e5cfa5d2 | ||
|
e5d5c357b3 | ||
|
db776f0fd2 | ||
|
dff5130082 | ||
|
8f2cc4b8b2 | ||
|
bc083f989c |
29
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Docs Test
|
||||
|
||||
on:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
test-docs:
|
||||
name: Test docs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
run_install: true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
cache: pnpm
|
||||
|
||||
- name: Build test
|
||||
env:
|
||||
NODE_OPTIONS: --max_old_space_size=4096
|
||||
run: pnpm docs:build
|
13
.gitignore
vendored
@ -1,15 +1,14 @@
|
||||
/node_modules
|
||||
/package-lock.json
|
||||
node_modules/
|
||||
# *.drawio
|
||||
*.drawio.bkp
|
||||
.DS_Store
|
||||
# VS Code Config file
|
||||
.vscode/
|
||||
# Vuepress Cache
|
||||
.cache/
|
||||
# Vuepress Temp
|
||||
.temp/
|
||||
# Vuepress Output
|
||||
# VuePress Cache
|
||||
**/.vuepress/.cache/
|
||||
# VuePress Temp
|
||||
**/.vuepress/.temp/
|
||||
# VuePress Output
|
||||
dist/
|
||||
# Build files
|
||||
packages/*/lib/
|
||||
|
4
.husky/pre-commit
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm nano-staged
|
20
.markdownlint.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"default": true,
|
||||
"MD003": {
|
||||
"style": "atx"
|
||||
},
|
||||
"MD004": {
|
||||
"style": "dash"
|
||||
},
|
||||
"MD013": false,
|
||||
"MD024": {
|
||||
"allow_different_nesting": true
|
||||
},
|
||||
"MD035": {
|
||||
"style": "---"
|
||||
},
|
||||
"MD040": false,
|
||||
"MD045": false,
|
||||
"MD046": false,
|
||||
"MD049": false
|
||||
}
|
4
.markdownlintignore
Normal file
@ -0,0 +1,4 @@
|
||||
**/node_modules/**
|
||||
|
||||
# markdown snippets
|
||||
*.snippet.md
|
15
.prettierignore
Normal file
@ -0,0 +1,15 @@
|
||||
# Vuepress Cache
|
||||
**/.vuepress/.cache/**
|
||||
# Vuepress Temp
|
||||
**/.vuepress/.temp/**
|
||||
# Vuepress Output
|
||||
dist/
|
||||
|
||||
# Node modules
|
||||
node_modules/
|
||||
|
||||
# pnpm lock file
|
||||
pnpm-lock.yaml
|
||||
|
||||
index.html
|
||||
sw.js
|
375
README.en.md
@ -1,375 +0,0 @@
|
||||
<p style="text-align:center">
|
||||
<a href="https://github.com/Snailclimb/JavaGuide" target="_blank">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/csdn/1c00413c65d1995993bf2b0daf7b4f03.png" width=""/>
|
||||
</a>
|
||||
</p>
|
||||
<p style="text-align:center">
|
||||
<a href="https://javaguide.cn/"><img src="https://img.shields.io/badge/阅读-read-brightgreen.svg" alt="阅读"></a>
|
||||
<img src="https://img.shields.io/github/stars/Snailclimb/JavaGuide" alt="stars"/>
|
||||
<img src="https://img.shields.io/github/forks/Snailclimb/JavaGuide" alt="forks"/>
|
||||
<img src="https://img.shields.io/github/issues/Snailclimb/JavaGuide" alt="issues"/>
|
||||
</p>
|
||||
<h3 style="text-align:center">Recommended</h3>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="text-align:center" valign="middle">
|
||||
<a href="https://sourl.cn/e7ee87">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiu.png" style="margin: 0 auto;width:850px" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
## Java
|
||||
|
||||
### Basis
|
||||
|
||||
**Knowledge points/interview questions**: (Must see:+1: ):
|
||||
|
||||
- [Java Basics Knowledge Points/Interview Questions Summary(1)](docs/java/basis/java-basic-questions-01.md)
|
||||
- [Java Basics Knowledge Points/Interview Questions Summary(2)](docs/java/basis/java-basic-questions-01.md)
|
||||
- [Java Basics Knowledge Points/Interview Questions Summary(3)](docs/java/basis/java-basic-questions-01.md)
|
||||
|
||||
**Important Knowledge Points Explained:**
|
||||
|
||||
- [Why only value passing in Java?](docs/java/basis/why-thereis-only-value-passing-in-java.md)
|
||||
- [What is the reflection mechanism? What are the application scenarios of reflection mechanism?](docs/java/basis/反射机制详解.md)
|
||||
- (docs/java/basis/proxy-model-detail.md) [proxy-model-detail: static proxy + JDK/CGLIB dynamic proxy practice](docs/java/basis/代理模式详解.md)
|
||||
- [What are the common IO models and what is the difference between BIO, NIO, AIO in Java?](docs/java/basis/io模型详解.md)
|
||||
- [BigDecimal solve floating point problem](docs/java/basis/bigdecimal.md)
|
||||
|
||||
### Collection
|
||||
|
||||
1. **[Java collection FAQ summary](docs/java/collection/java集合框架基础知识&面试题总结.md)** (must see :+1:)
|
||||
2. [Summary of considerations for using Java containers](docs/java/collection/java集合使用注意事项.md)
|
||||
3. **source code analysis** : [ArrayList source code + expansion mechanism analysis](docs/java/collection/arraylist-source-code.md),
|
||||
[HashMap(JDK1.8) source code + underlying data structure analysis](docs/java/collection/ hashmap-source-code.md),
|
||||
[ConcurrentHashMap source code + underlying data structure analysis](docs/java/collection/concurrent-hash-map-source-code.md)
|
||||
|
||||
### Concurrency
|
||||
|
||||
**Knowledge/Interview Questions:** (Must see :+1:)
|
||||
|
||||
1. **[Java concurrency basic common interview questions summary](docs/java/concurrent/java并发基础常见面试题总结.md)**
|
||||
2. **[Java concurrency advanced common interview questions summary](docs/java/concurrent/java并发进阶常见面试题总结.md)**
|
||||
|
||||
**Important Knowledge Points Explained:**
|
||||
|
||||
1. **Thread pool**: [Java thread pool learning summary](./docs/java/concurrent/java-thread-pool-summary.md), [Java thread pooling best practices](./docs/java/concurrent/java-thread-pool-best-practices.md)
|
||||
2. [ThreadLocal keyword resolution](docs/java/concurrent/threadlocal.md)
|
||||
3. [Java concurrency container summary](docs/java/concurrent/java-concurrent-collections.md)
|
||||
4. [Atomic atomic class summary](docs/java/concurrent/atomic-classes.md)
|
||||
5. [AQS principle and AQS synchronization component summary](docs/java/concurrent/aqs.md)
|
||||
6. [Getting Started with CompletableFuture](docs/java/concurrent/completablefuture-intro.md)
|
||||
|
||||
### JVM (must see :+1:)
|
||||
|
||||
This part of JVM mainly refers to [JVM Virtual Machine Specification-Java8 ](https://docs.oracle.com/javase/specs/jvms/se8/html/index.html) and Mr. Zhou's [In-depth Understanding of Java Virtual Machine (3rd Edition)](https://book.douban.com/subject/34907497/) (Highly recommended to read more than once!) .
|
||||
|
||||
1. **[Java Memory Regions](https://javaguide.cn/java/jvm/jvm-garbage-collection/)**
|
||||
2. **[JVM Garbage Collection](https://javaguide.cn/java/jvm/jvm-garbage-collection/)**
|
||||
3. [JDK monitoring and troubleshooting tools](https://javaguide.cn/java/jvm/jdk-monitoring-and-troubleshooting-tools/)
|
||||
4. [Class file structure](https://javaguide.cn/java/jvm/class-file-structure/)
|
||||
5. **[Class loading process](https://javaguide.cn/java/jvm/class-loading-process/)**
|
||||
6. [Class loader](https://javaguide.cn/java/jvm/classloader/)
|
||||
7. **[[To be completed] Summary of the most important JVM parameters (half of the translation is perfected)](https://javaguide.cn/java/jvm/jvm-parameters-intro/)**
|
||||
8. **[[Extra Meal] The Big White Word takes you through the JVM](https://javaguide.cn/java/jvm/jvm-intro/)**
|
||||
|
||||
### New features
|
||||
|
||||
1. **Java 8**: [Java 8 new features summary](docs/java/new-features/Java8新特性总结.md), [Java8 common new features summary](docs/java/new-features/java8-common-new-features.md)
|
||||
2. **Java9~Java15** : [An article to take you through the important new features of JDK9~15!](./docs/java/new-features/java新特性总结.md)
|
||||
|
||||
### Tips
|
||||
|
||||
1. [JAD decompile](docs/java/tips/JAD反编译tricks.md)
|
||||
2. [Handy for locating common Java performance problems](./docs/java/tips/locate-performance-problems/手把手教你定位常见Java性能问题.md)
|
||||
|
||||
|
||||
## Computer Basics
|
||||
|
||||
👉 **[Illustrated Computer Fundamentals PDF Download](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100021725&idx=1&sn=2db9664ca25363139a81691043e9fd8f&chksm=4ea19a1679d61300d8990f7e43bfc7f476577a81b712cf0f9c6f6552a8b219bc081efddb5c54#rd)** .
|
||||
|
||||
### Operating system
|
||||
|
||||
1. [OS FAQ summary!](docs/cs-basics/operating-system/操作系统常见面试题&知识点总结.md)
|
||||
2. [Backend programmer essential Linux basics summary](docs/cs-basics/operating-system/linux-intro.md)
|
||||
3. [Introduction to Shell Programming](docs/cs-basics/operating-system/shell-intro.md)
|
||||
|
||||
### Networking
|
||||
|
||||
1. [Computer Network Common Interview Questions](docs/cs-basics/network/计算机网络常见面试题.md)
|
||||
2. [Xie Xiren teacher's "computer network" content summary](docs/cs-basics/network/谢希仁老师的《计算机网络》内容总结.md)
|
||||
|
||||
### Data Structures
|
||||
|
||||
**Diagrammatic Data Structures :**
|
||||
|
||||
1. [Linear data structure :array, chain table, stack, queue](docs/cs-basics/data-structure/线性数据结构.md)
|
||||
2. [diagram](docs/cs-basics/data-structure/图.md)
|
||||
3. [heap](docs/cs-basics/data-structure/堆.md)
|
||||
4. [tree](docs/cs-basics/data-structure/树.md) : focus on [red-black-tree](docs/cs-basics/data-structure/红黑树.md), B-, B+, B* tree, LSM tree
|
||||
|
||||
Other common data structures : 1.
|
||||
|
||||
1. [Bloom filter](docs/cs-basics/data-structure/bloom-filter.md)
|
||||
|
||||
|
||||
### Algorithm
|
||||
|
||||
This part of the algorithm is very important, if you do not know how to learn the algorithm, you can look at what I wrote.
|
||||
|
||||
- [Recommended Algorithm Learning Books + Resources](https://www.zhihu.com/question/323359308/answer/1545320858) 。
|
||||
- [如何刷Leetcode?](https://www.zhihu.com/question/31092580/answer/1534887374)
|
||||
|
||||
**Summary of common algorithm problems** :
|
||||
|
||||
- [Several Common String Algorithm Questions Summarized ](docs/cs-basics/algorithms/几道常见的字符串算法题.md)
|
||||
- [Summary of several common algorithm problems of the chain table ](docs/cs-basics/algorithms/几道常见的链表算法题.md)
|
||||
- [Link offer some programming questions](docs/cs-basics/algorithms/剑指offer部分编程题.md)
|
||||
|
||||
In addition,[GeeksforGeeks]( https://www.geeksforgeeks.org/fundamentals-of-algorithms/) This site summarizes the common algorithms, which are more comprehensive and systematic.
|
||||
|
||||
## Database
|
||||
|
||||
### MySQL
|
||||
|
||||
**Summary:**
|
||||
|
||||
1. [Database Basics Summary](docs/database/数据库基础知识.md)
|
||||
2. **[MySQL Knowledge Summary](docs/database/mysql/mysql知识点&面试题总结.md)** (Must see:+1:)
|
||||
5. [MySQL High Performance Optimization Specification Recommendations](docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md)
|
||||
|
||||
**Important knowledge points:**
|
||||
|
||||
1. [Transaction isolation level (graphic detail)](docs/database/mysql/transaction-isolation-level.md)
|
||||
2. [MySQL's Three Major Logs (binlog, redo log and undo log) Explained](docs/database/mysql/mysql-logs.md)
|
||||
3. [InnoDB storage engine implementation of MVCC](docs/database/mysql/innodb-implementation-of-mvcc.md)
|
||||
4. [How does a SQL statement get executed in MySQL?](docs/database/mysql/how-sql-executed-in-mysql.md)
|
||||
5. [Character set details: Why is it not recommended to use utf8 in MySQL?](docs/database/字符集.md)
|
||||
6. [A little thought on how to store time in the database](docs/database/mysql/some-thoughts-on-database-storage-time.md)
|
||||
|
||||
### Redis
|
||||
|
||||
1. [Redis FAQ Summary](docs/database/redis/redis-questions-01.md)
|
||||
2. [3 common cache read and write strategies](docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md)
|
||||
|
||||
## Search Engine
|
||||
|
||||
It is used to improve search efficiency and functions similarly to browser search engines. The more common search engines are Elasticsearch (recommended) and Solr.
|
||||
|
||||
## System design
|
||||
|
||||
### System design essential foundation
|
||||
|
||||
#### RESTful API
|
||||
|
||||
When we do back-end development, our main job is to provide APIs for front-end or other back-end services such as APIs for querying user data. a RESTful API is an API built on REST, and it is an API designed to be better used.
|
||||
|
||||
Related reading: [RestFul API Brief Tutorial](docs/system-design/basis/RESTfulAPI.md)
|
||||
|
||||
#### Name
|
||||
|
||||
During programming, you must pay attention to naming. Because a good naming is a comment, others will know what your variable, method or class does as soon as they see your naming!
|
||||
|
||||
Read more about: [Java Naming](docs/system-design/naming.md) 。
|
||||
|
||||
### Common frameworks
|
||||
|
||||
If you have not touched Java Web development, you can first look at my summary of [J2EE Basics](docs/system-design/J2EE基础知识.md). Although much of the content in this article is now obsolete, it will give you a deeper understanding of Java backend technology development.
|
||||
|
||||
#### Spring/SpringBoot (must see :+1:)
|
||||
|
||||
**Knowledge/Interview Questions:**
|
||||
|
||||
1. **[Spring FAQ Summary](docs/system-design/framework/spring/Spring常见问题总结.md)**
|
||||
2. **[SpringBoot Getting Started Guide](https://github.com/Snailclimb/springboot-guide)**
|
||||
|
||||
**Important Knowledge Points Explained:** 1.
|
||||
|
||||
1. **[Spring/Spring Boot common annotations summary! Arrangement!](./docs/system-design/framework/spring/Spring&SpringBoot常用注解总结.md)**
|
||||
2. **[Spring Transaction Summary](docs/system-design/framework/spring/Spring事务总结.md)**
|
||||
3. [What design patterns are used in Spring?](docs/system-design/framework/spring/Spring设计模式总结.md)
|
||||
4. **[SpringBoot auto-assembly principle?"](docs/system-design/framework/spring/SpringBoot自动装配原理.md)**
|
||||
|
||||
#### MyBatis
|
||||
|
||||
[MyBatis Common Interview Questions Summary](docs/system-design/framework/mybatis/mybatis-interview.md)
|
||||
|
||||
### Security
|
||||
|
||||
#### Certification Authorization
|
||||
|
||||
**[Fundamentals of Certification Authorization](docs/system-design/security/basis-of-authority-certification.md)** In this article I will introduce the common concepts of authentication and authorization: **Authentication**, **Authorization** and **Cookie**, **Session**, Token, **OAuth 2**, **SSO**. If you are not clear about these concepts, we suggest you read this article properly.
|
||||
|
||||
- **JWT** : JWT (JSON Web Token) is a form of authentication, where a JWT is essentially a signed piece of data in JSON format. Since it is signed, the recipient can verify its authenticity. Related reading.
|
||||
- [JWT Pros and Cons Analysis and Solutions to Common Problems](docs/system-design/security/jwt优缺点分析以及常见问题解决方案.md)
|
||||
- [Demo for beginners to get started with Spring Security With JWT](https://github.com/Snailclimb/spring-security-jwt-guide)
|
||||
|
||||
- **SSO(Single Sign On)**: **SSO(Single Sign On)** that is, single sign on means that a user has the right to access other systems related to him/her by logging into one of the multiple subsystems. For example, after we logged into Jingdong Finance, we also successfully logged into Jingdong Supermarket, Jingdong Home Appliances and other subsystems of Jingdong. Related reading: [**SSO Single Sign-On is enough to read this article! **](docs/system-design/security/sso-intro.md)
|
||||
|
||||
#### Data Desensitization
|
||||
|
||||
Data desensitization means that we deform sensitive information data according to specific rules, for example, we replace certain digits of cell phone numbers and ID numbers with *.
|
||||
|
||||
### Timed tasks
|
||||
|
||||
Recently, some friends asked about timing task related issues. So, I simply wrote an article to summarize some concepts of timed tasks and some common timed task technology options: ["Java Timed Tasks Revealed"].(./docs/system-design/定时任务.md)
|
||||
|
||||
## Distributed
|
||||
|
||||
### CAP theory and BASE theory
|
||||
|
||||
CAP is also the combination of the initials Consistency, Availability, and Partition Tolerance.
|
||||
|
||||
**BASE** is an acronym for **Basically Available**, **Soft-state**, and **Eventually Consistent**. The BASE theory is the result of a trade-off between consistency and availability in the CAP, and is derived from a summary of distributed practices for large-scale Internet systems, evolving from the CAP theorem, which significantly reduces our system requirements.
|
||||
|
||||
Related reading: [CAP Theory and BASE Theory Explained](docs/distributed-system/theorem&algorithm&protocol/cap&base-theorem.md)
|
||||
|
||||
### Paxos algorithm and Raft algorithm
|
||||
|
||||
The **Paxos algorithm** was born in 1990 as a classical algorithm for solving the consistency of distributed systems. However, since the Paxos algorithm was very difficult to understand and implement, there were continuous attempts to simplify it. Only in 2013 was a distributed consistency algorithm born that is easier to understand and implement than the Paxos algorithm - the **Raft algorithm**.
|
||||
|
||||
### RPC
|
||||
|
||||
RPC makes calling remote service calls as easy as calling local methods.
|
||||
|
||||
Dubbo is a home-grown RPC framework , open source by Ali . Related reading.
|
||||
|
||||
- [Dubbo FAQ Summary](docs/distributed-system/rpc/dubbo.md)
|
||||
|
||||
### API gateway
|
||||
|
||||
Gateways are mainly used for request forwarding, security authentication, protocol conversion, and disaster recovery.
|
||||
|
||||
Related reading.
|
||||
|
||||
- [Why gateways? What common gateway systems do you know of?](docs/distributed-system/api-gateway.md)
|
||||
- [Design and Implementation of Shepherd, a 10 Billion Dollar API Gateway Service](https://tech.meituan.com/2021/05/20/shepherd-api-gateway.html)
|
||||
|
||||
### Distributed IDs
|
||||
|
||||
In complex distributed systems, a large amount of data and messages often need to be uniquely identified. For example, after the data volume is too large, it is often necessary to split the data into libraries and tables, and after the splitting of the libraries and tables, a unique ID is needed to identify a piece of data or a message, and the self-incrementing ID of the database obviously cannot meet the demand. Related reading: [Why distributed id? What are the distributed id generation solutions?](docs/distributed-system/distributed-id.md)
|
||||
|
||||
### Distributed transactions
|
||||
|
||||
** A distributed transaction is one in which the participants of the transaction, the server supporting the transaction, the resource server, and the transaction manager are located on different nodes of different distributed systems. **
|
||||
|
||||
Simply put, a large operation consists of different small operations that are distributed across different servers and belong to different applications, and the distributed transaction needs to guarantee that all of these small operations either succeed or fail. Essentially, distributed transactions are about ensuring data consistency across different databases.
|
||||
|
||||
### Distributed Orchestration
|
||||
|
||||
**ZooKeeper**.
|
||||
|
||||
> The first two articles may have content overlapping parts, we recommend reading them both.
|
||||
|
||||
1. [[Getting Started] Summary of ZooKeeper-related concepts](docs/distributed-system/分布式协调/zookeeper/zookeeper-intro.md)
|
||||
2. [[Advanced] Summary of ZooKeeper Related Concepts](docs/distributed-system/分布式协调/zookeeper/zookeeper-plus.md)
|
||||
3. [[Hands-on] ZooKeeper hands-on](docs/distributed-system/分布式协调/zookeeper/zookeeper-in-action.md)
|
||||
|
||||
## High performance
|
||||
|
||||
### Message Queues
|
||||
|
||||
Message queues are used in distributed systems primarily for decoupling and peak shaving. Related reading: [Message Queues FAQ Summary](docs/high-performance/message-queue/message-queue.md)。
|
||||
|
||||
1. **RabbitMQ** : [Getting Started with RabbitMQ](docs/high-performance/message-queue/rabbitmq-intro.md)
|
||||
2. **RocketMQ** : [Getting Started with RocketMQ](docs/high-performance/message-queue/rocketmq-intro)、[A few simple questions and answers for RocketMQ](docs/high-performance/message-queue/rocketmq-questions.md)
|
||||
3. **Kafka** : [Kafka FAQ Summary](docs/high-performance/message-queue/kafka知识点&面试题总结.md)
|
||||
|
||||
### Read-write separation & split database and split table
|
||||
|
||||
Read-write separation is mainly to separate the database read and write operations on different database nodes. The master server is responsible for writes and the slave server is responsible for reads. Alternatively, one master and one slave or one master and multiple slaves can be used.
|
||||
|
||||
Read-write separation can substantially improve read performance and marginally improve write performance. Therefore, read-write separation is more suitable for scenarios where there are more concurrent read requests from a single machine.
|
||||
|
||||
Library and table separation is to solve the problem of continuous database performance degradation due to the excessive amount of library and table data.
|
||||
|
||||
Common library and table splitting tools are: `sharding-jdbc` (Dangdang), `TSharding` (Mushroom Street), `MyCAT` (based on Cobar), `Cobar` (Alibaba).... We recommend using `sharding-jdbc`. Because, `sharding-jdbc` is a lightweight `Java` framework, served as a `jar` package, no extra O&M work for us, and good compatibility.
|
||||
|
||||
Related reading: [read-write separation & sharding summary of common problems](docs/high-performance/读写分离&分库分表.md)
|
||||
|
||||
### Load Balancing
|
||||
|
||||
Load balancing systems are often used to distribute tasks such as user request processing to multiple servers to improve the performance and reliability of a website, application or database.
|
||||
|
||||
Common load balancing systems include 3 types.
|
||||
|
||||
1. **DNS load balancing**: generally used to achieve geographic level balancing.
|
||||
2. **Hardware Load Balancing**: Load balancing is achieved through a separate hardware device such as F5 (hardware is usually expensive).
|
||||
3. **Software load balancing**: Load balancing is achieved by load balancing software such as Nginx.
|
||||
|
||||
## High Availability
|
||||
|
||||
Highly available describes a system that is available most of the time and can provide services to us. High availability means that the system is available even in the event of a hardware failure or system upgrade.
|
||||
|
||||
Related reading: **"[How to design a highly available system? What are the areas to consider?](docs/high-availability/高可用系统设计.md)》** 。
|
||||
|
||||
### Flow limiting
|
||||
|
||||
Flow limiting considers how to respond to system failures from the perspective of user access pressure.
|
||||
|
||||
The purpose of flow limiting is to limit the frequency of requests received by the server-side interface to prevent the service from hanging. For example, if an interface is limited to 100 requests per second, requests that exceed the limit are either dropped or placed in a queue for processing. Limiting the flow can effectively deal with the excessive number of burst requests. Related reading: [What is flow limiting? What are the flow limiting algorithms?](docs/high-availability/limit-request.md)
|
||||
|
||||
### Downgrading
|
||||
|
||||
Downgrading is the consideration of how to respond to system failures from the perspective of system functional priorities.
|
||||
|
||||
Service degradation refers to the strategic downgrading of some services and pages based on the current business situation and traffic when the server is under pressure, in order to free up server resources to ensure the normal operation of core tasks.
|
||||
|
||||
### Meltdown
|
||||
|
||||
Meltdown and degradation are two concepts that are easily confused and do not have the same meaning.
|
||||
|
||||
Downgrades are intended to deal with failures of the system itself, while meltdowns are intended to deal with failures of external systems or third-party systems on which the current system depends.
|
||||
|
||||
### Queuing
|
||||
|
||||
An alternative type of flow limitation, analogous to real-world queuing. If you've played League of Legends, you'll know that every time there's an event, you have to go through a queue to get into the game.
|
||||
|
||||
### Clustering
|
||||
|
||||
Deploy multiple copies of the same service to avoid single points of failure.
|
||||
|
||||
### Timeout and retry mechanism
|
||||
|
||||
** Once a user's request goes beyond a certain time without a response, the request is ended and an exception is thrown. ** Failure to set a timeout may result in slow response times, or even a buildup of requests that prevents the system from processing them.
|
||||
|
||||
In addition, the number of retries is generally set to 3. More retries will not be beneficial, but will add pressure to the server (some scenarios may not be suitable to use the failure retry mechanism).
|
||||
|
||||
### Disaster recovery design and offsite multi-live
|
||||
|
||||
**Disaster recovery** = disaster recovery + backup.
|
||||
|
||||
- **Backup** : Backup several copies of all important data generated by the system.
|
||||
- **Disaster Tolerant** : Create two identical systems in offsite locations. When the system in one place suddenly hangs, the whole application system can be switched to the other one so that the system can provide services normally.
|
||||
|
||||
**Offsite Multi-Live** describes the deployment of services offsite and the simultaneous provisioning of services to the outside world. The main difference from traditional disaster recovery design is "multi-live", i.e., all sites are providing services to the public at the same time. Off-site multiplication is designed to deal with unexpected situations such as fires, earthquakes, and other natural or perceived disasters.
|
||||
|
||||
Related reading.
|
||||
|
||||
- [Read this article to understand off-site multi-live](https://mp.weixin.qq.com/s/T6mMDdtTfBuIiEowCpqu6Q)
|
||||
- [Four steps to build offsite multi-live](https://mp.weixin.qq.com/s/hMD-IS__4JE5_nQhYPYSTg)
|
||||
- ["Learning Architecture from Scratch" - 28 | Guarantees for Highly Available Business: Offsite Multi-Live Architecture](http://gk.link/a/10pKZ)
|
||||
|
||||
## About the Author
|
||||
|
||||
- [Personal Introduction Q & A](https://javaguide.cn/about-the-author/)
|
||||
- [I used to be an Internet addict too](https://javaguide.cn/about-the-author/internet-addiction-teenager/)
|
||||
- [Feelings after one month of onboarding](https://javaguide.cn/about-the-author/feelings-after-one-month-of-induction-training/)
|
||||
- [Feelings from graduation to six months of employment](https://javaguide.cn/about-the-author/feelings-of-half-a-year-from-graduation-to-entry/)
|
||||
- [A training institution stole my article into a video also on the B station popular](https://javaguide.cn/about-the-author/my-article-was-stolen-and-made-into-video-and-it-became-popular/)
|
||||
|
||||
## Public
|
||||
|
||||
If you want to follow my updated articles and the dry goods I share in real time, you can follow my public number.
|
||||
|
||||
**《Java Interview Blitz》:** A PDF version of "Java Interview Blitz" derived from this document specifically for interviews [Public](#公众号) Reply back to **"Interview Blitz "** and get it for free!
|
||||
|
||||
<div align="center">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png" style="margin: 0 auto;" />
|
||||
</div>
|
||||
|
||||

|
146
README.md
@ -1,46 +1,39 @@
|
||||
推荐你通过在线阅读网站进行阅读,体验更好,速度更快!
|
||||
|
||||
* **[JavaGuide 在线阅读网站(新版,推荐👍)](https://javaguide.cn/)**
|
||||
* [JavaGuide 在线阅读版(老版)](https://snailclimb.gitee.io/javaguide/#/)
|
||||
- **[JavaGuide 在线阅读网站(新版,推荐 👍)](https://javaguide.cn/)**
|
||||
- [JavaGuide 在线阅读版(老版)](https://snailclimb.gitee.io/javaguide/#/)
|
||||
|
||||
[<img src="https://oss.javaguide.cn/xingqiu/xingqiu.png" style="width:850px;margin: 0 auto" />](https://sourl.cn/e7ee87)
|
||||
|
||||
<div align="center">
|
||||
<p>
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiu.png" style="margin: 0 auto; width: 850px;" />
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/Snailclimb/JavaGuide" target="_blank">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/csdn/1c00413c65d1995993bf2b0daf7b4f03.png" width="" />
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://javaguide.cn/"><img src="https://img.shields.io/badge/阅读-read-brightgreen.svg" alt="阅读" /></a>
|
||||
<img src="https://img.shields.io/github/stars/Snailclimb/JavaGuide" alt="stars" />
|
||||
<img src="https://img.shields.io/github/forks/Snailclimb/JavaGuide" alt="forks" />
|
||||
<img src="https://img.shields.io/github/issues/Snailclimb/JavaGuide" alt="issues" />
|
||||
</p>
|
||||
<p>
|
||||
<a href="https://github.com/Snailclimb/JavaGuide">Github</a> |
|
||||
<a href="https://gitee.com/SnailClimb/JavaGuide">Gitee</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
[](https://github.com/Snailclimb/JavaGuide)
|
||||
|
||||
[](https://javaguide.cn/)
|
||||

|
||||

|
||||

|
||||
|
||||
[GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide)
|
||||
|
||||
</div>
|
||||
|
||||
> 1. **面试专版** :准备面试的小伙伴可以考虑面试专版:[《Java 面试指北 》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html) (质量很高,专为面试打造,配合 JavaGuide 食用)。
|
||||
> 1. **知识星球** :专属面试小册/一对一交流/简历修改/专属求职指南,欢迎加入 [JavaGuide 知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看星球的详细介绍,一定一定一定确定自己真的需要再加入,一定一定要看完详细介绍之后再加我)。
|
||||
> 2. **转载须知** :以下所有文章如非文首说明为转载皆为我(Guide 哥)的原创,转载在文首注明出处,如发现恶意抄袭/搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!
|
||||
> 1. **转载须知** :以下所有文章如非文首说明为转载皆为我(Guide)的原创,转载在文首注明出处,如发现恶意抄袭/搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!
|
||||
|
||||
<div align="center">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png" style="margin: 0 auto;" />
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/gongzhonghaoxuanchuan.png" style="margin: 0 auto;" />
|
||||
</div>
|
||||
|
||||
<!-- #region home -->
|
||||
|
||||
## 项目相关
|
||||
|
||||
* [项目介绍](./docs/javaguide/intro.md)
|
||||
* [贡献指南](./docs/javaguide/contribution-guideline.md)
|
||||
* [常见问题](./docs/javaguide/faq.md)
|
||||
* [项目待办](./docs/javaguide/todo.md)
|
||||
- [项目介绍](./docs/javaguide/intro.md)
|
||||
- [贡献指南](./docs/javaguide/contribution-guideline.md)
|
||||
- [常见问题](./docs/javaguide/faq.md)
|
||||
- [项目待办](./docs/javaguide/todo.md)
|
||||
|
||||
## Java
|
||||
|
||||
@ -74,15 +67,15 @@
|
||||
|
||||
**源码分析** :
|
||||
|
||||
* [ArrayList 源码+扩容机制分析](./docs/java/collection/arraylist-source-code.md)
|
||||
* [HashMap(JDK1.8)源码+底层数据结构分析](./docs/java/collection/hashmap-source-code.md)
|
||||
* [ConcurrentHashMap 源码+底层数据结构分析](./docs/java/collection/concurrent-hash-map-source-code.md)
|
||||
- [ArrayList 源码+扩容机制分析](./docs/java/collection/arraylist-source-code.md)
|
||||
- [HashMap(JDK1.8)源码+底层数据结构分析](./docs/java/collection/hashmap-source-code.md)
|
||||
- [ConcurrentHashMap 源码+底层数据结构分析](./docs/java/collection/concurrent-hash-map-source-code.md)
|
||||
|
||||
### IO
|
||||
|
||||
* [IO 基础知识总结](./docs/java/io/io-basis.md)
|
||||
* [IO 设计模式总结](./docs/java/io/io-design-patterns.md)
|
||||
* [IO 模型详解](./docs/java/io/io-model.md)
|
||||
- [IO 基础知识总结](./docs/java/io/io-basis.md)
|
||||
- [IO 设计模式总结](./docs/java/io/io-design-patterns.md)
|
||||
- [IO 模型详解](./docs/java/io/io-model.md)
|
||||
|
||||
### 并发
|
||||
|
||||
@ -100,11 +93,11 @@
|
||||
- [Java 并发容器总结](./docs/java/concurrent/java-concurrent-collections.md)
|
||||
- [Atomic 原子类总结](./docs/java/concurrent/atomic-classes.md)
|
||||
- [AQS 详解](./docs/java/concurrent/aqs.md)
|
||||
- [CompletableFuture详解](./docs/java/concurrent/completablefuture-intro.md)
|
||||
- [CompletableFuture 详解](./docs/java/concurrent/completablefuture-intro.md)
|
||||
|
||||
### JVM (必看 :+1:)
|
||||
|
||||
JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle.com/javase/specs/jvms/se8/html/index.html) 和周志明老师的[《深入理解Java虚拟机(第3版)》](https://book.douban.com/subject/34907497/) (强烈建议阅读多遍!)。
|
||||
JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle.com/javase/specs/jvms/se8/html/index.html) 和周志明老师的[《深入理解 Java 虚拟机(第 3 版)》](https://book.douban.com/subject/34907497/) (强烈建议阅读多遍!)。
|
||||
|
||||
- **[Java 内存区域](./docs/java/jvm/memory-area.md)**
|
||||
- **[JVM 垃圾回收](./docs/java/jvm/jvm-garbage-collection.md)**
|
||||
@ -117,7 +110,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
### 新特性
|
||||
|
||||
- **Java 8** :[Java 8 新特性总结(翻译)](./docs/java/new-features/java8-tutorial-translate.md)、[Java8常用新特性总结](./docs/java/new-features/java8-common-new-features.md)
|
||||
- **Java 8** :[Java 8 新特性总结(翻译)](./docs/java/new-features/java8-tutorial-translate.md)、[Java8 常用新特性总结](./docs/java/new-features/java8-common-new-features.md)
|
||||
- [Java 9 新特性概览](./docs/java/new-features/java9.md)
|
||||
- [Java 10 新特性概览](./docs/java/new-features/java10.md)
|
||||
- [Java 11 新特性概览](./docs/java/new-features/java11.md)
|
||||
@ -148,10 +141,10 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
**重要知识点详解** :
|
||||
|
||||
- [OSI 和 TCP/IP 网络分层模型详解(基础)](./docs/cs-basics/network/osi&tcp-ip-model.md)
|
||||
- [OSI 和 TCP/IP 网络分层模型详解(基础)](./docs/cs-basics/network/osi-and-tcp-ip-model.md)
|
||||
- [应用层常见协议总结(应用层)](./docs/cs-basics/network/application-layer-protocol.md)
|
||||
- [HTTP vs HTTPS(应用层)](./docs/cs-basics/network/http&https.md)
|
||||
- [HTTP 1.0 vs HTTP 1.1(应用层)](./docs/cs-basics/network/http1.0&http1.1.md)
|
||||
- [HTTP vs HTTPS(应用层)](./docs/cs-basics/network/http-vs-https.md)
|
||||
- [HTTP 1.0 vs HTTP 1.1(应用层)](./docs/cs-basics/network/http1.0-vs-http1.1.md)
|
||||
- [HTTP 常见状态码(应用层)](./docs/cs-basics/network/http-status-codes.md)
|
||||
- [DNS 域名系统详解(应用层)](./docs/cs-basics/network/dns.md)
|
||||
- [TCP 三次握手和四次挥手(传输层)](./docs/cs-basics/network/tcp-connection-and-disconnection.md)
|
||||
@ -166,7 +159,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
- [线性数据结构 :数组、链表、栈、队列](./docs/cs-basics/data-structure/linear-data-structure.md)
|
||||
- [图](./docs/cs-basics/data-structure/graph.md)
|
||||
- [堆](./docs/cs-basics/data-structure/heap.md)
|
||||
- [树](./docs/cs-basics/data-structure/tree.md) :重点关注[红黑树](./docs/cs-basics/data-structure/red-black-tree.md)、B-,B+,B*树、LSM树
|
||||
- [树](./docs/cs-basics/data-structure/tree.md) :重点关注[红黑树](./docs/cs-basics/data-structure/red-black-tree.md)、B-,B+,B\*树、LSM 树
|
||||
|
||||
其他常用数据结构 :
|
||||
|
||||
@ -176,17 +169,17 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
算法这部分内容非常重要,如果你不知道如何学习算法的话,可以看下我写的:
|
||||
|
||||
* [算法学习书籍+资源推荐](https://www.zhihu.com/question/323359308/answer/1545320858) 。
|
||||
* [如何刷Leetcode?](https://www.zhihu.com/question/31092580/answer/1534887374)
|
||||
- [算法学习书籍+资源推荐](https://www.zhihu.com/question/323359308/answer/1545320858) 。
|
||||
- [如何刷 Leetcode?](https://www.zhihu.com/question/31092580/answer/1534887374)
|
||||
|
||||
**常见算法问题总结** :
|
||||
|
||||
* [几道常见的字符串算法题总结 ](./docs/cs-basics/algorithms/string-algorithm-problems.md)
|
||||
* [几道常见的链表算法题总结 ](./docs/cs-basics/algorithms/linkedlist-algorithm-problems.md)
|
||||
* [剑指 offer 部分编程题](./docs/cs-basics/algorithms/the-sword-refers-to-offer.md)
|
||||
* [十大经典排序算法](./docs/cs-basics/algorithms/10-classical-sorting-algorithms.md)
|
||||
- [几道常见的字符串算法题总结 ](./docs/cs-basics/algorithms/string-algorithm-problems.md)
|
||||
- [几道常见的链表算法题总结 ](./docs/cs-basics/algorithms/linkedlist-algorithm-problems.md)
|
||||
- [剑指 offer 部分编程题](./docs/cs-basics/algorithms/the-sword-refers-to-offer.md)
|
||||
- [十大经典排序算法](./docs/cs-basics/algorithms/10-classical-sorting-algorithms.md)
|
||||
|
||||
另外,[GeeksforGeeks]( https://www.geeksforgeeks.org/fundamentals-of-algorithms/) 这个网站总结了常见的算法 ,比较全面系统。
|
||||
另外,[GeeksforGeeks](https://www.geeksforgeeks.org/fundamentals-of-algorithms/) 这个网站总结了常见的算法 ,比较全面系统。
|
||||
|
||||
## 数据库
|
||||
|
||||
@ -196,9 +189,8 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
- [NoSQL 基础知识总结](./docs/database/nosql.md)
|
||||
- [字符集详解](./docs/database/character-set.md)
|
||||
- SQL :
|
||||
- [SQL语法基础知识总结](./docs/database/sql/sql-syntax-summary.md)
|
||||
- [SQL常见面试题总结](./docs/database/sql/sql-questions-01.md)
|
||||
|
||||
- [SQL 语法基础知识总结](./docs/database/sql/sql-syntax-summary.md)
|
||||
- [SQL 常见面试题总结](./docs/database/sql/sql-questions-01.md)
|
||||
|
||||
### MySQL
|
||||
|
||||
@ -209,14 +201,14 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
**重要知识点:**
|
||||
|
||||
- [MySQL索引详解](./docs/database/mysql/mysql-index.md)
|
||||
- [MySQL事务隔离级别图文详解)](./docs/database/mysql/transaction-isolation-level.md)
|
||||
- [MySQL三大日志(binlog、redo log和undo log)详解](./docs/database/mysql/mysql-logs.md)
|
||||
- [MySQL 索引详解](./docs/database/mysql/mysql-index.md)
|
||||
- [MySQL 事务隔离级别图文详解)](./docs/database/mysql/transaction-isolation-level.md)
|
||||
- [MySQL 三大日志(binlog、redo log 和 undo log)详解](./docs/database/mysql/mysql-logs.md)
|
||||
- [InnoDB 存储引擎对 MVCC 的实现](./docs/database/mysql/innodb-implementation-of-mvcc.md)
|
||||
- [SQL 语句在 MySQL 中的执行过程](./docs/database/mysql/how-sql-executed-in-mysql.md)
|
||||
- [MySQL 查询缓存详解](./docs/database/mysql/mysql-query-cache.md)
|
||||
- [MySQL执行计划分析](./docs/database/mysql/mysql-query-execution-plan.md)
|
||||
- [MySQL自增主键一定是连续的吗](./docs/database/mysql/mysql-auto-increment-primary-key-continuous.md)
|
||||
- [MySQL 执行计划分析](./docs/database/mysql/mysql-query-execution-plan.md)
|
||||
- [MySQL 自增主键一定是连续的吗](./docs/database/mysql/mysql-auto-increment-primary-key-continuous.md)
|
||||
- [MySQL 时间类型数据存储建议](./docs/database/mysql/some-thoughts-on-database-storage-time.md)
|
||||
- [MySQL 隐式转换造成索引失效](./docs/database/mysql/index-invalidation-caused-by-implicit-conversion.md)
|
||||
|
||||
@ -229,7 +221,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
**重要知识点:**
|
||||
|
||||
- [3种常用的缓存读写策略详解](./docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md)
|
||||
- [3 种常用的缓存读写策略详解](./docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md)
|
||||
- [Redis 5 种基本数据结构详解](./docs/database/redis/redis-data-structures-01.md)
|
||||
- [Redis 3 种特殊数据结构详解](./docs/database/redis/redis-data-structures-02.md)
|
||||
- [Redis 持久化机制详解](./docs/database/redis/redis-persistence.md)
|
||||
@ -244,7 +236,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
## 搜索引擎
|
||||
|
||||
[Elasticsearch常见面试题总结(付费)](./docs/database/elasticsearch/elasticsearch-questions-01.md)
|
||||
[Elasticsearch 常见面试题总结(付费)](./docs/database/elasticsearch/elasticsearch-questions-01.md)
|
||||
|
||||

|
||||
|
||||
@ -260,13 +252,13 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
### Docker
|
||||
|
||||
* [Docker 核心概念总结](./docs/tools/docker/docker-intro.md)
|
||||
* [Docker 实战](./docs/tools/docker/docker-in-action.md)
|
||||
- [Docker 核心概念总结](./docs/tools/docker/docker-intro.md)
|
||||
- [Docker 实战](./docs/tools/docker/docker-in-action.md)
|
||||
|
||||
### Git
|
||||
|
||||
* [Git 核心概念总结](./docs/tools/git/git-intro.md)
|
||||
* [Github 实用小技巧总结](./docs/tools/git/github-tips.md)
|
||||
- [Git 核心概念总结](./docs/tools/git/git-intro.md)
|
||||
- [Github 实用小技巧总结](./docs/tools/git/github-tips.md)
|
||||
|
||||
## 系统设计
|
||||
|
||||
@ -308,14 +300,13 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
- [认证授权基础概念详解](./docs/system-design/security/basis-of-authority-certification.md)
|
||||
- [JWT 基础概念详解](./docs/system-design/security/jwt-intro.md)
|
||||
- [JWT 优缺点分析以及常见问题解决方案](./docs/system-design/security/advantages&disadvantages-of-jwt.md)
|
||||
- [JWT 优缺点分析以及常见问题解决方案](./docs/system-design/security/advantages-and-disadvantages-of-jwt.md)
|
||||
- [SSO 单点登录详解](./docs/system-design/security/sso-intro.md)
|
||||
- [权限系统设计详解](./docs/system-design/security/design-of-authority-system.md)
|
||||
|
||||
|
||||
#### 数据脱敏
|
||||
|
||||
数据脱敏说的就是我们根据特定的规则对敏感信息数据进行变形,比如我们把手机号、身份证号某些位数使用 * 来代替。
|
||||
数据脱敏说的就是我们根据特定的规则对敏感信息数据进行变形,比如我们把手机号、身份证号某些位数使用 \* 来代替。
|
||||
|
||||
#### 敏感词过滤
|
||||
|
||||
@ -333,10 +324,10 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
### 理论&算法&协议
|
||||
|
||||
- [CAP 理论和 BASE 理论详解](./docs/distributed-system/theorem&algorithm&protocol/cap&base-theorem.md)
|
||||
- [Paxos 算法详解](./docs/distributed-system/theorem&algorithm&protocol/paxos-algorithm.md)
|
||||
- [Raft 算法详解](./docs/distributed-system/theorem&algorithm&protocol/raft-algorithm.md)
|
||||
- [Gossip 协议详解](./docs/distributed-system/theorem&algorithm&protocol/gossip-protocl.md)
|
||||
- [CAP 理论和 BASE 理论详解](./docs/distributed-system/protocol/cap-and-base-theorem.md)
|
||||
- [Paxos 算法详解](./docs/distributed-system/protocol/paxos-algorithm.md)
|
||||
- [Raft 算法详解](./docs/distributed-system/protocol/raft-algorithm.md)
|
||||
- [Gossip 协议详解](./docs/distributed-system/protocol/gossip-protocl.md)
|
||||
|
||||
### API 网关
|
||||
|
||||
@ -360,8 +351,8 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
### RPC
|
||||
|
||||
* [RPC 基础常见知识点&面试题总结](./docs/distributed-system/rpc/rpc-intro.md)
|
||||
* [Dubbo 常见知识点&面试题总结](./docs/distributed-system/rpc/dubbo.md)
|
||||
- [RPC 基础常见知识点&面试题总结](./docs/distributed-system/rpc/rpc-intro.md)
|
||||
- [Dubbo 常见知识点&面试题总结](./docs/distributed-system/rpc/dubbo.md)
|
||||
|
||||
### ZooKeeper
|
||||
|
||||
@ -411,7 +402,7 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
### 降级&熔断
|
||||
|
||||
[降级&熔断详解](./docs/high-availability/fallback&circuit-breaker.md)
|
||||
[降级&熔断详解](./docs/high-availability/fallback-and-circuit-breaker.md)
|
||||
|
||||
### 超时&重试
|
||||
|
||||
@ -423,16 +414,16 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||
### 灾备设计和异地多活
|
||||
|
||||
**灾备** = 容灾+备份。
|
||||
**灾备** = 容灾+备份。
|
||||
|
||||
* **备份** : 将系统所产生的的所有重要数据多备份几份。
|
||||
* **容灾** : 在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。
|
||||
- **备份** : 将系统所产生的的所有重要数据多备份几份。
|
||||
- **容灾** : 在异地建立两个完全相同的系统。当某个地方的系统突然挂掉,整个应用系统可以切换到另一个,这样系统就可以正常提供服务了。
|
||||
|
||||
**异地多活** 描述的是将服务部署在异地并且服务同时对外提供服务。和传统的灾备设计的最主要区别在于“多活”,即所有站点都是同时在对外提供服务的。异地多活是为了应对突发状况比如火灾、地震等自然或者人为灾害。
|
||||
|
||||
## Star 趋势
|
||||
|
||||

|
||||

|
||||
|
||||
## 公众号
|
||||
|
||||
@ -440,3 +431,4 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
|
||||
|
||||

|
||||
|
||||
<!-- #endregion home -->
|
||||
|
@ -1,14 +1,15 @@
|
||||
import { defineUserConfig } from "vuepress";
|
||||
import { themeConfig } from "./themeConfig";
|
||||
import { searchPlugin } from "@vuepress/plugin-search";
|
||||
import { searchProPlugin } from "vuepress-plugin-search-pro";
|
||||
|
||||
import theme from "./theme.js";
|
||||
|
||||
export default defineUserConfig({
|
||||
dest: "./dist",
|
||||
theme: themeConfig,
|
||||
shouldPrefetch: false,
|
||||
|
||||
title: "JavaGuide(Java面试+学习指南)",
|
||||
description:
|
||||
"「Java学习指北+Java面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,复习 Java 知识点,首选 JavaGuide! ",
|
||||
|
||||
head: [
|
||||
// meta
|
||||
["meta", { name: "robots", content: "all" }],
|
||||
@ -43,28 +44,20 @@ export default defineUserConfig({
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();`,
|
||||
],
|
||||
["link", { rel: "stylesheet", href: "/iconfont/iconfont.css" }],
|
||||
["link", { rel: "icon", href: "/favicon.ico" }],
|
||||
],
|
||||
|
||||
locales: {
|
||||
"/": {
|
||||
lang: "zh-CN",
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
searchPlugin({
|
||||
// https://v2.vuepress.vuejs.org/zh/reference/plugin/search.html
|
||||
// 排除首页
|
||||
isSearchable: (page) => page.path !== "/",
|
||||
maxSuggestions: 10,
|
||||
hotKeys: ["s", "/"],
|
||||
// 用于在页面的搜索索引中添加额外字段
|
||||
getExtraFields: () => [],
|
||||
locales: {
|
||||
"/": {
|
||||
placeholder: "搜索",
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
theme,
|
||||
|
||||
plugins: [searchProPlugin({ indexContent: true })],
|
||||
|
||||
pagePatterns: ["**/*.md", "!*.snippet.md", "!.vuepress", "!node_modules"],
|
||||
|
||||
shouldPrefetch: false,
|
||||
});
|
||||
|
@ -1,10 +1,19 @@
|
||||
import { navbar } from "vuepress-theme-hope";
|
||||
|
||||
export const navbarConfig = navbar([
|
||||
export default navbar([
|
||||
{ text: "面试指南", icon: "java", link: "/home.md" },
|
||||
{
|
||||
text: "知识星球",
|
||||
icon: "code",
|
||||
link: "/about-the-author/zhishixingqiu-two-years.md",
|
||||
},
|
||||
{ text: "开源项目", icon: "github", link: "/open-source-project/" },
|
||||
{ text: "技术书籍", icon: "book", link: "/books/" },
|
||||
{ text: "程序人生", icon: "article", link: "/high-quality-technical-articles/" },
|
||||
{
|
||||
text: "程序人生",
|
||||
icon: "article",
|
||||
link: "/high-quality-technical-articles/",
|
||||
},
|
||||
{
|
||||
text: "网站相关",
|
||||
icon: "about",
|
||||
|
@ -1,223 +0,0 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 2922463 */
|
||||
src: url('iconfont.woff2?t=1660530571134') format('woff2'),
|
||||
url('iconfont.woff?t=1660530571134') format('woff'),
|
||||
url('iconfont.ttf?t=1660530571134') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-history:before {
|
||||
content: "\e752";
|
||||
}
|
||||
|
||||
.icon-experience:before {
|
||||
content: "\e72b";
|
||||
}
|
||||
|
||||
.icon-code:before {
|
||||
content: "\e7fc";
|
||||
}
|
||||
|
||||
.icon-interview:before {
|
||||
content: "\e65d";
|
||||
}
|
||||
|
||||
.icon-about:before {
|
||||
content: "\e6e5";
|
||||
}
|
||||
|
||||
.icon-search:before {
|
||||
content: "\e7de";
|
||||
}
|
||||
|
||||
.icon-featured:before {
|
||||
content: "\e633";
|
||||
}
|
||||
|
||||
.icon-codelibrary-fill:before {
|
||||
content: "\e84b";
|
||||
}
|
||||
|
||||
.icon-tool:before {
|
||||
content: "\e70c";
|
||||
}
|
||||
|
||||
.icon-highavailable:before {
|
||||
content: "\ea23";
|
||||
}
|
||||
|
||||
.icon-big-data:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.icon-work:before {
|
||||
content: "\e8d7";
|
||||
}
|
||||
|
||||
.icon-project:before {
|
||||
content: "\e702";
|
||||
}
|
||||
|
||||
.icon-article:before {
|
||||
content: "\e672";
|
||||
}
|
||||
|
||||
.icon-a-MachineLearning:before {
|
||||
content: "\e617";
|
||||
}
|
||||
|
||||
.icon-github:before {
|
||||
content: "\e673";
|
||||
}
|
||||
|
||||
.icon-rss:before {
|
||||
content: "\e768";
|
||||
}
|
||||
|
||||
.icon-mianshi:before {
|
||||
content: "\e614";
|
||||
}
|
||||
|
||||
.icon-et-performance:before {
|
||||
content: "\e623";
|
||||
}
|
||||
|
||||
.icon-virtual_machine:before {
|
||||
content: "\e73f";
|
||||
}
|
||||
|
||||
.icon-basic:before {
|
||||
content: "\e67b";
|
||||
}
|
||||
|
||||
.icon-container:before {
|
||||
content: "\e641";
|
||||
}
|
||||
|
||||
.icon-MQ:before {
|
||||
content: "\e684";
|
||||
}
|
||||
|
||||
.icon-redis:before {
|
||||
content: "\e619";
|
||||
}
|
||||
|
||||
.icon-mysql:before {
|
||||
content: "\e613";
|
||||
}
|
||||
|
||||
.icon-recommend:before {
|
||||
content: "\e639";
|
||||
}
|
||||
|
||||
.icon-chat:before {
|
||||
content: "\e66a";
|
||||
}
|
||||
|
||||
.icon-tips:before {
|
||||
content: "\e62a";
|
||||
}
|
||||
|
||||
.icon-chajian1:before {
|
||||
content: "\e69d";
|
||||
}
|
||||
|
||||
.icon-star:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
.icon-docker1:before {
|
||||
content: "\e659";
|
||||
}
|
||||
|
||||
.icon-zuozhe:before {
|
||||
content: "\e688";
|
||||
}
|
||||
|
||||
.icon-pdf:before {
|
||||
content: "\e740";
|
||||
}
|
||||
|
||||
.icon-suanfaku:before {
|
||||
content: "\e63a";
|
||||
}
|
||||
|
||||
.icon-jichushuju:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
.icon-people-network-full:before {
|
||||
content: "\e865";
|
||||
}
|
||||
|
||||
.icon-git:before {
|
||||
content: "\e708";
|
||||
}
|
||||
|
||||
.icon-luxianchaxun:before {
|
||||
content: "\e622";
|
||||
}
|
||||
|
||||
.icon-computer:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.icon-framework:before {
|
||||
content: "\e69e";
|
||||
}
|
||||
|
||||
.icon-Tools:before {
|
||||
content: "\ed5f";
|
||||
}
|
||||
|
||||
.icon-xitongsheji:before {
|
||||
content: "\e63e";
|
||||
}
|
||||
|
||||
.icon-database:before {
|
||||
content: "\e8e4";
|
||||
}
|
||||
|
||||
.icon-security-fill:before {
|
||||
content: "\e78b";
|
||||
}
|
||||
|
||||
.icon-docker:before {
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.icon-java:before {
|
||||
content: "\e6e0";
|
||||
}
|
||||
|
||||
.icon-book:before {
|
||||
content: "\e7c7";
|
||||
}
|
||||
|
||||
.icon-network:before {
|
||||
content: "\e67a";
|
||||
}
|
||||
|
||||
.icon-caozuoxitong:before {
|
||||
content: "\e686";
|
||||
}
|
||||
|
||||
.icon-gaojixiaozuzhibeifen:before {
|
||||
content: "\e756";
|
||||
}
|
||||
|
||||
.icon-distributed-network:before {
|
||||
content: "\e8d5";
|
||||
}
|
||||
|
||||
.icon-intellijidea:before {
|
||||
content: "\ebd1";
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
export const aboutTheAuthor = [
|
||||
import { arraySidebar } from "vuepress-theme-hope";
|
||||
|
||||
export const aboutTheAuthor = arraySidebar([
|
||||
{
|
||||
text: "个人经历",
|
||||
icon: "zuozhe",
|
||||
collapsable: false,
|
||||
collapsible: false,
|
||||
children: [
|
||||
"internet-addiction-teenager",
|
||||
"my-college-life",
|
||||
@ -14,7 +16,7 @@ export const aboutTheAuthor = [
|
||||
{
|
||||
text: "杂谈",
|
||||
icon: "chat",
|
||||
collapsable: false,
|
||||
collapsible: false,
|
||||
children: [
|
||||
"writing-technology-blog-six-years",
|
||||
"my-article-was-stolen-and-made-into-video-and-it-became-popular",
|
||||
@ -22,4 +24,4 @@ export const aboutTheAuthor = [
|
||||
"zhishixingqiu-two-years",
|
||||
],
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
@ -1,4 +1,6 @@
|
||||
export const books = [
|
||||
import { arraySidebar } from "vuepress-theme-hope";
|
||||
|
||||
export const books = arraySidebar([
|
||||
{
|
||||
text: "计算机基础",
|
||||
link: "cs-basics",
|
||||
@ -30,4 +32,4 @@ export const books = [
|
||||
link: "distributed-system",
|
||||
icon: "distributed-network",
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
@ -1,9 +1,11 @@
|
||||
export const highQualityTechnicalArticles = [
|
||||
import { arraySidebar } from "vuepress-theme-hope";
|
||||
|
||||
export const highQualityTechnicalArticles = arraySidebar([
|
||||
{
|
||||
text: "练级攻略",
|
||||
icon: "et-performance",
|
||||
prefix: "advanced-programmer/",
|
||||
collapsable: false,
|
||||
collapsible: false,
|
||||
children: [
|
||||
"the-growth-strategy-of-the-technological-giant",
|
||||
"seven-tips-for-becoming-an-advanced-programmer",
|
||||
@ -14,10 +16,10 @@ export const highQualityTechnicalArticles = [
|
||||
text: "个人经历",
|
||||
icon: "experience",
|
||||
prefix: "personal-experience/",
|
||||
collapsable: false,
|
||||
collapsible: false,
|
||||
children: [
|
||||
"four-year-work-in-tencent-summary",
|
||||
"two-years-of-back-end-develop--experience-in-didi&toutiao",
|
||||
"two-years-of-back-end-develop--experience-in-didi-and-toutiao",
|
||||
"8-years-programmer-work-summary",
|
||||
"huawei-od-275-days",
|
||||
],
|
||||
@ -26,7 +28,7 @@ export const highQualityTechnicalArticles = [
|
||||
text: "程序员",
|
||||
icon: "code",
|
||||
prefix: "programmer/",
|
||||
collapsable: false,
|
||||
collapsible: false,
|
||||
children: [
|
||||
"how-do-programmers-publish-a-technical-book",
|
||||
"efficient-book-publishing-and-practice-guide",
|
||||
@ -36,7 +38,7 @@ export const highQualityTechnicalArticles = [
|
||||
text: "面试",
|
||||
icon: "interview",
|
||||
prefix: "interview/",
|
||||
collapsable: false,
|
||||
collapsible: false,
|
||||
children: [
|
||||
"the-experience-of-get-offer-from-over-20-big-companies",
|
||||
"the-experience-and-thinking-of-an-interview-experienced-by-an-older-programmer",
|
||||
@ -52,10 +54,10 @@ export const highQualityTechnicalArticles = [
|
||||
text: "工作",
|
||||
icon: "work",
|
||||
prefix: "work/",
|
||||
collapsable: false,
|
||||
collapsible: false,
|
||||
children: [
|
||||
"get-into-work-mode-quickly-when-you-join-a-company",
|
||||
"employee-performance",
|
||||
],
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { sidebar } from "vuepress-theme-hope";
|
||||
import { highQualityTechnicalArticles } from "./sidebar/high-quality-technical-articles";
|
||||
import { aboutTheAuthor } from "./sidebar/about-the-author";
|
||||
import { books } from "./sidebar/books";
|
||||
import { openSourceProject } from "./sidebar/open-source-project";
|
||||
|
||||
export const sidebarConfig = sidebar({
|
||||
import { aboutTheAuthor } from "./about-the-author.js";
|
||||
import { books } from "./books.js";
|
||||
import { highQualityTechnicalArticles } from "./high-quality-technical-articles.js";
|
||||
import { openSourceProject } from "./open-source-project.js";
|
||||
|
||||
export default sidebar({
|
||||
// 应该把更精确的路径放置在前边
|
||||
"/open-source-project/": openSourceProject,
|
||||
"/books/": books,
|
||||
@ -25,6 +26,7 @@ export const sidebarConfig = sidebar({
|
||||
prefix: "interview-preparation/",
|
||||
children: [
|
||||
"teach-you-how-to-prepare-for-the-interview-hand-in-hand",
|
||||
"resume-guide",
|
||||
"key-points-of-interview",
|
||||
"project-experience-guide",
|
||||
"interview-experience",
|
||||
@ -173,10 +175,10 @@ export const sidebarConfig = sidebar({
|
||||
icon: "star",
|
||||
collapsible: true,
|
||||
children: [
|
||||
"osi&tcp-ip-model",
|
||||
"osi-and-tcp-ip-model",
|
||||
"application-layer-protocol",
|
||||
"http&https",
|
||||
"http1.0&http1.1",
|
||||
"http-vs-https",
|
||||
"http1.0-vs-http1.1",
|
||||
"http-status-codes",
|
||||
"dns",
|
||||
"tcp-connection-and-disconnection",
|
||||
@ -198,10 +200,7 @@ export const sidebarConfig = sidebar({
|
||||
text: "Linux",
|
||||
collapsible: true,
|
||||
icon: "linux",
|
||||
children: [
|
||||
"linux-intro",
|
||||
"shell-intro",
|
||||
],
|
||||
children: ["linux-intro", "shell-intro"],
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -420,7 +419,7 @@ export const sidebarConfig = sidebar({
|
||||
children: [
|
||||
"basis-of-authority-certification",
|
||||
"jwt-intro",
|
||||
"advantages&disadvantages-of-jwt",
|
||||
"advantages-and-disadvantages-of-jwt",
|
||||
"sso-intro",
|
||||
"design-of-authority-system",
|
||||
"sentive-words-filter",
|
||||
@ -442,9 +441,9 @@ export const sidebarConfig = sidebar({
|
||||
{
|
||||
text: "理论&算法&协议",
|
||||
icon: "suanfaku",
|
||||
prefix: "theorem&algorithm&protocol/",
|
||||
prefix: "protocol/",
|
||||
children: [
|
||||
"cap&base-theorem",
|
||||
"cap-and-base-theorem",
|
||||
"paxos-algorithm",
|
||||
"raft-algorithm",
|
||||
"gossip-protocl",
|
||||
@ -510,7 +509,7 @@ export const sidebarConfig = sidebar({
|
||||
"high-availability-system-design",
|
||||
"redundancy",
|
||||
"limit-request",
|
||||
"fallback&circuit-breaker",
|
||||
"fallback-and-circuit-breaker",
|
||||
"timeout-and-retry",
|
||||
"performance-test",
|
||||
],
|
@ -1,4 +1,6 @@
|
||||
export const openSourceProject = [
|
||||
import { arraySidebar } from "vuepress-theme-hope";
|
||||
|
||||
export const openSourceProject = arraySidebar([
|
||||
{
|
||||
text: "技术教程",
|
||||
link: "tutorial",
|
||||
@ -34,4 +36,4 @@ export const openSourceProject = [
|
||||
link: "big-data",
|
||||
icon: "big-data",
|
||||
},
|
||||
];
|
||||
]);
|
||||
|
5
docs/.vuepress/styles/index.scss
Normal file
@ -0,0 +1,5 @@
|
||||
body {
|
||||
@media (min-width: 1440px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
$theme-color: #2980B9;
|
||||
$sidebar-width: 20rem;
|
||||
$sidebar-mobile-width: 16rem;
|
||||
$content-width: 60em;
|
||||
$theme-color: #2980b9;
|
||||
// $sidebar-width: 20rem;
|
||||
// $sidebar-mobile-width: 16rem;
|
||||
|
@ -1,19 +1,31 @@
|
||||
import { getDirname, path } from "@vuepress/utils";
|
||||
import { hopeTheme } from "vuepress-theme-hope";
|
||||
import { navbarConfig } from "./navbar";
|
||||
import { sidebarConfig } from "./sidebar";
|
||||
|
||||
export const themeConfig = hopeTheme({
|
||||
import navbar from "./navbar.js";
|
||||
import sidebar from "./sidebar/index.js";
|
||||
|
||||
const __dirname = getDirname(import.meta.url);
|
||||
|
||||
export default hopeTheme({
|
||||
logo: "/logo.png",
|
||||
hostname: "https://javaguide.cn/",
|
||||
|
||||
iconAssets: "//at.alicdn.com/t/c/font_2922463_9aayheyb3v7.css",
|
||||
|
||||
author: {
|
||||
name: "Guide",
|
||||
url: "https://javaguide.cn/article/",
|
||||
},
|
||||
|
||||
repo: "https://github.com/Snailclimb/JavaGuide",
|
||||
docsDir: "docs",
|
||||
iconAssets: "//at.alicdn.com/t/c/font_2922463_9aayheyb3v7.css",
|
||||
navbar: navbarConfig,
|
||||
sidebar: sidebarConfig,
|
||||
|
||||
navbar,
|
||||
sidebar,
|
||||
footer:
|
||||
'<a href="https://beian.miit.gov.cn/" target="_blank">鄂ICP备2020015769号-1</a>',
|
||||
displayFooter: true,
|
||||
|
||||
pageInfo: [
|
||||
"Author",
|
||||
"Category",
|
||||
@ -23,6 +35,7 @@ export const themeConfig = hopeTheme({
|
||||
"Word",
|
||||
"ReadingTime",
|
||||
],
|
||||
|
||||
blog: {
|
||||
intro: "/about-the-author/",
|
||||
sidebarDisplay: "mobile",
|
||||
@ -32,19 +45,33 @@ export const themeConfig = hopeTheme({
|
||||
Gitee: "https://gitee.com/SnailClimb",
|
||||
},
|
||||
},
|
||||
footer:
|
||||
'<a href="https://beian.miit.gov.cn/" target="_blank">鄂ICP备2020015769号-1</a>',
|
||||
displayFooter: true,
|
||||
|
||||
plugins: {
|
||||
blog: true,
|
||||
copyright: true,
|
||||
mdEnhance: {
|
||||
align: true,
|
||||
codetabs: true,
|
||||
container: true,
|
||||
figure: true,
|
||||
include: {
|
||||
resolvePath: (file, cwd) => {
|
||||
if (file.startsWith("@"))
|
||||
return path.resolve(
|
||||
__dirname,
|
||||
"../snippets",
|
||||
file.replace("@", "./")
|
||||
);
|
||||
|
||||
return path.resolve(cwd, file);
|
||||
},
|
||||
},
|
||||
tasklist: true,
|
||||
},
|
||||
feed: {
|
||||
atom: true,
|
||||
json: true,
|
||||
rss: true,
|
||||
},
|
||||
},
|
||||
});
|
@ -31,7 +31,7 @@ tag:
|
||||
|
||||
CSDN 我就不想多说了,就一大型文章垃圾场,都是各种不规范转载,各种收费下载的垃圾资源。这号称国内流量最大的技术网站贼恶心,吃香太难看,能不用就不要用吧!
|
||||
|
||||
像我自己平时用 Google 搜索的时候,都是直接屏蔽掉 CSDN 这个站点的。只需要下载一个叫做 Personal Blocklist 的 Chrome 插件,然后将 blog.csdn.net 添加进黑名单就可以了。
|
||||
像我自己平时用 Google 搜索的时候,都是直接屏蔽掉 CSDN 这个站点的。只需要下载一个叫做 Personal Blocklist 的 Chrome 插件,然后将 blog.csdn.net 添加进黑名单就可以了。
|
||||
|
||||

|
||||
|
||||
|
@ -7,18 +7,18 @@ tag:
|
||||
|
||||
不知不觉已经入职一个多月了,在入职之前我没有在某个公司实习过或者工作过,所以很多东西刚入职工作的我来说还是比较新颖的。学校到职场的转变,带来了角色的转变,其中的差别因人而异。对我而言,在学校的时候课堂上老师课堂上教的东西,自己会根据自己的兴趣选择性接受,甚至很多课程你不想去上的话,还可以逃掉。到了公司就不一样了,公司要求你会的技能你不得不学,除非你不想干了。在学校的时候大部分人编程的目的都是为了通过考试或者找到一份好工作,真正靠自己兴趣支撑起来的很少,到了工作岗位之后我们编程更多的是因为工作的要求,相比于学校的来说会一般会更有挑战而且压力更大。在学校的时候,我们最重要的就是对自己负责,我们不断学习知识去武装自己,但是到了公司之后我们不光要对自己负责,更要对公司负责,毕竟公司出钱请你过来,不是让你一直 on beach 的。
|
||||
|
||||
刚来公司的时候,因为公司要求,我换上了 Mac 电脑。由于之前一直用的是 Windows 系统,所以非常不习惯。刚开始用 Mac 系统的时候笨手笨脚,自己会很明显的感觉自己的编程效率降低了至少 3 成。当时内心还是挺不爽的,心里也总是抱怨为什么不直接用 Windows 系统或者 Linux 系统。不过也挺奇怪,大概一个星期之后,自己就开始慢慢适应使用 Mac 进行编程,甚至非常喜欢。我这里不想对比 Mac 和 Windows 编程体验哪一个更好,我觉得还是因人而异,相同价位的 Mac 的配置相比于 Windows确实要被甩几条街。不过 Mac 的编程和使用体验确实不错,当然你也可以选择使用 Linux 进行日常开发,相信一定很不错。 另外,Mac 不能玩一些主流网络游戏,对于一些克制不住自己想玩游戏的朋友是一个不错的选择。
|
||||
刚来公司的时候,因为公司要求,我换上了 Mac 电脑。由于之前一直用的是 Windows 系统,所以非常不习惯。刚开始用 Mac 系统的时候笨手笨脚,自己会很明显的感觉自己的编程效率降低了至少 3 成。当时内心还是挺不爽的,心里也总是抱怨为什么不直接用 Windows 系统或者 Linux 系统。不过也挺奇怪,大概一个星期之后,自己就开始慢慢适应使用 Mac 进行编程,甚至非常喜欢。我这里不想对比 Mac 和 Windows 编程体验哪一个更好,我觉得还是因人而异,相同价位的 Mac 的配置相比于 Windows 确实要被甩几条街。不过 Mac 的编程和使用体验确实不错,当然你也可以选择使用 Linux 进行日常开发,相信一定很不错。 另外,Mac 不能玩一些主流网络游戏,对于一些克制不住自己想玩游戏的朋友是一个不错的选择。
|
||||
|
||||
不得不说 ThoughtWorks 的培训机制还是很不错的。应届生入职之后一般都会安排培训,与往年不同的是,今年的培训多了中国本地班(TWU-C)。作为本地班的第一期学员,说句心里话还是很不错。8周的培训,除了工作需要用到的基本技术比如ES6、SpringBoot等等之外,还会增加一些新员工基本技能的培训比如如何高效开会、如何给别人正确的提 Feedback、如何对代码进行重构、如何进行 TDD 等等。培训期间不定期的有活动,比如Weekend Trip、 City Tour、Cake time等等。最后三周还会有一个实际的模拟项目,这个项目基本和我们正式工作的实际项目差不多,我个人感觉很不错。目前这个项目已经正式完成了一个迭代,我觉得在做项目的过程中,收获最大的不是项目中使用的技术,而是如何进行团队合作、如何正确使用 Git 团队协同开发、一个完成的迭代是什么样子的、做项目的过程中可能遇到那些问题、一个项目运作的完整流程等等。
|
||||
不得不说 ThoughtWorks 的培训机制还是很不错的。应届生入职之后一般都会安排培训,与往年不同的是,今年的培训多了中国本地班(TWU-C)。作为本地班的第一期学员,说句心里话还是很不错。8 周的培训,除了工作需要用到的基本技术比如 ES6、SpringBoot 等等之外,还会增加一些新员工基本技能的培训比如如何高效开会、如何给别人正确的提 Feedback、如何对代码进行重构、如何进行 TDD 等等。培训期间不定期的有活动,比如 Weekend Trip、 City Tour、Cake time 等等。最后三周还会有一个实际的模拟项目,这个项目基本和我们正式工作的实际项目差不多,我个人感觉很不错。目前这个项目已经正式完成了一个迭代,我觉得在做项目的过程中,收获最大的不是项目中使用的技术,而是如何进行团队合作、如何正确使用 Git 团队协同开发、一个完成的迭代是什么样子的、做项目的过程中可能遇到那些问题、一个项目运作的完整流程等等。
|
||||
|
||||
ThoughtWorks 非常提倡分享、提倡帮助他人成长,这一点在公司的这段时间深有感触。培训期间,我们每个人会有一个 Trainer 负责,Trainer 就是日常带我们上课和做项目的同事,一个 Trainer 大概会负责5-6个人。Trainer不定期都会给我们最近表现的 Feedback( 反馈) ,我个人觉得这个并不是这是走走形式,Trainer 们都很负责,很多时候都是在下班之后找我们聊天。同事们也都很热心,如果你遇到问题,向别人询问,其他人如果知道的话一般都会毫无保留的告诉你,如果遇到大部分都不懂的问题,甚至会组织一次技术 Session 分享。上周五我在我们小组内进行了一次关于 Feign 远程调用的技术分享,因为 team 里面大家对这部分知识都不太熟悉,但是后面的项目进展大概率会用到这部分知识。我刚好研究了这部分内容,所以就分享给了组内的其他同事,以便于项目更好的进行。
|
||||
ThoughtWorks 非常提倡分享、提倡帮助他人成长,这一点在公司的这段时间深有感触。培训期间,我们每个人会有一个 Trainer 负责,Trainer 就是日常带我们上课和做项目的同事,一个 Trainer 大概会负责 5-6 个人。Trainer 不定期都会给我们最近表现的 Feedback( 反馈) ,我个人觉得这个并不是这是走走形式,Trainer 们都很负责,很多时候都是在下班之后找我们聊天。同事们也都很热心,如果你遇到问题,向别人询问,其他人如果知道的话一般都会毫无保留的告诉你,如果遇到大部分都不懂的问题,甚至会组织一次技术 Session 分享。上周五我在我们小组内进行了一次关于 Feign 远程调用的技术分享,因为 team 里面大家对这部分知识都不太熟悉,但是后面的项目进展大概率会用到这部分知识。我刚好研究了这部分内容,所以就分享给了组内的其他同事,以便于项目更好的进行。
|
||||
|
||||
另外,ThoughtWorks 也是一家非常提倡 Feedback( 反馈) 文化的公司,反馈是告诉人们我们对他们的表现的看法以及他们应该如何更好地做到这一点。刚开始我并没有太在意,慢慢地自己确实感觉到正确的进行反馈对他人会有很大的帮助。因为人在做很多事情的时候,会很难发现别人很容易看到的一些小问题。就比如一个很有趣的现象一样,假如我们在做项目的时候没有测试这个角色,如果你完成了自己的模块,并且自己对这个模块测试了很多遍,你发现已经没啥问题了。但是,到了实际使用的时候会很大概率出现你之前从来没有注意的问题。解释这个问题的说法是:每个人的视野或多或少都是有盲点的,这与我们的关注点息息相关。对于自己做的东西,很多地方自己测试很多遍都不会发现,但是如果让其他人帮你进行测试的话,就很大可能会发现很多显而易见的问题。
|
||||
另外,ThoughtWorks 也是一家非常提倡 Feedback( 反馈) 文化的公司,反馈是告诉人们我们对他们的表现的看法以及他们应该如何更好地做到这一点。刚开始我并没有太在意,慢慢地自己确实感觉到正确的进行反馈对他人会有很大的帮助。因为人在做很多事情的时候,会很难发现别人很容易看到的一些小问题。就比如一个很有趣的现象一样,假如我们在做项目的时候没有测试这个角色,如果你完成了自己的模块,并且自己对这个模块测试了很多遍,你发现已经没啥问题了。但是,到了实际使用的时候会很大概率出现你之前从来没有注意的问题。解释这个问题的说法是:每个人的视野或多或少都是有盲点的,这与我们的关注点息息相关。对于自己做的东西,很多地方自己测试很多遍都不会发现,但是如果让其他人帮你进行测试的话,就很大可能会发现很多显而易见的问题。
|
||||
|
||||

|
||||
|
||||
工作之后,平时更新公众号、专栏还有维护 Github 的时间变少了。实际上,很多时候下班回来后,都有自己的时间来干自己的事情,但是自己也总是找工作太累或者时间比较零散的接口来推掉了。到了今天,翻看 Github 突然发现 14 天前别人在 Github 上给我提的 pr 我还没有处理。这一点确实是自己没有做好的地方,没有合理安排好自己的时间。实际上自己有很多想写的东西,后面会慢慢将他们提上日程。工作之后,更加发现下班后的几个小时如何度过确实很重要 ,如果你觉得自己没有完成好自己白天该做的工作的话,下班后你可以继续忙白天没有忙完的工作,如果白天的工作对于你游刃有余的话,下班回来之后,你大可去干自己感兴趣的事情,学习自己感兴趣的技术。做任何事情都要基于自身的基础,切不可好高骛远。
|
||||
|
||||
工作之后身边也会有很多厉害的人,多从他人身上学习我觉得是每个职场人都应该做的。这一届和我们一起培训的同事中,有一些技术很厉害的,也有一些技术虽然不是那么厉害,但是组织能力以及团队协作能力特别厉害的。有一个特别厉害的同事,在我们还在学 SpringBoot 各种语法的时候,他自己利用业余时间写了一个简化版的 SpringBoot ,涵盖了 Spring 的一些常用注解比如 `@RestController`、`@Autowried`、`@Pathvairable`、`@RestquestParam`等等(已经联系这位同事,想让他开源一下,后面会第一时间同步到公众号,期待一下吧!)。我觉得这位同事对于编程是真的有兴趣,他好像从初中就开始接触编程了,对于各种底层知识也非常感兴趣,自己写过实现过很多比较底层的东西。他的梦想是在 Github 上造一个 20k Star 以上的轮子。我相信以这位同事的能力一定会达成目标的,在这里祝福这位同事,希望他可以尽快实现这个目标。
|
||||
工作之后身边也会有很多厉害的人,多从他人身上学习我觉得是每个职场人都应该做的。这一届和我们一起培训的同事中,有一些技术很厉害的,也有一些技术虽然不是那么厉害,但是组织能力以及团队协作能力特别厉害的。有一个特别厉害的同事,在我们还在学 SpringBoot 各种语法的时候,他自己利用业余时间写了一个简化版的 SpringBoot ,涵盖了 Spring 的一些常用注解比如 `@RestController`、`@Autowried`、`@Pathvairable`、`@RestquestParam`等等(已经联系这位同事,想让他开源一下,后面会第一时间同步到公众号,期待一下吧!)。我觉得这位同事对于编程是真的有兴趣,他好像从初中就开始接触编程了,对于各种底层知识也非常感兴趣,自己写过实现过很多比较底层的东西。他的梦想是在 Github 上造一个 20k Star 以上的轮子。我相信以这位同事的能力一定会达成目标的,在这里祝福这位同事,希望他可以尽快实现这个目标。
|
||||
|
||||
这是我入职一个多月之后的个人感受,很多地方都是一带而过,后面我会抽时间分享自己在公司或者业余学到的比较有用的知识给各位,希望看过的人都能有所收获。
|
@ -31,7 +31,7 @@ tag:
|
||||
|
||||
不知道其他公司的程序员是怎么样的?我感觉技术积累很大程度在乎平时,单纯依靠工作绝大部分情况只会加快自己做需求的熟练度,当然,写多了之后或多或少也会提升你对代码质量的认识(前提是你有这个意识)。
|
||||
|
||||
工作之余,我会利用业余时间来学习自己想学的东西。工作中的例子就是我刚进公司的第一个项目用到了 Spring Security+JWT ,因为当时自己对于这个技术不太了解,然后就在工作之外大概花了一周的时间学习写了一个 Demo 分享了出来,Github 地址:https://github.com/Snailclimb/spring-security-jwt-guide 。以次为契机,我还分享了
|
||||
工作之余,我会利用业余时间来学习自己想学的东西。工作中的例子就是我刚进公司的第一个项目用到了 Spring Security+JWT ,因为当时自己对于这个技术不太了解,然后就在工作之外大概花了一周的时间学习写了一个 Demo 分享了出来,Github 地址:<https://github.com/Snailclimb/spring-security-jwt-guide> 。以次为契机,我还分享了
|
||||
|
||||
- [《一问带你区分清楚 Authentication,Authorization 以及 Cookie、Session、Token》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485626&idx=1&sn=3247aa9000693dd692de8a04ccffeec1&chksm=cea24771f9d5ce675ea0203633a95b68bfe412dc6a9d05f22d221161147b76161d1b470d54b3&token=684071313&lang=zh_CN&scene=21#wechat_redirect)
|
||||
- [JWT 身份认证优缺点分析以及常见问题解决方案](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485655&idx=1&sn=583eeeb081ea21a8ec6347c72aa223d6&chksm=cea2471cf9d5ce0aa135f2fb9aa32d98ebb3338292beaccc1aae43d1178b16c0125eb4139ca4&token=1737409938&lang=zh_CN#rd)
|
||||
|
@ -5,7 +5,7 @@ tag:
|
||||
- 个人经历
|
||||
---
|
||||
|
||||
2021-03-21,晚上12点,肝完了我正在做的一个项目的前端的某块功能,我随手打开了[我的 Github 主页](https://github.com/Snailclimb)。
|
||||
2021-03-21,晚上 12 点,肝完了我正在做的一个项目的前端的某块功能,我随手打开了[我的 Github 主页](https://github.com/Snailclimb)。
|
||||
|
||||
好家伙!几天没注意,[JavaGuide](https://github.com/Snailclimb/JavaGuide) 这个项目直接上了 100K star。
|
||||
|
||||
@ -15,7 +15,7 @@ tag:
|
||||
|
||||

|
||||
|
||||
维护这个项目的过程中,也被某些人 diss 过:“md项目,没啥含金量,给国人丢脸!”。
|
||||
维护这个项目的过程中,也被某些人 diss 过:“md 项目,没啥含金量,给国人丢脸!”。
|
||||
|
||||
对于说这类话的人,我觉得对我没啥影响,就持续完善,把 JavaGuide 做的更好吧!其实,国外的很多项目也是纯 MD 啊!就比如外国的朋友发起的 awesome 系列、求职面试系列。无需多说,行动自证!凎!
|
||||
|
||||
@ -23,13 +23,13 @@ tag:
|
||||
|
||||
我的公号的小伙伴都是通过这个项目关注我的,趁着午休,简单复盘一下,也算是对关注这个项目的小伙伴负责。
|
||||
|
||||
我在大三开始准备秋招面试的时候,创建了 JavaGuide 这个项目,**2018-05-07** 这一天我提交了**第 1 个 commit**。
|
||||
我在大三开始准备秋招面试的时候,创建了 JavaGuide 这个项目,**2018-05-07** 这一天我提交了**第 1 个 commit**。
|
||||
|
||||
到今天(2021-03-23)为止,这个仓库已经累计有 **2933** 次 commit,累计有 **207** 位朋友参与到了项目中来。
|
||||
|
||||

|
||||
|
||||
累计有 **511** 个 **issue** 和 **575** 个 **pr**。所有的 pr 都已经被处理,仅有15 个左右的 issue 我还未抽出时间处理。
|
||||
累计有 **511** 个 **issue** 和 **575** 个 **pr**。所有的 pr 都已经被处理,仅有 15 个左右的 issue 我还未抽出时间处理。
|
||||
|
||||

|
||||
|
||||
|
@ -5,7 +5,7 @@ tag:
|
||||
- 杂谈
|
||||
---
|
||||
|
||||
时间回到 2021-02-25,我在刷哔哩哔哩的时候发现,哔哩哔哩某UP主(某培训机构),擅自将我在知乎的一个回答做成了视频。
|
||||
时间回到 2021-02-25,我在刷哔哩哔哩的时候发现,哔哩哔哩某 UP 主(某培训机构),擅自将我在知乎的一个回答做成了视频。
|
||||
|
||||
原滋原味啊!我艹。甚至,连我开头的自我调侃还加上了!真的牛皮!
|
||||
|
||||
@ -31,7 +31,7 @@ tag:
|
||||
|
||||
其他的视频就不用多看了,是否还是剽窃别人的原创,原封不动地做成视频,大家心里应该有数。
|
||||
|
||||
他们这样做的目的就是一个:**引流到自己的QQ群,然后忽悠你买课程。**
|
||||
他们这样做的目的就是一个:**引流到自己的 QQ 群,然后忽悠你买课程。**
|
||||
|
||||
我并不认为是这完全都是培训机构的问题。培训机构的员工为了流量而做这种恶心的事情,也导致了现在这种事情被越来越频繁地发生。
|
||||
|
||||
|
@ -6,7 +6,7 @@ tag:
|
||||
- 个人经历
|
||||
---
|
||||
|
||||
> 关于初高中的生活,可以看 2020年我写的[我曾经也是网瘾少年](https://javaguide.cn/about-the-author/internet-addiction-teenager.html)这篇文章。
|
||||
> 关于初高中的生活,可以看 2020 年我写的[我曾经也是网瘾少年](https://javaguide.cn/about-the-author/internet-addiction-teenager.html)这篇文章。
|
||||
|
||||
2019 年 6 月份毕业,距今已经过去了 3 年。趁着高考以及应届生毕业之际,简单聊聊自己的大学生活。
|
||||
|
||||
|
@ -3,6 +3,8 @@ title: 个人介绍 Q&A
|
||||
category: 走近作者
|
||||
---
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
这篇文章我会通过 Q&A 的形式简单介绍一下我自己。
|
||||
|
||||
## 我是什么时候毕业的?
|
||||
@ -45,13 +47,13 @@ category: 走近作者
|
||||
|
||||

|
||||
|
||||
## 为什么自称 Guide哥?
|
||||
## 为什么自称 Guide?
|
||||
|
||||
可能是因为我的项目名字叫做 JavaGuide , 所以导致有很多人称呼我为 **Guide哥**。
|
||||
|
||||
后面,为了读者更方便称呼,我就将自己的笔名改成了 **Guide哥**。
|
||||
后面,为了读者更方便称呼,我就将自己的笔名改成了 **Guide**。
|
||||
|
||||
我早期写文章用的笔名是 SnailClimb 。很多人不知道这个名字是啥意思,给大家拆解一下就清楚了。SnailClimb=Snail(蜗牛)+Climb(攀登)。我从小就非常喜欢听周杰伦的歌曲,特别是他的《蜗牛》🐌 这首歌曲,另外,当年我高考发挥的算是比较失常,上了大学之后还算是比较“奋青”,所以,我就给自己起的笔名叫做 SnailClimb ,寓意自己要不断向上攀登,嘿嘿😁
|
||||
我早期写文章用的笔名是 SnailClimb 。很多人不知道这个名字是啥意思,给大家拆解一下就清楚了。SnailClimb=Snail(蜗牛)+Climb(攀登)。我从小就非常喜欢听周杰伦的歌曲,特别是他的《蜗牛》🐌 这首歌曲,另外,当年我高考发挥的算是比较失常,上了大学之后还算是比较“奋青”,所以,我就给自己起的笔名叫做 SnailClimb ,寓意自己要不断向上攀登,嘿嘿 😁
|
||||
|
||||

|
||||
|
||||
|
@ -122,7 +122,7 @@ tag:
|
||||
|
||||
最重要的是一定要重视 Markdown 规范,不然内容再好也会显得不专业。
|
||||
|
||||
Markdown 规范请参考:**https://javaguide.cn/javaguide/contribution-guideline.html** (很重要,尽量按照规范来,对你工作中写文档会非常有帮助)
|
||||
Markdown 规范请参考:**<https://javaguide.cn/javaguide/contribution-guideline.html>** (很重要,尽量按照规范来,对你工作中写文档会非常有帮助)
|
||||
|
||||
## 有没有什么写作技巧分享?
|
||||
|
||||
|
@ -4,6 +4,8 @@ category: 知识星球
|
||||
star: 2
|
||||
---
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
时间过的真快,知识星球我已经平稳运行了 3 年有余了!
|
||||
|
||||
在 2019 年 12 月 29 号,经过了大概一年左右的犹豫期,我正式确定要开始做一个自己的星球。
|
||||
@ -121,14 +123,18 @@ star: 2
|
||||
|
||||
## 如何加入?
|
||||
|
||||

|
||||

|
||||
|
||||
如果你想要进知识星球的话,建议你添加我的微信(guidege666)领取一个星球专属优惠券(一定要备注“优惠卷”),可以优惠 **30** 元,无任何套路,无任何潜在收费项。收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。
|
||||
你可以扫描上方的二维码原价加入(续费半价)。不过,我更建议你添加我的个人微信领取一个 **30** 元的星球专属优惠券(一定要备注“优惠卷”)。
|
||||
|
||||
**无任何套路,无任何潜在收费项。收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。用心做内容,不割韭菜!**
|
||||
|
||||
**一定要备注“优惠卷”**,不然通过不了。
|
||||
|
||||

|
||||
|
||||
进入星球之后,记得查看[星球使用指南](https://t.zsxq.com/0d18KSarv)(一定要看!) 。
|
||||
|
||||
随着时间推移,星球积累的干货资源越来越多,我花在星球上的时间也越来越多,星球的价格会逐步向上调整,想要加入的同学一定要尽早。
|
||||
|
||||
不过, **一定要确定需要再进** 。并且, **三天之内觉得内容不满意可以全额退款** 。
|
||||
|
@ -16,13 +16,11 @@ head:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
如果你比较喜欢动手,对于理论知识比较抵触的话,推荐你看看[《如何开发一个简单的数据库》](https://cstack.github.io/db_tutorial/) ,这个 project 会手把手教你编写一个简单的数据库。
|
||||
|
||||

|
||||
|
||||
Github上也已经有大佬用 Java 实现过一个简易的数据库,介绍的挺详细的,感兴趣的朋友可以去看看。地址:[https://github.com/alchemystar/Freedom](https://github.com/alchemystar/Freedom) 。
|
||||
Github 上也已经有大佬用 Java 实现过一个简易的数据库,介绍的挺详细的,感兴趣的朋友可以去看看。地址:[https://github.com/alchemystar/Freedom](https://github.com/alchemystar/Freedom) 。
|
||||
|
||||
除了这个用 Java 写的之外,**[db_tutorial](https://github.com/cstack/db_tutorial)** 这个项目是国外的一个大佬用 C 语言写的,朋友们也可以去瞅瞅。
|
||||
|
||||
@ -44,7 +42,7 @@ Github上也已经有大佬用 Java 实现过一个简易的数据库,介绍
|
||||
|
||||

|
||||
|
||||
不管是 MySQL 还是Oracle ,它们总体的架子是差不多的,不同的是其内部的实现比如数据库索引的数据结构、存储引擎的实现方式等等。
|
||||
不管是 MySQL 还是 Oracle ,它们总体的架子是差不多的,不同的是其内部的实现比如数据库索引的数据结构、存储引擎的实现方式等等。
|
||||
|
||||
这本书有些地方还是翻译的比较蹩脚,有能力看英文版的还是建议上手英文版。
|
||||
|
||||
|
@ -18,9 +18,9 @@ icon: "distributed-network"
|
||||
|
||||

|
||||
|
||||
作者专门写了一篇文章来介绍这本书的背后的故事,感兴趣的小伙伴可以自行查阅:https://zhuanlan.zhihu.com/p/487534882 。
|
||||
作者专门写了一篇文章来介绍这本书的背后的故事,感兴趣的小伙伴可以自行查阅:<https://zhuanlan.zhihu.com/p/487534882> 。
|
||||
|
||||
最后,放上这本书的代码仓库和勘误地址:https://github.com/tangwz/DistSysDeepDive 。
|
||||
最后,放上这本书的代码仓库和勘误地址:<https://github.com/tangwz/DistSysDeepDive> 。
|
||||
|
||||
## 《数据密集型应用系统设计》
|
||||
|
||||
@ -32,7 +32,7 @@ icon: "distributed-network"
|
||||
|
||||
书中介绍的大部分概念你可能之前都听过,但是在看了书中的内容之后,你可能会豁然开朗:“哇塞!原来是这样的啊!这不是某技术的原理么?”。
|
||||
|
||||
这本书我之前专门写过知乎回答介绍和推荐,没看过的朋友可以看看:[有哪些你看了以后大呼过瘾的编程书? ](https://www.zhihu.com/question/50408698/answer/2278198495) 。
|
||||
这本书我之前专门写过知乎回答介绍和推荐,没看过的朋友可以看看:[有哪些你看了以后大呼过瘾的编程书?](https://www.zhihu.com/question/50408698/answer/2278198495) 。
|
||||
|
||||
另外,如果你在阅读这本书的时候感觉难度比较大,很多地方读不懂的话,我这里推荐一下《深入理解分布式系统》作者写的[《DDIA 逐章精读》小册](https://ddia.qtmuniao.com)。
|
||||
|
||||
@ -40,7 +40,7 @@ icon: "distributed-network"
|
||||
|
||||

|
||||
|
||||
**[《深入理解分布式事务》](https://book.douban.com/subject/35626925/)** 这本书是的其中一位作者是 Apache ShenYu(incubating)网关创始人、Hmily、RainCat、Myth等分布式事务框架的创始人。
|
||||
**[《深入理解分布式事务》](https://book.douban.com/subject/35626925/)** 这本书是的其中一位作者是 Apache ShenYu(incubating)网关创始人、Hmily、RainCat、Myth 等分布式事务框架的创始人。
|
||||
|
||||
学习分布式事务的时候,可以参考一下这本书。虽有一些小错误以及逻辑不通顺的地方,但对于各种分布式事务解决方案的介绍,总体来说还是不错的。
|
||||
|
||||
|
@ -34,7 +34,7 @@ icon: "java"
|
||||
|
||||
我第一次看的时候还觉得有点枯燥,那时候还在上大二,看了 1/3 就没看下去了。
|
||||
|
||||
**[《Java 8实战》](https://book.douban.com/subject/26772632/)**
|
||||
**[《Java 8 实战》](https://book.douban.com/subject/26772632/)**
|
||||
|
||||

|
||||
|
||||
@ -64,7 +64,7 @@ _这本书还是非常适合我们用来学习 Java 多线程的。这本书的
|
||||
|
||||
这本书的质量也是非常过硬!给作者们点个赞!这本书有统一的排版规则和语言风格、清晰的表达方式和逻辑。并且每篇文章初稿写完后,作者们就会互相审校,合并到主分支时所有成员会再次审校,最后再通篇修订了三遍。
|
||||
|
||||
在线阅读:[https://redspider.gitbook.io/concurrent/](https://redspider.gitbook.io/concurrent/ ) 。
|
||||
在线阅读:[https://redspider.gitbook.io/concurrent/](https://redspider.gitbook.io/concurrent/) 。
|
||||
|
||||
**[《Java 并发实现原理:JDK 源码剖析》](https://book.douban.com/subject/35013531/)**
|
||||
|
||||
|
@ -3,11 +3,7 @@ title: 技术书籍精选
|
||||
category: 计算机书籍
|
||||
---
|
||||
|
||||
::: tip 这是一则或许对你有用的小广告
|
||||
👉 欢迎准备 Java 面试以及学习 Java 的同学加入我的[知识星球](./../about-the-author/zhishixingqiu-two-years.md),干货很多!收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。
|
||||
|
||||
👉 [《Java 面试指北》](./../zhuanlan/java-mian-shi-zhi-bei.md)持续更新完善中!这是一份教你如何更高效地准备面试的小册,涵盖常见八股文(系统设计、常见框架、分布式、高并发 ......)、优质面经等内容。
|
||||
:::
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
精选优质计算机书籍。
|
||||
|
||||
@ -18,6 +14,6 @@ category: 计算机书籍
|
||||
|
||||
如果内容对你有帮助的话,欢迎给本项目点个 Star。我会用我的业余时间持续完善这份书单,感谢!
|
||||
|
||||
本项目推荐的大部分书籍的 PDF 版本我已经整理到了云盘里,你可以在公众号“**Github掘金计划**” 后台回复“**书籍**”获取到。
|
||||
本项目推荐的大部分书籍的 PDF 版本我已经整理到了云盘里,你可以在公众号“**Github 掘金计划**” 后台回复“**书籍**”获取到。
|
||||
|
||||

|
@ -8,7 +8,7 @@ icon: "search"
|
||||
|
||||
Elasticsearch 在 Apache Lucene 的基础上开发而成,学习 ES 之前,建议简单了解一下 Lucene 的相关概念。
|
||||
|
||||
**[《Lucene实战》](https://book.douban.com/subject/6440615/)** 是国内为数不多的中文版本讲 Lucene 的书籍,适合用来学习和了解 Lucene 相关的概念和常见操作。
|
||||
**[《Lucene 实战》](https://book.douban.com/subject/6440615/)** 是国内为数不多的中文版本讲 Lucene 的书籍,适合用来学习和了解 Lucene 相关的概念和常见操作。
|
||||
|
||||

|
||||
|
||||
|
@ -127,5 +127,5 @@ Bob 大叔将自己对整洁代码的理解浓缩在了这本书中,真可谓
|
||||
|
||||
最后再推荐两个相关的文档:
|
||||
|
||||
- **阿里巴巴 Java 开发手册** :https://github.com/alibaba/p3c
|
||||
- **Google Java 编程风格指南:** http://www.hawstein.com/posts/google-java-style.html
|
||||
- **阿里巴巴 Java 开发手册** :<https://github.com/alibaba/p3c>
|
||||
- **Google Java 编程风格指南**: <http://www.hawstein.com/posts/google-java-style.html>
|
||||
|
@ -11,7 +11,7 @@ tag:
|
||||
|
||||
> Leetcode:给定两个非空链表来表示两个非负整数。位数按照逆序方式存储,它们的每个节点只存储单个数字。将两数相加返回一个新的链表。
|
||||
>
|
||||
>你可以假设除了数字 0 之外,这两个数字都不会以零开头。
|
||||
> 你可以假设除了数字 0 之外,这两个数字都不会以零开头。
|
||||
|
||||
示例:
|
||||
|
||||
@ -23,11 +23,11 @@ tag:
|
||||
|
||||
### 问题分析
|
||||
|
||||
Leetcode官方详细解答地址:
|
||||
Leetcode 官方详细解答地址:
|
||||
|
||||
https://leetcode-cn.com/problems/add-two-numbers/solution/
|
||||
https://leetcode-cn.com/problems/add-two-numbers/solution/
|
||||
|
||||
> 要对头结点进行操作时,考虑创建哑节点dummy,使用dummy->next表示真正的头节点。这样可以避免处理头节点为空的边界问题。
|
||||
> 要对头结点进行操作时,考虑创建哑节点 dummy,使用 dummy->next 表示真正的头节点。这样可以避免处理头节点为空的边界问题。
|
||||
|
||||
我们使用变量来跟踪进位,并从包含最低有效位的表头开始模拟逐
|
||||
位相加的过程。
|
||||
@ -36,7 +36,7 @@ Leetcode官方详细解答地址:
|
||||
|
||||
### Solution
|
||||
|
||||
**我们首先从最低有效位也就是列表 l1和 l2 的表头开始相加。注意需要考虑到进位的情况!**
|
||||
**我们首先从最低有效位也就是列表 l1 和 l2 的表头开始相加。注意需要考虑到进位的情况!**
|
||||
|
||||
```java
|
||||
/**
|
||||
@ -76,8 +76,8 @@ public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
|
||||
|
||||
## 2. 翻转链表
|
||||
|
||||
|
||||
### 题目描述
|
||||
|
||||
> 剑指 offer:输入一个链表,反转链表后,输出链表的所有元素。
|
||||
|
||||

|
||||
@ -88,7 +88,6 @@ public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
|
||||
|
||||
### Solution
|
||||
|
||||
|
||||
```java
|
||||
public class ListNode {
|
||||
int val;
|
||||
@ -162,18 +161,17 @@ public class Solution {
|
||||
1
|
||||
```
|
||||
|
||||
## 3. 链表中倒数第k个节点
|
||||
## 3. 链表中倒数第 k 个节点
|
||||
|
||||
### 题目描述
|
||||
|
||||
> 剑指offer: 输入一个链表,输出该链表中倒数第k个结点。
|
||||
> 剑指 offer: 输入一个链表,输出该链表中倒数第 k 个结点。
|
||||
|
||||
### 问题分析
|
||||
|
||||
> **链表中倒数第k个节点也就是正数第(L-K+1)个节点,知道了只一点,这一题基本就没问题!**
|
||||
|
||||
首先两个节点/指针,一个节点 node1 先开始跑,指针 node1 跑到 k-1 个节点后,另一个节点 node2 开始跑,当 node1 跑到最后时,node2 所指的节点就是倒数第k个节点也就是正数第(L-K+1)个节点。
|
||||
> **链表中倒数第 k 个节点也就是正数第(L-K+1)个节点,知道了只一点,这一题基本就没问题!**
|
||||
|
||||
首先两个节点/指针,一个节点 node1 先开始跑,指针 node1 跑到 k-1 个节点后,另一个节点 node2 开始跑,当 node1 跑到最后时,node2 所指的节点就是倒数第 k 个节点也就是正数第(L-K+1)个节点。
|
||||
|
||||
### Solution
|
||||
|
||||
@ -221,9 +219,7 @@ public class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## 4. 删除链表的倒数第N个节点
|
||||
|
||||
## 4. 删除链表的倒数第 N 个节点
|
||||
|
||||
> Leetcode:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
|
||||
|
||||
@ -248,8 +244,7 @@ public class Solution {
|
||||
|
||||
### 问题分析
|
||||
|
||||
|
||||
我们注意到这个问题可以容易地简化成另一个问题:删除从列表开头数起的第 (L - n + 1)个结点,其中 L是列表的长度。只要我们找到列表的长度 L,这个问题就很容易解决。
|
||||
我们注意到这个问题可以容易地简化成另一个问题:删除从列表开头数起的第 (L - n + 1)个结点,其中 L 是列表的长度。只要我们找到列表的长度 L,这个问题就很容易解决。
|
||||
|
||||

|
||||
|
||||
@ -301,14 +296,11 @@ public class Solution {
|
||||
- **时间复杂度 O(L)** :该算法对列表进行了两次遍历,首先计算了列表的长度 LL 其次找到第 (L - n)(L−n) 个结点。 操作执行了 2L-n2L−n 步,时间复杂度为 O(L)O(L)。
|
||||
- **空间复杂度 O(1)** :我们只用了常量级的额外空间。
|
||||
|
||||
|
||||
|
||||
**进阶——一次遍历法:**
|
||||
|
||||
> 链表中倒数第 N 个节点也就是正数第(L-N+1)个节点。
|
||||
|
||||
> 链表中倒数第N个节点也就是正数第(L-N+1)个节点。
|
||||
|
||||
其实这种方法就和我们上面第四题找“链表中倒数第k个节点”所用的思想是一样的。**基本思路就是:** 定义两个节点 node1、node2;node1 节点先跑,node1节点 跑到第 n+1 个节点的时候,node2 节点开始跑.当node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点(L代表总链表长度,也就是倒数第 n+1 个节点)
|
||||
其实这种方法就和我们上面第四题找“链表中倒数第 k 个节点”所用的思想是一样的。**基本思路就是:** 定义两个节点 node1、node2;node1 节点先跑,node1 节点 跑到第 n+1 个节点的时候,node2 节点开始跑.当 node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点(L 代表总链表长度,也就是倒数第 n+1 个节点)
|
||||
|
||||
```java
|
||||
/**
|
||||
@ -345,25 +337,21 @@ public class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 5. 合并两个排序的链表
|
||||
|
||||
### 题目描述
|
||||
|
||||
> 剑指offer:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
|
||||
> 剑指 offer:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
|
||||
|
||||
### 问题分析
|
||||
|
||||
我们可以这样分析:
|
||||
|
||||
1. 假设我们有两个链表 A,B;
|
||||
2. A的头节点A1的值与B的头结点B1的值比较,假设A1小,则A1为头节点;
|
||||
3. A2再和B1比较,假设B1小,则,A1指向B1;
|
||||
4. A2再和B2比较
|
||||
就这样循环往复就行了,应该还算好理解。
|
||||
2. A 的头节点 A1 的值与 B 的头结点 B1 的值比较,假设 A1 小,则 A1 为头节点;
|
||||
3. A2 再和 B1 比较,假设 B1 小,则,A1 指向 B1;
|
||||
4. A2 再和 B2 比较
|
||||
就这样循环往复就行了,应该还算好理解。
|
||||
|
||||
考虑通过递归的方式实现!
|
||||
|
||||
@ -400,4 +388,3 @@ public ListNode Merge(ListNode list1,ListNode list2) {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -9,12 +9,12 @@ tag:
|
||||
|
||||
**题目描述:**
|
||||
|
||||
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
|
||||
大家都知道斐波那契数列,现在要求输入一个整数 n,请你输出斐波那契数列的第 n 项。
|
||||
n<=39
|
||||
|
||||
**问题分析:**
|
||||
|
||||
可以肯定的是这一题通过递归的方式是肯定能做出来,但是这样会有一个很大的问题,那就是递归大量的重复计算会导致内存溢出。另外可以使用迭代法,用fn1和fn2保存计算过程中的结果,并复用起来。下面我会把两个方法示例代码都给出来并给出两个方法的运行时间对比。
|
||||
可以肯定的是这一题通过递归的方式是肯定能做出来,但是这样会有一个很大的问题,那就是递归大量的重复计算会导致内存溢出。另外可以使用迭代法,用 fn1 和 fn2 保存计算过程中的结果,并复用起来。下面我会把两个方法示例代码都给出来并给出两个方法的运行时间对比。
|
||||
|
||||
**示例代码:**
|
||||
|
||||
@ -57,24 +57,24 @@ public int Fibonacci(int n) {
|
||||
|
||||
**题目描述:**
|
||||
|
||||
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
|
||||
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
|
||||
|
||||
**问题分析:**
|
||||
|
||||
正常分析法:
|
||||
|
||||
> a.如果两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1);
|
||||
> b.假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)
|
||||
> c.由a,b假设可以得出总跳法为: f(n) = f(n-1) + f(n-2)
|
||||
> a.如果两种跳法,1 阶或者 2 阶,那么假定第一次跳的是一阶,那么剩下的是 n-1 个台阶,跳法是 f(n-1);
|
||||
> b.假定第一次跳的是 2 阶,那么剩下的是 n-2 个台阶,跳法是 f(n-2)
|
||||
> c.由 a,b 假设可以得出总跳法为: f(n) = f(n-1) + f(n-2)
|
||||
> d.然后通过实际的情况可以得出:只有一阶的时候 f(1) = 1 ,只有两阶的时候可以有 f(2) = 2
|
||||
|
||||
找规律分析法:
|
||||
|
||||
> f(1) = 1, f(2) = 2, f(3) = 3, f(4) = 5, 可以总结出f(n) = f(n-1) + f(n-2)的规律。但是为什么会出现这样的规律呢?假设现在6个台阶,我们可以从第5跳一步到6,这样的话有多少种方案跳到5就有多少种方案跳到6,另外我们也可以从4跳两步跳到6,跳到4有多少种方案的话,就有多少种方案跳到6,其他的不能从3跳到6什么的啦,所以最后就是f(6) = f(5) + f(4);这样子也很好理解变态跳台阶的问题了。
|
||||
> f(1) = 1, f(2) = 2, f(3) = 3, f(4) = 5, 可以总结出 f(n) = f(n-1) + f(n-2)的规律。但是为什么会出现这样的规律呢?假设现在 6 个台阶,我们可以从第 5 跳一步到 6,这样的话有多少种方案跳到 5 就有多少种方案跳到 6,另外我们也可以从 4 跳两步跳到 6,跳到 4 有多少种方案的话,就有多少种方案跳到 6,其他的不能从 3 跳到 6 什么的啦,所以最后就是 f(6) = f(5) + f(4);这样子也很好理解变态跳台阶的问题了。
|
||||
|
||||
**所以这道题其实就是斐波那契数列的问题。**
|
||||
|
||||
代码只需要在上一题的代码稍做修改即可。和上一题唯一不同的就是这一题的初始元素变为 1 2 3 5 8.....而上一题为1 1 2 3 5 .......。另外这一题也可以用递归做,但是递归效率太低,所以我这里只给出了迭代方式的代码。
|
||||
代码只需要在上一题的代码稍做修改即可。和上一题唯一不同的就是这一题的初始元素变为 1 2 3 5 8.....而上一题为 1 1 2 3 5 .......。另外这一题也可以用递归做,但是递归效率太低,所以我这里只给出了迭代方式的代码。
|
||||
|
||||
**示例代码:**
|
||||
|
||||
@ -103,20 +103,20 @@ int jumpFloor(int number) {
|
||||
|
||||
**题目描述:**
|
||||
|
||||
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
|
||||
一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级……它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
|
||||
|
||||
**问题分析:**
|
||||
|
||||
假设n>=2,第一步有n种跳法:跳1级、跳2级、到跳n级
|
||||
跳1级,剩下n-1级,则剩下跳法是f(n-1)
|
||||
跳2级,剩下n-2级,则剩下跳法是f(n-2)
|
||||
假设 n>=2,第一步有 n 种跳法:跳 1 级、跳 2 级、到跳 n 级
|
||||
跳 1 级,剩下 n-1 级,则剩下跳法是 f(n-1)
|
||||
跳 2 级,剩下 n-2 级,则剩下跳法是 f(n-2)
|
||||
......
|
||||
跳n-1级,剩下1级,则剩下跳法是f(1)
|
||||
跳n级,剩下0级,则剩下跳法是f(0)
|
||||
所以在n>=2的情况下:
|
||||
跳 n-1 级,剩下 1 级,则剩下跳法是 f(1)
|
||||
跳 n 级,剩下 0 级,则剩下跳法是 f(0)
|
||||
所以在 n>=2 的情况下:
|
||||
f(n)=f(n-1)+f(n-2)+...+f(1)
|
||||
因为f(n-1)=f(n-2)+f(n-3)+...+f(1)
|
||||
所以f(n)=2*f(n-1) 又f(1)=1,所以可得**f(n)=2^(number-1)**
|
||||
因为 f(n-1)=f(n-2)+f(n-3)+...+f(1)
|
||||
所以 f(n)=2\*f(n-1) 又 f(1)=1,所以可得**f(n)=2^(number-1)**
|
||||
|
||||
**示例代码:**
|
||||
|
||||
@ -128,11 +128,11 @@ int JumpFloorII(int number) {
|
||||
|
||||
**补充:**
|
||||
|
||||
java中有三种移位运算符:
|
||||
java 中有三种移位运算符:
|
||||
|
||||
1. “<<” : **左移运算符**,等同于乘2的n次方
|
||||
2. “>>”: **右移运算符**,等同于除2的n次方
|
||||
3. “>>>” : **无符号右移运算符**,不管移动前最高位是0还是1,右移后左侧产生的空位部分都以0来填充。与>>类似。
|
||||
1. “<<” : **左移运算符**,等同于乘 2 的 n 次方
|
||||
2. “>>”: **右移运算符**,等同于除 2 的 n 次方
|
||||
3. “>>>” : **无符号右移运算符**,不管移动前最高位是 0 还是 1,右移后左侧产生的空位部分都以 0 来填充。与>>类似。
|
||||
|
||||
```java
|
||||
int a = 16;
|
||||
@ -140,7 +140,6 @@ int b = a << 2;//左移2,等同于16 * 2的2次方,也就是16 * 4
|
||||
int c = a >> 2;//右移2,等同于16 / 2的2次方,也就是16 / 4
|
||||
```
|
||||
|
||||
|
||||
## 二维数组查找
|
||||
|
||||
**题目描述:**
|
||||
@ -152,8 +151,8 @@ int c = a >> 2;//右移2,等同于16 / 2的2次方,也就是16 / 4
|
||||
这一道题还是比较简单的,我们需要考虑的是如何做,效率最快。这里有一种很好理解的思路:
|
||||
|
||||
> 矩阵是有序的,从左下角来看,向上数字递减,向右数字递增,
|
||||
> 因此从左下角开始查找,当要查找数字比左下角数字大时。右移
|
||||
> 要查找数字比左下角数字小时,上移。这样找的速度最快。
|
||||
> 因此从左下角开始查找,当要查找数字比左下角数字大时。右移
|
||||
> 要查找数字比左下角数字小时,上移。这样找的速度最快。
|
||||
|
||||
**示例代码:**
|
||||
|
||||
@ -180,11 +179,11 @@ public boolean Find(int target, int [][] array) {
|
||||
|
||||
**题目描述:**
|
||||
|
||||
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
|
||||
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为 We Are Happy.则经过替换之后的字符串为 We%20Are%20Happy。
|
||||
|
||||
**问题分析:**
|
||||
|
||||
这道题不难,我们可以通过循环判断字符串的字符是否为空格,是的话就利用append()方法添加追加“%20”,否则还是追加原字符。
|
||||
这道题不难,我们可以通过循环判断字符串的字符是否为空格,是的话就利用 append()方法添加追加“%20”,否则还是追加原字符。
|
||||
|
||||
或者最简单的方法就是利用:replaceAll(String regex,String replacement)方法了,一行代码就可以解决。
|
||||
|
||||
@ -223,17 +222,14 @@ public String replaceSpace(StringBuffer str) {
|
||||
|
||||
**题目描述:**
|
||||
|
||||
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
|
||||
给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent。求 base 的 exponent 次方。
|
||||
|
||||
**问题解析:**
|
||||
|
||||
这道题算是比较麻烦和难一点的一个了。我这里采用的是**二分幂**思想,当然也可以采用**快速幂**。
|
||||
更具剑指offer书中细节,该题的解题思路如下:
|
||||
1.当底数为0且指数<0时,会出现对0求倒数的情况,需进行错误处理,设置一个全局变量;
|
||||
2.判断底数是否等于0,由于base为double型,所以不能直接用==判断
|
||||
3.优化求幂函数(二分幂)。
|
||||
当n为偶数,a^n =(a^n/2)*(a^n/2);
|
||||
当n为奇数,a^n = a^[(n-1)/2] * a^[(n-1)/2] * a。时间复杂度O(logn)
|
||||
更具剑指 offer 书中细节,该题的解题思路如下: 1.当底数为 0 且指数<0 时,会出现对 0 求倒数的情况,需进行错误处理,设置一个全局变量; 2.判断底数是否等于 0,由于 base 为 double 型,所以不能直接用==判断 3.优化求幂函数(二分幂)。
|
||||
当 n 为偶数,a^n =(a^n/2)_(a^n/2);
|
||||
当 n 为奇数,a^n = a^[(n-1)/2] _ a^[(n-1)/2] \* a。时间复杂度 O(logn)
|
||||
|
||||
**时间复杂度**:O(logn)
|
||||
|
||||
@ -286,7 +282,7 @@ public class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
当然这一题也可以采用笨方法:累乘。不过这种方法的时间复杂度为O(n),这样没有前一种方法效率高。
|
||||
当然这一题也可以采用笨方法:累乘。不过这种方法的时间复杂度为 O(n),这样没有前一种方法效率高。
|
||||
|
||||
```java
|
||||
// 使用累乘
|
||||
@ -311,11 +307,11 @@ public double powerAnother(double base, int exponent) {
|
||||
**问题解析:**
|
||||
|
||||
这道题有挺多种解法的,给大家介绍一种我觉得挺好理解的方法:
|
||||
我们首先统计奇数的个数假设为n,然后新建一个等长数组,然后通过循环判断原数组中的元素为偶数还是奇数。如果是则从数组下标0的元素开始,把该奇数添加到新数组;如果是偶数则从数组下标为n的元素开始把该偶数添加到新数组中。
|
||||
我们首先统计奇数的个数假设为 n,然后新建一个等长数组,然后通过循环判断原数组中的元素为偶数还是奇数。如果是则从数组下标 0 的元素开始,把该奇数添加到新数组;如果是偶数则从数组下标为 n 的元素开始把该偶数添加到新数组中。
|
||||
|
||||
**示例代码:**
|
||||
|
||||
时间复杂度为O(n),空间复杂度为O(n)的算法
|
||||
时间复杂度为 O(n),空间复杂度为 O(n)的算法
|
||||
|
||||
```java
|
||||
public class Solution {
|
||||
@ -346,26 +342,26 @@ public class Solution {
|
||||
}
|
||||
```
|
||||
|
||||
## 链表中倒数第k个节点
|
||||
## 链表中倒数第 k 个节点
|
||||
|
||||
**题目描述:**
|
||||
|
||||
输入一个链表,输出该链表中倒数第k个结点
|
||||
输入一个链表,输出该链表中倒数第 k 个结点
|
||||
|
||||
**问题分析:**
|
||||
|
||||
**一句话概括:**
|
||||
两个指针一个指针p1先开始跑,指针p1跑到k-1个节点后,另一个节点p2开始跑,当p1跑到最后时,p2所指的指针就是倒数第k个节点。
|
||||
两个指针一个指针 p1 先开始跑,指针 p1 跑到 k-1 个节点后,另一个节点 p2 开始跑,当 p1 跑到最后时,p2 所指的指针就是倒数第 k 个节点。
|
||||
|
||||
**思想的简单理解:**
|
||||
前提假设:链表的结点个数(长度)为n。
|
||||
规律一:要找到倒数第k个结点,需要向前走多少步呢?比如倒数第一个结点,需要走n步,那倒数第二个结点呢?很明显是向前走了n-1步,所以可以找到规律是找到倒数第k个结点,需要向前走n-k+1步。
|
||||
前提假设:链表的结点个数(长度)为 n。
|
||||
规律一:要找到倒数第 k 个结点,需要向前走多少步呢?比如倒数第一个结点,需要走 n 步,那倒数第二个结点呢?很明显是向前走了 n-1 步,所以可以找到规律是找到倒数第 k 个结点,需要向前走 n-k+1 步。
|
||||
|
||||
**算法开始:**
|
||||
|
||||
1. 设两个都指向head的指针p1和p2,当p1走了k-1步的时候,停下来。p2之前一直不动。
|
||||
2. p1的下一步是走第k步,这个时候,p2开始一起动了。至于为什么p2这个时候动呢?看下面的分析。
|
||||
3. 当p1走到链表的尾部时,即p1走了n步。由于我们知道p2是在p1走了k-1步才开始动的,也就是说p1和p2永远差k-1步。所以当p1走了n步时,p2走的应该是在n-(k-1)步。即p2走了n-k+1步,此时巧妙的是p2正好指向的是规律一的倒数第k个结点处。
|
||||
1. 设两个都指向 head 的指针 p1 和 p2,当 p1 走了 k-1 步的时候,停下来。p2 之前一直不动。
|
||||
2. p1 的下一步是走第 k 步,这个时候,p2 开始一起动了。至于为什么 p2 这个时候动呢?看下面的分析。
|
||||
3. 当 p1 走到链表的尾部时,即 p1 走了 n 步。由于我们知道 p2 是在 p1 走了 k-1 步才开始动的,也就是说 p1 和 p2 永远差 k-1 步。所以当 p1 走了 n 步时,p2 走的应该是在 n-(k-1)步。即 p2 走了 n-k+1 步,此时巧妙的是 p2 正好指向的是规律一的倒数第 k 个结点处。
|
||||
这样是不是很好理解了呢?
|
||||
|
||||
**考察内容:**
|
||||
@ -425,7 +421,7 @@ public class Solution {
|
||||
|
||||
链表的很常规的一道题,这一道题思路不算难,但自己实现起来真的可能会感觉无从下手,我是参考了别人的代码。
|
||||
思路就是我们根据链表的特点,前一个节点指向下一个节点的特点,把后面的节点移到前面来。
|
||||
就比如下图:我们把1节点和2节点互换位置,然后再将3节点指向2节点,4节点指向3节点,这样以来下面的链表就被反转了。
|
||||
就比如下图:我们把 1 节点和 2 节点互换位置,然后再将 3 节点指向 2 节点,4 节点指向 3 节点,这样以来下面的链表就被反转了。
|
||||
|
||||

|
||||
|
||||
@ -475,9 +471,9 @@ public class Solution {
|
||||
我们可以这样分析:
|
||||
|
||||
1. 假设我们有两个链表 A,B;
|
||||
2. A的头节点A1的值与B的头结点B1的值比较,假设A1小,则A1为头节点;
|
||||
3. A2再和B1比较,假设B1小,则,A1指向B1;
|
||||
4. A2再和B2比较。。。。。。。
|
||||
2. A 的头节点 A1 的值与 B 的头结点 B1 的值比较,假设 A1 小,则 A1 为头节点;
|
||||
3. A2 再和 B1 比较,假设 B1 小,则,A1 指向 B1;
|
||||
4. A2 再和 B2 比较。。。。。。。
|
||||
就这样循环往复就行了,应该还算好理解。
|
||||
|
||||
**考察内容:**
|
||||
@ -569,17 +565,17 @@ public ListNode Merge(ListNode list1,ListNode list2) {
|
||||
|
||||
**题目描述:**
|
||||
|
||||
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
|
||||
用两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。 队列中的元素为 int 类型。
|
||||
|
||||
**问题分析:**
|
||||
|
||||
先来回顾一下栈和队列的基本特点:
|
||||
**栈:**后进先出(LIFO)
|
||||
**队列:** 先进先出
|
||||
很明显我们需要根据JDK给我们提供的栈的一些基本方法来实现。先来看一下Stack类的一些基本方法:
|
||||
很明显我们需要根据 JDK 给我们提供的栈的一些基本方法来实现。先来看一下 Stack 类的一些基本方法:
|
||||

|
||||
|
||||
既然题目给了我们两个栈,我们可以这样考虑当push的时候将元素push进stack1,pop的时候我们先把stack1的元素pop到stack2,然后再对stack2执行pop操作,这样就可以保证是先进先出的。(负[pop]负[pop]得正[先进先出])
|
||||
既然题目给了我们两个栈,我们可以这样考虑当 push 的时候将元素 push 进 stack1,pop 的时候我们先把 stack1 的元素 pop 到 stack2,然后再对 stack2 执行 pop 操作,这样就可以保证是先进先出的。(负[pop]负[pop]得正[先进先出])
|
||||
|
||||
**考察内容:**
|
||||
|
||||
@ -621,34 +617,34 @@ public class Solution {
|
||||
|
||||
**题目描述:**
|
||||
|
||||
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
|
||||
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
|
||||
|
||||
**题目分析:**
|
||||
|
||||
这道题想了半天没有思路,参考了Alias的答案,他的思路写的也很详细应该很容易看懂。
|
||||
这道题想了半天没有思路,参考了 Alias 的答案,他的思路写的也很详细应该很容易看懂。
|
||||
作者:Alias
|
||||
https://www.nowcoder.com/questionTerminal/d77d11405cc7470d82554cb392585106
|
||||
来源:牛客网
|
||||
|
||||
【思路】借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。
|
||||
【思路】借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是 1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是 4,很显然 1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。
|
||||
|
||||
举例:
|
||||
|
||||
入栈1,2,3,4,5
|
||||
入栈 1,2,3,4,5
|
||||
|
||||
出栈4,5,3,2,1
|
||||
出栈 4,5,3,2,1
|
||||
|
||||
首先1入辅助栈,此时栈顶1≠4,继续入栈2
|
||||
首先 1 入辅助栈,此时栈顶 1≠4,继续入栈 2
|
||||
|
||||
此时栈顶2≠4,继续入栈3
|
||||
此时栈顶 2≠4,继续入栈 3
|
||||
|
||||
此时栈顶3≠4,继续入栈4
|
||||
此时栈顶 3≠4,继续入栈 4
|
||||
|
||||
此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3
|
||||
此时栈顶 4 = 4,出栈 4,弹出序列向后一位,此时为 5,,辅助栈里面是 1,2,3
|
||||
|
||||
此时栈顶3≠5,继续入栈5
|
||||
此时栈顶 3≠5,继续入栈 5
|
||||
|
||||
此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3
|
||||
此时栈顶 5=5,出栈 5,弹出序列向后一位,此时为 3,,辅助栈里面是 1,2,3
|
||||
|
||||
….
|
||||
依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。
|
||||
|
@ -57,7 +57,6 @@ tag:
|
||||
1. 判断给定数据是否存在:比如判断一个数字是否存在于包含大量数字的数字集中(数字集很大,5 亿以上!)、 防止缓存穿透(判断请求的数据是否有效避免直接绕过缓存请求数据库)等等、邮箱的垃圾邮件过滤、黑名单功能等等。
|
||||
2. 去重:比如爬给定网址的时候对已经爬取过的 URL 去重。
|
||||
|
||||
|
||||
## 编码实战
|
||||
|
||||
### 通过 Java 编程手动实现布隆过滤器
|
||||
@ -245,9 +244,9 @@ Redis v4.0 之后有了 Module(模块/插件) 功能,Redis Modules 让 Red
|
||||
另外,官网推荐了一个 RedisBloom 作为 Redis 布隆过滤器的 Module,地址:https://github.com/RedisBloom/RedisBloom
|
||||
其他还有:
|
||||
|
||||
* redis-lua-scaling-bloom-filter(lua 脚本实现):https://github.com/erikdubbelboer/redis-lua-scaling-bloom-filter
|
||||
* pyreBloom(Python 中的快速 Redis 布隆过滤器) :https://github.com/seomoz/pyreBloom
|
||||
* ......
|
||||
- redis-lua-scaling-bloom-filter(lua 脚本实现):https://github.com/erikdubbelboer/redis-lua-scaling-bloom-filter
|
||||
- pyreBloom(Python 中的快速 Redis 布隆过滤器) :https://github.com/seomoz/pyreBloom
|
||||
- ......
|
||||
|
||||
RedisBloom 提供了多种语言的客户端支持,包括:Python、Java、JavaScript 和 PHP。
|
||||
|
||||
@ -287,7 +286,7 @@ root@21396d02c252:/data# redis-cli
|
||||
|
||||
可选参数:
|
||||
|
||||
* expansion:如果创建了一个新的子过滤器,则其大小将是当前过滤器的大小乘以`expansion`。默认扩展值为 2。这意味着每个后续子过滤器将是前一个子过滤器的两倍。
|
||||
- expansion:如果创建了一个新的子过滤器,则其大小将是当前过滤器的大小乘以`expansion`。默认扩展值为 2。这意味着每个后续子过滤器将是前一个子过滤器的两倍。
|
||||
|
||||
### 实际使用
|
||||
|
||||
|
@ -14,7 +14,7 @@ tag:
|
||||
|
||||
但是,图形结构的元素之间的关系是任意的。
|
||||
|
||||
**何为图呢?** 简单来说,图就是由顶点的有穷非空集合和顶点之间的边组成的集合。通常表示为:**G(V,E)**,其中,G表示一个图,V表示顶点的集合,E表示边的集合。
|
||||
**何为图呢?** 简单来说,图就是由顶点的有穷非空集合和顶点之间的边组成的集合。通常表示为:**G(V,E)**,其中,G 表示一个图,V 表示顶点的集合,E 表示边的集合。
|
||||
|
||||
下图所展示的就是图这种数据结构,并且还是一张有向图。
|
||||
|
||||
@ -25,24 +25,28 @@ tag:
|
||||
## 图的基本概念
|
||||
|
||||
### 顶点
|
||||
|
||||
图中的数据元素,我们称之为顶点,图至少有一个顶点(非空有穷集合)
|
||||
|
||||
对应到好友关系图,每一个用户就代表一个顶点。
|
||||
|
||||
### 边
|
||||
|
||||
顶点之间的关系用边表示。
|
||||
|
||||
对应到好友关系图,两个用户是好友的话,那两者之间就存在一条边。
|
||||
|
||||
### 度
|
||||
|
||||
度表示一个顶点包含多少条边,在有向图中,还分为出度和入度,出度表示从该顶点出去的边的条数,入度表示进入该顶点的边的条数。
|
||||
|
||||
对应到好友关系图,度就代表了某个人的好友数量。
|
||||
|
||||
### 无向图和有向图
|
||||
边表示的是顶点之间的关系,有的关系是双向的,比如同学关系,A是B的同学,那么B也肯定是A的同学,那么在表示A和B的关系时,就不用关注方向,用不带箭头的边表示,这样的图就是无向图。
|
||||
|
||||
有的关系是有方向的,比如父子关系,师生关系,微博的关注关系,A是B的爸爸,但B肯定不是A的爸爸,A关注B,B不一定关注A。在这种情况下,我们就用带箭头的边表示二者的关系,这样的图就是有向图。
|
||||
边表示的是顶点之间的关系,有的关系是双向的,比如同学关系,A 是 B 的同学,那么 B 也肯定是 A 的同学,那么在表示 A 和 B 的关系时,就不用关注方向,用不带箭头的边表示,这样的图就是无向图。
|
||||
|
||||
有的关系是有方向的,比如父子关系,师生关系,微博的关注关系,A 是 B 的爸爸,但 B 肯定不是 A 的爸爸,A 关注 B,B 不一定关注 A。在这种情况下,我们就用带箭头的边表示二者的关系,这样的图就是有向图。
|
||||
|
||||
### 无权图和带权图
|
||||
|
||||
@ -55,16 +59,18 @@ tag:
|
||||

|
||||
|
||||
## 图的存储
|
||||
|
||||
### 邻接矩阵存储
|
||||
|
||||
邻接矩阵将图用二维矩阵存储,是一种较为直观的表示方式。
|
||||
|
||||
如果第i个顶点和第j个顶点之间有关系,且关系权值为n,则 `A[i][j]=n` 。
|
||||
如果第 i 个顶点和第 j 个顶点之间有关系,且关系权值为 n,则 `A[i][j]=n` 。
|
||||
|
||||
在无向图中,我们只关心关系的有无,所以当顶点i和顶点j有关系时,`A[i][j]`=1,当顶点i和顶点j没有关系时,`A[i][j]`=0。如下图所示:
|
||||
在无向图中,我们只关心关系的有无,所以当顶点 i 和顶点 j 有关系时,`A[i][j]`=1,当顶点 i 和顶点 j 没有关系时,`A[i][j]`=0。如下图所示:
|
||||
|
||||

|
||||
|
||||
值得注意的是:**无向图的邻接矩阵是一个对称矩阵,因为在无向图中,顶点i和顶点j有关系,则顶点j和顶点i必有关系。**
|
||||
值得注意的是:**无向图的邻接矩阵是一个对称矩阵,因为在无向图中,顶点 i 和顶点 j 有关系,则顶点 j 和顶点 i 必有关系。**
|
||||
|
||||

|
||||
|
||||
@ -74,7 +80,7 @@ tag:
|
||||
|
||||
针对上面邻接矩阵比较浪费内存空间的问题,诞生了图的另外一种存储方法—**邻接表** 。
|
||||
|
||||
邻接链表使用一个链表来存储某个顶点的所有后继相邻顶点。对于图中每个顶点Vi,把所有邻接于Vi的顶点Vj链成一个单链表,这个单链表称为顶点Vi的 **邻接表**。如下图所示:
|
||||
邻接链表使用一个链表来存储某个顶点的所有后继相邻顶点。对于图中每个顶点 Vi,把所有邻接于 Vi 的顶点 Vj 链成一个单链表,这个单链表称为顶点 Vi 的 **邻接表**。如下图所示:
|
||||
|
||||

|
||||
|
||||
@ -82,38 +88,40 @@ tag:
|
||||
|
||||
大家可以数一数邻接表中所存储的元素的个数以及图中边的条数,你会发现:
|
||||
|
||||
- 在无向图中,邻接表元素个数等于边的条数的两倍,如左图所示的无向图中,边的条数为7,邻接表存储的元素个数为14。
|
||||
- 在有向图中,邻接表元素个数等于边的条数,如右图所示的有向图中,边的条数为8,邻接表存储的元素个数为8。
|
||||
- 在无向图中,邻接表元素个数等于边的条数的两倍,如左图所示的无向图中,边的条数为 7,邻接表存储的元素个数为 14。
|
||||
- 在有向图中,邻接表元素个数等于边的条数,如右图所示的有向图中,边的条数为 8,邻接表存储的元素个数为 8。
|
||||
|
||||
## 图的搜索
|
||||
|
||||
### 广度优先搜索
|
||||
|
||||
广度优先搜索就像水面上的波纹一样一层一层向外扩展,如下图所示:
|
||||
|
||||

|
||||
|
||||
**广度优先搜索的具体实现方式用到了之前所学过的线性数据结构——队列** 。具体过程如下图所示:
|
||||
|
||||
**第1步:**
|
||||
**第 1 步:**
|
||||
|
||||

|
||||
|
||||
**第2步:**
|
||||
**第 2 步:**
|
||||
|
||||

|
||||
|
||||
**第3步:**
|
||||
**第 3 步:**
|
||||
|
||||

|
||||
|
||||
**第4步:**
|
||||
**第 4 步:**
|
||||
|
||||

|
||||
|
||||
**第5步:**
|
||||
**第 5 步:**
|
||||
|
||||

|
||||
|
||||
**第6步:**
|
||||
**第 6 步:**
|
||||
|
||||

|
||||
|
||||
@ -123,30 +131,28 @@ tag:
|
||||
|
||||

|
||||
|
||||
|
||||
**和广度优先搜索类似,深度优先搜索的具体实现用到了另一种线性数据结构——栈** 。具体过程如下图所示:
|
||||
|
||||
**第1步:**
|
||||
**第 1 步:**
|
||||
|
||||

|
||||
|
||||
**第2步:**
|
||||
**第 2 步:**
|
||||
|
||||

|
||||
|
||||
**第3步:**
|
||||
**第 3 步:**
|
||||
|
||||

|
||||
|
||||
**第4步:**
|
||||
**第 4 步:**
|
||||
|
||||

|
||||
|
||||
**第5步:**
|
||||
**第 5 步:**
|
||||
|
||||

|
||||
|
||||
**第6步:**
|
||||
**第 6 步:**
|
||||
|
||||

|
||||
|
||||
|
@ -23,11 +23,12 @@ tag:
|
||||
|
||||

|
||||
|
||||
第1个和第2个是堆。第1个是最大堆,每个节点都比子树中所有节点大。第2个是最小堆,每个节点都比子树中所有节点小。
|
||||
第 1 个和第 2 个是堆。第 1 个是最大堆,每个节点都比子树中所有节点大。第 2 个是最小堆,每个节点都比子树中所有节点小。
|
||||
|
||||
第3个不是,第三个中,根结点1比2和15小,而15却比3大,19比5大,不满足堆的性质。
|
||||
第 3 个不是,第三个中,根结点 1 比 2 和 15 小,而 15 却比 3 大,19 比 5 大,不满足堆的性质。
|
||||
|
||||
## 堆的用途
|
||||
|
||||
当我们只关心所有数据中的最大值或者最小值,存在多次获取最大值或者最小值,多次插入或删除数据时,就可以使用堆。
|
||||
|
||||
有小伙伴可能会想到用有序数组,初始化一个有序数组时间复杂度是 `O(nlog(n))`,查找最大值或者最小值时间复杂度都是 `O(1)`,但是,涉及到更新(插入或删除)数据时,时间复杂度为 `O(n)`,即使是使用复杂度为 `O(log(n))` 的二分法找到要插入或者删除的数据,在移动数据时也需要 `O(n)` 的时间复杂度。
|
||||
@ -39,25 +40,30 @@ tag:
|
||||
## 堆的分类
|
||||
|
||||
堆分为 **最大堆** 和 **最小堆**。二者的区别在于节点的排序方式。
|
||||
|
||||
- **最大堆** :堆中的每一个节点的值都大于等于子树中所有节点的值
|
||||
- **最小堆** :堆中的每一个节点的值都小于等于子树中所有节点的值
|
||||
|
||||
如下图所示,图1是最大堆,图2是最小堆
|
||||
如下图所示,图 1 是最大堆,图 2 是最小堆
|
||||
|
||||

|
||||
|
||||
|
||||
## 堆的存储
|
||||
之前介绍树的时候说过,由于完全二叉树的优秀性质,利用数组存储二叉树即节省空间,又方便索引(若根结点的序号为1,那么对于树中任意节点i,其左子节点序号为 `2*i`,右子节点序号为 `2*i+1`)。
|
||||
|
||||
之前介绍树的时候说过,由于完全二叉树的优秀性质,利用数组存储二叉树即节省空间,又方便索引(若根结点的序号为 1,那么对于树中任意节点 i,其左子节点序号为 `2*i`,右子节点序号为 `2*i+1`)。
|
||||
|
||||
为了方便存储和索引,(二叉)堆可以用完全二叉树的形式进行存储。存储的方式如下图所示:
|
||||
|
||||

|
||||
|
||||
## 堆的操作
|
||||
堆的更新操作主要包括两种 : **插入元素** 和 **删除堆顶元素**。操作过程需要着重掌握和理解。
|
||||
|
||||
堆的更新操作主要包括两种 : **插入元素** 和 **删除堆顶元素**。操作过程需要着重掌握和理解。
|
||||
|
||||
> 在进入正题之前,再重申一遍,堆是一个公平的公司,有能力的人自然会走到与他能力所匹配的位置
|
||||
|
||||
### 插入元素
|
||||
|
||||
> 插入元素,作为一个新入职的员工,初来乍到,这个员工需要从基层做起
|
||||
|
||||
**1.将要插入的元素放到最后**
|
||||
@ -85,20 +91,16 @@ tag:
|
||||
|
||||
> 在堆这个公司中,会出现老大离职的现象,老大离职之后,他的位置就空出来了
|
||||
|
||||
首先删除堆顶元素,使得数组中下标为1的位置空出。
|
||||
|
||||
|
||||
首先删除堆顶元素,使得数组中下标为 1 的位置空出。
|
||||
|
||||

|
||||
|
||||
|
||||
> 那么他的位置由谁来接替呢,当然是他的直接下属了,谁能力强就让谁上呗
|
||||
|
||||
比较根结点的左子节点和右子节点,也就是下标为2,3的数组元素,将较大的元素填充到根结点(下标为1)的位置。
|
||||
比较根结点的左子节点和右子节点,也就是下标为 2,3 的数组元素,将较大的元素填充到根结点(下标为 1)的位置。
|
||||
|
||||

|
||||
|
||||
|
||||
> 这个时候又空出一个位置了,老规矩,谁有能力谁上
|
||||
|
||||
一直循环比较空出位置的左右子节点,并将较大者移至空位,直到堆的最底部
|
||||
@ -108,6 +110,7 @@ tag:
|
||||
这个时候已经完成了自底向上的堆化,没有元素可以填补空缺了,但是,我们可以看到数组中出现了“气泡”,这会导致存储空间的浪费。接下来我们试试自顶向下堆化。
|
||||
|
||||
#### 自顶向下堆化
|
||||
|
||||
自顶向下的堆化用一个词形容就是“石沉大海”,那么第一件事情,就是把石头抬起来,从海面扔下去。这个石头就是堆的最后一个元素,我们将最后一个元素移动到堆顶。
|
||||
|
||||

|
||||
@ -118,14 +121,11 @@ tag:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 堆的操作总结
|
||||
|
||||
- **插入元素** :先将元素放至数组末尾,再自底向上堆化,将末尾元素上浮
|
||||
- **删除堆顶元素** :删除堆顶元素,将末尾元素放至堆顶,再自顶向下堆化,将堆顶元素下沉。也可以自底向上堆化,只是会产生“气泡”,浪费存储空间。最好采用自顶向下堆化的方式。
|
||||
|
||||
|
||||
## 堆排序
|
||||
|
||||
堆排序的过程分为两步:
|
||||
@ -137,22 +137,22 @@ tag:
|
||||
|
||||
如果你已经足够了解堆化的过程,那么建堆的过程掌握起来就比较容易了。建堆的过程就是一个对所有非叶节点的自顶向下堆化过程。
|
||||
|
||||
首先要了解哪些是非叶节点,最后一个节点的父结点及它之前的元素,都是非叶节点。也就是说,如果节点个数为n,那么我们需要对n/2到1的节点进行自顶向下(沉底)堆化。
|
||||
首先要了解哪些是非叶节点,最后一个节点的父结点及它之前的元素,都是非叶节点。也就是说,如果节点个数为 n,那么我们需要对 n/2 到 1 的节点进行自顶向下(沉底)堆化。
|
||||
|
||||
具体过程如下图:
|
||||
|
||||

|
||||
|
||||
将初始的无序数组抽象为一棵树,图中的节点个数为6,所以4,5,6节点为叶节点,1,2,3节点为非叶节点,所以要对1-3号节点进行自顶向下(沉底)堆化,注意,顺序是从后往前堆化,从3号节点开始,一直到1号节点。
|
||||
3号节点堆化结果:
|
||||
将初始的无序数组抽象为一棵树,图中的节点个数为 6,所以 4,5,6 节点为叶节点,1,2,3 节点为非叶节点,所以要对 1-3 号节点进行自顶向下(沉底)堆化,注意,顺序是从后往前堆化,从 3 号节点开始,一直到 1 号节点。
|
||||
3 号节点堆化结果:
|
||||
|
||||

|
||||
|
||||
2号节点堆化结果:
|
||||
2 号节点堆化结果:
|
||||
|
||||

|
||||
|
||||
1号节点堆化结果:
|
||||
1 号节点堆化结果:
|
||||
|
||||

|
||||
|
||||
|
@ -10,13 +10,12 @@ tag:
|
||||
|
||||
1. 每个节点非红即黑;
|
||||
2. 根节点总是黑色的;
|
||||
3. 每个叶子节点都是黑色的空节点(NIL节点);
|
||||
3. 每个叶子节点都是黑色的空节点(NIL 节点);
|
||||
4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
|
||||
5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
|
||||
|
||||
**红黑树的应用** :TreeMap、TreeSet以及JDK1.8的HashMap底层都用到了红黑树。
|
||||
**红黑树的应用** :TreeMap、TreeSet 以及 JDK1.8 的 HashMap 底层都用到了红黑树。
|
||||
|
||||
**为什么要用红黑树?** 简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。详细了解可以查看 [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐)
|
||||
|
||||
**相关阅读** :[《红黑树深入剖析及Java实现》](https://zhuanlan.zhihu.com/p/24367771)(美团点评技术团队)
|
||||
|
||||
**相关阅读** :[《红黑树深入剖析及 Java 实现》](https://zhuanlan.zhihu.com/p/24367771)(美团点评技术团队)
|
||||
|
@ -38,7 +38,7 @@ tag:
|
||||
|
||||
**二叉树** 的分支通常被称作“**左子树**”或“**右子树**”。并且,**二叉树** 的分支具有左右次序,不能随意颠倒。
|
||||
|
||||
**二叉树** 的第 i 层至多拥有 `2^(i-1)` 个节点,深度为 k 的二叉树至多总共有 `2^(k+1)-1` 个节点(满二叉树的情况),至少有 2^(k) 个节点(关于节点的深度的定义国内争议比较多,我个人比较认可维基百科对[节点深度的定义](https://zh.wikipedia.org/wiki/%E6%A0%91_(%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)#/%E6%9C%AF%E8%AF%AD))。
|
||||
**二叉树** 的第 i 层至多拥有 `2^(i-1)` 个节点,深度为 k 的二叉树至多总共有 `2^(k+1)-1` 个节点(满二叉树的情况),至少有 2^(k) 个节点(关于节点的深度的定义国内争议比较多,我个人比较认可维基百科对[节点深度的定义](<https://zh.wikipedia.org/wiki/%E6%A0%91_(%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)#/%E6%9C%AF%E8%AF%AD>))。
|
||||
|
||||

|
||||
|
||||
@ -48,8 +48,6 @@ tag:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 完全二叉树
|
||||
|
||||
除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则这个二叉树就是 **完全二叉树** 。
|
||||
|
@ -5,8 +5,6 @@ tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
|
||||
|
||||
## HTTP:超文本传输协议
|
||||
|
||||
**超文本传输协议(HTTP,HyperText Transfer Protocol)** 是一种用于传输超文本和多媒体内容的协议,主要是为 Web 浏览器与 Web 服务器之间的通信而设计的。当我们使用浏览器浏览网页的时候,我们网页就是通过 HTTP 请求进行加载的。
|
||||
|
@ -5,12 +5,11 @@ tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
|
||||
本文是我在大二学习计算机网络期间整理, 大部分内容都来自于谢希仁老师的[《计算机网络》第七版 ](https://www.elias.ltd/usr/local/etc/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%88%E7%AC%AC7%E7%89%88%EF%BC%89%E8%B0%A2%E5%B8%8C%E4%BB%81.pdf)这本书。为了内容更容易理解,我对之前的整理进行了一波重构,并配上了一些相关的示意图便于理解。
|
||||
|
||||

|
||||
|
||||
相关问题:[如何评价谢希仁的计算机网络(第七版)? - 知乎](https://www.zhihu.com/question/327872966) 。
|
||||
相关问题:[如何评价谢希仁的计算机网络(第七版)? - 知乎](https://www.zhihu.com/question/327872966) 。
|
||||
|
||||
## 1. 计算机网络概述
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: DNS 域名系统详解(应用层)
|
||||
title: DNS 域名系统详解(应用层)
|
||||
category: 计算机基础
|
||||
tag:
|
||||
- 计算机网络
|
||||
|
@ -1,11 +1,11 @@
|
||||
---
|
||||
title: HTTP 常见状态码总结(应用层)
|
||||
title: HTTP 常见状态码总结(应用层)
|
||||
category: 计算机基础
|
||||
tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
HTTP 状态码用于描述 HTTP 请求的结果,比如2xx 就代表请求被成功处理。
|
||||
HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被成功处理。
|
||||
|
||||

|
||||
|
||||
@ -15,32 +15,32 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如2xx 就代表请求被
|
||||
|
||||
### 2xx Success(成功状态码)
|
||||
|
||||
- **200 OK** :请求被成功处理。比如我们发送一个查询用户数据的HTTP 请求到服务端,服务端正确返回了用户数据。这个是我们平时最常见的一个 HTTP 状态码。
|
||||
- **200 OK** :请求被成功处理。比如我们发送一个查询用户数据的 HTTP 请求到服务端,服务端正确返回了用户数据。这个是我们平时最常见的一个 HTTP 状态码。
|
||||
- **201 Created** :请求被成功处理并且在服务端创建了一个新的资源。比如我们通过 POST 请求创建一个新的用户。
|
||||
- **202 Accepted** :服务端已经接收到了请求,但是还未处理。
|
||||
- **204 No Content** : 服务端已经成功处理了请求,但是没有返回任何内容。
|
||||
- **204 No Content** : 服务端已经成功处理了请求,但是没有返回任何内容。
|
||||
|
||||
这里格外提一下 204 状态码,平时学习/工作中见到的次数并不多。
|
||||
|
||||
[HTTP RFC 2616对204状态码的描述](https://tools.ietf.org/html/rfc2616#section-10.2.5)如下:
|
||||
[HTTP RFC 2616 对 204 状态码的描述](https://tools.ietf.org/html/rfc2616#section-10.2.5)如下:
|
||||
|
||||
> The server has fulfilled the request but does not need to return an
|
||||
> entity-body, and might want to return updated metainformation. The
|
||||
> response MAY include new or updated metainformation in the form of
|
||||
> entity-headers, which if present SHOULD be associated with the
|
||||
> requested variant.
|
||||
> The server has fulfilled the request but does not need to return an
|
||||
> entity-body, and might want to return updated metainformation. The
|
||||
> response MAY include new or updated metainformation in the form of
|
||||
> entity-headers, which if present SHOULD be associated with the
|
||||
> requested variant.
|
||||
>
|
||||
> If the client is a user agent, it SHOULD NOT change its document view
|
||||
> from that which caused the request to be sent. This response is
|
||||
> primarily intended to allow input for actions to take place without
|
||||
> causing a change to the user agent's active document view, although
|
||||
> any new or updated metainformation SHOULD be applied to the document
|
||||
> currently in the user agent's active view.
|
||||
> If the client is a user agent, it SHOULD NOT change its document view
|
||||
> from that which caused the request to be sent. This response is
|
||||
> primarily intended to allow input for actions to take place without
|
||||
> causing a change to the user agent's active document view, although
|
||||
> any new or updated metainformation SHOULD be applied to the document
|
||||
> currently in the user agent's active view.
|
||||
>
|
||||
> The 204 response MUST NOT include a message-body, and thus is always
|
||||
> terminated by the first empty line after the header fields.
|
||||
> The 204 response MUST NOT include a message-body, and thus is always
|
||||
> terminated by the first empty line after the header fields.
|
||||
|
||||
简单来说,204状态码描述的是我们向服务端发送 HTTP 请求之后,只关注处理结果是否成功的场景。也就是说我们需要的就是一个结果:true/false。
|
||||
简单来说,204 状态码描述的是我们向服务端发送 HTTP 请求之后,只关注处理结果是否成功的场景。也就是说我们需要的就是一个结果:true/false。
|
||||
|
||||
举个例子:你要追一个女孩子,你问女孩子:“我能追你吗?”,女孩子回答:“好!”。我们把这个女孩子当做是服务端就很好理解 204 状态码了。
|
||||
|
||||
@ -51,15 +51,15 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如2xx 就代表请求被
|
||||
|
||||
### 4xx Client Error(客户端错误状态码)
|
||||
|
||||
- **400 Bad Request** : 发送的HTTP请求存在问题。比如请求参数不合法、请求方法错误。
|
||||
- **400 Bad Request** : 发送的 HTTP 请求存在问题。比如请求参数不合法、请求方法错误。
|
||||
- **401 Unauthorized** : 未认证却请求需要认证之后才能访问的资源。
|
||||
- **403 Forbidden** :直接拒绝HTTP请求,不处理。一般用来针对非法请求。
|
||||
- **403 Forbidden** :直接拒绝 HTTP 请求,不处理。一般用来针对非法请求。
|
||||
- **404 Not Found** : 你请求的资源未在服务端找到。比如你请求某个用户的信息,服务端并没有找到指定的用户。
|
||||
- **409 Conflict** : 表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。
|
||||
|
||||
### 5xx Server Error(服务端错误状态码)
|
||||
|
||||
- **500 Internal Server Error** : 服务端出问题了(通常是服务端出Bug了)。比如你服务端处理请求的时候突然抛出异常,但是异常并未在服务端被正确处理。
|
||||
- **500 Internal Server Error** : 服务端出问题了(通常是服务端出 Bug 了)。比如你服务端处理请求的时候突然抛出异常,但是异常并未在服务端被正确处理。
|
||||
- **502 Bad Gateway** :我们的网关将请求转发到服务端,但是服务端返回的却是一个错误的响应。
|
||||
|
||||
### 参考
|
||||
@ -68,4 +68,3 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如2xx 就代表请求被
|
||||
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
|
||||
- https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
- https://segmentfault.com/a/1190000018264501
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: HTTP vs HTTPS(应用层)
|
||||
title: HTTP vs HTTPS(应用层)
|
||||
category: 计算机基础
|
||||
tag:
|
||||
- 计算机网络
|
||||
@ -39,7 +39,7 @@ HTTPS 协议中,SSL 通道通常使用基于密钥的加密算法,密钥长
|
||||
|
||||
保密性好、信任度高。
|
||||
|
||||
## HTTPS 的核心—SSL/TLS协议
|
||||
## HTTPS 的核心—SSL/TLS 协议
|
||||
|
||||
HTTPS 之所以能达到较高的安全性要求,就是结合了 SSL/TLS 和 TCP 协议,对通信数据进行加密,解决了 HTTP 数据透明的问题。接下来重点介绍一下 SSL/TLS 的工作原理。
|
||||
|
||||
@ -61,7 +61,7 @@ SSL/TLS 的核心要素是**非对称加密**。非对称加密采用两个密
|
||||
>
|
||||
> 这样,通信信息就不会被其他人截获了,这依赖于私钥的保密性。
|
||||
|
||||

|
||||

|
||||
|
||||
非对称加密的公钥和私钥需要采用一种复杂的数学机制生成(密码学认为,为了较高的安全性,尽量不要自己创造加密方案)。公私钥对的生成算法依赖于单向陷门函数。
|
||||
|
||||
@ -69,7 +69,7 @@ SSL/TLS 的核心要素是**非对称加密**。非对称加密采用两个密
|
||||
>
|
||||
> 单向陷门函数:一个较弱的单向函数。已知单向陷门函数 f,陷门 h,给定任意一个输入 x,易计算出输出 y=f(x;h);而给定一个输出 y,假设存在 f(x;h)=y,很难根据 f 来计算出 x,但可以根据 f 和 h 来推导出 x。
|
||||
|
||||

|
||||

|
||||
|
||||
上图就是一个单向函数(不是单项陷门函数),假设有一个绝世秘籍,任何知道了这个秘籍的人都可以把苹果汁榨成苹果,那么这个秘籍就是“陷门”了吧。
|
||||
|
||||
@ -81,7 +81,7 @@ SSL/TLS 的核心要素是**非对称加密**。非对称加密采用两个密
|
||||
|
||||
> 对称加密:通信双方共享唯一密钥 k,加解密算法已知,加密方利用密钥 k 加密,解密方利用密钥 k 解密,保密性依赖于密钥 k 的保密性。
|
||||
|
||||

|
||||

|
||||
|
||||
对称加密的密钥生成代价比公私钥对的生成代价低得多,那么有的人会问了,为什么 SSL/TLS 还需要使用非对称加密呢?因为对称加密的保密性完全依赖于密钥的保密性。在双方通信之前,需要商量一个用于对称加密的密钥。我们知道网络通信的信道是不安全的,传输报文对任何人是可见的,密钥的交换肯定不能直接在网络信道中传输。因此,使用非对称加密,对对称加密的密钥进行加密,保护该密钥不在网络信道中被窃听。这样,通信双方只需要一次非对称加密,交换对称加密的密钥,在之后的信息通信中,使用绝对安全的密钥,对信息进行对称加密,即可保证传输消息的保密性。
|
||||
|
||||
@ -99,7 +99,7 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
|
||||
>
|
||||
> 同样的,S 公钥即使做加密,也难以避免这种信任性问题,C 被 AS 拐跑了!
|
||||
|
||||

|
||||

|
||||
|
||||
为了公钥传输的信赖性问题,第三方机构应运而生——证书颁发机构(CA,Certificate Authority)。CA 默认是受信任的第三方。CA 会给各个服务器颁发证书,证书存储在服务器上,并附有 CA 的**电子签名**(见下节)。
|
||||
|
||||
@ -117,7 +117,7 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
|
||||
>
|
||||
> 客户端对证书数据(包含服务器的公钥)做相同的散列处理,得到摘要,并将该摘要与之前从签名中解码出的摘要做对比,如果相同,则身份验证成功;否则验证失败。
|
||||
|
||||

|
||||

|
||||
|
||||
总结来说,带有证书的公钥传输机制如下:
|
||||
|
||||
@ -127,7 +127,7 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
|
||||
4. C 获得 S 的证书,信任 CA 并知晓 CA 公钥,使用 CA 公钥对 S 证书上的签名解密,同时对消息进行散列处理,得到摘要。比较摘要,验证 S 证书的真实性。
|
||||
5. 如果 C 验证 S 证书是真实的,则信任 S 的公钥(在 S 证书中)。
|
||||
|
||||

|
||||

|
||||
|
||||
对于数字签名,我这里讲的比较简单,如果你没有搞清楚的话,强烈推荐你看看[数字签名及数字证书原理](https://www.bilibili.com/video/BV18N411X7ty/)这个视频,这是我看过最清晰的讲解。
|
||||
|
||||
@ -138,6 +138,3 @@ SSL/TLS 介绍到这里,了解信息安全的朋友又会想到一个安全隐
|
||||
- **端口号** :HTTP 默认是 80,HTTPS 默认是 443。
|
||||
- **URL 前缀** :HTTP 的 URL 前缀是 `http://`,HTTPS 的 URL 前缀是 `https://`。
|
||||
- **安全性和资源消耗** : HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
|
||||
|
||||
|
||||
|
@ -1,105 +0,0 @@
|
||||
---
|
||||
title: HTTP 1.0 vs HTTP 1.1(应用层)
|
||||
category: 计算机基础
|
||||
tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
这篇文章会从下面几个维度来对比 HTTP 1.0 和 HTTP 1.1:
|
||||
|
||||
- 响应状态码
|
||||
- 缓存处理
|
||||
- 连接方式
|
||||
- Host头处理
|
||||
- 带宽优化
|
||||
|
||||
## 响应状态码
|
||||
|
||||
HTTP/1.0仅定义了16种状态码。HTTP/1.1中新加入了大量的状态码,光是错误响应状态码就新增了24种。比如说,`100 (Continue)`——在请求大资源前的预热请求,`206 (Partial Content)`——范围请求的标识码,`409 (Conflict)`——请求与当前资源的规定冲突,`410 (Gone)`——资源已被永久转移,而且没有任何已知的转发地址。
|
||||
|
||||
## 缓存处理
|
||||
|
||||
缓存技术通过避免用户与源服务器的频繁交互,节约了大量的网络带宽,降低了用户接收信息的延迟。
|
||||
|
||||
### HTTP/1.0
|
||||
|
||||
HTTP/1.0提供的缓存机制非常简单。服务器端使用`Expires`标签来标志(时间)一个响应体,在`Expires`标志时间内的请求,都会获得该响应体缓存。服务器端在初次返回给客户端的响应体中,有一个`Last-Modified`标签,该标签标记了被请求资源在服务器端的最后一次修改。在请求头中,使用`If-Modified-Since`标签,该标签标志一个时间,意为客户端向服务器进行问询:“该时间之后,我要请求的资源是否有被修改过?”通常情况下,请求头中的`If-Modified-Since`的值即为上一次获得该资源时,响应体中的`Last-Modified`的值。
|
||||
|
||||
如果服务器接收到了请求头,并判断`If-Modified-Since`时间后,资源确实没有修改过,则返回给客户端一个`304 not modified`响应头,表示”缓冲可用,你从浏览器里拿吧!”。
|
||||
|
||||
如果服务器判断`If-Modified-Since`时间后,资源被修改过,则返回给客户端一个`200 OK`的响应体,并附带全新的资源内容,表示”你要的我已经改过的,给你一份新的”。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### HTTP/1.1
|
||||
|
||||
HTTP/1.1的缓存机制在HTTP/1.0的基础上,大大增加了灵活性和扩展性。基本工作原理和HTTP/1.0保持不变,而是增加了更多细致的特性。其中,请求头中最常见的特性就是`Cache-Control`,详见MDN Web文档 [Cache-Control](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control).
|
||||
|
||||
## 连接方式
|
||||
|
||||
**HTTP/1.0 默认使用短连接** ,也就是说,客户端和服务器每进行一次 HTTP 操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源(如 JavaScript 文件、图像文件、CSS 文件等),每遇到这样一个 Web 资源,浏览器就会重新建立一个TCP连接,这样就会导致有大量的“握手报文”和“挥手报文”占用了带宽。
|
||||
|
||||
**为了解决 HTTP/1.0 存在的资源浪费的问题, HTTP/1.1 优化为默认长连接模式 。** 采用长连接模式的请求报文会通知服务端:“我向你请求连接,并且连接成功建立后,请不要关闭”。因此,该TCP连接将持续打开,为后续的客户端-服务端的数据交互服务。也就是说在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。
|
||||
|
||||
如果 TCP 连接一直保持的话也是对资源的浪费,因此,一些服务器软件(如 Apache)还会支持超时时间的时间。在超时时间之内没有新的请求达到,TCP 连接才会被关闭。
|
||||
|
||||
有必要说明的是,HTTP/1.0仍提供了长连接选项,即在请求头中加入`Connection: Keep-alive`。同样的,在HTTP/1.1中,如果不希望使用长连接选项,也可以在请求头中加入`Connection: close`,这样会通知服务器端:“我不需要长连接,连接成功后即可关闭”。
|
||||
|
||||
**HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。**
|
||||
|
||||
**实现长连接需要客户端和服务端都支持长连接。**
|
||||
|
||||
## Host头处理
|
||||
|
||||
域名系统(DNS)允许多个主机名绑定到同一个IP地址上,但是HTTP/1.0并没有考虑这个问题,假设我们有一个资源URL是http://example1.org/home.html,HTTP/1.0的请求报文中,将会请求的是`GET /home.html HTTP/1.0`.也就是不会加入主机名。这样的报文送到服务器端,服务器是理解不了客户端想请求的真正网址。
|
||||
|
||||
因此,HTTP/1.1在请求头中加入了`Host`字段。加入`Host`字段的报文头部将会是:
|
||||
|
||||
```
|
||||
GET /home.html HTTP/1.1
|
||||
Host: example1.org
|
||||
```
|
||||
|
||||
这样,服务器端就可以确定客户端想要请求的真正的网址了。
|
||||
|
||||
## 带宽优化
|
||||
|
||||
### 范围请求
|
||||
|
||||
HTTP/1.1引入了范围请求(range request)机制,以避免带宽的浪费。当客户端想请求一个文件的一部分,或者需要继续下载一个已经下载了部分但被终止的文件,HTTP/1.1可以在请求中加入`Range`头部,以请求(并只能请求字节型数据)数据的一部分。服务器端可以忽略`Range`头部,也可以返回若干`Range`响应。
|
||||
|
||||
如果一个响应包含部分数据的话,那么将带有`206 (Partial Content)`状态码。该状态码的意义在于避免了HTTP/1.0代理缓存错误地把该响应认为是一个完整的数据响应,从而把他当作为一个请求的响应缓存。
|
||||
|
||||
在范围响应中,`Content-Range`头部标志指示出了该数据块的偏移量和数据块的长度。
|
||||
|
||||
### 状态码100
|
||||
|
||||
HTTP/1.1中新加入了状态码`100`。该状态码的使用场景为,存在某些较大的文件请求,服务器可能不愿意响应这种请求,此时状态码`100`可以作为指示请求是否会被正常响应,过程如下图:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
然而在HTTP/1.0中,并没有`100 (Continue)`状态码,要想触发这一机制,可以发送一个`Expect`头部,其中包含一个`100-continue`的值。
|
||||
|
||||
### 压缩
|
||||
|
||||
许多格式的数据在传输时都会做预压缩处理。数据的压缩可以大幅优化带宽的利用。然而,HTTP/1.0对数据压缩的选项提供的不多,不支持压缩细节的选择,也无法区分端到端(end-to-end)压缩或者是逐跳(hop-by-hop)压缩。
|
||||
|
||||
HTTP/1.1则对内容编码(content-codings)和传输编码(transfer-codings)做了区分。内容编码总是端到端的,传输编码总是逐跳的。
|
||||
|
||||
HTTP/1.0包含了`Content-Encoding`头部,对消息进行端到端编码。HTTP/1.1加入了`Transfer-Encoding`头部,可以对消息进行逐跳传输编码。HTTP/1.1还加入了`Accept-Encoding`头部,是客户端用来指示他能处理什么样的内容编码。
|
||||
|
||||
## 总结
|
||||
|
||||
1. **连接方式** : HTTP 1.0 为短连接,HTTP 1.1 支持长连接。
|
||||
1. **状态响应码** : HTTP/1.1中新加入了大量的状态码,光是错误响应状态码就新增了24种。比如说,`100 (Continue)`——在请求大资源前的预热请求,`206 (Partial Content)`——范围请求的标识码,`409 (Conflict)`——请求与当前资源的规定冲突,`410 (Gone)`——资源已被永久转移,而且没有任何已知的转发地址。
|
||||
1. **缓存处理** : 在 HTTP1.0 中主要使用 header 里的 If-Modified-Since,Expires 来做为缓存判断的标准,HTTP1.1 则引入了更多的缓存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match 等更多可供选择的缓存头来控制缓存策略。
|
||||
1. **带宽优化及网络连接的使用** :HTTP1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
|
||||
1. **Host头处理** : HTTP/1.1在请求头中加入了`Host`字段。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[Key differences between HTTP/1.0 and HTTP/1.1](http://www.ra.ethz.ch/cdstore/www8/data/2136/pdf/pd1.pdf)
|
105
docs/cs-basics/network/http1.0-vs-http1.1.md
Normal file
@ -0,0 +1,105 @@
|
||||
---
|
||||
title: HTTP 1.0 vs HTTP 1.1(应用层)
|
||||
category: 计算机基础
|
||||
tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
这篇文章会从下面几个维度来对比 HTTP 1.0 和 HTTP 1.1:
|
||||
|
||||
- 响应状态码
|
||||
- 缓存处理
|
||||
- 连接方式
|
||||
- Host 头处理
|
||||
- 带宽优化
|
||||
|
||||
## 响应状态码
|
||||
|
||||
HTTP/1.0 仅定义了 16 种状态码。HTTP/1.1 中新加入了大量的状态码,光是错误响应状态码就新增了 24 种。比如说,`100 (Continue)`——在请求大资源前的预热请求,`206 (Partial Content)`——范围请求的标识码,`409 (Conflict)`——请求与当前资源的规定冲突,`410 (Gone)`——资源已被永久转移,而且没有任何已知的转发地址。
|
||||
|
||||
## 缓存处理
|
||||
|
||||
缓存技术通过避免用户与源服务器的频繁交互,节约了大量的网络带宽,降低了用户接收信息的延迟。
|
||||
|
||||
### HTTP/1.0
|
||||
|
||||
HTTP/1.0 提供的缓存机制非常简单。服务器端使用`Expires`标签来标志(时间)一个响应体,在`Expires`标志时间内的请求,都会获得该响应体缓存。服务器端在初次返回给客户端的响应体中,有一个`Last-Modified`标签,该标签标记了被请求资源在服务器端的最后一次修改。在请求头中,使用`If-Modified-Since`标签,该标签标志一个时间,意为客户端向服务器进行问询:“该时间之后,我要请求的资源是否有被修改过?”通常情况下,请求头中的`If-Modified-Since`的值即为上一次获得该资源时,响应体中的`Last-Modified`的值。
|
||||
|
||||
如果服务器接收到了请求头,并判断`If-Modified-Since`时间后,资源确实没有修改过,则返回给客户端一个`304 not modified`响应头,表示”缓冲可用,你从浏览器里拿吧!”。
|
||||
|
||||
如果服务器判断`If-Modified-Since`时间后,资源被修改过,则返回给客户端一个`200 OK`的响应体,并附带全新的资源内容,表示”你要的我已经改过的,给你一份新的”。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### HTTP/1.1
|
||||
|
||||
HTTP/1.1 的缓存机制在 HTTP/1.0 的基础上,大大增加了灵活性和扩展性。基本工作原理和 HTTP/1.0 保持不变,而是增加了更多细致的特性。其中,请求头中最常见的特性就是`Cache-Control`,详见 MDN Web 文档 [Cache-Control](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control).
|
||||
|
||||
## 连接方式
|
||||
|
||||
**HTTP/1.0 默认使用短连接** ,也就是说,客户端和服务器每进行一次 HTTP 操作,就建立一次连接,任务结束就中断连接。当客户端浏览器访问的某个 HTML 或其他类型的 Web 页中包含有其他的 Web 资源(如 JavaScript 文件、图像文件、CSS 文件等),每遇到这样一个 Web 资源,浏览器就会重新建立一个 TCP 连接,这样就会导致有大量的“握手报文”和“挥手报文”占用了带宽。
|
||||
|
||||
**为了解决 HTTP/1.0 存在的资源浪费的问题, HTTP/1.1 优化为默认长连接模式 。** 采用长连接模式的请求报文会通知服务端:“我向你请求连接,并且连接成功建立后,请不要关闭”。因此,该 TCP 连接将持续打开,为后续的客户端-服务端的数据交互服务。也就是说在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。
|
||||
|
||||
如果 TCP 连接一直保持的话也是对资源的浪费,因此,一些服务器软件(如 Apache)还会支持超时时间的时间。在超时时间之内没有新的请求达到,TCP 连接才会被关闭。
|
||||
|
||||
有必要说明的是,HTTP/1.0 仍提供了长连接选项,即在请求头中加入`Connection: Keep-alive`。同样的,在 HTTP/1.1 中,如果不希望使用长连接选项,也可以在请求头中加入`Connection: close`,这样会通知服务器端:“我不需要长连接,连接成功后即可关闭”。
|
||||
|
||||
**HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。**
|
||||
|
||||
**实现长连接需要客户端和服务端都支持长连接。**
|
||||
|
||||
## Host 头处理
|
||||
|
||||
域名系统(DNS)允许多个主机名绑定到同一个 IP 地址上,但是 HTTP/1.0 并没有考虑这个问题,假设我们有一个资源 URL 是http://example1.org/home.html,HTTP/1.0的请求报文中,将会请求的是`GET /home.html HTTP/1.0`.也就是不会加入主机名。这样的报文送到服务器端,服务器是理解不了客户端想请求的真正网址。
|
||||
|
||||
因此,HTTP/1.1 在请求头中加入了`Host`字段。加入`Host`字段的报文头部将会是:
|
||||
|
||||
```
|
||||
GET /home.html HTTP/1.1
|
||||
Host: example1.org
|
||||
```
|
||||
|
||||
这样,服务器端就可以确定客户端想要请求的真正的网址了。
|
||||
|
||||
## 带宽优化
|
||||
|
||||
### 范围请求
|
||||
|
||||
HTTP/1.1 引入了范围请求(range request)机制,以避免带宽的浪费。当客户端想请求一个文件的一部分,或者需要继续下载一个已经下载了部分但被终止的文件,HTTP/1.1 可以在请求中加入`Range`头部,以请求(并只能请求字节型数据)数据的一部分。服务器端可以忽略`Range`头部,也可以返回若干`Range`响应。
|
||||
|
||||
如果一个响应包含部分数据的话,那么将带有`206 (Partial Content)`状态码。该状态码的意义在于避免了 HTTP/1.0 代理缓存错误地把该响应认为是一个完整的数据响应,从而把他当作为一个请求的响应缓存。
|
||||
|
||||
在范围响应中,`Content-Range`头部标志指示出了该数据块的偏移量和数据块的长度。
|
||||
|
||||
### 状态码 100
|
||||
|
||||
HTTP/1.1 中新加入了状态码`100`。该状态码的使用场景为,存在某些较大的文件请求,服务器可能不愿意响应这种请求,此时状态码`100`可以作为指示请求是否会被正常响应,过程如下图:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
然而在 HTTP/1.0 中,并没有`100 (Continue)`状态码,要想触发这一机制,可以发送一个`Expect`头部,其中包含一个`100-continue`的值。
|
||||
|
||||
### 压缩
|
||||
|
||||
许多格式的数据在传输时都会做预压缩处理。数据的压缩可以大幅优化带宽的利用。然而,HTTP/1.0 对数据压缩的选项提供的不多,不支持压缩细节的选择,也无法区分端到端(end-to-end)压缩或者是逐跳(hop-by-hop)压缩。
|
||||
|
||||
HTTP/1.1 则对内容编码(content-codings)和传输编码(transfer-codings)做了区分。内容编码总是端到端的,传输编码总是逐跳的。
|
||||
|
||||
HTTP/1.0 包含了`Content-Encoding`头部,对消息进行端到端编码。HTTP/1.1 加入了`Transfer-Encoding`头部,可以对消息进行逐跳传输编码。HTTP/1.1 还加入了`Accept-Encoding`头部,是客户端用来指示他能处理什么样的内容编码。
|
||||
|
||||
## 总结
|
||||
|
||||
1. **连接方式** : HTTP 1.0 为短连接,HTTP 1.1 支持长连接。
|
||||
1. **状态响应码** : HTTP/1.1 中新加入了大量的状态码,光是错误响应状态码就新增了 24 种。比如说,`100 (Continue)`——在请求大资源前的预热请求,`206 (Partial Content)`——范围请求的标识码,`409 (Conflict)`——请求与当前资源的规定冲突,`410 (Gone)`——资源已被永久转移,而且没有任何已知的转发地址。
|
||||
1. **缓存处理** : 在 HTTP1.0 中主要使用 header 里的 If-Modified-Since,Expires 来做为缓存判断的标准,HTTP1.1 则引入了更多的缓存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match 等更多可供选择的缓存头来控制缓存策略。
|
||||
1. **带宽优化及网络连接的使用** :HTTP1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
|
||||
1. **Host 头处理** : HTTP/1.1 在请求头中加入了`Host`字段。
|
||||
|
||||
## 参考资料
|
||||
|
||||
[Key differences between HTTP/1.0 and HTTP/1.1](http://www.ra.ethz.ch/cdstore/www8/data/2136/pdf/pd1.pdf)
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -1,13 +1,13 @@
|
||||
---
|
||||
title: 网络攻击常见手段总结
|
||||
title: 网络攻击常见手段总结
|
||||
category: 计算机基础
|
||||
tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
> 本文整理完善自[TCP/IP常见攻击手段 - 暖蓝笔记 - 2021](https://mp.weixin.qq.com/s/AZwWrOlLxRSSi-ywBgZ0fA)这篇文章。
|
||||
> 本文整理完善自[TCP/IP 常见攻击手段 - 暖蓝笔记 - 2021](https://mp.weixin.qq.com/s/AZwWrOlLxRSSi-ywBgZ0fA)这篇文章。
|
||||
|
||||
这篇文章的内容主要是介绍 TCP/IP常见攻击手段,尤其是 DDoS 攻击,也会补充一些其他的常见网络攻击手段。
|
||||
这篇文章的内容主要是介绍 TCP/IP 常见攻击手段,尤其是 DDoS 攻击,也会补充一些其他的常见网络攻击手段。
|
||||
|
||||
## IP 欺骗
|
||||
|
||||
@ -153,15 +153,15 @@ HTTP 洪水攻击有两种:
|
||||
|
||||
### DNS Flood 是什么?
|
||||
|
||||
域名系统(DNS)服务器是互联网的“电话簿“;互联网设备通过这些服务器来查找特定 Web 服务器以便访问互联网内容。DNS Flood攻击是一种分布式拒绝服务(DDoS)攻击,攻击者用大量流量淹没某个域的 DNS 服务器,以尝试中断该域的 DNS 解析。如果用户无法找到电话簿,就无法查找到用于调用特定资源的地址。通过中断 DNS 解析,DNS Flood攻击将破坏网站、API 或 Web 应用程序响应合法流量的能力。很难将 DNS Flood攻击与正常的大流量区分开来,因为这些大规模流量往往来自多个唯一地址,查询该域的真实记录,模仿合法流量。
|
||||
域名系统(DNS)服务器是互联网的“电话簿“;互联网设备通过这些服务器来查找特定 Web 服务器以便访问互联网内容。DNS Flood 攻击是一种分布式拒绝服务(DDoS)攻击,攻击者用大量流量淹没某个域的 DNS 服务器,以尝试中断该域的 DNS 解析。如果用户无法找到电话簿,就无法查找到用于调用特定资源的地址。通过中断 DNS 解析,DNS Flood 攻击将破坏网站、API 或 Web 应用程序响应合法流量的能力。很难将 DNS Flood 攻击与正常的大流量区分开来,因为这些大规模流量往往来自多个唯一地址,查询该域的真实记录,模仿合法流量。
|
||||
|
||||
### DNS Flood 的攻击原理是什么?
|
||||
|
||||

|
||||
|
||||
域名系统的功能是将易于记忆的名称(例如example.com)转换成难以记住的网站服务器地址(例如192.168.0.1),因此成功攻击 DNS 基础设施将导致大多数人无法使用互联网。DNS Flood攻击是一种相对较新的基于 DNS 的攻击,这种攻击是在高带宽[物联网(IoT)](https://www.cloudflare.com/learning/ddos/glossary/internet-of-things-iot/)[僵尸网络](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-botnet/)(如 [Mirai](https://www.cloudflare.com/learning/ddos/glossary/mirai-botnet/))兴起后激增的。DNS Flood攻击使用 IP 摄像头、DVR 盒和其他 IoT 设备的高带宽连接直接淹没主要提供商的 DNS 服务器。来自 IoT 设备的大量请求淹没 DNS 提供商的服务,阻止合法用户访问提供商的 DNS 服务器。
|
||||
域名系统的功能是将易于记忆的名称(例如 example.com)转换成难以记住的网站服务器地址(例如 192.168.0.1),因此成功攻击 DNS 基础设施将导致大多数人无法使用互联网。DNS Flood 攻击是一种相对较新的基于 DNS 的攻击,这种攻击是在高带宽[物联网(IoT)](https://www.cloudflare.com/learning/ddos/glossary/internet-of-things-iot/)[僵尸网络](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-botnet/)(如 [Mirai](https://www.cloudflare.com/learning/ddos/glossary/mirai-botnet/))兴起后激增的。DNS Flood 攻击使用 IP 摄像头、DVR 盒和其他 IoT 设备的高带宽连接直接淹没主要提供商的 DNS 服务器。来自 IoT 设备的大量请求淹没 DNS 提供商的服务,阻止合法用户访问提供商的 DNS 服务器。
|
||||
|
||||
DNS Flood攻击不同于 [DNS 放大攻击](https://www.cloudflare.com/zh-cn/learning/ddos/dns-amplification-ddos-attack/)。与 DNS Flood攻击不同,DNS 放大攻击反射并放大不安全 DNS 服务器的流量,以便隐藏攻击的源头并提高攻击的有效性。DNS 放大攻击使用连接带宽较小的设备向不安全的 DNS 服务器发送无数请求。这些设备对非常大的 DNS 记录发出小型请求,但在发出请求时,攻击者伪造返回地址为目标受害者。这种放大效果让攻击者能借助有限的攻击资源来破坏较大的目标。
|
||||
DNS Flood 攻击不同于 [DNS 放大攻击](https://www.cloudflare.com/zh-cn/learning/ddos/dns-amplification-ddos-attack/)。与 DNS Flood 攻击不同,DNS 放大攻击反射并放大不安全 DNS 服务器的流量,以便隐藏攻击的源头并提高攻击的有效性。DNS 放大攻击使用连接带宽较小的设备向不安全的 DNS 服务器发送无数请求。这些设备对非常大的 DNS 记录发出小型请求,但在发出请求时,攻击者伪造返回地址为目标受害者。这种放大效果让攻击者能借助有限的攻击资源来破坏较大的目标。
|
||||
|
||||
### 如何防护 DNS Flood?
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: OSI 和 TCP/IP 网络分层模型详解(基础)
|
||||
title: OSI 和 TCP/IP 网络分层模型详解(基础)
|
||||
category: 计算机基础
|
||||
tag:
|
||||
- 计算机网络
|
@ -5,6 +5,8 @@ tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
上篇主要是计算机网络基础和应用层相关的内容。
|
||||
|
||||
## 计算机网络基础
|
||||
@ -38,7 +40,7 @@ tag:
|
||||
|
||||

|
||||
|
||||
关于每一层作用的详细介绍,请看 [OSI 和 TCP/IP 网络分层模型详解(基础)](https://javaguide.cn/cs-basics/network/osi&tcp-ip-model.html) 这篇文章。
|
||||
关于每一层作用的详细介绍,请看 [OSI 和 TCP/IP 网络分层模型详解(基础)](https://javaguide.cn/cs-basics/network/osi-and-tcp-ip-model.html) 这篇文章。
|
||||
|
||||
#### 为什么网络要分层?
|
||||
|
||||
@ -132,51 +134,51 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
|
||||
|
||||
### HTTP Header 中常见的字段有哪些?
|
||||
|
||||
| 请求头字段名 | 说明 | 示例 |
|
||||
| :------------------ | :----------------------------------------------------------- | :----------------------------------------------------------- |
|
||||
| Accept | 能够接受的回应内容类型(Content-Types)。 | Accept: text/plain |
|
||||
| Accept-Charset | 能够接受的字符集 | Accept-Charset: utf-8 |
|
||||
| Accept-Datetime | 能够接受的按照时间来表示的版本 | Accept-Datetime: Thu, 31 May 2007 20:35:00 GMT |
|
||||
| Accept-Encoding | 能够接受的编码方式列表。参考 HTTP 压缩。 | Accept-Encoding: gzip, deflate |
|
||||
| Accept-Language | 能够接受的回应内容的自然语言列表。 | Accept-Language: en-US |
|
||||
| Authorization | 用于超文本传输协议的认证的认证信息 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
|
||||
| Cache-Control | 用来指定在这次的请求/响应链中的所有缓存机制 都必须 遵守的指令 | Cache-Control: no-cache |
|
||||
| Connection | 该浏览器想要优先使用的连接类型 | Connection: keep-alive Connection: Upgrade |
|
||||
| Content-Length | 以 八位字节数组 (8 位的字节)表示的请求体的长度 | Content-Length: 348 |
|
||||
| Content-MD5 | 请求体的内容的二进制 MD5 散列值,以 Base64 编码的结果 | Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== |
|
||||
| Content-Type | 请求体的 多媒体类型 (用于 POST 和 PUT 请求中) | Content-Type: application/x-www-form-urlencoded |
|
||||
| Cookie | 之前由服务器通过 Set- Cookie (下文详述)发送的一个 超文本传输协议 Cookie | Cookie: \$Version=1; Skin=new; |
|
||||
| Date | 发送该消息的日期和时间(按照 RFC 7231 中定义的"超文本传输协议日期"格式来发送) | Date: Tue, 15 Nov 1994 08:12:31 GMT |
|
||||
| Expect | 表明客户端要求服务器做出特定的行为 | Expect: 100-continue |
|
||||
| From | 发起此请求的用户的邮件地址 | From: [user@example.com](mailto:user@example.com) |
|
||||
| Host | 服务器的域名(用于虚拟主机 ),以及服务器所监听的传输控制协议端口号。如果所请求的端口是对应的服务的标准端口,则端口号可被省略。 | Host: en.wikipedia.org:80 |
|
||||
| If-Match | 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要作用时,用作像 PUT 这样的方法中,仅当从用户上次更新某个资源以来,该资源未被修改的情况下,才更新该资源。 | If-Match: “737060cd8c284d8af7ad3082f209582d” |
|
||||
| If-Modified-Since | 允许在对应的内容未被修改的情况下返回 304 未修改( 304 Not Modified ) | If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT |
|
||||
| If-None-Match | 允许在对应的内容未被修改的情况下返回 304 未修改( 304 Not Modified ) | If-None-Match: “737060cd8c284d8af7ad3082f209582d” |
|
||||
| If-Range | 如果该实体未被修改过,则向我发送我所缺少的那一个或多个部分;否则,发送整个新的实体 | If-Range: “737060cd8c284d8af7ad3082f209582d” |
|
||||
| If-Unmodified-Since | 仅当该实体自某个特定时间已来未被修改的情况下,才发送回应。 | If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT |
|
||||
| Max-Forwards | 限制该消息可被代理及网关转发的次数。 | Max-Forwards: 10 |
|
||||
| Origin | 发起一个针对 跨来源资源共享 的请求。 | Origin: [http://www.example-social-network.com](http://www.example-social-network.com/) |
|
||||
| Pragma | 与具体的实现相关,这些字段可能在请求/回应链中的任何时候产生多种效果。 | Pragma: no-cache |
|
||||
| Proxy-Authorization | 用来向代理进行认证的认证信息。 | Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
|
||||
| Range | 仅请求某个实体的一部分。字节偏移以 0 开始。参见字节服务。 | Range: bytes=500-999 |
|
||||
| Referer | 表示浏览器所访问的前一个页面,正是那个页面上的某个链接将浏览器带到了当前所请求的这个页面。 | Referer: [http://en.wikipedia.org/wiki/Main_Page](https://en.wikipedia.org/wiki/Main_Page) |
|
||||
| TE | 浏览器预期接受的传输编码方式:可使用回应协议头 Transfer-Encoding 字段中的值; | TE: trailers, deflate |
|
||||
| Upgrade | 要求服务器升级到另一个协议。 | Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 |
|
||||
| User-Agent | 浏览器的浏览器身份标识字符串 | User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0 |
|
||||
| Via | 向服务器告知,这个请求是由哪些代理发出的。 | Via: 1.0 fred, 1.1 example.com (Apache/1.1) |
|
||||
| Warning | 一个一般性的警告,告知,在实体内容体中可能存在错误。 | Warning: 199 Miscellaneous warning |
|
||||
| 请求头字段名 | 说明 | 示例 |
|
||||
| :------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------- |
|
||||
| Accept | 能够接受的回应内容类型(Content-Types)。 | Accept: text/plain |
|
||||
| Accept-Charset | 能够接受的字符集 | Accept-Charset: utf-8 |
|
||||
| Accept-Datetime | 能够接受的按照时间来表示的版本 | Accept-Datetime: Thu, 31 May 2007 20:35:00 GMT |
|
||||
| Accept-Encoding | 能够接受的编码方式列表。参考 HTTP 压缩。 | Accept-Encoding: gzip, deflate |
|
||||
| Accept-Language | 能够接受的回应内容的自然语言列表。 | Accept-Language: en-US |
|
||||
| Authorization | 用于超文本传输协议的认证的认证信息 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
|
||||
| Cache-Control | 用来指定在这次的请求/响应链中的所有缓存机制 都必须 遵守的指令 | Cache-Control: no-cache |
|
||||
| Connection | 该浏览器想要优先使用的连接类型 | Connection: keep-alive Connection: Upgrade |
|
||||
| Content-Length | 以 八位字节数组 (8 位的字节)表示的请求体的长度 | Content-Length: 348 |
|
||||
| Content-MD5 | 请求体的内容的二进制 MD5 散列值,以 Base64 编码的结果 | Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ== |
|
||||
| Content-Type | 请求体的 多媒体类型 (用于 POST 和 PUT 请求中) | Content-Type: application/x-www-form-urlencoded |
|
||||
| Cookie | 之前由服务器通过 Set- Cookie (下文详述)发送的一个 超文本传输协议 Cookie | Cookie: \$Version=1; Skin=new; |
|
||||
| Date | 发送该消息的日期和时间(按照 RFC 7231 中定义的"超文本传输协议日期"格式来发送) | Date: Tue, 15 Nov 1994 08:12:31 GMT |
|
||||
| Expect | 表明客户端要求服务器做出特定的行为 | Expect: 100-continue |
|
||||
| From | 发起此请求的用户的邮件地址 | From: [user@example.com](mailto:user@example.com) |
|
||||
| Host | 服务器的域名(用于虚拟主机 ),以及服务器所监听的传输控制协议端口号。如果所请求的端口是对应的服务的标准端口,则端口号可被省略。 | Host: en.wikipedia.org:80 |
|
||||
| If-Match | 仅当客户端提供的实体与服务器上对应的实体相匹配时,才进行对应的操作。主要作用时,用作像 PUT 这样的方法中,仅当从用户上次更新某个资源以来,该资源未被修改的情况下,才更新该资源。 | If-Match: “737060cd8c284d8af7ad3082f209582d” |
|
||||
| If-Modified-Since | 允许在对应的内容未被修改的情况下返回 304 未修改( 304 Not Modified ) | If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT |
|
||||
| If-None-Match | 允许在对应的内容未被修改的情况下返回 304 未修改( 304 Not Modified ) | If-None-Match: “737060cd8c284d8af7ad3082f209582d” |
|
||||
| If-Range | 如果该实体未被修改过,则向我发送我所缺少的那一个或多个部分;否则,发送整个新的实体 | If-Range: “737060cd8c284d8af7ad3082f209582d” |
|
||||
| If-Unmodified-Since | 仅当该实体自某个特定时间已来未被修改的情况下,才发送回应。 | If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT |
|
||||
| Max-Forwards | 限制该消息可被代理及网关转发的次数。 | Max-Forwards: 10 |
|
||||
| Origin | 发起一个针对 跨来源资源共享 的请求。 | Origin: [http://www.example-social-network.com](http://www.example-social-network.com/) |
|
||||
| Pragma | 与具体的实现相关,这些字段可能在请求/回应链中的任何时候产生多种效果。 | Pragma: no-cache |
|
||||
| Proxy-Authorization | 用来向代理进行认证的认证信息。 | Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== |
|
||||
| Range | 仅请求某个实体的一部分。字节偏移以 0 开始。参见字节服务。 | Range: bytes=500-999 |
|
||||
| Referer | 表示浏览器所访问的前一个页面,正是那个页面上的某个链接将浏览器带到了当前所请求的这个页面。 | Referer: [http://en.wikipedia.org/wiki/Main_Page](https://en.wikipedia.org/wiki/Main_Page) |
|
||||
| TE | 浏览器预期接受的传输编码方式:可使用回应协议头 Transfer-Encoding 字段中的值; | TE: trailers, deflate |
|
||||
| Upgrade | 要求服务器升级到另一个协议。 | Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 |
|
||||
| User-Agent | 浏览器的浏览器身份标识字符串 | User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:12.0) Gecko/20100101 Firefox/21.0 |
|
||||
| Via | 向服务器告知,这个请求是由哪些代理发出的。 | Via: 1.0 fred, 1.1 example.com (Apache/1.1) |
|
||||
| Warning | 一个一般性的警告,告知,在实体内容体中可能存在错误。 | Warning: 199 Miscellaneous warning |
|
||||
|
||||
### HTTP 和 HTTPS 有什么区别?(重要)
|
||||
|
||||

|
||||

|
||||
|
||||
- **端口号** :HTTP 默认是 80,HTTPS 默认是 443。
|
||||
- **URL 前缀** :HTTP 的 URL 前缀是 `http://`,HTTPS 的 URL 前缀是 `https://`。
|
||||
- **安全性和资源消耗** : HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS 是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
|
||||
- **SEO(搜索引擎优化)** :搜索引擎通常会更青睐使用 HTTPS 协议的网站,因为 HTTPS 能够提供更高的安全性和用户隐私保护。使用 HTTPS 协议的网站在搜索结果中可能会被优先显示,从而对 SEO 产生影响。
|
||||
|
||||
关于 HTTP 和 HTTPS 更详细的对比总结,可以看我写的这篇文章:[HTTP vs HTTPS(应用层)](./http&https.md) 。
|
||||
关于 HTTP 和 HTTPS 更详细的对比总结,可以看我写的这篇文章:[HTTP vs HTTPS(应用层)](./http-vs-https.md) 。
|
||||
|
||||
### HTTP/1.0 和 HTTP/1.1 有什么区别?
|
||||
|
||||
@ -188,7 +190,7 @@ HTTP 状态码用于描述 HTTP 请求的结果,比如 2xx 就代表请求被
|
||||
- **带宽** :HTTP/1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP/1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
|
||||
- **Host 头(Host Header)处理** :HTTP/1.1 引入了 Host 头字段,允许在同一 IP 地址上托管多个域名,从而支持虚拟主机的功能。而 HTTP/1.0 没有 Host 头字段,无法实现虚拟主机。
|
||||
|
||||
关于 HTTP/1.0 和 HTTP/1.1 更详细的对比总结,可以看我写的这篇文章:[HTTP/1.0 vs HTTP/1.1(应用层)](./http1.0&http1.1.md) 。
|
||||
关于 HTTP/1.0 和 HTTP/1.1 更详细的对比总结,可以看我写的这篇文章:[HTTP/1.0 vs HTTP/1.1(应用层)](./http1.0-vs-http1.1.md) 。
|
||||
|
||||
### HTTP/1.1 和 HTTP/2.0 有什么区别?
|
||||
|
||||
|
@ -96,21 +96,19 @@ tag:
|
||||
|
||||
每个连入互联网的设备或域(如计算机、服务器、路由器等)都被分配一个 **IP 地址(Internet Protocol address)**,作为唯一标识符。每个 IP 地址都是一个字符序列,如 192.168.1.1(IPv4)、2001:0db8:85a3:0000:0000:8a2e:0370:7334(IPv6) 。
|
||||
|
||||
当网络设备发送IP数据包时,数据包中包含了 **源IP地址** 和 **目的IP地址** 。源IP地址用于标识数据包的发送方设备或域,而目的IP地址则用于标识数据包的接收方设备或域。这类似于一封邮件中同时包含了目的地地址和回邮地址。
|
||||
当网络设备发送 IP 数据包时,数据包中包含了 **源 IP 地址** 和 **目的 IP 地址** 。源 IP 地址用于标识数据包的发送方设备或域,而目的 IP 地址则用于标识数据包的接收方设备或域。这类似于一封邮件中同时包含了目的地地址和回邮地址。
|
||||
|
||||
网络设备根据目的IP地址来判断数据包的目的地,并将数据包转发到正确的目的地网络或子网络,从而实现了设备间的通信。
|
||||
网络设备根据目的 IP 地址来判断数据包的目的地,并将数据包转发到正确的目的地网络或子网络,从而实现了设备间的通信。
|
||||
|
||||
这种基于IP地址的寻址方式是互联网通信的基础,它允许数据包在不同的网络之间传递,从而实现了全球范围内的网络互联互通。IP地址的唯一性和全局性保证了网络中的每个设备都可以通过其独特的IP地址进行标识和寻址。
|
||||
这种基于 IP 地址的寻址方式是互联网通信的基础,它允许数据包在不同的网络之间传递,从而实现了全球范围内的网络互联互通。IP 地址的唯一性和全局性保证了网络中的每个设备都可以通过其独特的 IP 地址进行标识和寻址。
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 什么是 IP 地址过滤?
|
||||
|
||||
**IP 地址过滤(IP Address Filtering)** 简单来说就是限制或阻止特定IP地址或IP地址范围的访问。例如,你有一个图片服务突然被某一个 IP 地址攻击,那我们就可以禁止这个 IP 地址访问图片服务。
|
||||
**IP 地址过滤(IP Address Filtering)** 简单来说就是限制或阻止特定 IP 地址或 IP 地址范围的访问。例如,你有一个图片服务突然被某一个 IP 地址攻击,那我们就可以禁止这个 IP 地址访问图片服务。
|
||||
|
||||
IP地址过滤是一种简单的网络安全措施,实际应用中一般会结合其他网络安全措施,如认证、授权、加密等一起使用。单独使用 IP地址过滤并不能完全保证网络的安全。
|
||||
IP 地址过滤是一种简单的网络安全措施,实际应用中一般会结合其他网络安全措施,如认证、授权、加密等一起使用。单独使用 IP 地址过滤并不能完全保证网络的安全。
|
||||
|
||||
### IPv4 和 IPv6 有什么区别?
|
||||
|
||||
|
@ -5,40 +5,39 @@ tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
|
||||
为了准确无误地把数据送达目标处,TCP 协议采用了三次握手策略。
|
||||
|
||||
## 建立连接-TCP 三次握手
|
||||
## 建立连接-TCP 三次握手
|
||||
|
||||

|
||||
|
||||
建立一个 TCP 连接需要“三次握手”,缺一不可 :
|
||||
|
||||
|
||||
- **一次握手**:客户端发送带有 SYN(SEQ=x) 标志的数据包 -> 服务端,然后客户端进入 **SYN_SEND** 状态,等待服务器的确认;
|
||||
- **二次握手**:服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1) 标志的数据包 –> 客户端,然后服务端进入 **SYN_RECV** 状态
|
||||
- **三次握手**:客户端发送带有 ACK(ACK=y+1) 标志的数据包 –> 服务端,然后客户端和服务器端都进入**ESTABLISHED** 状态,完成TCP三次握手。
|
||||
- **三次握手**:客户端发送带有 ACK(ACK=y+1) 标志的数据包 –> 服务端,然后客户端和服务器端都进入**ESTABLISHED** 状态,完成 TCP 三次握手。
|
||||
|
||||
**当建立了 3 次握手之后,客户端和服务端就可以传输数据啦!**
|
||||
|
||||
### 为什么要三次握手?
|
||||
### 为什么要三次握手?
|
||||
|
||||
三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。
|
||||
|
||||
1. **第一次握手** :Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常
|
||||
2. **第二次握手** :Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常
|
||||
3. **第三次握手** :Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常
|
||||
|
||||
三次握手就能确认双方收发功能都正常,缺一不可。
|
||||
|
||||
更详细的解答可以看这个:[TCP 为什么是三次握手,而不是两次或四次? - 车小胖的回答 - 知乎](https://www.zhihu.com/question/24853633/answer/115173386) 。
|
||||
更详细的解答可以看这个:[TCP 为什么是三次握手,而不是两次或四次? - 车小胖的回答 - 知乎](https://www.zhihu.com/question/24853633/answer/115173386) 。
|
||||
|
||||
### 第2次握手传回了ACK,为什么还要传回SYN?
|
||||
### 第 2 次握手传回了 ACK,为什么还要传回 SYN?
|
||||
|
||||
服务端传回发送端所发送的 ACK 是为了告诉客户端:“我接收到的信息确实就是你所发送的信号了”,这表明从客户端到服务端的通信是正常的。回传 SYN 则是为了建立并确认从服务端到客户端的通信。
|
||||
|
||||
> SYN 同步序列编号(Synchronize Sequence Numbers) 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement)消息响应。这样在客户机和服务器之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务器之间传递。
|
||||
|
||||
## 断开连接-TCP 四次挥手
|
||||
## 断开连接-TCP 四次挥手
|
||||
|
||||

|
||||
|
||||
@ -51,9 +50,9 @@ tag:
|
||||
|
||||
**只要四次挥手没有结束,客户端和服务端就可以继续传输数据!**
|
||||
|
||||
### 为什么要四次挥手?
|
||||
### 为什么要四次挥手?
|
||||
|
||||
TCP是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
|
||||
TCP 是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。
|
||||
|
||||
举个例子:A 和 B 打电话,通话即将结束后。
|
||||
|
||||
@ -62,30 +61,24 @@ TCP是全双工通信,可以双向传输数据。任何一方都可以在数
|
||||
3. **第三次挥手** :于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”
|
||||
4. **第四次挥手** :A 回答“知道了”,这样通话才算结束。
|
||||
|
||||
### 为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?
|
||||
|
||||
### 为什么不能把服务器发送的 ACK 和 FIN 合并起来,变成三次挥手?
|
||||
|
||||
因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务器到客户端的数据传送。
|
||||
|
||||
### 如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?
|
||||
|
||||
### 如果第二次挥手时服务器的 ACK 没有送达客户端,会怎样?
|
||||
|
||||
客户端没有收到 ACK 确认,会重新发送 FIN 请求。
|
||||
|
||||
### 为什么第四次挥手客户端需要等待 2\*MSL(报文段最长寿命)时间后才进入 CLOSED 状态?
|
||||
|
||||
### 为什么第四次挥手客户端需要等待 2\*MSL(报文段最长寿命)时间后才进入 CLOSED 状态?
|
||||
|
||||
第四次挥手时,客户端发送给服务器的 ACK 有可能丢失,如果服务端因为某些原因而没有收到 ACK 的话,服务端就会重发 FIN,如果客户端在 2\*MSL 的时间内收到了 FIN,就会重新发送 ACK 并再次等待 2MSL,防止 Server 没有收到 ACK 而不断重发 FIN。
|
||||
|
||||
> **MSL(Maximum Segment Lifetime)** : 一个片段在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间。如果直到 2MSL,Client 都没有再次收到 FIN,那么 Client 推断 ACK 已经被成功接收,则结束 TCP 连接。
|
||||
|
||||
## 参考
|
||||
|
||||
## 参考
|
||||
|
||||
- 《计算机网络(第 7 版)》
|
||||
|
||||
- 《图解 HTTP》
|
||||
|
||||
- TCP and UDP Tutorial:https://www.9tut.com/tcp-and-udp-tutorial
|
||||
|
||||
|
||||
|
@ -5,16 +5,16 @@ tag:
|
||||
- 计算机网络
|
||||
---
|
||||
|
||||
## TCP 如何保证传输的可靠性?
|
||||
## TCP 如何保证传输的可靠性?
|
||||
|
||||
1. **基于数据块传输** :应用数据被分割成 TCP 认为最适合发送的数据块,再传输给网络层,数据块被称为报文段或段。
|
||||
2. **对失序数据包重新排序以及去重**:TCP 为了保证不发生丢包,就给每个包一个序列号,有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据就可以实现数据包去重。
|
||||
3. **校验和** : TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
|
||||
4. **超时重传** : 当发送方发送数据之后,它启动一个定时器,等待目的端确认收到这个报文段。接收端实体对已成功收到的包发回一个相应的确认信息(ACK)。如果发送端实体在合理的往返时延(RTT)内未收到确认消息,那么对应的数据包就被假设为[已丢失](https://zh.wikipedia.org/wiki/丢包 )并进行重传。
|
||||
4. **超时重传** : 当发送方发送数据之后,它启动一个定时器,等待目的端确认收到这个报文段。接收端实体对已成功收到的包发回一个相应的确认信息(ACK)。如果发送端实体在合理的往返时延(RTT)内未收到确认消息,那么对应的数据包就被假设为[已丢失](https://zh.wikipedia.org/wiki/丢包)并进行重传。
|
||||
5. **流量控制** : TCP 连接的每一方都有固定大小的缓冲空间,TCP 的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议(TCP 利用滑动窗口实现流量控制)。
|
||||
6. **拥塞控制** : 当网络拥塞时,减少数据的发送。
|
||||
|
||||
## TCP 如何实现流量控制?
|
||||
## TCP 如何实现流量控制?
|
||||
|
||||
**TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。** 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。
|
||||
|
||||
@ -25,16 +25,16 @@ tag:
|
||||
- 发送端不等同于客户端
|
||||
- 接收端不等同于服务端
|
||||
|
||||
TCP 为全双工(Full-Duplex, FDX)通信,双方可以进行双向通信,客户端和服务端既可能是发送端又可能是服务端。因此,两端各有一个发送缓冲区与接收缓冲区,两端都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制(TCP传输速率不能大于应用的数据处理速率)。通信双方的发送窗口和接收窗口的要求相同
|
||||
TCP 为全双工(Full-Duplex, FDX)通信,双方可以进行双向通信,客户端和服务端既可能是发送端又可能是服务端。因此,两端各有一个发送缓冲区与接收缓冲区,两端都各自维护一个发送窗口和一个接收窗口。接收窗口大小取决于应用、系统、硬件的限制(TCP 传输速率不能大于应用的数据处理速率)。通信双方的发送窗口和接收窗口的要求相同
|
||||
|
||||
**TCP 发送窗口可以划分成四个部分** :
|
||||
|
||||
1. 已经发送并且确认的TCP段(已经发送并确认);
|
||||
2. 已经发送但是没有确认的TCP段(已经发送未确认);
|
||||
3. 未发送但是接收方准备接收的TCP段(可以发送);
|
||||
4. 未发送并且接收方也并未准备接受的TCP段(不可发送)。
|
||||
1. 已经发送并且确认的 TCP 段(已经发送并确认);
|
||||
2. 已经发送但是没有确认的 TCP 段(已经发送未确认);
|
||||
3. 未发送但是接收方准备接收的 TCP 段(可以发送);
|
||||
4. 未发送并且接收方也并未准备接受的 TCP 段(不可发送)。
|
||||
|
||||
**TCP发送窗口结构图示** :
|
||||
**TCP 发送窗口结构图示** :
|
||||
|
||||

|
||||
|
||||
@ -42,13 +42,13 @@ TCP 为全双工(Full-Duplex, FDX)通信,双方可以进行双向通信,客
|
||||
- **SND.UNA**:Send Unacknowledged 指针,指向发送窗口的第一个字节。
|
||||
- **SND.NXT**:Send Next 指针,指向可用窗口的第一个字节。
|
||||
|
||||
**可用窗口大小** = `SND.UNA + SND.WND - SND.NXT` 。
|
||||
**可用窗口大小** = `SND.UNA + SND.WND - SND.NXT` 。
|
||||
|
||||
**TCP 接收窗口可以划分成三个部分** :
|
||||
|
||||
1. 已经接收并且已经确认的 TCP 段(已经接收并确认);
|
||||
2. 等待接收且允许发送方发送 TCP 段(可以接收未确认);
|
||||
3. 不可接收且不允许发送方发送TCP段(不可接收)。
|
||||
3. 不可接收且不允许发送方发送 TCP 段(不可接收)。
|
||||
|
||||
**TCP 接收窗口结构图示** :
|
||||
|
||||
@ -58,8 +58,7 @@ TCP 为全双工(Full-Duplex, FDX)通信,双方可以进行双向通信,客
|
||||
|
||||
另外,这里的滑动窗口大小只是为了演示使用,实际窗口大小通常会远远大于这个值。
|
||||
|
||||
## TCP 的拥塞控制是怎么实现的?
|
||||
|
||||
## TCP 的拥塞控制是怎么实现的?
|
||||
|
||||
在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
|
||||
|
||||
@ -73,13 +72,13 @@ TCP 的拥塞控制采用了四种算法,即 **慢开始** 、 **拥塞避免*
|
||||
- **拥塞避免:** 拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢增大,即每经过一个往返时间 RTT 就把发送方的 cwnd 加 1.
|
||||
- **快重传与快恢复:** 在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。 当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。
|
||||
|
||||
## ARQ 协议了解吗?
|
||||
## ARQ 协议了解吗?
|
||||
|
||||
**自动重传请求**(Automatic Repeat-reQuest,ARQ)是 OSI 模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认信息(Acknowledgements,就是我们常说的 ACK),它通常会重新发送,直到收到确认或者重试超过一定的次数。
|
||||
|
||||
ARQ 包括停止等待 ARQ 协议和连续 ARQ 协议。
|
||||
|
||||
### 停止等待 ARQ 协议
|
||||
### 停止等待 ARQ 协议
|
||||
|
||||
停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复 ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组;
|
||||
|
||||
@ -98,8 +97,7 @@ ARQ 包括停止等待 ARQ 协议和连续 ARQ 协议。
|
||||
- **确认丢失** :确认消息在传输过程丢失。当 A 发送 M1 消息,B 收到后,B 向 A 发送了一个 M1 确认消息,但却在传输过程中丢失。而 A 并不知道,在超时计时过后,A 重传 M1 消息,B 再次收到该消息后采取以下两点措施:1. 丢弃这个重复的 M1 消息,不向上层交付。 2. 向 A 发送确认消息。(不会认为已经发送过了,就不再发送。A 能重传,就证明 B 的确认消息丢失)。
|
||||
- **确认迟到** :确认消息在传输过程中迟到。A 发送 M1 消息,B 收到并发送确认。在超时时间内没有收到确认消息,A 重传 M1 消息,B 仍然收到并继续发送确认消息(B 收到了 2 份 M1)。此时 A 收到了 B 第二次发送的确认消息。接着发送其他数据。过了一会,A 收到了 B 第一次发送的对 M1 的确认消息(A 也收到了 2 份确认消息)。处理如下:1. A 收到重复的确认后,直接丢弃。2. B 收到重复的 M1 后,也直接丢弃重复的 M1。
|
||||
|
||||
### 连续 ARQ 协议
|
||||
|
||||
### 连续 ARQ 协议
|
||||
|
||||
连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。
|
||||
|
||||
@ -107,13 +105,12 @@ ARQ 包括停止等待 ARQ 协议和连续 ARQ 协议。
|
||||
|
||||
**缺点:** 不能向发送方反映出接收方已经正确收到的所有分组的信息。 比如:发送方发送了 5 条 消息,中间第三条丢失(3 号),这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落,而只好把后三个全部重传一次。这也叫 Go-Back-N(回退 N),表示需要退回来重传已经发送过的 N 个消息。
|
||||
|
||||
## Reference
|
||||
|
||||
## Reference
|
||||
|
||||
1. 《计算机网络(第 7 版)》
|
||||
2. 《图解 HTTP》
|
||||
3. [https://www.9tut.com/tcp-and-udp-tutorial](https://www.9tut.com/tcp-and-udp-tutorial )
|
||||
4. [https://github.com/wolverinn/Waking-Up/blob/master/Computer%20Network.md](https://github.com/wolverinn/Waking-Up/blob/master/Computer%20Network.md )
|
||||
5. TCP Flow Control—[https://www.brianstorti.com/tcp-flow-control/](https://www.brianstorti.com/tcp-flow-control/ )
|
||||
3. [https://www.9tut.com/tcp-and-udp-tutorial](https://www.9tut.com/tcp-and-udp-tutorial)
|
||||
4. [https://github.com/wolverinn/Waking-Up/blob/master/Computer%20Network.md](https://github.com/wolverinn/Waking-Up/blob/master/Computer%20Network.md)
|
||||
5. TCP Flow Control—[https://www.brianstorti.com/tcp-flow-control/](https://www.brianstorti.com/tcp-flow-control/)
|
||||
6. TCP 流量控制(Flow Control):https://notfalse.net/24/tcp-flow-control
|
||||
7. TCP之滑动窗口原理 : https://cloud.tencent.com/developer/article/1857363
|
||||
7. TCP 之滑动窗口原理 : https://cloud.tencent.com/developer/article/1857363
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Linux 基础知识总结
|
||||
title: Linux 基础知识总结
|
||||
category: 计算机基础
|
||||
tag:
|
||||
- 操作系统
|
||||
@ -339,7 +339,7 @@ Linux 系统是一个多用户多任务的分时操作系统,任何一个要
|
||||
- `top [选项]`:用于实时查看系统的 CPU 使用率、内存使用率、进程信息等。
|
||||
- `htop [选项]`:类似于 `top`,但提供了更加交互式和友好的界面,可让用户交互式操作,支持颜色主题,可横向或纵向滚动浏览进程列表,并支持鼠标操作。
|
||||
- `uptime [选项]`:用于查看系统总共运行了多长时间、系统的平均负载等信息。
|
||||
- `vmstat [间隔时间] [重复次数]` :vmstat (Virtual Memory Statistics) 的含义为显示虚拟内存状态,但是它可以报告关于进程、内存、I/O等系统整体运行状态。
|
||||
- `vmstat [间隔时间] [重复次数]` :vmstat (Virtual Memory Statistics) 的含义为显示虚拟内存状态,但是它可以报告关于进程、内存、I/O 等系统整体运行状态。
|
||||
- `free [选项]`:用于查看系统的内存使用情况,包括已用内存、可用内存、缓冲区和缓存等。
|
||||
- `df [选项] [文件系统]`:用于查看系统的磁盘空间使用情况,包括磁盘空间的总量、已使用量和可用量等,可以指定文件系统上。例如:`df -a`,查看全部文件系统。
|
||||
- `du [选项] [文件]`:用于查看指定目录或文件的磁盘空间使用情况,可以指定不同的选项来控制输出格式和单位。
|
||||
|
@ -12,6 +12,8 @@ head:
|
||||
content: 很多读者抱怨计算操作系统的知识点比较繁杂,自己也没有多少耐心去看,但是面试的时候又经常会遇到。所以,我带着我整理好的操作系统的常见问题来啦!这篇文章总结了一些我觉得比较重要的操作系统相关的问题比如进程管理、内存管理、虚拟内存等等。
|
||||
---
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
很多读者抱怨计算操作系统的知识点比较繁杂,自己也没有多少耐心去看,但是面试的时候又经常会遇到。所以,我带着我整理好的操作系统的常见问题来啦!这篇文章总结了一些我觉得比较重要的操作系统相关的问题比如 **用户态和内核态、系统调用、进程和线程、死锁、内存管理、虚拟内存、文件系统**等等。
|
||||
|
||||
这篇文章只是对一些操作系统比较重要概念的一个概览,深入学习的话,建议大家还是老老实实地去看书。另外, 这篇文章的很多内容参考了《现代操作系统》第三版这本书,非常感谢。
|
||||
@ -45,8 +47,6 @@ head:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 操作系统主要有哪些功能?
|
||||
|
||||
从资源管理的角度来看,操作系统有 6 大功能:
|
||||
|
@ -115,7 +115,7 @@ ER 图由下面 3 个要素组成:
|
||||
- `truncate` (清空数据) : `truncate table 表名` ,只删除表中的数据,再插入数据的时候自增长 id 又从 1 开始,在清空表中数据的时候使用。
|
||||
- `delete`(删除数据) : `delete from 表名 where 列名=值`,删除某一行的数据,如果不加 `where` 子句和`truncate table 表名`作用类似。
|
||||
|
||||
`truncate` 和不带 where``子句的 `delete`、以及 `drop` 都会删除表内的数据,但是 **`truncate` 和 `delete` 只删除数据不删除表的结构(定义),执行 `drop` 语句,此表的结构也会删除,也就是执行 `drop` 之后对应的表不复存在。**
|
||||
`truncate` 和不带 `where`子句的 `delete`、以及 `drop` 都会删除表内的数据,但是 **`truncate` 和 `delete` 只删除数据不删除表的结构(定义),执行 `drop` 语句,此表的结构也会删除,也就是执行`drop` 之后对应的表不复存在。**
|
||||
|
||||
### 属于不同的数据库语言
|
||||
|
||||
|
@ -5,10 +5,9 @@ tag:
|
||||
- 数据库基础
|
||||
---
|
||||
|
||||
|
||||
MySQL 字符编码集中有两套 UTF-8 编码实现:**`utf8`** 和 **`utf8mb4`**。
|
||||
|
||||
如果使用 **`utf8`** 的话,存储emoji 符号和一些比较复杂的汉字、繁体字就会出错。
|
||||
如果使用 **`utf8`** 的话,存储 emoji 符号和一些比较复杂的汉字、繁体字就会出错。
|
||||
|
||||
为什么会这样呢?这篇文章可以从源头给你解答。
|
||||
|
||||
@ -65,7 +64,7 @@ GB18030 完全兼容 GB2312 和 GBK 字符集,纳入中国国内少数民族
|
||||
|
||||
BIG5 主要针对的是繁体中文,收录了 13000 多个汉字。
|
||||
|
||||
### Unicode & UTF-8编码
|
||||
### Unicode & UTF-8 编码
|
||||
|
||||
为了更加适合本国语言,诞生了很多种字符集。
|
||||
|
||||
@ -73,7 +72,7 @@ BIG5 主要针对的是繁体中文,收录了 13000 多个汉字。
|
||||
|
||||
就比如说你使用 UTF-8 编码方式打开 GB2312 编码格式的文件就会出现乱码。示例:“牛”这个汉字 GB2312 编码后的十六进制数值为 “C5A3”,而 “C5A3” 用 UTF-8 解码之后得到的却是 “ţ”。
|
||||
|
||||
你可以通过这个网站在线进行编码和解码:https://www.haomeili.net/HanZi/ZiFuBianMaZhuanHuan
|
||||
你可以通过这个网站在线进行编码和解码:<https://www.haomeili.net/HanZi/ZiFuBianMaZhuanHuan>
|
||||
|
||||

|
||||
|
||||
@ -140,7 +139,7 @@ CREATE TABLE `user` (
|
||||
```sql
|
||||
INSERT INTO `user` (`id`, `name`, `phone`, `password`)
|
||||
VALUES
|
||||
('A00003', 'guide哥😘😘😘', '181631312312', '123456');
|
||||
('A00003', 'guide哥😘😘😘', '181631312312', '123456');
|
||||
|
||||
```
|
||||
|
||||
@ -152,9 +151,9 @@ Incorrect string value: '\xF0\x9F\x98\x98\xF0\x9F...' for column 'name' at row 1
|
||||
|
||||
## 参考
|
||||
|
||||
- 字符集和字符编码(Charset & Encoding): https://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html
|
||||
- 十分钟搞清字符集和字符编码:http://cenalulu.github.io/linux/character-encoding/
|
||||
- Unicode-维基百科:https://zh.wikipedia.org/wiki/Unicode
|
||||
- GB2312-维基百科:https://zh.wikipedia.org/wiki/GB_2312
|
||||
- UTF-8-维基百科:https://zh.wikipedia.org/wiki/UTF-8
|
||||
- GB18030-维基百科: https://zh.wikipedia.org/wiki/GB_18030
|
||||
- 字符集和字符编码(Charset & Encoding): <https://www.cnblogs.com/skynet/archive/2011/05/03/2035105.html>
|
||||
- 十分钟搞清字符集和字符编码:<http://cenalulu.github.io/linux/character-encoding/>
|
||||
- Unicode-维基百科:<https://zh.wikipedia.org/wiki/Unicode>
|
||||
- GB2312-维基百科:<https://zh.wikipedia.org/wiki/GB_2312>
|
||||
- UTF-8-维基百科:<https://zh.wikipedia.org/wiki/UTF-8>
|
||||
- GB18030-维基百科: <https://zh.wikipedia.org/wiki/GB_18030>
|
||||
|
@ -10,46 +10,4 @@ tag:
|
||||
|
||||

|
||||
|
||||
[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)(点击链接即可查看详细介绍)的部分内容展示如下,你可以将其看作是 [JavaGuide](https://javaguide.cn/#/) 的补充完善,两者可以配合使用。
|
||||
|
||||

|
||||
|
||||
最近几年,市面上有越来越多的“技术大佬”开始办培训班/训练营,动辄成千上万的学费,却并没有什么干货,单纯的就是割韭菜。
|
||||
|
||||
为了帮助更多同学准备 Java 面试以及学习 Java ,我创建了一个纯粹的知识星球。虽然收费只有培训班/训练营的百分之一,但是知识星球里的内容质量更高,提供的服务也更全面。
|
||||
|
||||
欢迎准备 Java 面试以及学习 Java 的同学加入我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html),干货非常多,学习氛围也很不错!收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/image-20220311203414600.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
下面是星球提供的部分服务(点击下方图片即可获取知识星球的详细介绍):
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiufuwu.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!**
|
||||
|
||||
如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍: [JavaGuide 知识星球详细介绍](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。
|
||||
|
||||
这里再送一个 30 元的新人优惠券(续费半价)。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuanheyi.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
进入星球之后,记得添加微信,我会发你详细的星球使用指南。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/IMG_3007.jpg" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
<!-- @include: @planet.snippet.md -->
|
@ -28,15 +28,15 @@ MongoDB 的存储结构区别于传统的关系型数据库,主要由如下三
|
||||
|
||||
**SQL 与 MongoDB 常见术语对比** :
|
||||
|
||||
| SQL | MongoDB |
|
||||
| ----------------------- | ------------------------------ |
|
||||
| 表(Table) | 集合(Collection) |
|
||||
| 行(Row) | 文档(Document) |
|
||||
| 列(Col) | 字段(Field) |
|
||||
| 主键(Primary Key) | 对象 ID(Objectid) |
|
||||
| 索引(Index) | 索引(Index) |
|
||||
| SQL | MongoDB |
|
||||
| ------------------------ | ------------------------------- |
|
||||
| 表(Table) | 集合(Collection) |
|
||||
| 行(Row) | 文档(Document) |
|
||||
| 列(Col) | 字段(Field) |
|
||||
| 主键(Primary Key) | 对象 ID(Objectid) |
|
||||
| 索引(Index) | 索引(Index) |
|
||||
| 嵌套表(Embedded Table) | 嵌入式文档(Embedded Document) |
|
||||
| 数组(Array) | 数组(Array) |
|
||||
| 数组(Array) | 数组(Array) |
|
||||
|
||||
#### 文档
|
||||
|
||||
@ -126,7 +126,7 @@ MongoDB 预留了几个特殊的数据库。
|
||||
|
||||
与 MySQL 一样,MongoDB 采用的也是 **插件式的存储引擎架构** ,支持不同类型的存储引擎,不同的存储引擎解决不同场景的问题。在创建数据库或集合时,可以指定存储引擎。
|
||||
|
||||
> 插件式的存储引擎架构可以实现 Server 层和存储引擎层的解耦,可以支持多种存储引擎,如MySQL既可以支持B-Tree结构的InnoDB存储引擎,还可以支持LSM结构的RocksDB存储引擎。
|
||||
> 插件式的存储引擎架构可以实现 Server 层和存储引擎层的解耦,可以支持多种存储引擎,如 MySQL 既可以支持 B-Tree 结构的 InnoDB 存储引擎,还可以支持 LSM 结构的 RocksDB 存储引擎。
|
||||
|
||||
在存储引擎刚出来的时候,默认是使用 MMAPV1 存储引擎,MongoDB4.x 版本不再支持 MMAPv1 存储引擎。
|
||||
|
||||
@ -141,13 +141,13 @@ MongoDB 预留了几个特殊的数据库。
|
||||
|
||||
目前绝大部分流行的数据库存储引擎都是基于 B/B+ Tree 或者 LSM(Log Structured Merge) Tree 来实现的。对于 NoSQL 数据库来说,绝大部分(比如 HBase、Cassandra、RocksDB)都是基于 LSM 树,MongoDB 不太一样。
|
||||
|
||||
上面也说了,自 MongoDB 3.2 以后,默认的存储引擎为WiredTiger 存储引擎。在 WiredTiger 引擎官网上,我们发现 WiredTiger 使用的是 B+ 树作为其存储结构:
|
||||
上面也说了,自 MongoDB 3.2 以后,默认的存储引擎为 WiredTiger 存储引擎。在 WiredTiger 引擎官网上,我们发现 WiredTiger 使用的是 B+ 树作为其存储结构:
|
||||
|
||||
```
|
||||
WiredTiger maintains a table's data in memory using a data structure called a B-Tree ( B+ Tree to be specific), referring to the nodes of a B-Tree as pages. Internal pages carry only keys. The leaf pages store both keys and values.
|
||||
```
|
||||
|
||||
此外,WiredTiger 还支持 [LSM(Log Structured Merge)](https://source.wiredtiger.com/3.1.0/lsm.html) 树作为存储结构,MongoDB 在使用WiredTiger 作为存储引擎时,默认使用的是 B+ 树。
|
||||
此外,WiredTiger 还支持 [LSM(Log Structured Merge)](https://source.wiredtiger.com/3.1.0/lsm.html) 树作为存储结构,MongoDB 在使用 WiredTiger 作为存储引擎时,默认使用的是 B+ 树。
|
||||
|
||||
如果想要了解 MongoDB 使用 B 树的原因,可以看看这篇文章:[为什么 MongoDB 使用 B 树?](https://mp.weixin.qq.com/s/mMWdpbYRiT6LQcdaj4hgXQ)。
|
||||
|
||||
@ -155,13 +155,13 @@ WiredTiger maintains a table's data in memory using a data structure called a B-
|
||||
|
||||
- **root page(根节点)** : B+ 树的根节点。
|
||||
- **internal page(内部节点)** :不实际存储数据的中间索引节点。
|
||||
- **leaf page(叶子节点)**:真正存储数据的叶子节点,包含一个页头(page header)、块头(block header)和真正的数据(key/value),其中页头定义了页的类型、页中实际载荷数据的大小、页中记录条数等信息;块头定义了此页的checksum、块在磁盘上的寻址位置等信息。
|
||||
- **leaf page(叶子节点)**:真正存储数据的叶子节点,包含一个页头(page header)、块头(block header)和真正的数据(key/value),其中页头定义了页的类型、页中实际载荷数据的大小、页中记录条数等信息;块头定义了此页的 checksum、块在磁盘上的寻址位置等信息。
|
||||
|
||||
其整体结构如下图所示:
|
||||
|
||||

|
||||
|
||||
如果想要深入研究学习 WiredTiger 存储引擎,推荐阅读 MongoDB 中文社区的 [WiredTiger存储引擎系列](https://mongoing.com/archives/category/wiredtiger%e5%ad%98%e5%82%a8%e5%bc%95%e6%93%8e%e7%b3%bb%e5%88%97)。
|
||||
如果想要深入研究学习 WiredTiger 存储引擎,推荐阅读 MongoDB 中文社区的 [WiredTiger 存储引擎系列](https://mongoing.com/archives/category/wiredtiger%e5%ad%98%e5%82%a8%e5%bc%95%e6%93%8e%e7%b3%bb%e5%88%97)。
|
||||
|
||||
## MongoDB 聚合
|
||||
|
||||
@ -196,17 +196,17 @@ MongoDB 聚合管道由多个阶段组成,每个阶段在文档通过管道时
|
||||
|
||||
**常用阶段操作符** :
|
||||
|
||||
| 操作符 | 简述 |
|
||||
| --------- | ------------------------------------------------------------ |
|
||||
| \$match | 匹配操作符,用于对文档集合进行筛选 |
|
||||
| 操作符 | 简述 |
|
||||
| --------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| \$match | 匹配操作符,用于对文档集合进行筛选 |
|
||||
| \$project | 投射操作符,用于重构每一个文档的字段,可以提取字段,重命名字段,甚至可以对原有字段进行操作后新增字段 |
|
||||
| \$sort | 排序操作符,用于根据一个或多个字段对文档进行排序 |
|
||||
| \$limit | 限制操作符,用于限制返回文档的数量 |
|
||||
| \$skip | 跳过操作符,用于跳过指定数量的文档 |
|
||||
| \$count | 统计操作符,用于统计文档的数量 |
|
||||
| \$group | 分组操作符,用于对文档集合进行分组 |
|
||||
| \$unwind | 拆分操作符,用于将数组中的每一个值拆分为单独的文档 |
|
||||
| \$lookup | 连接操作符,用于连接同一个数据库中另一个集合,并获取指定的文档,类似于 populate |
|
||||
| \$sort | 排序操作符,用于根据一个或多个字段对文档进行排序 |
|
||||
| \$limit | 限制操作符,用于限制返回文档的数量 |
|
||||
| \$skip | 跳过操作符,用于跳过指定数量的文档 |
|
||||
| \$count | 统计操作符,用于统计文档的数量 |
|
||||
| \$group | 分组操作符,用于对文档集合进行分组 |
|
||||
| \$unwind | 拆分操作符,用于将数组中的每一个值拆分为单独的文档 |
|
||||
| \$lookup | 连接操作符,用于连接同一个数据库中另一个集合,并获取指定的文档,类似于 populate |
|
||||
|
||||
更多操作符介绍详见官方文档:https://docs.mongodb.com/manual/reference/operator/aggregation/
|
||||
|
||||
@ -246,7 +246,7 @@ db.orders.aggregate([
|
||||
- **隔离性**(`Isolation`): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。WiredTiger 存储引擎支持读未提交( read-uncommitted )、读已提交( read-committed )和快照( snapshot )隔离,MongoDB 启动时默认选快照隔离。在不同隔离级别下,一个事务的生命周期内,可能出现脏读、不可重复读、幻读等现象。
|
||||
- **持久性**(`Durability`): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
|
||||
|
||||
关于事务的详细介绍这篇文章就不多说了,感兴趣的可以看看我写的[MySQL常见面试题总结](https://javaguide.cn/database/mysql/mysql-questions-01.html)这篇文章,里面有详细介绍到。
|
||||
关于事务的详细介绍这篇文章就不多说了,感兴趣的可以看看我写的[MySQL 常见面试题总结](https://javaguide.cn/database/mysql/mysql-questions-01.html)这篇文章,里面有详细介绍到。
|
||||
|
||||
MongoDB 单文档原生支持原子性,也具备事务的特性。当谈论 MongoDB 事务的时候,通常指的是 **多文档** 。MongoDB 4.0 加入了对多文档 ACID 事务的支持,但只支持复制集部署模式下的 ACID 事务,也就是说事务的作用域限制为一个副本集内。MongoDB 4.2 引入了 **分布式事务** ,增加了对分片集群上多文档事务的支持,并合并了对副本集上多文档事务的现有支持。
|
||||
|
||||
@ -258,8 +258,8 @@ MongoDB 单文档原生支持原子性,也具备事务的特性。当谈论 Mo
|
||||
|
||||
**注意** :
|
||||
|
||||
- 从MongoDB 4.2开始,多文档事务支持副本集和分片集群,其中:主节点使用WiredTiger存储引擎,同时从节点使用WiredTiger存储引擎或In-Memory存储引擎。在MongoDB 4.0中,只有使用WiredTiger存储引擎的副本集支持事务。
|
||||
- 在MongoDB 4.2及更早版本中,你无法在事务中创建集合。从 MongoDB 4.4 开始,您可以在事务中创建集合和索引。有关详细信息,请参阅 [在事务中创建集合和索引](https://www.mongodb.com/docs/upcoming/core/transactions/#std-label-transactions-create-collections-indexes)。
|
||||
- 从 MongoDB 4.2 开始,多文档事务支持副本集和分片集群,其中:主节点使用 WiredTiger 存储引擎,同时从节点使用 WiredTiger 存储引擎或 In-Memory 存储引擎。在 MongoDB 4.0 中,只有使用 WiredTiger 存储引擎的副本集支持事务。
|
||||
- 在 MongoDB 4.2 及更早版本中,你无法在事务中创建集合。从 MongoDB 4.4 开始,您可以在事务中创建集合和索引。有关详细信息,请参阅 [在事务中创建集合和索引](https://www.mongodb.com/docs/upcoming/core/transactions/#std-label-transactions-create-collections-indexes)。
|
||||
|
||||
## MongoDB 数据压缩
|
||||
|
||||
@ -281,4 +281,4 @@ WiredTiger 日志也会被压缩,默认使用的也是 Snappy 压缩算法。
|
||||
- 技术干货| MongoDB 事务原理 - MongoDB 中文社区:https://mongoing.com/archives/82187
|
||||
- Transactions - MongoDB 官方文档:https://www.mongodb.com/docs/manual/core/transactions/
|
||||
- WiredTiger Storage Engine - MongoDB 官方文档:https://www.mongodb.com/docs/manual/core/wiredtiger/
|
||||
- WiredTiger存储引擎之一:基础数据结构分析:https://mongoing.com/topic/archives-35143
|
||||
- WiredTiger 存储引擎之一:基础数据结构分析:https://mongoing.com/topic/archives-35143
|
||||
|
@ -166,7 +166,7 @@ MongoDB 的分片集群由如下三个部分组成(下图来源于[官方文
|
||||
|
||||
- **Config Servers**:配置服务器,本质上是一个 MongoDB 的副本集,负责存储集群的各种元数据和配置,如分片地址、Chunks 等
|
||||
- **Mongos**:路由服务,不存具体数据,从 Config 获取集群配置讲请求转发到特定的分片,并且整合分片结果返回给客户端。
|
||||
- **Shard**:每个分片是整体数据的一部分子集,从MongoDB3.6版本开始,每个Shard必须部署为副本集(replica set)架构
|
||||
- **Shard**:每个分片是整体数据的一部分子集,从 MongoDB3.6 版本开始,每个 Shard 必须部署为副本集(replica set)架构
|
||||
|
||||
#### 为什么要用分片集群?
|
||||
|
||||
@ -196,12 +196,12 @@ MongoDB 的分片集群由如下三个部分组成(下图来源于[官方文
|
||||
|
||||
选择合适的片键对 sharding 效率影响很大,主要基于如下四个因素(摘自[分片集群使用注意事项 - - 腾讯云文档](https://cloud.tencent.com/document/product/240/44611)):
|
||||
|
||||
- **取值基数** 取值基数建议尽可能大,如果用小基数的片键,因为备选值有限,那么块的总数量就有限,随着数据增多,块的大小会越来越大,导致水平扩展时移动块会非常困难。 例如:选择年龄做一个基数,范围最多只有100个,随着数据量增多,同一个值分布过多时,导致 chunck 的增长超出 chuncksize 的范围,引起 jumbo chunk,从而无法迁移,导致数据分布不均匀,性能瓶颈。
|
||||
- **取值基数** 取值基数建议尽可能大,如果用小基数的片键,因为备选值有限,那么块的总数量就有限,随着数据增多,块的大小会越来越大,导致水平扩展时移动块会非常困难。 例如:选择年龄做一个基数,范围最多只有 100 个,随着数据量增多,同一个值分布过多时,导致 chunck 的增长超出 chuncksize 的范围,引起 jumbo chunk,从而无法迁移,导致数据分布不均匀,性能瓶颈。
|
||||
- **取值分布** 取值分布建议尽量均匀,分布不均匀的片键会造成某些块的数据量非常大,同样有上面数据分布不均匀,性能瓶颈的问题。
|
||||
- **查询带分片** 查询时建议带上分片,使用分片键进行条件查询时,mongos 可以直接定位到具体分片,否则 mongos 需要将查询分发到所有分片,再等待响应返回。
|
||||
- **避免单调递增或递减** 单调递增的 sharding key,数据文件挪动小,但写入会集中,导致最后一篇的数据量持续增大,不断发生迁移,递减同理。
|
||||
|
||||
综上,在选择片键时要考虑以上4个条件,尽可能满足更多的条件,才能降低 MoveChunks 对性能的影响,从而获得最优的性能体验。
|
||||
综上,在选择片键时要考虑以上 4 个条件,尽可能满足更多的条件,才能降低 MoveChunks 对性能的影响,从而获得最优的性能体验。
|
||||
|
||||
#### 分片策略有哪些?
|
||||
|
||||
|
@ -418,7 +418,7 @@ h. DISTINCT, ALL 选项
|
||||
默认为 all, 全部记录
|
||||
```
|
||||
|
||||
### UNION
|
||||
### UNION
|
||||
|
||||
```sql
|
||||
/* UNION */ ------------------
|
||||
@ -684,7 +684,7 @@ end
|
||||
3. Replace 语法 如果有记录,则执行 before insert, before delete, after delete, after insert
|
||||
```
|
||||
|
||||
### SQL编程
|
||||
### SQL 编程
|
||||
|
||||
```mysql
|
||||
/* SQL编程 */ ------------------
|
||||
@ -953,4 +953,3 @@ OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...
|
||||
6. SQL对大小写不敏感
|
||||
7. 清除已有语句:\c
|
||||
```
|
||||
|
||||
|
@ -15,7 +15,7 @@ tag:
|
||||
|
||||
### 1.1 MySQL 基本架构概览
|
||||
|
||||
下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到用户的 SQL 语句在 MySQL 内部是如何执行的。
|
||||
下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到用户的 SQL 语句在 MySQL 内部是如何执行的。
|
||||
|
||||
先简单介绍一下下图涉及的一些组件的基本作用帮助大家理解这幅图,在 1.2 节中会详细介绍到这些组件的作用。
|
||||
|
||||
@ -27,7 +27,7 @@ tag:
|
||||
|
||||

|
||||
|
||||
简单来说 MySQL 主要分为 Server 层和存储引擎层:
|
||||
简单来说 MySQL 主要分为 Server 层和存储引擎层:
|
||||
|
||||
- **Server 层**:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binlog 日志模块。
|
||||
- **存储引擎**: 主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。**现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5 版本开始就被当做默认存储引擎了。**
|
||||
@ -84,15 +84,16 @@ select * from tb_student A where A.age='18' and A.name=' 张三 ';
|
||||
|
||||
结合上面的说明,我们分析下这个语句的执行流程:
|
||||
|
||||
* 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 SQL 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。
|
||||
* 通过分析器进行词法分析,提取 SQL 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student,需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 SQL 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
|
||||
* 接下来就是优化器进行确定执行方案,上面的 SQL 语句,可以有两种执行方案:
|
||||
- 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 SQL 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。
|
||||
- 通过分析器进行词法分析,提取 SQL 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student,需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 SQL 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
|
||||
- 接下来就是优化器进行确定执行方案,上面的 SQL 语句,可以有两种执行方案:
|
||||
|
||||
a.先查询学生表中姓名为“张三”的学生,然后判断是否年龄是 18。
|
||||
b.先找出学生中年龄 18 岁的学生,然后再查询姓名为“张三”的学生。
|
||||
那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。
|
||||
|
||||
* 进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。
|
||||
那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。
|
||||
|
||||
- 进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。
|
||||
|
||||
### 2.2 更新语句
|
||||
|
||||
@ -101,12 +102,13 @@ select * from tb_student A where A.age='18' and A.name=' 张三 ';
|
||||
```
|
||||
update tb_student A set A.age='19' where A.name=' 张三 ';
|
||||
```
|
||||
|
||||
我们来给张三修改下年龄,在实际数据库肯定不会设置年龄这个字段的,不然要被技术负责人打的。其实这条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候肯定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块是 **binlog(归档日志)** ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 **redo log(重做日志)**,我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:
|
||||
|
||||
* 先查询到张三这一条数据,如果有缓存,也是会用到缓存。
|
||||
* 然后拿到查询的语句,把 age 改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。
|
||||
* 执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。
|
||||
* 更新完成。
|
||||
- 先查询到张三这一条数据,如果有缓存,也是会用到缓存。
|
||||
- 然后拿到查询的语句,把 age 改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。
|
||||
- 执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。
|
||||
- 更新完成。
|
||||
|
||||
**这里肯定有同学会问,为什么要用两个日志模块,用一个日志模块不行吗?**
|
||||
|
||||
@ -114,25 +116,25 @@ update tb_student A set A.age='19' where A.name=' 张三 ';
|
||||
|
||||
并不是说只用一个日志模块不可以,只是 InnoDB 引擎就是通过 redo log 来支持事务的。那么,又会有同学问,我用两个日志模块,但是不要这么复杂行不行,为什么 redo log 要引入 prepare 预提交状态?这里我们用反证法来说明下为什么要这么做?
|
||||
|
||||
* **先写 redo log 直接提交,然后写 binlog**,假设写完 redo log 后,机器挂了,binlog 日志没有被写入,那么机器重启后,这台机器会通过 redo log 恢复数据,但是这个时候 binlog 并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
|
||||
* **先写 binlog,然后写 redo log**,假设写完了 binlog,机器异常重启了,由于没有 redo log,本机是无法恢复这一条记录的,但是 binlog 又有记录,那么和上面同样的道理,就会产生数据不一致的情况。
|
||||
- **先写 redo log 直接提交,然后写 binlog**,假设写完 redo log 后,机器挂了,binlog 日志没有被写入,那么机器重启后,这台机器会通过 redo log 恢复数据,但是这个时候 binlog 并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
|
||||
- **先写 binlog,然后写 redo log**,假设写完了 binlog,机器异常重启了,由于没有 redo log,本机是无法恢复这一条记录的,但是 binlog 又有记录,那么和上面同样的道理,就会产生数据不一致的情况。
|
||||
|
||||
如果采用 redo log 两阶段提交的方式就不一样了,写完 binlog 后,然后再提交 redo log 就会防止出现上述的问题,从而保证了数据的一致性。那么问题来了,有没有一个极端的情况呢?假设 redo log 处于预提交状态,binlog 也已经写完了,这个时候发生了异常重启会怎么样呢?
|
||||
这个就要依赖于 MySQL 的处理机制了,MySQL 的处理过程如下:
|
||||
|
||||
* 判断 redo log 是否完整,如果判断是完整的,就立即提交。
|
||||
* 如果 redo log 只是预提交但不是 commit 状态,这个时候就会去判断 binlog 是否完整,如果完整就提交 redo log, 不完整就回滚事务。
|
||||
- 判断 redo log 是否完整,如果判断是完整的,就立即提交。
|
||||
- 如果 redo log 只是预提交但不是 commit 状态,这个时候就会去判断 binlog 是否完整,如果完整就提交 redo log, 不完整就回滚事务。
|
||||
|
||||
这样就解决了数据一致性的问题。
|
||||
|
||||
## 三 总结
|
||||
|
||||
* MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。
|
||||
* 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。
|
||||
* 查询语句的执行流程如下:权限校验(如果命中缓存)--->查询缓存--->分析器--->优化器--->权限校验--->执行器--->引擎
|
||||
* 更新语句执行流程如下:分析器---->权限校验---->执行器--->引擎---redo log(prepare 状态)--->binlog--->redo log(commit状态)
|
||||
- MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。
|
||||
- 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。
|
||||
- 查询语句的执行流程如下:权限校验(如果命中缓存)--->查询缓存--->分析器--->优化器--->权限校验--->执行器--->引擎
|
||||
- 更新语句执行流程如下:分析器---->权限校验---->执行器--->引擎---redo log(prepare 状态)--->binlog--->redo log(commit 状态)
|
||||
|
||||
## 四 参考
|
||||
|
||||
* 《MySQL 实战45讲》
|
||||
* MySQL 5.6参考手册:<https://dev.MySQL.com/doc/refman/5.6/en/>
|
||||
- 《MySQL 实战 45 讲》
|
||||
- MySQL 5.6 参考手册:<https://dev.MySQL.com/doc/refman/5.6/en/>
|
||||
|
@ -40,13 +40,13 @@ tag:
|
||||
|
||||

|
||||
|
||||
但如果马上重启 MySQL 实例,重启后这个表的 AUTO_INCREMENT 就会变成 1。也就是说,MySQL 重启可能会修改一个表的 AUTO_INCREMENT 的值。
|
||||
但如果马上重启 MySQL 实例,重启后这个表的 AUTO_INCREMENT 就会变成 1。 也就是说,MySQL 重启可能会修改一个表的 AUTO_INCREMENT 的值。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
以上,是在我本地 MySQL 5.x 版本的实验,实际上,**到了 MySQL 8.0 版本后,自增值的变更记录被放在了 redo log 中,提供了自增值持久化的能力** ,也就是实现了“如果发生重启,表的自增值可以根据 redo log 恢复为 MySQL 重启前的值”
|
||||
以上,是在我本地 MySQL 5.x 版本的实验,实际上,**到了 MySQL 8.0 版本后,自增值的变更记录被放在了 redo log 中,提供了自增值持久化的能力** ,也就是实现了“如果发生重启,表的自增值可以根据 redo log 恢复为 MySQL 重启前的值”
|
||||
|
||||
也就是说对于上面这个例子来说,重启实例后这个表的 AUTO_INCREMENT 仍然是 2。
|
||||
|
||||
@ -76,7 +76,7 @@ tag:
|
||||
|
||||
这个奇数偶数其实是通过 `auto_increment_offset` 和 `auto_increment_increment` 这两个参数来决定的,这俩分别用来表示自增的初始值和步长,默认值都是 1。
|
||||
|
||||
所以,上面的例子中生成新的自增值的步骤实际是这样的:从 `auto_increment_offset` 开始,以 `auto_increment_increment` 为步长,持续叠加,直到找到第一个大于 100 的值,作为新的自增值。
|
||||
所以,上面的例子中生成新的自增值的步骤实际是这样的:从 `auto_increment_offset` 开始,以 `auto_increment_increment` 为步长,持续叠加,直到找到第一个大于 100 的值,作为新的自增值。
|
||||
|
||||
所以,这种情况下,自增值可能会是 102,103 等等之类的,就会导致不连续的主键 id。
|
||||
|
||||
@ -149,7 +149,7 @@ tag:
|
||||
|
||||
现在有两个并行执行的事务 A 和 B,在申请自增值的时候,为了避免两个事务申请到相同的自增 id,肯定要加锁,然后顺序申请,对吧。
|
||||
|
||||
1. 假设事务 A 申请到了 id = 1, 事务 B 申请到 id=2,那么这时候表 t 的自增值是3,之后继续执行。
|
||||
1. 假设事务 A 申请到了 id = 1, 事务 B 申请到 id=2,那么这时候表 t 的自增值是 3,之后继续执行。
|
||||
2. 事务 B 正确提交了,但事务 A 出现了唯一键冲突,也就是 id = 1 的那行记录插入失败了,那如果允许事务 A 把自增 id 回退,也就是把表的当前自增值改回 1,那么就会出现这样的情况:表里面已经有 id = 2 的行,而当前的自增 id 值是 1。
|
||||
3. 接下来,继续执行的其他事务就会申请到 id=2。这时,就会出现插入语句报错“主键冲突”。
|
||||
|
||||
|
@ -14,7 +14,7 @@ head:
|
||||
|
||||
缓存是一个有效且实用的系统性能优化的手段,不论是操作系统还是各种软件和网站或多或少都用到了缓存。
|
||||
|
||||
然而,有经验的 DBA 都建议生产环境中把 MySQL 自带的 Query Cache(查询缓存)给关掉。而且,从 MySQL 5.7.20 开始,就已经默认弃用查询缓存了。在 MySQL 8.0及之后,更是直接删除了查询缓存的功能。
|
||||
然而,有经验的 DBA 都建议生产环境中把 MySQL 自带的 Query Cache(查询缓存)给关掉。而且,从 MySQL 5.7.20 开始,就已经默认弃用查询缓存了。在 MySQL 8.0 及之后,更是直接删除了查询缓存的功能。
|
||||
|
||||
这又是为什么呢?查询缓存真就这么鸡肋么?
|
||||
|
||||
@ -27,7 +27,7 @@ head:
|
||||
|
||||
## MySQL 查询缓存介绍
|
||||
|
||||
MySQL体系架构如下图所示:
|
||||
MySQL 体系架构如下图所示:
|
||||
|
||||

|
||||
|
||||
@ -90,10 +90,10 @@ mysql> show variables like '%query_cache%';
|
||||
|
||||
**建议** :
|
||||
|
||||
- `query_cache_size`不建议设置的过大。过大的空间不但挤占实例其他内存结构的空间,而且会增加在缓存中搜索的开销。建议根据实例规格,初始值设置为10MB到100MB之间的值,而后根据运行使用情况调整。
|
||||
- `query_cache_size`不建议设置的过大。过大的空间不但挤占实例其他内存结构的空间,而且会增加在缓存中搜索的开销。建议根据实例规格,初始值设置为 10MB 到 100MB 之间的值,而后根据运行使用情况调整。
|
||||
- 建议通过调整 `query_cache_size` 的值来开启、关闭查询缓存,因为修改`query_cache_type` 参数需要重启 MySQL Server 生效。
|
||||
|
||||
8.0 版本之前,`my.cnf` 加入以下配置,重启 MySQL 开启查询缓存
|
||||
8.0 版本之前,`my.cnf` 加入以下配置,重启 MySQL 开启查询缓存
|
||||
|
||||
```properties
|
||||
query_cache_type=1
|
||||
@ -196,11 +196,11 @@ MySQL 中的查询缓存虽然能够提升数据库的查询性能,但是查
|
||||
|
||||
> 根据我们的经验,在高并发压力环境中查询缓存会导致系统性能的下降,甚至僵死。如果你一 定要使用查询缓存,那么不要设置太大内存,而且只有在明确收益的时候才使用(数据库内容修改次数较少)。
|
||||
|
||||
**确实是这样的!实际项目中,更建议使用本地缓存(比如 Caffeine)或者分布式缓存(比如Redis) ,性能更好,更通用一些。**
|
||||
**确实是这样的!实际项目中,更建议使用本地缓存(比如 Caffeine)或者分布式缓存(比如 Redis) ,性能更好,更通用一些。**
|
||||
|
||||
## 参考
|
||||
|
||||
- 《高性能 MySQL》
|
||||
- MySQL缓存机制:https://zhuanlan.zhihu.com/p/55947158
|
||||
- RDS MySQL查询缓存(Query Cache)的设置和使用 - 阿里元云数据库 RDS 文档:https://help.aliyun.com/document_detail/41717.html
|
||||
- MySQL 缓存机制:https://zhuanlan.zhihu.com/p/55947158
|
||||
- RDS MySQL 查询缓存(Query Cache)的设置和使用 - 阿里元云数据库 RDS 文档:https://help.aliyun.com/document_detail/41717.html
|
||||
- 8.10.3 The MySQL Query Cache - MySQL 官方文档:https://dev.mysql.com/doc/refman/5.7/en/query-cache.html
|
@ -14,7 +14,7 @@ head:
|
||||
|
||||
> 本文来自公号 MySQL 技术,JavaGuide 对其做了补充完善。原文地址:https://mp.weixin.qq.com/s/d5OowNLtXBGEAbT31sSH4g
|
||||
|
||||
优化 SQL 的第一步应该是读懂 SQL 的执行计划。本篇文章,我们一起来学习下 MySQL `EXPLAIN` 执行计划相关知识。
|
||||
优化 SQL 的第一步应该是读懂 SQL 的执行计划。本篇文章,我们一起来学习下 MySQL `EXPLAIN` 执行计划相关知识。
|
||||
|
||||
## 什么是执行计划?
|
||||
|
||||
@ -24,7 +24,7 @@ head:
|
||||
|
||||
## 如何获取执行计划?
|
||||
|
||||
MySQL 为我们提供了 `EXPLAIN` 命令,来获取执行计划的相关信息。
|
||||
MySQL 为我们提供了 `EXPLAIN` 命令,来获取执行计划的相关信息。
|
||||
|
||||
需要注意的是,`EXPLAIN` 语句并不会真的去执行相关的语句,而是通过查询优化器对语句进行分析,找出最优的查询方案,并显示对应的信息。
|
||||
|
||||
@ -50,8 +50,8 @@ mysql> explain SELECT * FROM dept_emp WHERE emp_no IN (SELECT emp_no FROM dept_e
|
||||
|
||||
| **列名** | **含义** |
|
||||
| ------------- | -------------------------------------------- |
|
||||
| id | SELECT查询的序列标识符 |
|
||||
| select_type | SELECT关键字对应的查询类型 |
|
||||
| id | SELECT 查询的序列标识符 |
|
||||
| select_type | SELECT 关键字对应的查询类型 |
|
||||
| table | 用到的表名 |
|
||||
| partitions | 匹配的分区,对于未分区的表,值为 NULL |
|
||||
| type | 表的访问方法 |
|
||||
@ -89,8 +89,7 @@ id 如果相同,从上往下依次执行。id 不同,id 值越大,执行
|
||||
查询用到的表名,每行都有对应的表名,表名除了正常的表之外,也可能是以下列出的值:
|
||||
|
||||
- **`<unionM,N>`** : 本行引用了 id 为 M 和 N 的行的 UNION 结果;
|
||||
- **`<derivedN>`** : 本行引用了 id 为 N 的表所产生的的派生表结果。派生表有可能产生自 FROM 语句中的子查询。
|
||||
-**`<subqueryN>`** : 本行引用了 id 为 N 的表所产生的的物化子查询结果。
|
||||
- **`<derivedN>`** : 本行引用了 id 为 N 的表所产生的的派生表结果。派生表有可能产生自 FROM 语句中的子查询。 -**`<subqueryN>`** : 本行引用了 id 为 N 的表所产生的的物化子查询结果。
|
||||
|
||||
### type(重要)
|
||||
|
||||
|
@ -13,6 +13,8 @@ head:
|
||||
content: 一篇文章总结MySQL常见的知识点和面试题,涵盖MySQL基础、MySQL基础架构、MySQL存储引擎、MySQL查询缓存、MySQL事务、MySQL锁等内容。
|
||||
---
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
## MySQL 基础
|
||||
|
||||
### 什么是关系型数据库?
|
||||
@ -700,7 +702,7 @@ mysql> EXPLAIN SELECT `score`,`name` FROM `cus_order` ORDER BY `score` DESC;
|
||||
|
||||
- [一树一溪的 MySQL 系列教程](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=Mzg3NTc3NjM4Nw==&action=getalbum&album_id=2372043523518300162&scene=173&from_msgid=2247484308&from_itemidx=1&count=3&nolastread=1#wechat_redirect)
|
||||
- [Yes 的 MySQL 系列教程](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxNTE3NjQ3MA==&action=getalbum&album_id=1903249596194095112&scene=173&from_msgid=2247490365&from_itemidx=1&count=3&nolastread=1#wechat_redirect)
|
||||
- [写完这篇 我的SQL优化能力直接进入新层次 - 变成派大星 - 2022](https://juejin.cn/post/7161964571853815822)
|
||||
- [写完这篇 我的 SQL 优化能力直接进入新层次 - 变成派大星 - 2022](https://juejin.cn/post/7161964571853815822)
|
||||
- [两万字详解!InnoDB 锁专题! - 捡田螺的小男孩 - 2022](https://juejin.cn/post/7094049650428084232)
|
||||
- [MySQL 的自增主键一定是连续的吗? - 飞天小牛肉 - 2022](https://mp.weixin.qq.com/s/qci10h9rJx_COZbHV3aygQ)
|
||||
- [深入理解 MySQL 索引底层原理 - 腾讯技术工程 - 2020](https://zhuanlan.zhihu.com/p/113917726)
|
||||
|
@ -73,7 +73,7 @@ SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTE
|
||||
|
||||
### 脏读(读未提交)
|
||||
|
||||
%E5%AE%9E%E4%BE%8B.jpg)
|
||||
%E5%AE%9E%E4%BE%8B.jpg>)
|
||||
|
||||
### 避免脏读(读已提交)
|
||||
|
||||
|
@ -19,16 +19,16 @@ NoSQL 数据库代表:HBase 、Cassandra、MongoDB、Redis。
|
||||
|
||||
## SQL 和 NoSQL 有什么区别?
|
||||
|
||||
| | SQL 数据库 | NoSQL 数据库 |
|
||||
| :----------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| 数据存储模型 | 结构化存储,具有固定行和列的表格 | 非结构化存储。文档:JSON 文档,键值:键值对,宽列:包含行和动态列的表,图:节点和边 |
|
||||
| 发展历程 | 开发于 1970 年代,重点是减少数据重复 | 开发于 2000 年代后期,重点是提升可扩展性,减少大规模数据的存储成本 |
|
||||
| 例子 | Oracle、MySQL、Microsoft SQL Server 、PostgreSQL | 文档:MongoDB、CouchDB,键值:Redis 、DynamoDB,宽列:Cassandra 、 HBase,图表:Neo4j 、 Amazon Neptune、Giraph |
|
||||
| ACID 属性 | 提供原子性、一致性、隔离性和持久性 (ACID) 属性 | 通常不支持 ACID 事务,为了可扩展、高性能进行了权衡,少部分支持比如 MongoDB 。不过,MongoDB 对 ACID 事务 的支持和 MySQL 还是有所区别的。 |
|
||||
| 性能 | 性能通常取决于磁盘子系统。要获得最佳性能,通常需要优化查询、索引和表结构。 | 性能通常由底层硬件集群大小、网络延迟以及调用应用程序来决定。 |
|
||||
| 扩展 | 垂直(使用性能更强大的服务器进行扩展)、读写分离、分库分表 | 横向(增加服务器的方式横向扩展,通常是基于分片机制) |
|
||||
| 用途 | 普通企业级的项目的数据存储 | 用途广泛比如图数据库支持分析和遍历连接数据之间的关系、键值数据库可以处理大量数据扩展和极高的状态变化 |
|
||||
| 查询语法 | 结构化查询语言 (SQL) | 数据访问语法可能因数据库而异 |
|
||||
| | SQL 数据库 | NoSQL 数据库 |
|
||||
| :----------- | -------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 数据存储模型 | 结构化存储,具有固定行和列的表格 | 非结构化存储。文档:JSON 文档,键值:键值对,宽列:包含行和动态列的表,图:节点和边 |
|
||||
| 发展历程 | 开发于 1970 年代,重点是减少数据重复 | 开发于 2000 年代后期,重点是提升可扩展性,减少大规模数据的存储成本 |
|
||||
| 例子 | Oracle、MySQL、Microsoft SQL Server 、PostgreSQL | 文档:MongoDB、CouchDB,键值:Redis 、DynamoDB,宽列:Cassandra 、 HBase,图表:Neo4j 、 Amazon Neptune、Giraph |
|
||||
| ACID 属性 | 提供原子性、一致性、隔离性和持久性 (ACID) 属性 | 通常不支持 ACID 事务,为了可扩展、高性能进行了权衡,少部分支持比如 MongoDB 。不过,MongoDB 对 ACID 事务 的支持和 MySQL 还是有所区别的。 |
|
||||
| 性能 | 性能通常取决于磁盘子系统。要获得最佳性能,通常需要优化查询、索引和表结构。 | 性能通常由底层硬件集群大小、网络延迟以及调用应用程序来决定。 |
|
||||
| 扩展 | 垂直(使用性能更强大的服务器进行扩展)、读写分离、分库分表 | 横向(增加服务器的方式横向扩展,通常是基于分片机制) |
|
||||
| 用途 | 普通企业级的项目的数据存储 | 用途广泛比如图数据库支持分析和遍历连接数据之间的关系、键值数据库可以处理大量数据扩展和极高的状态变化 |
|
||||
| 查询语法 | 结构化查询语言 (SQL) | 数据访问语法可能因数据库而异 |
|
||||
|
||||
## NoSQL 数据库有什么优势?
|
||||
|
||||
@ -54,6 +54,6 @@ NoSQL 数据库主要可以分为下面四种类型:
|
||||
|
||||
## 参考
|
||||
|
||||
- NoSQL 是什么?- MongoDB 官方文档:https://www.mongodb.com/zh-cn/nosql-explained
|
||||
- 什么是 NoSQL? - AWS:https://aws.amazon.com/cn/nosql/
|
||||
- NoSQL vs. SQL Databases - MongoDB 官方文档:https://www.mongodb.com/zh-cn/nosql-explained/nosql-vs-sql
|
||||
- NoSQL 是什么?- MongoDB 官方文档:<https://www.mongodb.com/zh-cn/nosql-explained>
|
||||
- 什么是 NoSQL? - AWS:<https://aws.amazon.com/cn/nosql/>
|
||||
- NoSQL vs. SQL Databases - MongoDB 官方文档:<https://www.mongodb.com/zh-cn/nosql-explained/nosql-vs-sql>
|
||||
|
@ -5,12 +5,11 @@ tag:
|
||||
- Redis
|
||||
---
|
||||
|
||||
|
||||
看到很多小伙伴简历上写了“**熟练使用缓存**”,但是被我问到“**缓存常用的3种读写策略**”的时候却一脸懵逼。
|
||||
看到很多小伙伴简历上写了“**熟练使用缓存**”,但是被我问到“**缓存常用的 3 种读写策略**”的时候却一脸懵逼。
|
||||
|
||||
在我看来,造成这个问题的原因是我们在学习 Redis 的时候,可能只是简单了写一些 Demo,并没有去关注缓存的读写策略,或者说压根不知道这回事。
|
||||
|
||||
但是,搞懂3种常见的缓存读写策略对于实际工作中使用缓存以及面试中被问到缓存都是非常有帮助的!
|
||||
但是,搞懂 3 种常见的缓存读写策略对于实际工作中使用缓存以及面试中被问到缓存都是非常有帮助的!
|
||||
|
||||
**下面介绍到的三种模式各有优劣,不存在最佳模式,根据具体的业务场景选择适合自己的缓存读写模式。**
|
||||
|
||||
|
@ -6,49 +6,6 @@ tag:
|
||||
---
|
||||
|
||||
**缓存基础** 相关的面试题为我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)(点击链接即可查看详细介绍以及加入方法)专属内容,已经整理到了[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)中。
|
||||
|
||||

|
||||
|
||||
[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)(点击链接即可查看详细介绍)的部分内容展示如下,你可以将其看作是 [JavaGuide](https://javaguide.cn) 的补充完善,两者可以配合使用。
|
||||
|
||||

|
||||
|
||||
最近几年,市面上有越来越多的“技术大佬”开始办培训班/训练营,动辄成千上万的学费,却并没有什么干货,单纯的就是割韭菜。
|
||||
|
||||
为了帮助更多同学准备 Java 面试以及学习 Java ,我创建了一个纯粹的知识星球。虽然收费只有培训班/训练营的百分之一,但是知识星球里的内容质量更高,提供的服务也更全面。
|
||||
|
||||
欢迎准备 Java 面试以及学习 Java 的同学加入我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html),干货非常多,学习氛围也很不错!收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/image-20220311203414600.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
下面是星球提供的部分服务(点击下方图片即可获取知识星球的详细介绍):
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiufuwu.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!**
|
||||
|
||||
如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍: [JavaGuide 知识星球详细介绍](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。
|
||||
|
||||
这里再送一个 30 元的新人优惠券(续费半价)。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuanheyi.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
进入星球之后,记得添加微信,我会发你详细的星球使用指南。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/IMG_3007.jpg" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
<!-- @include: @planet.snippet.md -->
|
@ -9,47 +9,4 @@ tag:
|
||||
|
||||

|
||||
|
||||
|
||||
[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)(点击链接即可查看详细介绍)的部分内容展示如下,你可以将其看作是 [JavaGuide](https://javaguide.cn) 的补充完善,两者可以配合使用。
|
||||
|
||||

|
||||
|
||||
最近几年,市面上有越来越多的“技术大佬”开始办培训班/训练营,动辄成千上万的学费,却并没有什么干货,单纯的就是割韭菜。
|
||||
|
||||
为了帮助更多同学准备 Java 面试以及学习 Java ,我创建了一个纯粹的知识星球。虽然收费只有培训班/训练营的百分之一,但是知识星球里的内容质量更高,提供的服务也更全面。
|
||||
|
||||
欢迎准备 Java 面试以及学习 Java 的同学加入我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html),干货非常多,学习氛围也很不错!收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/image-20220311203414600.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
下面是星球提供的部分服务(点击下方图片即可获取知识星球的详细介绍):
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiufuwu.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!**
|
||||
|
||||
如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍: [JavaGuide 知识星球详细介绍](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。
|
||||
|
||||
这里再送一个 30 元的新人优惠券(续费半价)。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuanheyi.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
进入星球之后,记得添加微信,我会发你详细的星球使用指南。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/IMG_3007.jpg" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
<!-- @include: @planet.snippet.md -->
|
@ -5,7 +5,7 @@ tag:
|
||||
- Redis
|
||||
---
|
||||
|
||||
> 本文整理完善自:https://mp.weixin.qq.com/s/0Nqfq_eQrUb12QH6eBbHXA ,作者:阿Q说代码
|
||||
> 本文整理完善自:https://mp.weixin.qq.com/s/0Nqfq_eQrUb12QH6eBbHXA ,作者:阿 Q 说代码
|
||||
|
||||
这篇文章会详细总结一下可能导致 Redis 阻塞的情况,这些情况也是影响 Redis 性能的关键因素,使用 Redis 的时候应该格外注意!
|
||||
|
||||
@ -54,7 +54,7 @@ Redis AOF 持久化机制是在执行完命令之后再记录日志,这和关
|
||||
|
||||
当后台线程( `aof_fsync` 线程)调用 `fsync` 函数同步 AOF 文件时,需要等待,直到写入完成。当磁盘压力太大的时候,会导致 `fsync` 操作发生阻塞,主线程调用 `write` 函数时也会被阻塞。`fsync` 完成后,主线程执行 `write` 才能成功返回。
|
||||
|
||||
关于 AOF 工作流程的详细介绍可以查看:[Redis持久化机制详解](./redis-persistence.md),有助于理解 AOF 刷盘阻塞。
|
||||
关于 AOF 工作流程的详细介绍可以查看:[Redis 持久化机制详解](./redis-persistence.md),有助于理解 AOF 刷盘阻塞。
|
||||
|
||||
### AOF 重写阻塞
|
||||
|
||||
@ -64,7 +64,7 @@ Redis AOF 持久化机制是在执行完命令之后再记录日志,这和关
|
||||
|
||||
阻塞就是出现在第 2 步的过程中,将缓冲区中新数据写到新文件的过程中会产生**阻塞**。
|
||||
|
||||
相关阅读:[Redis AOF重写阻塞问题分析](https://cloud.tencent.com/developer/article/1633077)。
|
||||
相关阅读:[Redis AOF 重写阻塞问题分析](https://cloud.tencent.com/developer/article/1633077)。
|
||||
|
||||
## 大 Key
|
||||
|
||||
@ -111,13 +111,13 @@ Redis 集群可以进行节点的动态扩容缩容,这一过程目前还处
|
||||
|
||||
## Swap(内存交换)
|
||||
|
||||
什么是 Swap?Swap 直译过来是交换的意思,Linux中的Swap常被称为内存交换或者交换分区。类似于 Windows 中的虚拟内存,就是当内存不足的时候,把一部分硬盘空间虚拟成内存使用,从而解决内存容量不足的情况。因此,Swap 分区的作用就是牺牲硬盘,增加内存,解决 VPS 内存不够用或者爆满的问题。
|
||||
什么是 Swap?Swap 直译过来是交换的意思,Linux 中的 Swap 常被称为内存交换或者交换分区。类似于 Windows 中的虚拟内存,就是当内存不足的时候,把一部分硬盘空间虚拟成内存使用,从而解决内存容量不足的情况。因此,Swap 分区的作用就是牺牲硬盘,增加内存,解决 VPS 内存不够用或者爆满的问题。
|
||||
|
||||
Swap 对于Redis来说是非常致命的,Redis保证高性能的一个重要前提是所有的数据在内存中。如果操作系统把Redis使用的部分内存换出硬盘,由于内存与硬盘读写的速度并几个数量级,会导致发生交换后的Redis性能急剧下降。
|
||||
Swap 对于 Redis 来说是非常致命的,Redis 保证高性能的一个重要前提是所有的数据在内存中。如果操作系统把 Redis 使用的部分内存换出硬盘,由于内存与硬盘读写的速度并几个数量级,会导致发生交换后的 Redis 性能急剧下降。
|
||||
|
||||
识别 Redis 发生 Swap 的检查方法如下:
|
||||
|
||||
1、查询Redis进程号
|
||||
1、查询 Redis 进程号
|
||||
|
||||
```bash
|
||||
reids-cli -p 6383 info server | grep process_id
|
||||
@ -136,19 +136,19 @@ Swap: 0kB
|
||||
.....
|
||||
```
|
||||
|
||||
如果交换量都是0KB或者个别的是4KB,则正常。
|
||||
如果交换量都是 0KB 或者个别的是 4KB,则正常。
|
||||
|
||||
预防内存交换的方法:
|
||||
|
||||
- 保证机器充足的可用内存
|
||||
- 确保所有Redis实例设置最大可用内存(maxmemory),防止极端情况Redis内存不可控的增长
|
||||
- 降低系统使用swap优先级,如`echo 10 > /proc/sys/vm/swappiness`
|
||||
- 确保所有 Redis 实例设置最大可用内存(maxmemory),防止极端情况 Redis 内存不可控的增长
|
||||
- 降低系统使用 swap 优先级,如`echo 10 > /proc/sys/vm/swappiness`
|
||||
|
||||
## CPU 竞争
|
||||
|
||||
Redis是典型的CPU密集型应用,不建议和其他多核CPU密集型服务部署在一起。当其他进程过度消耗CPU时,将严重影响Redis的吞吐量。
|
||||
Redis 是典型的 CPU 密集型应用,不建议和其他多核 CPU 密集型服务部署在一起。当其他进程过度消耗 CPU 时,将严重影响 Redis 的吞吐量。
|
||||
|
||||
可以通过`reids-cli --stat`获取当前Redis使用情况。通过`top`命令获取进程对CPU的利用率等信息 通过`info commandstats`统计信息分析出命令不合理开销时间,查看是否是因为高算法复杂度或者过度的内存优化问题。
|
||||
可以通过`reids-cli --stat`获取当前 Redis 使用情况。通过`top`命令获取进程对 CPU 的利用率等信息 通过`info commandstats`统计信息分析出命令不合理开销时间,查看是否是因为高算法复杂度或者过度的内存优化问题。
|
||||
|
||||
## 网络问题
|
||||
|
||||
@ -156,5 +156,5 @@ Redis是典型的CPU密集型应用,不建议和其他多核CPU密集型服务
|
||||
|
||||
## 参考
|
||||
|
||||
- Redis阻塞的6大类场景分析与总结:https://mp.weixin.qq.com/s/eaZCEtTjTuEmXfUubVHjew
|
||||
- Redis开发与运维笔记-Redis的噩梦-阻塞:https://mp.weixin.qq.com/s/TDbpz9oLH6ifVv6ewqgSgA
|
||||
- Redis 阻塞的 6 大类场景分析与总结:https://mp.weixin.qq.com/s/eaZCEtTjTuEmXfUubVHjew
|
||||
- Redis 开发与运维笔记-Redis 的噩梦-阻塞:https://mp.weixin.qq.com/s/TDbpz9oLH6ifVv6ewqgSgA
|
||||
|
@ -387,17 +387,17 @@ Sorted Set 类似于 Set,但和 Set 相比,Sorted Set 增加了一个权重
|
||||
|
||||
### 常用命令
|
||||
|
||||
| 命令 | 介绍 |
|
||||
| --------------------------------------------- | ------------------------------------------------------------ |
|
||||
| ZADD key score1 member1 score2 member2 ... | 向指定有序集合添加一个或多个元素 |
|
||||
| ZCARD KEY | 获取指定有序集合的元素数量 |
|
||||
| ZSCORE key member | 获取指定有序集合中指定元素的 score 值 |
|
||||
| 命令 | 介绍 |
|
||||
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| ZADD key score1 member1 score2 member2 ... | 向指定有序集合添加一个或多个元素 |
|
||||
| ZCARD KEY | 获取指定有序集合的元素数量 |
|
||||
| ZSCORE key member | 获取指定有序集合中指定元素的 score 值 |
|
||||
| ZINTERSTORE destination numkeys key1 key2 ... | 将给定所有有序集合的交集存储在 destination 中,对相同元素对应的 score 值进行 SUM 聚合操作,numkeys 为集合数量 |
|
||||
| ZUNIONSTORE destination numkeys key1 key2 ... | 求并集,其它和 ZINTERSTORE 类似 |
|
||||
| ZDIFFSTORE destination numkeys key1 key2 ... | 求差集,其它和 ZINTERSTORE 类似 |
|
||||
| ZRANGE key start end | 获取指定有序集合 start 和 end 之间的元素(score 从低到高) |
|
||||
| ZREVRANGE key start end | 获取指定有序集合 start 和 end 之间的元素(score 从高到底) |
|
||||
| ZREVRANK key member | 获取指定有序集合中指定元素的排名(score 从大到小排序) |
|
||||
| ZUNIONSTORE destination numkeys key1 key2 ... | 求并集,其它和 ZINTERSTORE 类似 |
|
||||
| ZDIFFSTORE destination numkeys key1 key2 ... | 求差集,其它和 ZINTERSTORE 类似 |
|
||||
| ZRANGE key start end | 获取指定有序集合 start 和 end 之间的元素(score 从低到高) |
|
||||
| ZREVRANGE key start end | 获取指定有序集合 start 和 end 之间的元素(score 从高到底) |
|
||||
| ZREVRANK key member | 获取指定有序集合中指定元素的排名(score 从大到小排序) |
|
||||
|
||||
更多 Redis Sorted Set 命令以及详细使用指南,请查看 Redis 官网对应的介绍:https://redis.io/commands/?group=sorted-set 。
|
||||
|
||||
|
@ -26,11 +26,11 @@ Bitmap 存储的是连续的二进制数字(0 和 1),通过 Bitmap, 只需
|
||||
|
||||
### 常用命令
|
||||
|
||||
| 命令 | 介绍 |
|
||||
| ------------------------------------- | ------------------------------------------------------------ |
|
||||
| SETBIT key offset value | 设置指定 offset 位置的值 |
|
||||
| GETBIT key offset | 获取指定 offset 位置的值 |
|
||||
| BITCOUNT key start end | 获取 start 和 end 之前值为 1 的元素个数 |
|
||||
| 命令 | 介绍 |
|
||||
| ------------------------------------- | ---------------------------------------------------------------- |
|
||||
| SETBIT key offset value | 设置指定 offset 位置的值 |
|
||||
| GETBIT key offset | 获取指定 offset 位置的值 |
|
||||
| BITCOUNT key start end | 获取 start 和 end 之前值为 1 的元素个数 |
|
||||
| BITOP operation destkey key1 key2 ... | 对一个或多个 Bitmap 进行运算,可用运算符有 AND, OR, XOR 以及 NOT |
|
||||
|
||||
**Bitmap 基本操作演示** :
|
||||
@ -86,10 +86,10 @@ HyperLogLog 的使用非常简单,但原理非常复杂。HyperLogLog 的原
|
||||
|
||||
HyperLogLog 相关的命令非常少,最常用的也就 3 个。
|
||||
|
||||
| 命令 | 介绍 |
|
||||
| ----------------------------------------- | ------------------------------------------------------------ |
|
||||
| PFADD key element1 element2 ... | 添加一个或多个元素到 HyperLogLog 中 |
|
||||
| PFCOUNT key1 key2 | 获取一个或者多个 HyperLogLog 的唯一计数。 |
|
||||
| 命令 | 介绍 |
|
||||
| ----------------------------------------- | -------------------------------------------------------------------------------- |
|
||||
| PFADD key element1 element2 ... | 添加一个或多个元素到 HyperLogLog 中 |
|
||||
| PFCOUNT key1 key2 | 获取一个或者多个 HyperLogLog 的唯一计数。 |
|
||||
| PFMERGE destkey sourcekey1 sourcekey2 ... | 将多个 HyperLogLog 合并到 destkey 中,destkey 会结合多个源,算出对应的唯一计数。 |
|
||||
|
||||
**HyperLogLog 基本操作演示** :
|
||||
@ -132,13 +132,13 @@ Geospatial index(地理空间索引,简称 GEO) 主要用于存储地理
|
||||
|
||||
### 常用命令
|
||||
|
||||
| 命令 | 介绍 |
|
||||
| ------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| GEOADD key longitude1 latitude1 member1 ... | 添加一个或多个元素对应的经纬度信息到 GEO 中 |
|
||||
| GEOPOS key member1 member2 ... | 返回给定元素的经纬度信息 |
|
||||
| GEODIST key member1 member2 M/KM/FT/MI | 返回两个给定元素之间的距离 |
|
||||
| 命令 | 介绍 |
|
||||
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
|
||||
| GEOADD key longitude1 latitude1 member1 ... | 添加一个或多个元素对应的经纬度信息到 GEO 中 |
|
||||
| GEOPOS key member1 member2 ... | 返回给定元素的经纬度信息 |
|
||||
| GEODIST key member1 member2 M/KM/FT/MI | 返回两个给定元素之间的距离 |
|
||||
| GEORADIUS key longitude latitude radius distance | 获取指定位置附近 distance 范围内的其他元素,支持 ASC(由近到远)、DESC(由远到近)、Count(数量) 等参数 |
|
||||
| GEORADIUSBYMEMBER key member radius distance | 类似于 GEORADIUS 命令,只是参照的中心点是 GEO 中的元素 |
|
||||
| GEORADIUSBYMEMBER key member radius distance | 类似于 GEORADIUS 命令,只是参照的中心点是 GEO 中的元素 |
|
||||
|
||||
**基本操作** :
|
||||
|
||||
|
@ -12,6 +12,8 @@ head:
|
||||
content: 一篇文章总结Redis常见的知识点和面试题,涵盖Redis基础、Redis常见数据结构、Redis线程模型、Redis内存管理、Redis事务、Redis性能优化等内容。
|
||||
---
|
||||
|
||||
<!-- @include: @small-advertisement.snippet.md -->
|
||||
|
||||
## Redis 基础
|
||||
|
||||
### 什么是 Redis?
|
||||
@ -247,13 +249,13 @@ struct __attribute__ ((__packed__)) sdshdr64 {
|
||||
|
||||
通过源码可以看出,SDS 共有五种实现方式 SDS_TYPE_5(并未用到)、SDS_TYPE_8、SDS_TYPE_16、SDS_TYPE_32、SDS_TYPE_64,其中只有后四种实际用到。Redis 会根据初始化的长度决定使用哪种类型,从而减少内存的使用。
|
||||
|
||||
| 类型 | 字节 | 位 |
|
||||
| -------- | ---- | ---- |
|
||||
| sdshdr5 | < 1 | <8 |
|
||||
| sdshdr8 | 1 | 8 |
|
||||
| sdshdr16 | 2 | 16 |
|
||||
| sdshdr32 | 4 | 32 |
|
||||
| sdshdr64 | 8 | 64 |
|
||||
| 类型 | 字节 | 位 |
|
||||
| -------- | ---- | --- |
|
||||
| sdshdr5 | < 1 | <8 |
|
||||
| sdshdr8 | 1 | 8 |
|
||||
| sdshdr16 | 2 | 16 |
|
||||
| sdshdr32 | 4 | 32 |
|
||||
| sdshdr64 | 8 | 64 |
|
||||
|
||||
对于后四种实现都包含了下面这 4 个属性:
|
||||
|
||||
@ -371,7 +373,7 @@ Bitmap 存储的是连续的二进制数字(0 和 1),通过 Bitmap, 只需
|
||||
|
||||
### 使用 HyperLogLog 统计页面 UV 怎么做?
|
||||
|
||||
使用 HyperLogLog 统计页面 UV主要需要用到下面这两个命令:
|
||||
使用 HyperLogLog 统计页面 UV 主要需要用到下面这两个命令:
|
||||
|
||||
- `PFADD key element1 element2 ...`:添加一个或多个元素到 HyperLogLog 中。
|
||||
- `PFCOUNT key1 key2`:获取一个或者多个 HyperLogLog 的唯一计数。
|
||||
@ -444,7 +446,7 @@ Redis 通过 **IO 多路复用程序** 来监听来自客户端的大量连接
|
||||
- Redis 的性能瓶颈不在 CPU ,主要在内存和网络;
|
||||
- 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
|
||||
|
||||
相关阅读:[为什么 Redis 选择单线程模型?](https://draveness.me/whys-the-design-redis-single-thread/) 。
|
||||
相关阅读:[为什么 Redis 选择单线程模型?](https://draveness.me/whys-the-design-redis-single-thread/) 。
|
||||
|
||||
### Redis6.0 之后为何引入了多线程?
|
||||
|
||||
@ -593,4 +595,3 @@ Redis 提供 6 种数据淘汰策略:
|
||||
- 《Redis 设计与实现》
|
||||
- Redis 命令手册:https://www.redis.com.cn/commands.html
|
||||
- WHY Redis choose single thread (vs multi threads): [https://medium.com/@jychen7/sharing-redis-single-thread-vs-multi-threads-5870bd44d153](https://medium.com/@jychen7/sharing-redis-single-thread-vs-multi-threads-5870bd44d153)
|
||||
|
||||
|
@ -617,15 +617,15 @@ ORDER BY order_date
|
||||
| `DATE()` | 返回日期时间的日期部分 |
|
||||
| `DATEDIFF` | 计算两个日期之差 |
|
||||
| `DATE_FORMAT()` | 返回一个格式化的日期或时间串 |
|
||||
| `DAY()` | 返回一个日期的天数部分 |
|
||||
| `DAYOFWEEK()` | 对于一个日期,返回对应的星期几 |
|
||||
| `HOUR()` | 返回一个时间的小时部分 |
|
||||
| `MINUTE()` | 返回一个时间的分钟部分 |
|
||||
| `MONTH()` | 返回一个日期的月份部分 |
|
||||
| `NOW()` | 返回当前日期和时间 |
|
||||
| `SECOND()` | 返回一个时间的秒部分 |
|
||||
| `TIME()` | 返回一个日期时间的时间部分 |
|
||||
| `YEAR()` | 返回一个日期的年份部分 |
|
||||
| `DAY()` | 返回一个日期的天数部分 |
|
||||
| `DAYOFWEEK()` | 对于一个日期,返回对应的星期几 |
|
||||
| `HOUR()` | 返回一个时间的小时部分 |
|
||||
| `MINUTE()` | 返回一个时间的分钟部分 |
|
||||
| `MONTH()` | 返回一个日期的月份部分 |
|
||||
| `NOW()` | 返回当前日期和时间 |
|
||||
| `SECOND()` | 返回一个时间的秒部分 |
|
||||
| `TIME()` | 返回一个日期时间的时间部分 |
|
||||
| `YEAR()` | 返回一个日期的年份部分 |
|
||||
|
||||
## 汇总数据
|
||||
|
||||
@ -1203,14 +1203,14 @@ ORDER BY c.cust_name
|
||||
|
||||
SQL 允许在 `JOIN` 左边加上一些修饰性的关键词,从而形成不同类型的连接,如下表所示:
|
||||
|
||||
| 连接类型 | 说明 |
|
||||
| ---------------------------------------- | ------------------------------------------------------------ |
|
||||
| INNER JOIN 内连接 | (默认连接方式)只有当两个表都存在满足条件的记录时才会返回行。 |
|
||||
| LEFT JOIN / LEFT OUTER JOIN 左(外)连接 | 返回左表中的所有行,即使右表中没有满足条件的行也是如此。 |
|
||||
| RIGHT JOIN / RIGHT OUTER JOIN 右(外)连接 | 返回右表中的所有行,即使左表中没有满足条件的行也是如此。 |
|
||||
| FULL JOIN / FULL OUTER JOIN 全(外)连接 | 只要其中有一个表存在满足条件的记录,就返回行。 |
|
||||
| 连接类型 | 说明 |
|
||||
| ---------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| INNER JOIN 内连接 | (默认连接方式)只有当两个表都存在满足条件的记录时才会返回行。 |
|
||||
| LEFT JOIN / LEFT OUTER JOIN 左(外)连接 | 返回左表中的所有行,即使右表中没有满足条件的行也是如此。 |
|
||||
| RIGHT JOIN / RIGHT OUTER JOIN 右(外)连接 | 返回右表中的所有行,即使左表中没有满足条件的行也是如此。 |
|
||||
| FULL JOIN / FULL OUTER JOIN 全(外)连接 | 只要其中有一个表存在满足条件的记录,就返回行。 |
|
||||
| SELF JOIN | 将一个表连接到自身,就像该表是两个表一样。为了区分两个表,在 SQL 语句中需要至少重命名一个表。 |
|
||||
| CROSS JOIN | 交叉连接,从两个或者多个连接表中返回记录集的笛卡尔积。 |
|
||||
| CROSS JOIN | 交叉连接,从两个或者多个连接表中返回记录集的笛卡尔积。 |
|
||||
|
||||
下图展示了 LEFT JOIN、RIGHT JOIN、INNER JOIN、OUTER JOIN 相关的 7 种用法。
|
||||
|
||||
|
@ -489,14 +489,14 @@ order by c.cust_name;
|
||||
|
||||
SQL 允许在 `JOIN` 左边加上一些修饰性的关键词,从而形成不同类型的连接,如下表所示:
|
||||
|
||||
| 连接类型 | 说明 |
|
||||
| ---------------------------------------- | ------------------------------------------------------------ |
|
||||
| INNER JOIN 内连接 | (默认连接方式)只有当两个表都存在满足条件的记录时才会返回行。 |
|
||||
| LEFT JOIN / LEFT OUTER JOIN 左(外)连接 | 返回左表中的所有行,即使右表中没有满足条件的行也是如此。 |
|
||||
| RIGHT JOIN / RIGHT OUTER JOIN 右(外)连接 | 返回右表中的所有行,即使左表中没有满足条件的行也是如此。 |
|
||||
| FULL JOIN / FULL OUTER JOIN 全(外)连接 | 只要其中有一个表存在满足条件的记录,就返回行。 |
|
||||
| 连接类型 | 说明 |
|
||||
| ---------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| INNER JOIN 内连接 | (默认连接方式)只有当两个表都存在满足条件的记录时才会返回行。 |
|
||||
| LEFT JOIN / LEFT OUTER JOIN 左(外)连接 | 返回左表中的所有行,即使右表中没有满足条件的行也是如此。 |
|
||||
| RIGHT JOIN / RIGHT OUTER JOIN 右(外)连接 | 返回右表中的所有行,即使左表中没有满足条件的行也是如此。 |
|
||||
| FULL JOIN / FULL OUTER JOIN 全(外)连接 | 只要其中有一个表存在满足条件的记录,就返回行。 |
|
||||
| SELF JOIN | 将一个表连接到自身,就像该表是两个表一样。为了区分两个表,在 SQL 语句中需要至少重命名一个表。 |
|
||||
| CROSS JOIN | 交叉连接,从两个或者多个连接表中返回记录集的笛卡尔积。 |
|
||||
| CROSS JOIN | 交叉连接,从两个或者多个连接表中返回记录集的笛卡尔积。 |
|
||||
|
||||
下图展示了 LEFT JOIN、RIGHT JOIN、INNER JOIN、OUTER JOIN 相关的 7 种用法。
|
||||
|
||||
@ -555,7 +555,7 @@ SELECT column_name(s) FROM table2;
|
||||
| -------------------- | ---------------------- |
|
||||
| `LEFT()`、`RIGHT()` | 左边或者右边的字符 |
|
||||
| `LOWER()`、`UPPER()` | 转换为小写或者大写 |
|
||||
| `LTRIM()`、`RTRIM()` | 去除左边或者右边的空格 |
|
||||
| `LTRIM()`、`RTRIM()` | 去除左边或者右边的空格 |
|
||||
| `LENGTH()` | 长度 |
|
||||
| `SOUNDEX()` | 转换为语音值 |
|
||||
|
||||
@ -918,41 +918,41 @@ SELECT user FROM user;
|
||||
|
||||
下表说明了可用于`GRANT`和`REVOKE`语句的所有允许权限:
|
||||
|
||||
| **特权** | **说明** | **级别** | | | | | |
|
||||
| ----------------------- | ------------------------------------------------------------ | -------- | ------ | -------- | -------- | ---- | ---- |
|
||||
| **全局** | 数据库 | **表** | **列** | **程序** | **代理** | | |
|
||||
| ALL [PRIVILEGES] | 授予除 GRANT OPTION 之外的指定访问级别的所有权限 | | | | | | |
|
||||
| ALTER | 允许用户使用 ALTER TABLE 语句 | X | X | X | | | |
|
||||
| ALTER ROUTINE | 允许用户更改或删除存储的例程 | X | X | | | X | |
|
||||
| CREATE | 允许用户创建数据库和表 | X | X | X | | | |
|
||||
| CREATE ROUTINE | 允许用户创建存储的例程 | X | X | | | | |
|
||||
| CREATE TABLESPACE | 允许用户创建,更改或删除表空间和日志文件组 | X | | | | | |
|
||||
| CREATE TEMPORARY TABLES | 允许用户使用 CREATE TEMPORARY TABLE 创建临时表 | X | X | | | | |
|
||||
| CREATE USER | 允许用户使用 CREATE USER,DROP USER,RENAME USER 和 REVOKE ALL PRIVILEGES 语句。 | X | | | | | |
|
||||
| CREATE VIEW | 允许用户创建或修改视图。 | X | X | X | | | |
|
||||
| DELETE | 允许用户使用 DELETE | X | X | X | | | |
|
||||
| DROP | 允许用户删除数据库,表和视图 | X | X | X | | | |
|
||||
| EVENT | 启用事件计划程序的事件使用。 | X | X | | | | |
|
||||
| EXECUTE | 允许用户执行存储的例程 | X | X | X | | | |
|
||||
| FILE | 允许用户读取数据库目录中的任何文件。 | X | | | | | |
|
||||
| GRANT OPTION | 允许用户拥有授予或撤消其他帐户权限的权限。 | X | X | X | | X | X |
|
||||
| INDEX | 允许用户创建或删除索引。 | X | X | X | | | |
|
||||
| INSERT | 允许用户使用 INSERT 语句 | X | X | X | X | | |
|
||||
| LOCK TABLES | 允许用户对具有 SELECT 权限的表使用 LOCK TABLES | X | X | | | | |
|
||||
| PROCESS | 允许用户使用 SHOW PROCESSLIST 语句查看所有进程。 | X | | | | | |
|
||||
| PROXY | 启用用户代理。 | | | | | | |
|
||||
| REFERENCES | 允许用户创建外键 | X | X | X | X | | |
|
||||
| RELOAD | 允许用户使用 FLUSH 操作 | X | | | | | |
|
||||
| REPLICATION CLIENT | 允许用户查询以查看主服务器或从属服务器的位置 | X | | | | | |
|
||||
| REPLICATION SLAVE | 允许用户使用复制从属从主服务器读取二进制日志事件。 | X | | | | | |
|
||||
| SELECT | 允许用户使用 SELECT 语句 | X | X | X | X | | |
|
||||
| SHOW DATABASES | 允许用户显示所有数据库 | X | | | | | |
|
||||
| SHOW VIEW | 允许用户使用 SHOW CREATE VIEW 语句 | X | X | X | | | |
|
||||
| SHUTDOWN | 允许用户使用 mysqladmin shutdown 命令 | X | | | | | |
|
||||
| SUPER | 允许用户使用其他管理操作,例如 CHANGE MASTER TO,KILL,PURGE BINARY LOGS,SET GLOBAL 和 mysqladmin 命令 | X | | | | | |
|
||||
| TRIGGER | 允许用户使用 TRIGGER 操作。 | X | X | X | | | |
|
||||
| UPDATE | 允许用户使用 UPDATE 语句 | X | X | X | X | | |
|
||||
| USAGE | 相当于“没有特权” | | | | | | |
|
||||
| **特权** | **说明** | **级别** | | | | | |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------------------------- | -------- | ------ | -------- | -------- | --- | --- |
|
||||
| **全局** | 数据库 | **表** | **列** | **程序** | **代理** | | |
|
||||
| ALL [PRIVILEGES] | 授予除 GRANT OPTION 之外的指定访问级别的所有权限 | | | | | | |
|
||||
| ALTER | 允许用户使用 ALTER TABLE 语句 | X | X | X | | | |
|
||||
| ALTER ROUTINE | 允许用户更改或删除存储的例程 | X | X | | | X | |
|
||||
| CREATE | 允许用户创建数据库和表 | X | X | X | | | |
|
||||
| CREATE ROUTINE | 允许用户创建存储的例程 | X | X | | | | |
|
||||
| CREATE TABLESPACE | 允许用户创建,更改或删除表空间和日志文件组 | X | | | | | |
|
||||
| CREATE TEMPORARY TABLES | 允许用户使用 CREATE TEMPORARY TABLE 创建临时表 | X | X | | | | |
|
||||
| CREATE USER | 允许用户使用 CREATE USER,DROP USER,RENAME USER 和 REVOKE ALL PRIVILEGES 语句。 | X | | | | | |
|
||||
| CREATE VIEW | 允许用户创建或修改视图。 | X | X | X | | | |
|
||||
| DELETE | 允许用户使用 DELETE | X | X | X | | | |
|
||||
| DROP | 允许用户删除数据库,表和视图 | X | X | X | | | |
|
||||
| EVENT | 启用事件计划程序的事件使用。 | X | X | | | | |
|
||||
| EXECUTE | 允许用户执行存储的例程 | X | X | X | | | |
|
||||
| FILE | 允许用户读取数据库目录中的任何文件。 | X | | | | | |
|
||||
| GRANT OPTION | 允许用户拥有授予或撤消其他帐户权限的权限。 | X | X | X | | X | X |
|
||||
| INDEX | 允许用户创建或删除索引。 | X | X | X | | | |
|
||||
| INSERT | 允许用户使用 INSERT 语句 | X | X | X | X | | |
|
||||
| LOCK TABLES | 允许用户对具有 SELECT 权限的表使用 LOCK TABLES | X | X | | | | |
|
||||
| PROCESS | 允许用户使用 SHOW PROCESSLIST 语句查看所有进程。 | X | | | | | |
|
||||
| PROXY | 启用用户代理。 | | | | | | |
|
||||
| REFERENCES | 允许用户创建外键 | X | X | X | X | | |
|
||||
| RELOAD | 允许用户使用 FLUSH 操作 | X | | | | | |
|
||||
| REPLICATION CLIENT | 允许用户查询以查看主服务器或从属服务器的位置 | X | | | | | |
|
||||
| REPLICATION SLAVE | 允许用户使用复制从属从主服务器读取二进制日志事件。 | X | | | | | |
|
||||
| SELECT | 允许用户使用 SELECT 语句 | X | X | X | X | | |
|
||||
| SHOW DATABASES | 允许用户显示所有数据库 | X | | | | | |
|
||||
| SHOW VIEW | 允许用户使用 SHOW CREATE VIEW 语句 | X | X | X | | | |
|
||||
| SHUTDOWN | 允许用户使用 mysqladmin shutdown 命令 | X | | | | | |
|
||||
| SUPER | 允许用户使用其他管理操作,例如 CHANGE MASTER TO,KILL,PURGE BINARY LOGS,SET GLOBAL 和 mysqladmin 命令 | X | | | | | |
|
||||
| TRIGGER | 允许用户使用 TRIGGER 操作。 | X | X | X | | | |
|
||||
| UPDATE | 允许用户使用 UPDATE 语句 | X | X | X | X | | |
|
||||
| USAGE | 相当于“没有特权” | | | | | | |
|
||||
|
||||
### 创建账户
|
||||
|
||||
@ -1206,5 +1206,5 @@ DROP TRIGGER IF EXISTS trigger_insert_user;
|
||||
|
||||
## 文章推荐
|
||||
|
||||
- [后端程序员必备:SQL高性能优化指南!35+条优化建议立马GET!](https://mp.weixin.qq.com/s/I-ZT3zGTNBZ6egS7T09jyQ)
|
||||
- [后端程序员必备:书写高质量SQL的30条建议](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486461&idx=1&sn=60a22279196d084cc398936fe3b37772&chksm=cea24436f9d5cd20a4fa0e907590f3e700d7378b3f608d7b33bb52cfb96f503b7ccb65a1deed&token=1987003517&lang=zh_CN#rd)
|
||||
- [后端程序员必备:SQL 高性能优化指南!35+条优化建议立马 GET!](https://mp.weixin.qq.com/s/I-ZT3zGTNBZ6egS7T09jyQ)
|
||||
- [后端程序员必备:书写高质量 SQL 的 30 条建议](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486461&idx=1&sn=60a22279196d084cc398936fe3b37772&chksm=cea24436f9d5cd20a4fa0e907590f3e700d7378b3f608d7b33bb52cfb96f503b7ccb65a1deed&token=1987003517&lang=zh_CN#rd)
|
||||
|
@ -75,12 +75,12 @@ Zuul 主要通过过滤器(类似于 AOP)来过滤请求,从而实现网
|
||||
|
||||

|
||||
|
||||
- Github 地址 : https://github.com/Netflix/zuul
|
||||
- 官方 Wiki : https://github.com/Netflix/zuul/wiki
|
||||
- Github 地址 : <https://github.com/Netflix/zuul>
|
||||
- 官方 Wiki : <https://github.com/Netflix/zuul/wiki>
|
||||
|
||||
### Spring Cloud Gateway
|
||||
|
||||
SpringCloud Gateway 属于 Spring Cloud 生态系统中的网关,其诞生的目标是为了替代老牌网关 **Zuul **。准确点来说,应该是 Zuul 1.x。SpringCloud Gateway 起步要比 Zuul 2.x 更早。
|
||||
SpringCloud Gateway 属于 Spring Cloud 生态系统中的网关,其诞生的目标是为了替代老牌网关 **Zuul**。准确点来说,应该是 Zuul 1.x。SpringCloud Gateway 起步要比 Zuul 2.x 更早。
|
||||
|
||||
为了提升网关的性能,SpringCloud Gateway 基于 Spring WebFlux 。Spring WebFlux 使用 Reactor 库来实现响应式编程模型,底层基于 Netty 实现同步非阻塞的 I/O。
|
||||
|
||||
@ -90,8 +90,8 @@ Spring Cloud Gateway 不仅提供统一的路由方式,并且基于 Filter 链
|
||||
|
||||
Spring Cloud Gateway 和 Zuul 2.x 的差别不大,也是通过过滤器来处理请求。不过,目前更加推荐使用 Spring Cloud Gateway 而非 Zuul,Spring Cloud 生态对其支持更加友好。
|
||||
|
||||
- Github 地址 : https://github.com/spring-cloud/spring-cloud-gateway
|
||||
- 官网 : https://spring.io/projects/spring-cloud-gateway
|
||||
- Github 地址 : <https://github.com/spring-cloud/spring-cloud-gateway>
|
||||
- 官网 : <https://spring.io/projects/spring-cloud-gateway>
|
||||
|
||||
### Kong
|
||||
|
||||
@ -118,8 +118,8 @@ $ curl -X POST http://kong:8001/services/{service}/plugins \
|
||||
|
||||

|
||||
|
||||
- Github 地址: https://github.com/Kong/kong
|
||||
- 官网地址 : https://konghq.com/kong
|
||||
- Github 地址: <https://github.com/Kong/kong>
|
||||
- 官网地址 : <https://konghq.com/kong>
|
||||
|
||||
### APISIX
|
||||
|
||||
@ -144,8 +144,8 @@ APISIX 同样支持定制化的插件开发。开发者除了能够使用 Lua
|
||||
|
||||

|
||||
|
||||
- Github 地址 :https://github.com/apache/apisix
|
||||
- 官网地址: https://apisix.apache.org/zh/
|
||||
- Github 地址 :<https://github.com/apache/apisix>
|
||||
- 官网地址: <https://apisix.apache.org/zh/>
|
||||
|
||||
相关阅读:
|
||||
|
||||
@ -162,11 +162,11 @@ Shenyu 是一款基于 WebFlux 的可扩展、高性能、响应式网关,Apac
|
||||
|
||||
Shenyu 通过插件扩展功能,插件是 ShenYu 的灵魂,并且插件也是可扩展和热插拔的。不同的插件实现不同的功能。Shenyu 自带了诸如限流、熔断、转发 、重写、重定向、和路由监控等插件。
|
||||
|
||||
- Github 地址: https://github.com/apache/incubator-shenyu
|
||||
- 官网地址 : https://shenyu.apache.org/
|
||||
- Github 地址: <https://github.com/apache/incubator-shenyu>
|
||||
- 官网地址 : <https://shenyu.apache.org/>
|
||||
|
||||
## 参考
|
||||
|
||||
- Kong 插件开发教程[通俗易懂]:https://cloud.tencent.com/developer/article/2104299
|
||||
- API 网关 Kong 实战:https://xie.infoq.cn/article/10e4dab2de0bdb6f2c3c93da6
|
||||
- Spring Cloud Gateway 原理介绍和应用:https://blog.fintopia.tech/60e27b0e2078082a378ec5ed/
|
||||
- Kong 插件开发教程[通俗易懂]:<https://cloud.tencent.com/developer/article/2104299>
|
||||
- API 网关 Kong 实战:<https://xie.infoq.cn/article/10e4dab2de0bdb6f2c3c93da6>
|
||||
- Spring Cloud Gateway 原理介绍和应用:<https://blog.fintopia.tech/60e27b0e2078082a378ec5ed/>
|
||||
|
@ -8,49 +8,4 @@ icon: "configuration"
|
||||
|
||||

|
||||
|
||||
[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)(点击链接即可查看详细介绍)的部分内容展示如下,你可以将其看作是 [JavaGuide](https://javaguide.cn/#/) 的补充完善,两者可以配合使用。
|
||||
|
||||

|
||||
|
||||
最近几年,市面上有越来越多的“技术大佬”开始办培训班/训练营,动辄成千上万的学费,却并没有什么干货,单纯的就是割韭菜。
|
||||
|
||||
为了帮助更多同学准备 Java 面试以及学习 Java ,我创建了一个纯粹的知识星球。虽然收费只有培训班/训练营的百分之一,但是知识星球里的内容质量更高,提供的服务也更全面。
|
||||
|
||||
欢迎准备 Java 面试以及学习 Java 的同学加入我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html),干货非常多,学习氛围也很不错!收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/image-20220311203414600.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
下面是星球提供的部分服务(点击下方图片即可获取知识星球的详细介绍):
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiufuwu.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!**
|
||||
|
||||
如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍: [JavaGuide 知识星球详细介绍](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。
|
||||
|
||||
这里再送一个 30 元的新人优惠券(续费半价)。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuanheyi.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
进入星球之后,记得添加微信,我会发你详细的星球使用指南。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/IMG_3007.jpg" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
<!-- @include: @planet.snippet.md -->
|
||||
|
@ -84,9 +84,9 @@ COMMIT;
|
||||
|
||||
插入数据这里,我们没有使用 `insert into` 而是使用 `replace into` 来插入数据,具体步骤是这样的:
|
||||
|
||||
1)第一步: 尝试把数据插入到表中。
|
||||
- 第一步: 尝试把数据插入到表中。
|
||||
|
||||
2)第二步: 如果主键或唯一索引字段出现重复数据错误而插入失败时,先从表中删除含有重复关键字值的冲突行,然后再次尝试把数据插入到表中。
|
||||
- 第二步: 如果主键或唯一索引字段出现重复数据错误而插入失败时,先从表中删除含有重复关键字值的冲突行,然后再次尝试把数据插入到表中。
|
||||
|
||||
这种方式的优缺点也比较明显:
|
||||
|
||||
@ -103,7 +103,7 @@ COMMIT;
|
||||
|
||||
以 MySQL 举例,我们通过下面的方式即可。
|
||||
|
||||
**1.创建一个数据库表。**
|
||||
**1. 创建一个数据库表。**
|
||||
|
||||
```sql
|
||||
CREATE TABLE `sequence_id_generator` (
|
||||
@ -122,15 +122,15 @@ CREATE TABLE `sequence_id_generator` (
|
||||
|
||||
`version` 字段主要用于解决并发问题(乐观锁),`biz_type` 主要用于表示业务类型。
|
||||
|
||||
**2.先插入一行数据。**
|
||||
**2. 先插入一行数据。**
|
||||
|
||||
```sql
|
||||
INSERT INTO `sequence_id_generator` (`id`, `current_max_id`, `step`, `version`, `biz_type`)
|
||||
VALUES
|
||||
(1, 0, 100, 0, 101);
|
||||
(1, 0, 100, 0, 101);
|
||||
```
|
||||
|
||||
**3.通过 SELECT 获取指定业务下的批量唯一 ID**
|
||||
**3. 通过 SELECT 获取指定业务下的批量唯一 ID**
|
||||
|
||||
```sql
|
||||
SELECT `current_max_id`, `step`,`version` FROM `sequence_id_generator` where `biz_type` = 101
|
||||
@ -139,11 +139,11 @@ SELECT `current_max_id`, `step`,`version` FROM `sequence_id_generator` where `bi
|
||||
结果:
|
||||
|
||||
```
|
||||
id current_max_id step version biz_type
|
||||
1 0 100 0 101
|
||||
id current_max_id step version biz_type
|
||||
1 0 100 0 101
|
||||
```
|
||||
|
||||
**4.不够用的话,更新之后重新 SELECT 即可。**
|
||||
**4. 不够用的话,更新之后重新 SELECT 即可。**
|
||||
|
||||
```sql
|
||||
UPDATE sequence_id_generator SET current_max_id = 0+100, version=version+1 WHERE version = 0 AND `biz_type` = 101
|
||||
@ -153,8 +153,8 @@ SELECT `current_max_id`, `step`,`version` FROM `sequence_id_generator` where `bi
|
||||
结果:
|
||||
|
||||
```
|
||||
id current_max_id step version biz_type
|
||||
1 100 100 1 101
|
||||
id current_max_id step version biz_type
|
||||
1 100 100 1 101
|
||||
```
|
||||
|
||||
相比于数据库主键自增的方式,**数据库的号段模式对于数据库的访问次数更少,数据库压力更小。**
|
||||
|
@ -95,7 +95,7 @@ OK
|
||||
|
||||
### 如何实现锁的优雅续期?
|
||||
|
||||
对于 Java 开发的小伙伴来说,已经有了现成的解决方案:**[Redisson](https://github.com/redisson/redisson)** 。其他语言的解决方案,可以在 Redis 官方文档中找到,地址:https://redis.io/topics/distlock 。
|
||||
对于 Java 开发的小伙伴来说,已经有了现成的解决方案:**[Redisson](https://github.com/redisson/redisson)** 。其他语言的解决方案,可以在 Redis 官方文档中找到,地址:<https://redis.io/topics/distlock> 。
|
||||
|
||||

|
||||
|
||||
@ -116,7 +116,7 @@ public Config setLockWatchdogTimeout(long lockWatchdogTimeout) {
|
||||
return this;
|
||||
}
|
||||
public long getLockWatchdogTimeout() {
|
||||
return lockWatchdogTimeout;
|
||||
return lockWatchdogTimeout;
|
||||
}
|
||||
```
|
||||
|
||||
@ -276,14 +276,14 @@ InterProcessLock lock2 = new InterProcessSemaphoreMutex(client, lockPath2);
|
||||
InterProcessMultiLock lock = new InterProcessMultiLock(Arrays.asList(lock1, lock2));
|
||||
|
||||
if (!lock.acquire(10, TimeUnit.SECONDS)) {
|
||||
throw new IllegalStateException("不能获取多锁");
|
||||
throw new IllegalStateException("不能获取多锁");
|
||||
}
|
||||
System.out.println("已获取多锁");
|
||||
System.out.println("是否有第一个锁: " + lock1.isAcquiredInThisProcess());
|
||||
System.out.println("是否有第二个锁: " + lock2.isAcquiredInThisProcess());
|
||||
try {
|
||||
// 资源操作
|
||||
resource.use();
|
||||
resource.use();
|
||||
} finally {
|
||||
System.out.println("释放多个锁");
|
||||
lock.release();
|
||||
@ -394,6 +394,3 @@ private static class LockData
|
||||
## 总结
|
||||
|
||||
这篇文章我们介绍了分布式锁的基本概念以及实现分布式锁的两种常见方式。至于具体选择 Redis 还是 ZooKeeper 来实现分布式锁,还是要看业务的具体需求。如果对性能要求比较高的话,建议使用 Redis 实现分布式锁。如果对可靠性要求比较高的话,建议使用 ZooKeeper 实现分布式锁。
|
||||
|
||||
|
||||
|
||||
|
@ -5,13 +5,13 @@ tag:
|
||||
- ZooKeeper
|
||||
---
|
||||
|
||||
这篇文章简单给演示一下 ZooKeeper 常见命令的使用以及 ZooKeeper Java客户端 Curator 的基本使用。介绍到的内容都是最基本的操作,能满足日常工作的基本需要。
|
||||
这篇文章简单给演示一下 ZooKeeper 常见命令的使用以及 ZooKeeper Java 客户端 Curator 的基本使用。介绍到的内容都是最基本的操作,能满足日常工作的基本需要。
|
||||
|
||||
如果文章有任何需要改善和完善的地方,欢迎在评论区指出,共同进步!
|
||||
|
||||
## ZooKeeper 安装
|
||||
|
||||
### 使用Docker 安装 zookeeper
|
||||
### 使用 Docker 安装 zookeeper
|
||||
|
||||
**a.使用 Docker 下载 ZooKeeper**
|
||||
|
||||
@ -27,11 +27,11 @@ docker run -d --name zookeeper -p 2181:2181 zookeeper:3.5.8
|
||||
|
||||
### 连接 ZooKeeper 服务
|
||||
|
||||
**a.进入ZooKeeper容器中**
|
||||
**a.进入 ZooKeeper 容器中**
|
||||
|
||||
先使用 `docker ps` 查看 ZooKeeper 的 ContainerID,然后使用 `docker exec -it ContainerID /bin/bash` 命令进入容器中。
|
||||
|
||||
**b.先进入 bin 目录,然后通过 `./zkCli.sh -server 127.0.0.1:2181`命令连接ZooKeeper 服务**
|
||||
**b.先进入 bin 目录,然后通过 `./zkCli.sh -server 127.0.0.1:2181`命令连接 ZooKeeper 服务**
|
||||
|
||||
```bash
|
||||
root@eaf70fc620cb:/apache-zookeeper-3.5.8-bin# cd bin
|
||||
@ -130,7 +130,7 @@ numChildren = 1
|
||||
|
||||
### 查看节点信息和状态(ls2 命令)
|
||||
|
||||
`ls2` 命令更像是 `ls` 命令和 `stat` 命令的结合。 `ls2` 命令返回的信息包括 2 部分:
|
||||
`ls2` 命令更像是 `ls` 命令和 `stat` 命令的结合。 `ls2` 命令返回的信息包括 2 部分:
|
||||
|
||||
1. 子节点列表
|
||||
2. 当前节点的 stat 信息。
|
||||
@ -162,15 +162,15 @@ numChildren = 1
|
||||
|
||||
在后面我会介绍到 Java 客户端 API 的使用以及开源 ZooKeeper 客户端 ZkClient 和 Curator 的使用。
|
||||
|
||||
## ZooKeeper Java客户端 Curator简单使用
|
||||
## ZooKeeper Java 客户端 Curator 简单使用
|
||||
|
||||
Curator 是Netflix公司开源的一套 ZooKeeper Java客户端框架,相比于 Zookeeper 自带的客户端 zookeeper 来说,Curator 的封装更加完善,各种 API 都可以比较方便地使用。
|
||||
Curator 是 Netflix 公司开源的一套 ZooKeeper Java 客户端框架,相比于 Zookeeper 自带的客户端 zookeeper 来说,Curator 的封装更加完善,各种 API 都可以比较方便地使用。
|
||||
|
||||

|
||||
|
||||
下面我们就来简单地演示一下 Curator 的使用吧!
|
||||
|
||||
Curator4.0+版本对ZooKeeper 3.5.x支持比较好。开始之前,请先将下面的依赖添加进你的项目。
|
||||
Curator4.0+版本对 ZooKeeper 3.5.x 支持比较好。开始之前,请先将下面的依赖添加进你的项目。
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
@ -187,7 +187,7 @@ Curator4.0+版本对ZooKeeper 3.5.x支持比较好。开始之前,请先将下
|
||||
|
||||
### 连接 ZooKeeper 客户端
|
||||
|
||||
通过 `CuratorFrameworkFactory` 创建 `CuratorFramework` 对象,然后再调用 `CuratorFramework` 对象的 `start()` 方法即可!
|
||||
通过 `CuratorFrameworkFactory` 创建 `CuratorFramework` 对象,然后再调用 `CuratorFramework` 对象的 `start()` 方法即可!
|
||||
|
||||
```java
|
||||
private static final int BASE_SLEEP_TIME = 1000;
|
||||
@ -214,14 +214,14 @@ zkClient.start();
|
||||
|
||||
#### 创建节点
|
||||
|
||||
我们在 [ZooKeeper常见概念解读](./zookeeper-intro.md) 中介绍到,我们通常是将 znode 分为 4 大类:
|
||||
我们在 [ZooKeeper 常见概念解读](./zookeeper-intro.md) 中介绍到,我们通常是将 znode 分为 4 大类:
|
||||
|
||||
- **持久(PERSISTENT)节点** :一旦创建就一直存在即使 ZooKeeper 集群宕机,直到将其删除。
|
||||
- **临时(EPHEMERAL)节点** :临时节点的生命周期是与 **客户端会话(session)** 绑定的,**会话消失则节点消失** 。并且,临时节点 **只能做叶子节点** ,不能创建子节点。
|
||||
- **持久顺序(PERSISTENT_SEQUENTIAL)节点** :除了具有持久(PERSISTENT)节点的特性之外, 子节点的名称还具有顺序性。比如 `/node1/app0000000001` 、`/node1/app0000000002` 。
|
||||
- **临时顺序(EPHEMERAL_SEQUENTIAL)节点** :除了具备临时(EPHEMERAL)节点的特性之外,子节点的名称还具有顺序性。
|
||||
|
||||
你在使用的ZooKeeper 的时候,会发现 `CreateMode` 类中实际有 7种 znode 类型 ,但是用的最多的还是上面介绍的 4 种。
|
||||
你在使用的 ZooKeeper 的时候,会发现 `CreateMode` 类中实际有 7 种 znode 类型 ,但是用的最多的还是上面介绍的 4 种。
|
||||
|
||||
**a.创建持久化节点**
|
||||
|
||||
@ -293,8 +293,3 @@ zkClient.setData().forPath("/node1/00001","c++".getBytes());//更新节点数据
|
||||
```java
|
||||
List<String> childrenPaths = zkClient.getChildren().forPath("/node1");
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -124,19 +124,19 @@ Stat 类中包含了一个数据节点的所有状态信息的字段,包括事
|
||||
|
||||
下面我们来看一下每个 znode 状态信息究竟代表的是什么吧!(下面的内容来源于《从 Paxos 到 ZooKeeper 分布式一致性原理与实践》,因为 Guide 确实也不是特别清楚,要学会参考资料的嘛! ) :
|
||||
|
||||
| znode 状态信息 | 解释 |
|
||||
| -------------- | ------------------------------------------------------------ |
|
||||
| cZxid | create ZXID,即该数据节点被创建时的事务 id |
|
||||
| ctime | create time,即该节点的创建时间 |
|
||||
| mZxid | modified ZXID,即该节点最终一次更新时的事务 id |
|
||||
| mtime | modified time,即该节点最后一次的更新时间 |
|
||||
| znode 状态信息 | 解释 |
|
||||
| -------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| cZxid | create ZXID,即该数据节点被创建时的事务 id |
|
||||
| ctime | create time,即该节点的创建时间 |
|
||||
| mZxid | modified ZXID,即该节点最终一次更新时的事务 id |
|
||||
| mtime | modified time,即该节点最后一次的更新时间 |
|
||||
| pZxid | 该节点的子节点列表最后一次修改时的事务 id,只有子节点列表变更才会更新 pZxid,子节点内容变更不会更新 |
|
||||
| cversion | 子节点版本号,当前节点的子节点每次变化时值增加 1 |
|
||||
| dataVersion | 数据节点内容版本号,节点创建时为 0,每更新一次节点内容(不管内容有无变化)该版本号的值增加 1 |
|
||||
| aclVersion | 节点的 ACL 版本号,表示该节点 ACL 信息变更次数 |
|
||||
| ephemeralOwner | 创建该临时节点的会话的 sessionId;如果当前节点为持久节点,则 ephemeralOwner=0 |
|
||||
| dataLength | 数据节点内容长度 |
|
||||
| numChildren | 当前节点的子节点个数 |
|
||||
| cversion | 子节点版本号,当前节点的子节点每次变化时值增加 1 |
|
||||
| dataVersion | 数据节点内容版本号,节点创建时为 0,每更新一次节点内容(不管内容有无变化)该版本号的值增加 1 |
|
||||
| aclVersion | 节点的 ACL 版本号,表示该节点 ACL 信息变更次数 |
|
||||
| ephemeralOwner | 创建该临时节点的会话的 sessionId;如果当前节点为持久节点,则 ephemeralOwner=0 |
|
||||
| dataLength | 数据节点内容长度 |
|
||||
| numChildren | 当前节点的子节点个数 |
|
||||
|
||||
### 版本(version)
|
||||
|
||||
@ -201,10 +201,10 @@ Session 有一个属性叫做:`sessionTimeout` ,`sessionTimeout` 代表会
|
||||
|
||||
ZooKeeper 集群中的所有机器通过一个 **Leader 选举过程** 来选定一台称为 “**Leader**” 的机器,Leader 既可以为客户端提供写服务又能提供读服务。除了 Leader 外,**Follower** 和 **Observer** 都只能提供读服务。Follower 和 Observer 唯一的区别在于 Observer 机器不参与 Leader 的选举过程,也不参与写操作的“过半写成功”策略,因此 Observer 机器可以在不影响写性能的情况下提升集群的读性能。
|
||||
|
||||
| 角色 | 说明 |
|
||||
| -------- | ------------------------------------------------------------ |
|
||||
| Leader | 为客户端提供读和写的服务,负责投票的发起和决议,更新系统状态。 |
|
||||
| Follower | 为客户端提供读服务,如果是写服务则转发给 Leader。参与选举过程中的投票。 |
|
||||
| 角色 | 说明 |
|
||||
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Leader | 为客户端提供读和写的服务,负责投票的发起和决议,更新系统状态。 |
|
||||
| Follower | 为客户端提供读服务,如果是写服务则转发给 Leader。参与选举过程中的投票。 |
|
||||
| Observer | 为客户端提供读服务,如果是写服务则转发给 Leader。不参与选举过程中的投票,也不参与“过半写成功”策略。在不影响写性能的情况下提升集群的读性能。此角色于 ZooKeeper3.3 系列新增的角色。 |
|
||||
|
||||
### ZooKeeper 集群 Leader 选举过程
|
||||
@ -263,9 +263,9 @@ ZAB 协议包括两种基本的模式,分别是
|
||||
|
||||
关于 **ZAB 协议&Paxos 算法** 需要讲和理解的东西太多了,具体可以看下面这几篇文章:
|
||||
|
||||
- [Paxos 算法详解](https://javaguide.cn/distributed-system/theorem&algorithm&protocol/paxos-algorithm.html)
|
||||
- [Paxos 算法详解](https://javaguide.cn/distributed-system/protocol/paxos-algorithm.html)
|
||||
- [Zookeeper ZAB 协议分析](https://dbaplus.cn/news-141-1875-1.html)
|
||||
- [Raft 算法详解](https://javaguide.cn/distributed-system/theorem&algorithm&protocol/raft-algorithm.html)
|
||||
- [Raft 算法详解](https://javaguide.cn/distributed-system/protocol/raft-algorithm.html)
|
||||
|
||||
## 总结
|
||||
|
||||
|
@ -7,7 +7,7 @@ tag:
|
||||
|
||||
> [FrancisQ](https://juejin.im/user/5c33853851882525ea106810) 投稿。
|
||||
|
||||
## 什么是ZooKeeper
|
||||
## 什么是 ZooKeeper
|
||||
|
||||
`ZooKeeper` 由 `Yahoo` 开发,后来捐赠给了 `Apache` ,现已成为 `Apache` 顶级项目。`ZooKeeper` 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 `Paxos` 算法的 `ZAB` 协议完成的。其主要功能包括:配置维护、分布式同步、集群管理、分布式事务等。
|
||||
|
||||
@ -21,7 +21,7 @@ tag:
|
||||
|
||||

|
||||
|
||||
但是,我现在换一种方式,我将一个秒杀服务 **拆分成多个子服务** ,比如创建订单服务,增加积分服务,扣优惠券服务等等,**然后我将这些子服务都部署在不同的服务器上** ,这个时候就是 **`Distributed` 分布式** 。
|
||||
但是,我现在换一种方式,我将一个秒杀服务 **拆分成多个子服务** ,比如创建订单服务,增加积分服务,扣优惠券服务等等,**然后我将这些子服务都部署在不同的服务器上** ,这个时候就是 **`Distributed` 分布式** 。
|
||||
|
||||

|
||||
|
||||
@ -31,7 +31,7 @@ tag:
|
||||
|
||||
比如各个分布式组件如何协调起来,如何减少各个系统之间的耦合度,分布式事务的处理,如何去配置整个分布式系统等等。`ZooKeeper` 主要就是解决这些问题的。
|
||||
|
||||
## 一致性问题
|
||||
## 一致性问题
|
||||
|
||||
设计一个分布式系统必定会遇到一个问题—— **因为分区容忍性(partition tolerance)的存在,就必定要求我们需要在系统可用性(availability)和数据一致性(consistency)中做出权衡** 。这就是著名的 `CAP` 定理。
|
||||
|
||||
@ -39,11 +39,11 @@ tag:
|
||||
|
||||

|
||||
|
||||
而上述前者就是 `Eureka` 的处理方式,它保证了AP(可用性),后者就是我们今天所要讲的 `ZooKeeper` 的处理方式,它保证了CP(数据一致性)。
|
||||
而上述前者就是 `Eureka` 的处理方式,它保证了 AP(可用性),后者就是我们今天所要讲的 `ZooKeeper` 的处理方式,它保证了 CP(数据一致性)。
|
||||
|
||||
## 一致性协议和算法
|
||||
|
||||
而为了解决数据一致性问题,在科学家和程序员的不断探索中,就出现了很多的一致性协议和算法。比如 2PC(两阶段提交),3PC(三阶段提交),Paxos算法等等。
|
||||
而为了解决数据一致性问题,在科学家和程序员的不断探索中,就出现了很多的一致性协议和算法。比如 2PC(两阶段提交),3PC(三阶段提交),Paxos 算法等等。
|
||||
|
||||
这时候请你思考一个问题,同学之间如果采用传纸条的方式去传播消息,那么就会出现一个问题——我咋知道我的小纸条有没有传到我想要传递的那个人手中呢?万一被哪个小家伙给劫持篡改了呢,对吧?
|
||||
|
||||
@ -57,9 +57,9 @@ tag:
|
||||
|
||||
两阶段提交是一种保证分布式系统数据一致性的协议,现在很多数据库都是采用的两阶段提交协议来完成 **分布式事务** 的处理。
|
||||
|
||||
在介绍2PC之前,我们先来想想分布式事务到底有什么问题呢?
|
||||
在介绍 2PC 之前,我们先来想想分布式事务到底有什么问题呢?
|
||||
|
||||
还拿秒杀系统的下订单和加积分两个系统来举例吧(我想你们可能都吐了🤮🤮🤮),我们此时下完订单会发个消息给积分系统告诉它下面该增加积分了。如果我们仅仅是发送一个消息也不收回复,那么我们的订单系统怎么能知道积分系统的收到消息的情况呢?如果我们增加一个收回复的过程,那么当积分系统收到消息后返回给订单系统一个 `Response` ,但在中间出现了网络波动,那个回复消息没有发送成功,订单系统是不是以为积分系统消息接收失败了?它是不是会回滚事务?但此时积分系统是成功收到消息的,它就会去处理消息然后给用户增加积分,这个时候就会出现积分加了但是订单没下成功。
|
||||
还拿秒杀系统的下订单和加积分两个系统来举例吧(我想你们可能都吐了 🤮🤮🤮),我们此时下完订单会发个消息给积分系统告诉它下面该增加积分了。如果我们仅仅是发送一个消息也不收回复,那么我们的订单系统怎么能知道积分系统的收到消息的情况呢?如果我们增加一个收回复的过程,那么当积分系统收到消息后返回给订单系统一个 `Response` ,但在中间出现了网络波动,那个回复消息没有发送成功,订单系统是不是以为积分系统消息接收失败了?它是不是会回滚事务?但此时积分系统是成功收到消息的,它就会去处理消息然后给用户增加积分,这个时候就会出现积分加了但是订单没下成功。
|
||||
|
||||
所以我们所需要解决的是在分布式系统中,整个调用链中,我们所有服务的数据处理要么都成功要么都失败,即所有服务的 **原子性问题** 。
|
||||
|
||||
@ -79,19 +79,19 @@ tag:
|
||||
|
||||

|
||||
|
||||
* **单点故障问题**,如果协调者挂了那么整个系统都处于不可用的状态了。
|
||||
* **阻塞问题**,即当协调者发送 `prepare` 请求,参与者收到之后如果能处理那么它将会进行事务的处理但并不提交,这个时候会一直占用着资源不释放,如果此时协调者挂了,那么这些资源都不会再释放了,这会极大影响性能。
|
||||
* **数据不一致问题**,比如当第二阶段,协调者只发送了一部分的 `commit` 请求就挂了,那么也就意味着,收到消息的参与者会进行事务的提交,而后面没收到的则不会进行事务提交,那么这时候就会产生数据不一致性问题。
|
||||
- **单点故障问题**,如果协调者挂了那么整个系统都处于不可用的状态了。
|
||||
- **阻塞问题**,即当协调者发送 `prepare` 请求,参与者收到之后如果能处理那么它将会进行事务的处理但并不提交,这个时候会一直占用着资源不释放,如果此时协调者挂了,那么这些资源都不会再释放了,这会极大影响性能。
|
||||
- **数据不一致问题**,比如当第二阶段,协调者只发送了一部分的 `commit` 请求就挂了,那么也就意味着,收到消息的参与者会进行事务的提交,而后面没收到的则不会进行事务提交,那么这时候就会产生数据不一致性问题。
|
||||
|
||||
### 3PC(三阶段提交)
|
||||
|
||||
因为2PC存在的一系列问题,比如单点,容错机制缺陷等等,从而产生了 **3PC(三阶段提交)** 。那么这三阶段又分别是什么呢?
|
||||
因为 2PC 存在的一系列问题,比如单点,容错机制缺陷等等,从而产生了 **3PC(三阶段提交)** 。那么这三阶段又分别是什么呢?
|
||||
|
||||
> 千万不要吧PC理解成个人电脑了,其实他们是 phase-commit 的缩写,即阶段提交。
|
||||
> 千万不要吧 PC 理解成个人电脑了,其实他们是 phase-commit 的缩写,即阶段提交。
|
||||
|
||||
1. **CanCommit阶段**:协调者向所有参与者发送 `CanCommit` 请求,参与者收到请求后会根据自身情况查看是否能执行事务,如果可以则返回 YES 响应并进入预备状态,否则返回 NO 。
|
||||
2. **PreCommit阶段**:协调者根据参与者返回的响应来决定是否可以进行下面的 `PreCommit` 操作。如果上面参与者返回的都是 YES,那么协调者将向所有参与者发送 `PreCommit` 预提交请求,**参与者收到预提交请求后,会进行事务的执行操作,并将 `Undo` 和 `Redo` 信息写入事务日志中** ,最后如果参与者顺利执行了事务则给协调者返回成功的响应。如果在第一阶段协调者收到了 **任何一个 NO** 的信息,或者 **在一定时间内** 并没有收到全部的参与者的响应,那么就会中断事务,它会向所有参与者发送中断请求(abort),参与者收到中断请求之后会立即中断事务,或者在一定时间内没有收到协调者的请求,它也会中断事务。
|
||||
3. **DoCommit阶段**:这个阶段其实和 `2PC` 的第二阶段差不多,如果协调者收到了所有参与者在 `PreCommit` 阶段的 YES 响应,那么协调者将会给所有参与者发送 `DoCommit` 请求,**参与者收到 `DoCommit` 请求后则会进行事务的提交工作**,完成后则会给协调者返回响应,协调者收到所有参与者返回的事务提交成功的响应之后则完成事务。若协调者在 `PreCommit` 阶段 **收到了任何一个 NO 或者在一定时间内没有收到所有参与者的响应** ,那么就会进行中断请求的发送,参与者收到中断请求后则会 **通过上面记录的回滚日志** 来进行事务的回滚操作,并向协调者反馈回滚状况,协调者收到参与者返回的消息后,中断事务。
|
||||
1. **CanCommit 阶段**:协调者向所有参与者发送 `CanCommit` 请求,参与者收到请求后会根据自身情况查看是否能执行事务,如果可以则返回 YES 响应并进入预备状态,否则返回 NO 。
|
||||
2. **PreCommit 阶段**:协调者根据参与者返回的响应来决定是否可以进行下面的 `PreCommit` 操作。如果上面参与者返回的都是 YES,那么协调者将向所有参与者发送 `PreCommit` 预提交请求,**参与者收到预提交请求后,会进行事务的执行操作,并将 `Undo` 和 `Redo` 信息写入事务日志中** ,最后如果参与者顺利执行了事务则给协调者返回成功的响应。如果在第一阶段协调者收到了 **任何一个 NO** 的信息,或者 **在一定时间内** 并没有收到全部的参与者的响应,那么就会中断事务,它会向所有参与者发送中断请求(abort),参与者收到中断请求之后会立即中断事务,或者在一定时间内没有收到协调者的请求,它也会中断事务。
|
||||
3. **DoCommit 阶段**:这个阶段其实和 `2PC` 的第二阶段差不多,如果协调者收到了所有参与者在 `PreCommit` 阶段的 YES 响应,那么协调者将会给所有参与者发送 `DoCommit` 请求,**参与者收到 `DoCommit` 请求后则会进行事务的提交工作**,完成后则会给协调者返回响应,协调者收到所有参与者返回的事务提交成功的响应之后则完成事务。若协调者在 `PreCommit` 阶段 **收到了任何一个 NO 或者在一定时间内没有收到所有参与者的响应** ,那么就会进行中断请求的发送,参与者收到中断请求后则会 **通过上面记录的回滚日志** 来进行事务的回滚操作,并向协调者反馈回滚状况,协调者收到参与者返回的消息后,中断事务。
|
||||
|
||||

|
||||
|
||||
@ -99,7 +99,7 @@ tag:
|
||||
|
||||
总之,`3PC` 通过一系列的超时机制很好的缓解了阻塞问题,但是最重要的一致性并没有得到根本的解决,比如在 `PreCommit` 阶段,当一个参与者收到了请求之后其他参与者和协调者挂了或者出现了网络分区,这个时候收到消息的参与者都会进行事务提交,这就会出现数据不一致性问题。
|
||||
|
||||
所以,要解决一致性问题还需要靠 `Paxos` 算法⭐️ ⭐️ ⭐️ 。
|
||||
所以,要解决一致性问题还需要靠 `Paxos` 算法 ⭐️ ⭐️ ⭐️ 。
|
||||
|
||||
### `Paxos` 算法
|
||||
|
||||
@ -109,8 +109,8 @@ tag:
|
||||
|
||||
#### prepare 阶段
|
||||
|
||||
* `Proposer提案者`:负责提出 `proposal`,每个提案者在提出提案时都会首先获取到一个 **具有全局唯一性的、递增的提案编号N**,即在整个集群中是唯一的编号 N,然后将该编号赋予其要提出的提案,在**第一阶段是只将提案编号发送给所有的表决者**。
|
||||
* `Acceptor表决者`:每个表决者在 `accept` 某提案后,会将该提案编号N记录在本地,这样每个表决者中保存的已经被 accept 的提案中会存在一个**编号最大的提案**,其编号假设为 `maxN`。每个表决者仅会 `accept` 编号大于自己本地 `maxN` 的提案,在批准提案时表决者会将以前接受过的最大编号的提案作为响应反馈给 `Proposer` 。
|
||||
- `Proposer提案者`:负责提出 `proposal`,每个提案者在提出提案时都会首先获取到一个 **具有全局唯一性的、递增的提案编号 N**,即在整个集群中是唯一的编号 N,然后将该编号赋予其要提出的提案,在**第一阶段是只将提案编号发送给所有的表决者**。
|
||||
- `Acceptor表决者`:每个表决者在 `accept` 某提案后,会将该提案编号 N 记录在本地,这样每个表决者中保存的已经被 accept 的提案中会存在一个**编号最大的提案**,其编号假设为 `maxN`。每个表决者仅会 `accept` 编号大于自己本地 `maxN` 的提案,在批准提案时表决者会将以前接受过的最大编号的提案作为响应反馈给 `Proposer` 。
|
||||
|
||||
> 下面是 `prepare` 阶段的流程图,你可以对照着参考一下。
|
||||
|
||||
@ -128,13 +128,13 @@ tag:
|
||||
|
||||

|
||||
|
||||
而如果 `Proposer` 如果没有收到超过半数的 `accept` 那么它将会将 **递增** 该 `Proposal` 的编号,然后 **重新进入 `Prepare` 阶段** 。
|
||||
而如果 `Proposer` 如果没有收到超过半数的 `accept` 那么它将会将 **递增** 该 `Proposal` 的编号,然后 **重新进入 `Prepare` 阶段** 。
|
||||
|
||||
> 对于 `Learner` 来说如何去学习 `Acceptor` 批准的提案内容,这有很多方式,读者可以自己去了解一下,这里不做过多解释。
|
||||
|
||||
#### paxos 算法的死循环问题
|
||||
|
||||
其实就有点类似于两个人吵架,小明说我是对的,小红说我才是对的,两个人据理力争的谁也不让谁🤬🤬。
|
||||
其实就有点类似于两个人吵架,小明说我是对的,小红说我才是对的,两个人据理力争的谁也不让谁 🤬🤬。
|
||||
|
||||
比如说,此时提案者 P1 提出一个方案 M1,完成了 `Prepare` 阶段的工作,这个时候 `acceptor` 则批准了 M1,但是此时提案者 P2 同时也提出了一个方案 M2,它也完成了 `Prepare` 阶段的工作。然后 P1 的方案已经不能在第二阶段被批准了(因为 `acceptor` 已经批准了比 M1 更大的 M2),所以 P1 自增方案变为 M3 重新进入 `Prepare` 阶段,然后 `acceptor` ,又批准了新的 M3 方案,它又不能批准 M2 了,这个时候 M2 又自增进入 `Prepare` 阶段。。。
|
||||
|
||||
@ -146,7 +146,7 @@ tag:
|
||||
|
||||
## 引出 ZAB
|
||||
|
||||
### Zookeeper 架构
|
||||
### Zookeeper 架构
|
||||
|
||||
作为一个优秀高效且可靠的分布式协调框架,`ZooKeeper` 在解决分布式数据一致性问题时并没有直接使用 `Paxos` ,而是专门定制了一致性协议叫做 `ZAB(ZooKeeper Atomic Broadcast)` 原子广播协议,该协议能够很好地支持 **崩溃恢复** 。
|
||||
|
||||
@ -156,9 +156,9 @@ tag:
|
||||
|
||||
和介绍 `Paxos` 一样,在介绍 `ZAB` 协议之前,我们首先来了解一下在 `ZAB` 中三个主要的角色,`Leader 领导者`、`Follower跟随者`、`Observer观察者` 。
|
||||
|
||||
* `Leader` :集群中 **唯一的写请求处理者** ,能够发起投票(投票也是为了进行写请求)。
|
||||
* `Follower`:能够接收客户端的请求,如果是读请求则可以自己处理,**如果是写请求则要转发给 `Leader`** 。在选举过程中会参与投票,**有选举权和被选举权** 。
|
||||
* `Observer` :就是没有选举权和被选举权的 `Follower` 。
|
||||
- `Leader` :集群中 **唯一的写请求处理者** ,能够发起投票(投票也是为了进行写请求)。
|
||||
- `Follower`:能够接收客户端的请求,如果是读请求则可以自己处理,**如果是写请求则要转发给 `Leader`** 。在选举过程中会参与投票,**有选举权和被选举权** 。
|
||||
- `Observer` :就是没有选举权和被选举权的 `Follower` 。
|
||||
|
||||
在 `ZAB` 协议中对 `zkServer`(即上面我们说的三个角色的总称) 还有两种模式的定义,分别是 **消息广播** 和 **崩溃恢复** 。
|
||||
|
||||
@ -174,11 +174,11 @@ tag:
|
||||
|
||||

|
||||
|
||||
嗯。。。看起来很简单,貌似懂了🤥🤥🤥。这两个 `Queue` 哪冒出来的?答案是 **`ZAB` 需要让 `Follower` 和 `Observer` 保证顺序性** 。何为顺序性,比如我现在有一个写请求A,此时 `Leader` 将请求A广播出去,因为只需要半数同意就行,所以可能这个时候有一个 `Follower` F1因为网络原因没有收到,而 `Leader` 又广播了一个请求B,因为网络原因,F1竟然先收到了请求B然后才收到了请求A,这个时候请求处理的顺序不同就会导致数据的不同,从而 **产生数据不一致问题** 。
|
||||
嗯。。。看起来很简单,貌似懂了 🤥🤥🤥。这两个 `Queue` 哪冒出来的?答案是 **`ZAB` 需要让 `Follower` 和 `Observer` 保证顺序性** 。何为顺序性,比如我现在有一个写请求 A,此时 `Leader` 将请求 A 广播出去,因为只需要半数同意就行,所以可能这个时候有一个 `Follower` F1 因为网络原因没有收到,而 `Leader` 又广播了一个请求 B,因为网络原因,F1 竟然先收到了请求 B 然后才收到了请求 A,这个时候请求处理的顺序不同就会导致数据的不同,从而 **产生数据不一致问题** 。
|
||||
|
||||
所以在 `Leader` 这端,它为每个其他的 `zkServer` 准备了一个 **队列** ,采用先进先出的方式发送消息。由于协议是 **通过 `TCP`** 来进行网络通信的,保证了消息的发送顺序性,接受顺序性也得到了保证。
|
||||
|
||||
除此之外,在 `ZAB` 中还定义了一个 **全局单调递增的事务ID `ZXID`** ,它是一个64位long型,其中高32位表示 `epoch` 年代,低32位表示事务id。`epoch` 是会根据 `Leader` 的变化而变化的,当一个 `Leader` 挂了,新的 `Leader` 上位的时候,年代(`epoch`)就变了。而低32位可以简单理解为递增的事务id。
|
||||
除此之外,在 `ZAB` 中还定义了一个 **全局单调递增的事务 ID `ZXID`** ,它是一个 64 位 long 型,其中高 32 位表示 `epoch` 年代,低 32 位表示事务 id。`epoch` 是会根据 `Leader` 的变化而变化的,当一个 `Leader` 挂了,新的 `Leader` 上位的时候,年代(`epoch`)就变了。而低 32 位可以简单理解为递增的事务 id。
|
||||
|
||||
定义这个的原因也是为了顺序性,每个 `proposal` 在 `Leader` 中生成后需要 **通过其 `ZXID` 来进行排序** ,才能得到处理。
|
||||
|
||||
@ -188,15 +188,15 @@ tag:
|
||||
|
||||
`Leader` 选举可以分为两个不同的阶段,第一个是我们提到的 `Leader` 宕机需要重新选举,第二则是当 `Zookeeper` 启动时需要进行系统的 `Leader` 初始化选举。下面我先来介绍一下 `ZAB` 是如何进行初始化选举的。
|
||||
|
||||
假设我们集群中有3台机器,那也就意味着我们需要两台以上同意(超过半数)。比如这个时候我们启动了 `server1` ,它会首先 **投票给自己** ,投票内容为服务器的 `myid` 和 `ZXID` ,因为初始化所以 `ZXID` 都为0,此时 `server1` 发出的投票为 (1,0)。但此时 `server1` 的投票仅为1,所以不能作为 `Leader` ,此时还在选举阶段所以整个集群处于 **`Looking` 状态**。
|
||||
假设我们集群中有 3 台机器,那也就意味着我们需要两台以上同意(超过半数)。比如这个时候我们启动了 `server1` ,它会首先 **投票给自己** ,投票内容为服务器的 `myid` 和 `ZXID` ,因为初始化所以 `ZXID` 都为 0,此时 `server1` 发出的投票为 (1,0)。但此时 `server1` 的投票仅为 1,所以不能作为 `Leader` ,此时还在选举阶段所以整个集群处于 **`Looking` 状态**。
|
||||
|
||||
接着 `server2` 启动了,它首先也会将投票选给自己(2,0),并将投票信息广播出去(`server1`也会,只是它那时没有其他的服务器了),`server1` 在收到 `server2` 的投票信息后会将投票信息与自己的作比较。**首先它会比较 `ZXID` ,`ZXID` 大的优先为 `Leader`,如果相同则比较 `myid`,`myid` 大的优先作为 `Leader`**。所以此时`server1` 发现 `server2` 更适合做 `Leader`,它就会将自己的投票信息更改为(2,0)然后再广播出去,之后`server2` 收到之后发现和自己的一样无需做更改,并且自己的 **投票已经超过半数** ,则 **确定 `server2` 为 `Leader`**,`server1` 也会将自己服务器设置为 `Following` 变为 `Follower`。整个服务器就从 `Looking` 变为了正常状态。
|
||||
接着 `server2` 启动了,它首先也会将投票选给自己(2,0),并将投票信息广播出去(`server1`也会,只是它那时没有其他的服务器了),`server1` 在收到 `server2` 的投票信息后会将投票信息与自己的作比较。**首先它会比较 `ZXID` ,`ZXID` 大的优先为 `Leader`,如果相同则比较 `myid`,`myid` 大的优先作为 `Leader`**。所以此时`server1` 发现 `server2` 更适合做 `Leader`,它就会将自己的投票信息更改为(2,0)然后再广播出去,之后`server2` 收到之后发现和自己的一样无需做更改,并且自己的 **投票已经超过半数** ,则 **确定 `server2` 为 `Leader`**,`server1` 也会将自己服务器设置为 `Following` 变为 `Follower`。整个服务器就从 `Looking` 变为了正常状态。
|
||||
|
||||
当 `server3` 启动发现集群没有处于 `Looking` 状态时,它会直接以 `Follower` 的身份加入集群。
|
||||
|
||||
还是前面三个 `server` 的例子,如果在整个集群运行的过程中 `server2` 挂了,那么整个集群会如何重新选举 `Leader` 呢?其实和初始化选举差不多。
|
||||
|
||||
首先毫无疑问的是剩下的两个 `Follower` 会将自己的状态 **从 `Following` 变为 `Looking` 状态** ,然后每个 `server` 会向初始化投票一样首先给自己投票(这不过这里的 `zxid` 可能不是0了,这里为了方便随便取个数字)。
|
||||
首先毫无疑问的是剩下的两个 `Follower` 会将自己的状态 **从 `Following` 变为 `Looking` 状态** ,然后每个 `server` 会向初始化投票一样首先给自己投票(这不过这里的 `zxid` 可能不是 0 了,这里为了方便随便取个数字)。
|
||||
|
||||
假设 `server1` 给自己投票为(1,99),然后广播给其他 `server`,`server3` 首先也会给自己投票(3,95),然后也广播给其他 `server`。`server1` 和 `server3` 此时会收到彼此的投票信息,和一开始选举一样,他们也会比较自己的投票和收到的投票(`zxid` 大的优先,如果相同那么就 `myid` 大的优先)。这个时候 `server1` 收到了 `server3` 的投票发现没自己的合适故不变,`server3` 收到 `server1` 的投票结果后发现比自己的合适于是更改投票为(1,99)然后广播出去,最后 `server1` 收到了发现自己的投票已经超过半数就把自己设为 `Leader`,`server3` 也随之变为 `Follower`。
|
||||
|
||||
@ -208,9 +208,9 @@ tag:
|
||||
|
||||
如果只是 `Follower` 挂了,而且挂的没超过半数的时候,因为我们一开始讲了在 `Leader` 中会维护队列,所以不用担心后面的数据没接收到导致数据不一致性。
|
||||
|
||||
如果 `Leader` 挂了那就麻烦了,我们肯定需要先暂停服务变为 `Looking` 状态然后进行 `Leader` 的重新选举(上面我讲过了),但这个就要分为两种情况了,分别是 **确保已经被Leader提交的提案最终能够被所有的Follower提交** 和 **跳过那些已经被丢弃的提案** 。
|
||||
如果 `Leader` 挂了那就麻烦了,我们肯定需要先暂停服务变为 `Looking` 状态然后进行 `Leader` 的重新选举(上面我讲过了),但这个就要分为两种情况了,分别是 **确保已经被 Leader 提交的提案最终能够被所有的 Follower 提交** 和 **跳过那些已经被丢弃的提案** 。
|
||||
|
||||
确保已经被Leader提交的提案最终能够被所有的Follower提交是什么意思呢?
|
||||
确保已经被 Leader 提交的提案最终能够被所有的 Follower 提交是什么意思呢?
|
||||
|
||||
假设 `Leader (server2)` 发送 `commit` 请求(忘了请看上面的消息广播模式),他发送给了 `server3`,然后要发给 `server1` 的时候突然挂了。这个时候重新选举的时候我们如果把 `server1` 作为 `Leader` 的话,那么肯定会产生数据不一致性,因为 `server3` 肯定会提交刚刚 `server2` 发送的 `commit` 请求的提案,而 `server1` 根本没收到所以会丢弃。
|
||||
|
||||
@ -222,15 +222,15 @@ tag:
|
||||
|
||||
那么跳过那些已经被丢弃的提案又是什么意思呢?
|
||||
|
||||
假设 `Leader (server2)` 此时同意了提案N1,自身提交了这个事务并且要发送给所有 `Follower` 要 `commit` 的请求,却在这个时候挂了,此时肯定要重新进行 `Leader` 的选举,比如说此时选 `server1` 为 `Leader` (这无所谓)。但是过了一会,这个 **挂掉的 `Leader` 又重新恢复了** ,此时它肯定会作为 `Follower` 的身份进入集群中,需要注意的是刚刚 `server2` 已经同意提交了提案N1,但其他 `server` 并没有收到它的 `commit` 信息,所以其他 `server` 不可能再提交这个提案N1了,这样就会出现数据不一致性问题了,所以 **该提案N1最终需要被抛弃掉** 。
|
||||
假设 `Leader (server2)` 此时同意了提案 N1,自身提交了这个事务并且要发送给所有 `Follower` 要 `commit` 的请求,却在这个时候挂了,此时肯定要重新进行 `Leader` 的选举,比如说此时选 `server1` 为 `Leader` (这无所谓)。但是过了一会,这个 **挂掉的 `Leader` 又重新恢复了** ,此时它肯定会作为 `Follower` 的身份进入集群中,需要注意的是刚刚 `server2` 已经同意提交了提案 N1,但其他 `server` 并没有收到它的 `commit` 信息,所以其他 `server` 不可能再提交这个提案 N1 了,这样就会出现数据不一致性问题了,所以 **该提案 N1 最终需要被抛弃掉** 。
|
||||
|
||||

|
||||
|
||||
## Zookeeper的几个理论知识
|
||||
## Zookeeper 的几个理论知识
|
||||
|
||||
了解了 `ZAB` 协议还不够,它仅仅是 `Zookeeper` 内部实现的一种方式,而我们如何通过 `Zookeeper` 去做一些典型的应用场景呢?比如说集群管理,分布式锁,`Master` 选举等等。
|
||||
|
||||
这就涉及到如何使用 `Zookeeper` 了,但在使用之前我们还需要掌握几个概念。比如 `Zookeeper` 的 **数据模型** 、**会话机制**、**ACL**、**Watcher机制** 等等。
|
||||
这就涉及到如何使用 `Zookeeper` 了,但在使用之前我们还需要掌握几个概念。比如 `Zookeeper` 的 **数据模型** 、**会话机制**、**ACL**、**Watcher 机制** 等等。
|
||||
|
||||
### 数据模型
|
||||
|
||||
@ -242,24 +242,24 @@ tag:
|
||||
|
||||
其中节点类型可以分为 **持久节点**、**持久顺序节点**、**临时节点** 和 **临时顺序节点**。
|
||||
|
||||
* 持久节点:一旦创建就一直存在,直到将其删除。
|
||||
* 持久顺序节点:一个父节点可以为其子节点 **维护一个创建的先后顺序** ,这个顺序体现在 **节点名称** 上,是节点名称后自动添加一个由 10 位数字组成的数字串,从 0 开始计数。
|
||||
* 临时节点:临时节点的生命周期是与 **客户端会话** 绑定的,**会话消失则节点消失** 。临时节点 **只能做叶子节点** ,不能创建子节点。
|
||||
* 临时顺序节点:父节点可以创建一个维持了顺序的临时节点(和前面的持久顺序性节点一样)。
|
||||
- 持久节点:一旦创建就一直存在,直到将其删除。
|
||||
- 持久顺序节点:一个父节点可以为其子节点 **维护一个创建的先后顺序** ,这个顺序体现在 **节点名称** 上,是节点名称后自动添加一个由 10 位数字组成的数字串,从 0 开始计数。
|
||||
- 临时节点:临时节点的生命周期是与 **客户端会话** 绑定的,**会话消失则节点消失** 。临时节点 **只能做叶子节点** ,不能创建子节点。
|
||||
- 临时顺序节点:父节点可以创建一个维持了顺序的临时节点(和前面的持久顺序性节点一样)。
|
||||
|
||||
节点状态中包含了很多节点的属性比如 `czxid` 、`mzxid` 等等,在 `zookeeper` 中是使用 `Stat` 这个类来维护的。下面我列举一些属性解释。
|
||||
|
||||
* `czxid`:`Created ZXID`,该数据节点被 **创建** 时的事务ID。
|
||||
* `mzxid`:`Modified ZXID`,节点 **最后一次被更新时** 的事务ID。
|
||||
* `ctime`:`Created Time`,该节点被创建的时间。
|
||||
* `mtime`: `Modified Time`,该节点最后一次被修改的时间。
|
||||
* `version`:节点的版本号。
|
||||
* `cversion`:**子节点** 的版本号。
|
||||
* `aversion`:节点的 `ACL` 版本号。
|
||||
* `ephemeralOwner`:创建该节点的会话的 `sessionID` ,如果该节点为持久节点,该值为0。
|
||||
* `dataLength`:节点数据内容的长度。
|
||||
* `numChildre`:该节点的子节点个数,如果为临时节点为0。
|
||||
* `pzxid`:该节点子节点列表最后一次被修改时的事务ID,注意是子节点的 **列表** ,不是内容。
|
||||
- `czxid`:`Created ZXID`,该数据节点被 **创建** 时的事务 ID。
|
||||
- `mzxid`:`Modified ZXID`,节点 **最后一次被更新时** 的事务 ID。
|
||||
- `ctime`:`Created Time`,该节点被创建的时间。
|
||||
- `mtime`: `Modified Time`,该节点最后一次被修改的时间。
|
||||
- `version`:节点的版本号。
|
||||
- `cversion`:**子节点** 的版本号。
|
||||
- `aversion`:节点的 `ACL` 版本号。
|
||||
- `ephemeralOwner`:创建该节点的会话的 `sessionID` ,如果该节点为持久节点,该值为 0。
|
||||
- `dataLength`:节点数据内容的长度。
|
||||
- `numChildre`:该节点的子节点个数,如果为临时节点为 0。
|
||||
- `pzxid`:该节点子节点列表最后一次被修改时的事务 ID,注意是子节点的 **列表** ,不是内容。
|
||||
|
||||
### 会话
|
||||
|
||||
@ -269,21 +269,21 @@ tag:
|
||||
|
||||
### ACL
|
||||
|
||||
`ACL` 为 `Access Control Lists` ,它是一种权限控制。在 `zookeeper` 中定义了5种权限,它们分别为:
|
||||
`ACL` 为 `Access Control Lists` ,它是一种权限控制。在 `zookeeper` 中定义了 5 种权限,它们分别为:
|
||||
|
||||
* `CREATE` :创建子节点的权限。
|
||||
* `READ`:获取节点数据和子节点列表的权限。
|
||||
* `WRITE`:更新节点数据的权限。
|
||||
* `DELETE`:删除子节点的权限。
|
||||
* `ADMIN`:设置节点 ACL 的权限。
|
||||
- `CREATE` :创建子节点的权限。
|
||||
- `READ`:获取节点数据和子节点列表的权限。
|
||||
- `WRITE`:更新节点数据的权限。
|
||||
- `DELETE`:删除子节点的权限。
|
||||
- `ADMIN`:设置节点 ACL 的权限。
|
||||
|
||||
### Watcher机制
|
||||
### Watcher 机制
|
||||
|
||||
`Watcher` 为事件监听器,是 `zk` 非常重要的一个特性,很多功能都依赖于它,它有点类似于订阅的方式,即客户端向服务端 **注册** 指定的 `watcher` ,当服务端符合了 `watcher` 的某些事件或要求则会 **向客户端发送事件通知** ,客户端收到通知后找到自己定义的 `Watcher` 然后 **执行相应的回调方法** 。
|
||||
|
||||

|
||||
|
||||
## Zookeeper的几个典型应用场景
|
||||
## Zookeeper 的几个典型应用场景
|
||||
|
||||
前面说了这么多的理论知识,你可能听得一头雾水,这些玩意有啥用?能干啥事?别急,听我慢慢道来。
|
||||
|
||||
@ -307,7 +307,7 @@ tag:
|
||||
|
||||
分布式锁的实现方式有很多种,比如 `Redis` 、数据库 、`zookeeper` 等。个人认为 `zookeeper` 在实现分布式锁这方面是非常非常简单的。
|
||||
|
||||
上面我们已经提到过了 **zk在高并发的情况下保证节点创建的全局唯一性**,这玩意一看就知道能干啥了。实现互斥锁呗,又因为能在分布式的情况下,所以能实现分布式锁呗。
|
||||
上面我们已经提到过了 **zk 在高并发的情况下保证节点创建的全局唯一性**,这玩意一看就知道能干啥了。实现互斥锁呗,又因为能在分布式的情况下,所以能实现分布式锁呗。
|
||||
|
||||
如何实现呢?这玩意其实跟选主基本一样,我们也可以利用临时节点的创建来实现。
|
||||
|
||||
@ -329,9 +329,9 @@ tag:
|
||||
|
||||
### 命名服务
|
||||
|
||||
如何给一个对象设置ID,大家可能都会想到 `UUID`,但是 `UUID` 最大的问题就在于它太长了。。。(太长不一定是好事,嘿嘿嘿)。那么在条件允许的情况下,我们能不能使用 `zookeeper` 来实现呢?
|
||||
如何给一个对象设置 ID,大家可能都会想到 `UUID`,但是 `UUID` 最大的问题就在于它太长了。。。(太长不一定是好事,嘿嘿嘿)。那么在条件允许的情况下,我们能不能使用 `zookeeper` 来实现呢?
|
||||
|
||||
我们之前提到过 `zookeeper` 是通过 **树形结构** 来存储数据节点的,那也就是说,对于每个节点的 **全路径**,它必定是唯一的,我们可以使用节点的全路径作为命名方式了。而且更重要的是,路径是我们可以自己定义的,这对于我们对有些有语意的对象的ID设置可以更加便于理解。
|
||||
我们之前提到过 `zookeeper` 是通过 **树形结构** 来存储数据节点的,那也就是说,对于每个节点的 **全路径**,它必定是唯一的,我们可以使用节点的全路径作为命名方式了。而且更重要的是,路径是我们可以自己定义的,这对于我们对有些有语意的对象的 ID 设置可以更加便于理解。
|
||||
|
||||
### 集群管理和注册中心
|
||||
|
||||
@ -343,7 +343,7 @@ tag:
|
||||
|
||||

|
||||
|
||||
至于注册中心也很简单,我们同样也是让 **服务提供者** 在 `zookeeper` 中创建一个临时节点并且将自己的 `ip、port、调用方式` 写入节点,当 **服务消费者** 需要进行调用的时候会 **通过注册中心找到相应的服务的地址列表(IP端口什么的)** ,并缓存到本地(方便以后调用),当消费者调用服务时,不会再去请求注册中心,而是直接通过负载均衡算法从地址列表中取一个服务提供者的服务器调用服务。
|
||||
至于注册中心也很简单,我们同样也是让 **服务提供者** 在 `zookeeper` 中创建一个临时节点并且将自己的 `ip、port、调用方式` 写入节点,当 **服务消费者** 需要进行调用的时候会 **通过注册中心找到相应的服务的地址列表(IP 端口什么的)** ,并缓存到本地(方便以后调用),当消费者调用服务时,不会再去请求注册中心,而是直接通过负载均衡算法从地址列表中取一个服务提供者的服务器调用服务。
|
||||
|
||||
当服务提供者的某台服务器宕机或下线时,相应的地址会从服务提供者地址列表中移除。同时,注册中心会将新的服务地址列表发送给服务消费者的机器并缓存在消费者本机(当然你可以让消费者进行节点监听,我记得 `Eureka` 会先试错,然后再更新)。
|
||||
|
||||
@ -351,20 +351,20 @@ tag:
|
||||
|
||||
## 总结
|
||||
|
||||
看到这里的同学实在是太有耐心了👍👍👍不知道大家是否还记得我讲了什么😒。
|
||||
看到这里的同学实在是太有耐心了 👍👍👍 不知道大家是否还记得我讲了什么 😒。
|
||||
|
||||

|
||||
|
||||
这篇文章中我带大家入门了 `zookeeper` 这个强大的分布式协调框架。现在我们来简单梳理一下整篇文章的内容。
|
||||
|
||||
* 分布式与集群的区别
|
||||
- 分布式与集群的区别
|
||||
|
||||
* `2PC` 、`3PC` 以及 `paxos` 算法这些一致性框架的原理和实现。
|
||||
- `2PC` 、`3PC` 以及 `paxos` 算法这些一致性框架的原理和实现。
|
||||
|
||||
* `zookeeper` 专门的一致性算法 `ZAB` 原子广播协议的内容(`Leader` 选举、崩溃恢复、消息广播)。
|
||||
- `zookeeper` 专门的一致性算法 `ZAB` 原子广播协议的内容(`Leader` 选举、崩溃恢复、消息广播)。
|
||||
|
||||
* `zookeeper` 中的一些基本概念,比如 `ACL`,数据节点,会话,`watcher`机制等等。
|
||||
- `zookeeper` 中的一些基本概念,比如 `ACL`,数据节点,会话,`watcher`机制等等。
|
||||
|
||||
* `zookeeper` 的典型应用场景,比如选主,注册中心等等。
|
||||
- `zookeeper` 的典型应用场景,比如选主,注册中心等等。
|
||||
|
||||
如果忘了可以回去看看再次理解一下,如果有疑问和建议欢迎提出🤝🤝🤝。
|
||||
如果忘了可以回去看看再次理解一下,如果有疑问和建议欢迎提出 🤝🤝🤝。
|
||||
|
@ -8,46 +8,4 @@ icon: "transanction"
|
||||
|
||||

|
||||
|
||||
[《Java 面试指北》](https://javaguide.cn/zhuanlan/java-mian-shi-zhi-bei.html)(点击链接即可查看详细介绍)的部分内容展示如下,你可以将其看作是 [JavaGuide](https://javaguide.cn/#/) 的补充完善,两者可以配合使用。
|
||||
|
||||

|
||||
|
||||
最近几年,市面上有越来越多的“技术大佬”开始办培训班/训练营,动辄成千上万的学费,却并没有什么干货,单纯的就是割韭菜。
|
||||
|
||||
为了帮助更多同学准备 Java 面试以及学习 Java ,我创建了一个纯粹的知识星球。虽然收费只有培训班/训练营的百分之一,但是知识星球里的内容质量更高,提供的服务也更全面。
|
||||
|
||||
欢迎准备 Java 面试以及学习 Java 的同学加入我的[知识星球](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html),干货非常多,学习氛围也很不错!收费虽然是白菜价,但星球里的内容或许比你参加上万的培训班质量还要高。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/image-20220311203414600.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
下面是星球提供的部分服务(点击下方图片即可获取知识星球的详细介绍):
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiufuwu.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
**我有自己的原则,不割韭菜,用心做内容,真心希望帮助到你!**
|
||||
|
||||
如果你感兴趣的话,不妨花 3 分钟左右看看星球的详细介绍: [JavaGuide 知识星球详细介绍](https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html)。
|
||||
|
||||
这里再送一个 30 元的新人优惠券(续费半价)。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/xingqiu/xingqiuyouhuijuanheyi.png" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
进入星球之后,记得添加微信,我会发你详细的星球使用指南。
|
||||
|
||||
<div align="center">
|
||||
<a href="https://javaguide.cn/about-the-author/zhishixingqiu-two-years.html">
|
||||
<img src="https://oss.javaguide.cn/github/javaguide/IMG_3007.jpg" style="margin: 0 auto; " />
|
||||
</a>
|
||||
</div>
|
||||
<!-- @include: @planet.snippet.md -->
|
||||
|