36
.gitignore
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
.gradle
|
||||
/build/
|
||||
/**/build/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
/out/
|
||||
/**/out/
|
||||
.shelf/
|
||||
.ideaDataSources/
|
||||
dataSources/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
/node_modules/
|
||||
|
||||
### OS ###
|
||||
.DS_Store
|
||||
.Ds_Store´
|
||||
/node_modules
|
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
530
README.md
@ -1,337 +1,393 @@
|
||||
点击订阅[Java面试进阶指南](https://xiaozhuanlan.com/javainterview?rel=javaguide)(专为Java面试方向准备)。[为什么要弄这个专栏?](https://shimo.im/docs/9BJjNsNg7S4dCnz3/)
|
||||
👍推荐 [在线阅读](https://snailclimb.gitee.io/javaguide) (Github 访问速度比较慢可能会导致部分图片无法刷新出来)
|
||||
|
||||
书单已经被移动到[awesome-cs-books](https://github.com/CodingDocs/awesome-cs-books) 这个仓库。
|
||||
|
||||
> 1. **介绍**:关于 JavaGuide 的相关介绍请看:[关于 JavaGuide 的一些说明](https://www.yuque.com/snailclimb/dr6cvl/mr44yt) 。
|
||||
> 2. **PDF版本** : [《JavaGuide 面试突击版》PDF 版本](#公众号) 。[图解计算机基础 PDF 版](#优质原创PDF资源)。
|
||||
> 3. **知识星球** : 简历指导/Java学习/面试指导/面试小册。欢迎加入[我的知识星球](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100015911&idx=1&sn=2e8a0f5acb749ecbcbb417aa8a4e18cc&chksm=4ea1b0ec79d639fae37df1b86f196e8ce397accfd1dd2004bcadb66b4df5f582d90ae0d62448#rd) 。星球内部更新的[《Java面试进阶指北 打造个人的技术竞争力》](https://www.yuque.com/docs/share/f37fc804-bfe6-4b0d-b373-9c462188fec7)这个小册的质量很高,专为面试打造。
|
||||
> 4. **面试专版** :准备面试的小伙伴可以考虑面试专版:[《Java 面试进阶指南》](https://xiaozhuanlan.com/javainterview?rel=javaguide)
|
||||
> 6. **转载须知** :以下所有文章如非文首说明皆为我(Guide哥)的原创,转载在文首注明出处,如发现恶意抄袭/搬运,会动用法律武器维护自己的权益。让我们一起维护一个良好的技术创作环境!⛽️
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/Snailclimb/JavaGuide" target="_blank">
|
||||
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/logo - 副本.png" width=""/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://snailclimb.gitee.io/javaguide"><img src="https://img.shields.io/badge/阅读-read-brightgreen.svg" alt="阅读"></a>
|
||||
<a href="#联系我"><img src="https://img.shields.io/badge/chat-微信群-blue.svg" alt="微信群"></a>
|
||||
<a href="#公众号"><img src="https://img.shields.io/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-JavaGuide-lightgrey.svg" alt="公众号"></a>
|
||||
<a href="#公众号"><img src="https://img.shields.io/badge/PDF-Java面试突击-important.svg" alt="公众号"></a>
|
||||
<a href="#投稿"><img src="https://img.shields.io/badge/support-投稿-critical.svg" alt="投稿"></a>
|
||||
</p>
|
||||
|
||||
<h2 align="center">Special Sponsors</h2>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://coding.net/?utm_source=JavaGuide" target="_blank">
|
||||
<img src="http://pqrlmrv7w.bkt.clouddn.com/img/2019-4/006rNwoDgy1g2dw5gau7nj30eg02vwfr.jpg" width="390px"/>
|
||||
</a>
|
||||
<a href="http://www.lubanjava.com/luban/index.html?=javaguide
|
||||
" target="_blank">
|
||||
<img src="http://pqrlmrv7w.bkt.clouddn.com/img/2019-4/QQ图片20190514211856.gif" width="390px"/>
|
||||
</a>
|
||||
<img src="https://img.shields.io/github/stars/Snailclimb/JavaGuide" alt="投稿">
|
||||
<a href="https://xiaozhuanlan.com/javainterview?rel=javaguide"><img src="https://img.shields.io/badge/Java-面试指南-important" alt="投稿"></a>
|
||||
</p>
|
||||
|
||||
|
||||
推荐使用 <https://snailclimb.top/JavaGuide/> 在线阅读(访问速度慢的话,请使用 <https://snailclimb.gitee.io/javaguide> ),在线阅读内容本仓库同步一致。这种方式阅读的优势在于:有侧边栏阅读体验更好,Gitee pages 的访问速度相对来说也比较快。
|
||||
<h3 align="center">Sponsor</h3>
|
||||
|
||||
## 目录
|
||||
|
||||
- [Java](#java)
|
||||
- [基础](#基础)
|
||||
- [容器](#容器)
|
||||
- [并发](#并发)
|
||||
- [JVM](#jvm)
|
||||
- [I/O](#io)
|
||||
- [Java 8](#java-8)
|
||||
- [编程规范](#编程规范)
|
||||
- [网络](#网络)
|
||||
- [操作系统](#操作系统)
|
||||
- [Linux相关](#linux相关)
|
||||
- [数据结构与算法](#数据结构与算法)
|
||||
- [数据结构](#数据结构)
|
||||
- [算法](#算法)
|
||||
- [数据库](#数据库)
|
||||
- [MySQL](#mysql)
|
||||
- [Redis](#redis)
|
||||
- [系统设计](#系统设计)
|
||||
- [设计模式](#设计模式)
|
||||
- [常用框架](#常用框架)
|
||||
- [数据通信(消息队列、Dubbo)](#数据通信)
|
||||
- [网站架构](#网站架构)
|
||||
- [面试指南](#面试指南)
|
||||
- [备战面试](#备战面试)
|
||||
- [常见面试题总结](#常见面试题总结)
|
||||
- [面经](#面经)
|
||||
- [工具](#工具)
|
||||
- [Git](#git)
|
||||
- [Docker](#Docker)
|
||||
- [资料](#资料)
|
||||
- [书单](#书单)
|
||||
- [Github榜单](#Github榜单)
|
||||
- [待办](#待办)
|
||||
- [说明](#说明)
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://t.1yb.co/iskv">
|
||||
<img src="./media/sponsor/知识星球.png" style="margin: 0 auto;width:850px" /></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
## Java
|
||||
|
||||
### 基础
|
||||
|
||||
* [Java 基础知识回顾](docs/java/Java基础知识.md)
|
||||
* [J2EE 基础知识回顾](docs/java/J2EE基础知识.md)
|
||||
**知识点/面试题:**(必看:+1: )
|
||||
|
||||
1. **[Java 基础知识](docs/java/basis/Java基础知识.md)**
|
||||
2. **[Java 基础知识疑难点/易错点](docs/java/basis/Java基础知识疑难点.md)**
|
||||
|
||||
**重要知识点详解:**
|
||||
|
||||
1. [枚举](docs/java/basis/用好Java中的枚举真的没有那么简单.md) (很重要的一个数据结构,用好枚举真的没有那么简单!)
|
||||
2. [Java 常见关键字总结:final、static、this、super!](docs/java/basis/Java常见关键字总结.md)
|
||||
3. [什么是反射机制?反射机制的应用场景有哪些?](docs/java/basis/反射机制.md)
|
||||
4. [代理模式详解:静态代理+JDK/CGLIB 动态代理实战](docs/java/basis/代理模式详解.md)
|
||||
5. [常见的 IO 模型有哪些?Java 中的 BIO、NIO、AIO 有啥区别?](https://www.cnblogs.com/javaguide/p/io.html)
|
||||
|
||||
### 容器
|
||||
|
||||
* [常见面试题](docs/java/collection/Java集合框架常见面试题.md)
|
||||
* [ArrayList 源码学习](docs/java/collection/ArrayList.md)
|
||||
* [LinkedList 源码学习](docs/java/collection/LinkedList.md)
|
||||
* [HashMap(JDK1.8)源码学习](docs/java/collection/HashMap.md)
|
||||
1. **[Java 容器常见问题总结](docs/java/collection/Java集合框架常见面试题.md)** (必看 :+1:)
|
||||
2. **源码分析** :[ArrayList 源码+扩容机制分析](docs/java/collection/ArrayList源码+扩容机制分析.md) 、[LinkedList 源码](docs/java/collection/LinkedList源码分析.md) 、[HashMap(JDK1.8)源码+底层数据结构分析](<docs/java/collection/HashMap(JDK1.8)源码+底层数据结构分析.md>) 、[ConcurrentHashMap 源码+底层数据结构分析](docs/java/collection/ConcurrentHashMap源码+底层数据结构分析.md)
|
||||
|
||||
### 并发
|
||||
|
||||
* [Java 并发基础常见面试题总结](docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md)
|
||||
* [Java 并发进阶常见面试题总结](docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md)
|
||||
* [并发容器总结](docs/java/Multithread/并发容器总结.md)
|
||||
* [乐观锁与悲观锁](docs/essential-content-for-interview/面试必备之乐观锁与悲观锁.md)
|
||||
* [JUC 中的 Atomic 原子类总结](docs/java/Multithread/Atomic.md)
|
||||
* [AQS 原理以及 AQS 同步组件总结](docs/java/Multithread/AQS.md)
|
||||
并发这部分内容非常重要,还是面试中的重点中的重点!但是,学习起来难度较大,因此我写了:**[多线程学习指南](./docs/java/multi-thread/多线程学习指南.md)** 帮助你学习。
|
||||
|
||||
### JVM
|
||||
**知识点/面试题:** (必看 :+1:)
|
||||
|
||||
* [一 Java内存区域](docs/java/jvm/Java内存区域.md)
|
||||
* [二 JVM垃圾回收](docs/java/jvm/JVM垃圾回收.md)
|
||||
* [三 JDK 监控和故障处理工具](docs/java/jvm/JDK监控和故障处理工具总结.md)
|
||||
* [四 类文件结构](docs/java/jvm/类文件结构.md)
|
||||
* [五 类加载过程](docs/java/jvm/类加载过程.md)
|
||||
* [六 类加载器](docs/java/jvm/类加载器.md)
|
||||
1. **[Java 并发基础常见面试题总结](docs/java/multi-thread/2020最新Java并发基础常见面试题总结.md)**
|
||||
2. **[Java 并发进阶常见面试题总结](docs/java/multi-thread/2020最新Java并发进阶常见面试题总结.md)**
|
||||
|
||||
### I/O
|
||||
**重要知识点详解:**
|
||||
|
||||
* [BIO,NIO,AIO 总结 ](docs/java/BIO-NIO-AIO.md)
|
||||
* [Java IO 与 NIO系列文章](docs/java/Java%20IO与NIO.md)
|
||||
2. **线程池**:[Java 线程池学习总结](./docs/java/multi-thread/java线程池学习总结.md)、[拿来即用的线程池最佳实践](./docs/java/multi-thread/拿来即用的线程池最佳实践.md)
|
||||
4. [ ThreadLocal 关键字解析](docs/java/multi-thread/万字详解ThreadLocal关键字.md)
|
||||
5. [并发容器总结](docs/java/multi-thread/并发容器总结.md)
|
||||
6. [JUC 中的 Atomic 原子类总结](docs/java/multi-thread/Atomic原子类总结.md)
|
||||
7. [AQS 原理以及 AQS 同步组件总结](docs/java/multi-thread/AQS原理以及AQS同步组件总结.md)
|
||||
|
||||
### Java 8
|
||||
### JVM (必看 :+1:)
|
||||
|
||||
* [Java 8 新特性总结](docs/java/What's%20New%20in%20JDK8/Java8Tutorial.md)
|
||||
* [Java 8 学习资源推荐](docs/java/What's%20New%20in%20JDK8/Java8教程推荐.md)
|
||||
1. **[Java 内存区域](docs/java/jvm/Java内存区域.md)**
|
||||
2. **[JVM 垃圾回收](docs/java/jvm/JVM垃圾回收.md)**
|
||||
3. [JDK 监控和故障处理工具](docs/java/jvm/JDK监控和故障处理工具总结.md)
|
||||
4. [类文件结构](docs/java/jvm/类文件结构.md)
|
||||
5. **[类加载过程](docs/java/jvm/类加载过程.md)**
|
||||
6. [类加载器](docs/java/jvm/类加载器.md)
|
||||
7. **[【待完成】最重要的 JVM 参数指南(翻译完善了一半)](docs/java/jvm/最重要的JVM参数指南.md)**
|
||||
9. **[【加餐】大白话带你认识 JVM](docs/java/jvm/[加餐]大白话带你认识JVM.md)**
|
||||
|
||||
### 编程规范
|
||||
### 新特性
|
||||
|
||||
- [Java 编程规范](docs/java/Java编程规范.md)
|
||||
1. **Java 8** :[Java 8 新特性总结](docs/java/new-features/Java8新特性总结.md)、[Java8常用新特性总结](docs/java/new-features/java8-common-new-features.md) 、[Java 8 学习资源推荐](docs/java/new-features/Java8教程推荐.md)、[Java8 forEach 指南](docs/java/new-features/Java8foreach指南.md)
|
||||
2. **Java9~Java14** : [一文带你看遍 JDK9~14 的重要新特性!](./docs/java/new-features/一文带你看遍JDK9到14的重要新特性.md)
|
||||
|
||||
## 网络
|
||||
|
||||
* [计算机网络常见面试题](docs/network/计算机网络.md)
|
||||
* [计算机网络基础知识总结](docs/network/干货:计算机网络知识总结.md)
|
||||
* [HTTPS中的TLS](docs/network/HTTPS中的TLS.md)
|
||||
1. [计算机网络常见面试题](docs/network/计算机网络.md)
|
||||
2. [计算机网络基础知识总结](docs/network/计算机网络知识总结.md)
|
||||
|
||||
## 操作系统
|
||||
|
||||
### Linux相关
|
||||
|
||||
* [后端程序员必备的 Linux 基础知识](docs/operating-system/后端程序员必备的Linux基础知识.md)
|
||||
* [Shell 编程入门](docs/operating-system/Shell.md)
|
||||
1. [操作系统常见问题总结!](docs/operating-system/basis.md)
|
||||
2. [后端程序员必备的 Linux 基础知识](docs/operating-system/linux.md)
|
||||
3. [Shell 编程入门](docs/operating-system/Shell.md)
|
||||
|
||||
## 数据结构与算法
|
||||
|
||||
### 数据结构
|
||||
|
||||
- [数据结构知识学习与面试](docs/dataStructures-algorithms/数据结构.md)
|
||||
- **图解数据结构:**
|
||||
1. [线性数据结构 :数组、链表、栈、队列](docs/dataStructures-algorithms/data-structure/线性数据结构.md)
|
||||
2. [图](docs/dataStructures-algorithms/data-structure/图.md)
|
||||
- [不了解布隆过滤器?一文给你整的明明白白!](docs/dataStructures-algorithms/data-structure/bloom-filter.md)
|
||||
|
||||
### 算法
|
||||
|
||||
- [算法学习资源推荐](docs/dataStructures-algorithms/算法学习资源推荐.md)
|
||||
- [几道常见的子符串算法题总结 ](docs/dataStructures-algorithms/几道常见的子符串算法题.md)
|
||||
- [几道常见的链表算法题总结 ](docs/dataStructures-algorithms/几道常见的链表算法题.md)
|
||||
- [剑指offer部分编程题](docs/dataStructures-algorithms/剑指offer部分编程题.md)
|
||||
- [公司真题](docs/dataStructures-algorithms/公司真题.md)
|
||||
- [回溯算法经典案例之N皇后问题](docs/dataStructures-algorithms/Backtracking-NQueens.md)
|
||||
算法这部分内容非常重要,如果你不知道如何学习算法的话,可以看下我写的:
|
||||
|
||||
- [算法学习书籍+资源推荐](https://www.zhihu.com/question/323359308/answer/1545320858) 。
|
||||
- [如何刷Leetcode?](https://www.zhihu.com/question/31092580/answer/1534887374)
|
||||
|
||||
**常见算法问题总结:**
|
||||
|
||||
- [几道常见的字符串算法题总结 ](docs/dataStructures-algorithms/几道常见的子符串算法题.md)
|
||||
- [几道常见的链表算法题总结 ](docs/dataStructures-algorithms/几道常见的链表算法题.md)
|
||||
- [剑指 offer 部分编程题](docs/dataStructures-algorithms/剑指offer部分编程题.md)
|
||||
|
||||
## 数据库
|
||||
|
||||
### MySQL
|
||||
|
||||
* [MySQL 学习与面试](docs/database/MySQL.md)
|
||||
* [一千行MySQL学习笔记](docs/database/一千行MySQL命令.md)
|
||||
* [MySQL高性能优化规范建议](docs/database/MySQL高性能优化规范建议.md)
|
||||
* [搞定数据库索引就是这么简单](docs/database/MySQL%20Index.md)
|
||||
* [事务隔离级别(图文详解)](docs/database/事务隔离级别(图文详解).md)
|
||||
* [一条SQL语句在MySQL中如何执行的](docs/database/一条sql语句在mysql中如何执行的.md)
|
||||
**总结:**
|
||||
|
||||
1. **[MySQL知识点总结](docs/database/MySQL.md)** (必看 :+1:)
|
||||
2. [阿里巴巴开发手册数据库部分的一些最佳实践](docs/database/阿里巴巴开发手册数据库部分的一些最佳实践.md)
|
||||
3. [一千行 MySQL 学习笔记](docs/database/一千行MySQL命令.md)
|
||||
4. [MySQL 高性能优化规范建议](docs/database/MySQL高性能优化规范建议.md)
|
||||
|
||||
**重要知识点:**
|
||||
|
||||
1. [数据库索引总结 1](docs/database/MySQL%20Index.md)、[数据库索引总结 2](docs/database/数据库索引.md)
|
||||
2. [事务隔离级别(图文详解)](<docs/database/事务隔离级别(图文详解).md>)
|
||||
3. [一条 SQL 语句在 MySQL 中如何执行的](docs/database/一条sql语句在mysql中如何执行的.md)
|
||||
4. [关于数据库中如何存储时间的一点思考](docs/database/关于数据库存储时间的一点思考.md)
|
||||
|
||||
### Redis
|
||||
|
||||
* [Redis 总结](docs/database/Redis/Redis.md)
|
||||
* [Redlock分布式锁](docs/database/Redis/Redlock分布式锁.md)
|
||||
* [如何做可靠的分布式锁,Redlock真的可行么](docs/database/Redis/如何做可靠的分布式锁,Redlock真的可行么.md)
|
||||
2. [Redis 常见问题总结](docs/database/Redis/redis-all.md)
|
||||
3. [面试/工作必备!3种常用的缓存读写策略!](docs/database/Redis/3种常用的缓存读写策略.md)
|
||||
|
||||
## 系统设计
|
||||
|
||||
### 设计模式
|
||||
### 编码之道(必看 :+1:)
|
||||
|
||||
- [设计模式系列文章](docs/system-design/设计模式.md)
|
||||
1. [RestFul API 简明教程](docs/system-design/coding-way/RESTfulAPI简明教程.md)
|
||||
2. [Java 编程规范以及优雅 Java 代码实践总结](docs/java/Java编程规范.md)
|
||||
3. [Java 命名之道](docs/system-design/naming.md)
|
||||
|
||||
### 常用框架
|
||||
|
||||
#### Spring
|
||||
如果你没有接触过 Java Web 开发的话,可以先看一下我总结的 [《J2EE 基础知识》](docs/java/J2EE基础知识.md) 。虽然,这篇文章中的很多内容已经淘汰,但是可以让你对 Java 后台技术发展有更深的认识。
|
||||
|
||||
- [Spring 学习与面试](docs/system-design/framework/Spring学习与面试.md)
|
||||
- [Spring中bean的作用域与生命周期](docs/system-design/framework/SpringBean.md)
|
||||
- [SpringMVC 工作原理详解](docs/system-design/framework/SpringMVC%20%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3.md)
|
||||
#### Spring/SpringBoot (必看 :+1:)
|
||||
|
||||
**知识点/面试题:**
|
||||
|
||||
1. **[Spring 常见问题总结](docs/system-design/framework/spring/Spring常见问题总结.md)**
|
||||
2. **[SpringBoot 入门指南](https://github.com/Snailclimb/springboot-guide)**
|
||||
3. **[面试常问:“讲述一下 SpringBoot 自动装配原理?”](https://www.cnblogs.com/javaguide/p/springboot-auto-config.html)**
|
||||
|
||||
**重要知识点详解:**
|
||||
|
||||
1. **[Spring/Spring Boot 常用注解总结!安排!](./docs/system-design/framework/spring/SpringBoot+Spring常用注解总结.md)**
|
||||
2. **[Spring 事务总结](docs/system-design/framework/spring/Spring事务总结.md)**
|
||||
3. [Spring 中都用到了那些设计模式?](docs/system-design/framework/spring/Spring-Design-Patterns.md)
|
||||
|
||||
#### MyBatis
|
||||
|
||||
- [MyBatis 常见面试题总结](docs/system-design/framework/mybatis/mybatis-interview.md)
|
||||
|
||||
#### Netty (必看 :+1:)
|
||||
|
||||
1. [剖析面试最常见问题之 Netty(上)](https://xiaozhuanlan.com/topic/4028536971)
|
||||
2. [剖析面试最常见问题之 Netty(下)](https://xiaozhuanlan.com/topic/3985146207)
|
||||
|
||||
#### ZooKeeper
|
||||
|
||||
- [ZooKeeper 相关概念总结](docs/system-design/framework/ZooKeeper.md)
|
||||
- [ZooKeeper 数据模型和常见命令](docs/system-design/framework/ZooKeeper数据模型和常见命令.md)
|
||||
> 前两篇文章可能有内容重合部分,推荐都看一遍。
|
||||
|
||||
### 数据通信
|
||||
1. [【入门】ZooKeeper 相关概念总结](docs/system-design/distributed-system/zookeeper/zookeeper-intro.md)
|
||||
2. [【进阶】ZooKeeper 相关概念总结](docs/system-design/distributed-system/zookeeper/zookeeper-plus.md)
|
||||
3. [【实战】ZooKeeper 实战](docs/system-design/distributed-system/zookeeper/zookeeper-in-action.md)
|
||||
|
||||
- [数据通信(RESTful、RPC、消息队列)相关知识点总结](docs/system-design/data-communication/summary.md)
|
||||
- [Dubbo 总结:关于 Dubbo 的重要知识点](docs/system-design/data-communication/dubbo.md)
|
||||
- [消息队列总结](docs/system-design/data-communication/message-queue.md)
|
||||
- [RabbitMQ 入门](docs/system-design/data-communication/RabbitMQ.md)
|
||||
- [RocketMQ的几个简单问题与答案](docs/system-design/data-communication/RocketMQ-Questions.md)
|
||||
### 安全
|
||||
|
||||
### 网站架构
|
||||
#### 认证授权
|
||||
|
||||
**[《认证授权基础》](docs/system-design/authority-certification/basis-of-authority-certification.md)** 这篇文章中我会介绍认证授权常见概念: **Authentication**,**Authorization** 以及 **Cookie**、**Session**、Token、**OAuth 2**、**SSO** 。如果你不清楚这些概念的话,建议好好阅读一下这篇文章。
|
||||
|
||||
- **JWT** :JWT(JSON Web Token)是一种身份认证的方式,JWT 本质上就一段签名的 JSON 格式的数据。由于它是带有签名的,因此接收者便可以验证它的真实性。相关阅读:
|
||||
- [JWT 优缺点分析以及常见问题解决方案](docs/system-design/authority-certification/JWT优缺点分析以及常见问题解决方案.md)
|
||||
- [适合初学者入门 Spring Security With JWT 的 Demo](https://github.com/Snailclimb/spring-security-jwt-guide)
|
||||
|
||||
- **SSO(单点登录)** :**SSO(Single Sign On)** 即单点登录说的是用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。举个例子我们在登陆了京东金融之后,我们同时也成功登陆京东的京东超市、京东家电等子系统。相关阅读:[**SSO 单点登录看这篇就够了!**](docs/system-design/authority-certification/SSO单点登录看这一篇就够了.md)
|
||||
|
||||
#### 数据脱敏
|
||||
|
||||
数据脱敏说的就是我们根据特定的规则对敏感信息数据进行变形,比如我们把手机号、身份证号某些位数使用 * 来代替。相关阅读:
|
||||
|
||||
- [大厂也在用的 6种 数据脱敏方案,严防泄露数据的 “内鬼”](https://www.cnblogs.com/chengxy-nds/p/14107671.html)
|
||||
- [【进阶之路】基于ShardingSphere的线上业务数据脱敏解决方案](https://juejin.cn/post/6906074730437836813)
|
||||
|
||||
### 分布式
|
||||
|
||||
#### CAP 理论
|
||||
|
||||
CAP 也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性) 这三个单词首字母组合。
|
||||
|
||||
关于 CAP 的详细解读请看:[《CAP理论解读》](docs/system-design/distributed-system/CAP理论.md)。
|
||||
|
||||
#### BASE 理论
|
||||
|
||||
**BASE** 是 **Basically Available(基本可用)** 、**Soft-state(软状态)** 和 **Eventually Consistent(最终一致性)** 三个短语的缩写。BASE 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于 CAP 定理逐步演化而来的,它大大降低了我们对系统的要求。
|
||||
|
||||
关于 CAP 的详细解读请看:[《BASE理论解读》](docs/system-design/distributed-system/BASE理论.md)。
|
||||
|
||||
#### Paxos 算法和 Raft 算法
|
||||
|
||||
**Paxos 算法**诞生于 1990 年,这是一种解决分布式系统一致性的经典算法 。但是,由于 Paxos 算法非常难以理解和实现,不断有人尝试简化这一算法。到了2013 年才诞生了一个比 Paxos 算法更易理解和实现的分布式一致性算法—**Raft 算法**。
|
||||
|
||||
#### 搜索引擎
|
||||
|
||||
用于提高搜索效率,功能和浏览器搜索引擎类似。比较常见的搜索引擎是 Elasticsearch(推荐) 和 Solr。
|
||||
|
||||
#### RPC
|
||||
|
||||
RPC 让调用远程服务调用像调用本地方法那样简单。
|
||||
|
||||
Dubbo 是一款国产的 RPC 框架,由阿里开源。相关阅读:
|
||||
|
||||
- [Dubbo 常见问题总结](docs/system-design/distributed-system/rpc/Dubbo.md)
|
||||
- [服务之间的调用为啥不直接用 HTTP 而用 RPC?](docs/system-design/distributed-system/rpc/服务之间的调用为啥不直接用HTTP而用RPC.md)
|
||||
|
||||
#### API 网关
|
||||
|
||||
网关主要用于请求转发、安全认证、协议转换、容灾。
|
||||
|
||||
1. [为什么要网关?你知道有哪些常见的网关系统?](docs/system-design/distributed-system/api-gateway/为什么要网站有哪些常见的网站系统.md)
|
||||
2. [如何设计一个亿级网关(API Gateway)?](docs/system-design/distributed-system/api-gateway/如何设计一个亿级网关.md)
|
||||
|
||||
#### 分布式 id
|
||||
|
||||
在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。比如数据量太大之后,往往需要对进行对数据进行分库分表,分库分表后需要有一个唯一 ID 来标识一条数据或消息,数据库的自增 ID 显然不能满足需求。相关阅读:[为什么要分布式 id ?分布式 id 生成方案有哪些?](docs/system-design/micro-service/分布式id生成方案总结.md)
|
||||
|
||||
#### 分布式事务
|
||||
|
||||
**分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。**
|
||||
|
||||
简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
|
||||
|
||||
### 微服务
|
||||
|
||||
1. [ 大白话入门 Spring Cloud](docs/system-design/micro-service/spring-cloud.md)
|
||||
2. [微服务/分布式大厂真实面试问题解答](https://xiaozhuanlan.com/topic/2895047136)
|
||||
|
||||
### 高并发
|
||||
|
||||
#### 消息队列
|
||||
|
||||
消息队列在分布式系统中主要是为了解耦和削峰。相关阅读: [消息队列常见问题总结](docs/system-design/distributed-system/message-queue/message-queue.md)。
|
||||
|
||||
1. **RabbitMQ** : [RabbitMQ 入门](docs/system-design/distributed-system/message-queue/RabbitMQ入门看这一篇就够了.md)
|
||||
2. **RocketMQ** : [RocketMQ 入门](docs/system-design/distributed-system/message-queue/RocketMQ.md)、[RocketMQ 的几个简单问题与答案](docs/system-design/distributed-system/message-queue/RocketMQ-Questions.md)
|
||||
3. **Kafka** :[Kafka 常见问题总结](docs/system-design/distributed-system/message-queue/Kafka常见面试题总结.md)
|
||||
|
||||
#### 读写分离&分库分表
|
||||
|
||||
读写分离主要是为了将数据库的读和写操作分不到不同的数据库节点上。主服务器负责写,从服务器负责读。另外,一主一从或者一主多从都可以。
|
||||
|
||||
读写分离可以大幅提高读性能,小幅提高写的性能。因此,读写分离更适合单机并发读请求比较多的场景。
|
||||
|
||||
分库分表是为了解决由于库、表数据量过大,而导致数据库性能持续下降的问题。
|
||||
|
||||
常见的分库分表工具有:`sharding-jdbc`(当当)、`TSharding`(蘑菇街)、`MyCAT`(基于 Cobar)、`Cobar`(阿里巴巴)...。 推荐使用 `sharding-jdbc`。 因为,`sharding-jdbc` 是一款轻量级 `Java` 框架,以 `jar` 包形式提供服务,不要我们做额外的运维工作,并且兼容性也很好。
|
||||
|
||||
相关阅读: [读写分离&分库分表常见问题总结](docs/system-design/读写分离&分库分表.md)
|
||||
|
||||
#### 负载均衡
|
||||
|
||||
负载均衡系统通常用于将任务比如用户请求处理分配到多个服务器处理以提高网站、应用或者数据库的性能和可靠性。
|
||||
|
||||
常见的负载均衡系统包括 3 种:
|
||||
|
||||
1. **DNS 负载均衡** :一般用来实现地理级别的均衡。
|
||||
2. **硬件负载均衡** : 通过单独的硬件设备比如 F5 来实现负载均衡功能(硬件的价格一般很贵)。
|
||||
3. **软件负载均衡** :通过负载均衡软件比如 Nginx 来实现负载均衡功能。
|
||||
|
||||
### 高可用
|
||||
|
||||
高可用描述的是一个系统在大部分时间都是可用的,可以为我们提供服务的。高可用代表系统即使在发生硬件故障或者系统升级的时候,服务仍然是可用的 。
|
||||
|
||||
相关阅读: **《[如何设计一个高可用系统?要考虑哪些地方?](docs/system-design/high-availability/如何设计一个高可用系统要考虑哪些地方.md)》** 。
|
||||
|
||||
#### 限流
|
||||
|
||||
限流是从用户访问压力的角度来考虑如何应对系统故障。
|
||||
|
||||
限流为了对服务端的接口接受请求的频率进行限制,防止服务挂掉。比如某一接口的请求限制为 100 个每秒, 对超过限制的请求放弃处理或者放到队列中等待处理。限流可以有效应对突发请求过多。相关阅读:[限流算法有哪些?](docs/system-design/high-availability/limit-request.md)
|
||||
|
||||
#### 降级
|
||||
|
||||
降级是从系统功能优先级的角度考虑如何应对系统故障。
|
||||
|
||||
服务降级指的是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
|
||||
|
||||
#### 熔断
|
||||
|
||||
熔断和降级是两个比较容易混淆的概念,两者的含义并不相同。
|
||||
|
||||
降级的目的在于应对系统自身的故障,而熔断的目的在于应对当前系统依赖的外部系统或者第三方系统的故障。
|
||||
|
||||
#### 排队
|
||||
|
||||
另类的一种限流,类比于现实世界的排队。玩过英雄联盟的小伙伴应该有体会,每次一有活动,就要经历一波排队才能进入游戏。
|
||||
|
||||
#### 集群
|
||||
|
||||
相同的服务部署多份,避免单点故障。
|
||||
|
||||
#### 超时和重试机制
|
||||
|
||||
**一旦用户的请求超过某个时间得不到响应就结束此次请求并抛出异常。** 如果不进行超时设置可能会导致请求响应速度慢,甚至导致请求堆积进而让系统无法在处理请求。
|
||||
|
||||
另外,重试的次数一般设为 3 次,再多次的重试没有好处,反而会加重服务器压力(部分场景使用失败重试机制会不太适合)。
|
||||
|
||||
### 大型网站架构
|
||||
|
||||
- [一文读懂分布式应该学什么](docs/system-design/website-architecture/分布式.md)
|
||||
- [8 张图读懂大型网站技术架构](docs/system-design/website-architecture/8%20张图读懂大型网站技术架构.md)
|
||||
- [【面试精选】关于大型网站系统架构你不得不懂的10个问题](docs/system-design/website-architecture/【面试精选】关于大型网站系统架构你不得不懂的10个问题.md)
|
||||
|
||||
## 面试指南
|
||||
|
||||
### 备战面试
|
||||
|
||||
* [【备战面试1】程序员的简历就该这样写](docs/essential-content-for-interview/PreparingForInterview/程序员的简历之道.md)
|
||||
* [【备战面试2】初出茅庐的程序员该如何准备面试?](docs/essential-content-for-interview/PreparingForInterview/interviewPrepare.md)
|
||||
* [【备战面试3】7个大部分程序员在面试前很关心的问题](docs/essential-content-for-interview/PreparingForInterview/JavaProgrammerNeedKnow.md)
|
||||
* [【备战面试4】Github上开源的Java面试/学习相关的仓库推荐](docs/essential-content-for-interview/PreparingForInterview/JavaInterviewLibrary.md)
|
||||
* [【备战面试5】如果面试官问你“你有什么问题问我吗?”时,你该如何回答](docs/essential-content-for-interview/PreparingForInterview/如果面试官问你“你有什么问题问我吗?”时,你该如何回答.md)
|
||||
* [【备战面试6】美团面试常见问题总结(附详解答案)](docs/essential-content-for-interview/PreparingForInterview/美团面试常见问题总结.md)
|
||||
|
||||
### 常见面试题总结
|
||||
|
||||
* [第一周(2018-8-7)](docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/第一周(2018-8-7).md) (为什么 Java 中只有值传递、==与equals、 hashCode与equals)
|
||||
* [第二周(2018-8-13)](docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/第二周(2018-8-13).md)(String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的?、什么是反射机制?反射机制的应用场景有哪些?......)
|
||||
* [第三周(2018-08-22)](docs/java/collection/Java集合框架常见面试题.md) (Arraylist 与 LinkedList 异同、ArrayList 与 Vector 区别、HashMap的底层实现、HashMap 和 Hashtable 的区别、HashMap 的长度为什么是2的幂次方、HashSet 和 HashMap 区别、ConcurrentHashMap 和 Hashtable 的区别、ConcurrentHashMap线程安全的具体实现方式/底层具体实现、集合框架底层数据结构总结)
|
||||
* [第四周(2018-8-30).md](docs/essential-content-for-interview/MostCommonJavaInterviewQuestions/第四周(2018-8-30).md) (主要内容是几道面试常问的多线程基础题。)
|
||||
|
||||
### 面经
|
||||
|
||||
- [5面阿里,终获offer(2018年秋招)](docs/essential-content-for-interview/BATJrealInterviewExperience/5面阿里,终获offer.md)
|
||||
- [蚂蚁金服2019实习生面经总结(已拿口头offer)](docs/essential-content-for-interview/BATJrealInterviewExperience/蚂蚁金服实习生面经总结(已拿口头offer).md)
|
||||
- [2019年蚂蚁金服、头条、拼多多的面试总结](docs/essential-content-for-interview/BATJrealInterviewExperience/2019alipay-pinduoduo-toutiao.md)
|
||||
- [关于大型网站系统架构你不得不懂的 10 个问题](docs/system-design/website-architecture/关于大型网站系统架构你不得不懂的10个问题.md)
|
||||
|
||||
## 工具
|
||||
|
||||
### Git
|
||||
1. **Java** :[JAD 反编译](docs/java/JAD反编译tricks.md)、[手把手教你定位常见 Java 性能问题](./docs/java/手把手教你定位常见Java性能问题.md)
|
||||
2. **Git** :[Git 入门](docs/tools/Git.md)
|
||||
3. **Github** : [Github小技巧](docs/tools/Github技巧.md)
|
||||
4. **Docker** : [Docker 基本概念解读](docs/tools/Docker.md) 、[一文搞懂 Docker 镜像的常用操作!](docs/tools/Docker-Image.md)
|
||||
|
||||
* [Git入门](docs/tools/Git.md)
|
||||
## Java 学习常见问题汇总
|
||||
|
||||
### Docker
|
||||
1. [Java 学习路线和方法推荐](docs/questions/java-learning-path-and-methods.md)
|
||||
2. [Java 培训四个月能学会吗?](docs/questions/java-training-4-month.md)
|
||||
3. [新手学习 Java,有哪些 Java 相关的博客,专栏,和技术学习网站推荐?](docs/questions/java-learning-website-blog.md)
|
||||
4. [Java 还是大数据,你需要了解这些东西!](docs/questions/java-big-data.md)
|
||||
|
||||
* [Docker 入门](docs/tools/Docker.md)
|
||||
* [一文搞懂 Docker 镜像的常用操作!](docs/tools/Docker-Image.md)
|
||||
---
|
||||
|
||||
## 资料
|
||||
## 其他
|
||||
|
||||
### 书单
|
||||
### 贡献者
|
||||
|
||||
- [Java程序员必备书单](docs/data/java-recommended-books.md)
|
||||
[你可以点此链接查看JavaGuide的所有贡献者。](https://github.com/Snailclimb/JavaGuide/graphs/contributors) 感谢你们让 JavaGuide 变得更好!如果你们来到武汉一定要找我,我请你们吃饭玩耍。
|
||||
|
||||
### Github榜单
|
||||
*悄悄话:JavaGuide 会不定时为贡献者们送福利。*
|
||||
|
||||
- [Java 项目月榜单](docs/github-trending/JavaGithubTrending.md)
|
||||
### 待办
|
||||
|
||||
***
|
||||
- [ ] 数据结构总结重构
|
||||
|
||||
## 待办
|
||||
### 优质原创PDF资源
|
||||
|
||||
- [x] [Java 8 新特性总结](docs/java/What's%20New%20in%20JDK8/Java8Tutorial.md)
|
||||
- [x] [Java 8 新特性详解](docs/java/What's%20New%20in%20JDK8/Java8教程推荐.md)
|
||||
- [ ] Java 多线程类别知识重构(---正在进行中---)
|
||||
- [x] [BIO,NIO,AIO 总结 ](docs/java/BIO-NIO-AIO.md)
|
||||
- [ ] Netty 总结(---正在进行中---)
|
||||
- [ ] 数据结构总结重构(---正在进行中---)
|
||||

|
||||
|
||||
## 说明
|
||||
为了避免恶意传播,微信搜“**Github掘金计划**”后台回复 **“006”** 即可获取。
|
||||
|
||||
### 介绍
|
||||
<img src="https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images-2@main/%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1/qrcode_for_gh_8b9b6034ac19_258.jpg" style="text-align:right"/>
|
||||
|
||||
* **对于 Java 初学者来说:** 本文档倾向于给你提供一个比较详细的学习路径,让你对于Java整体的知识体系有一个初步认识。另外,本文的一些文章
|
||||
也是你学习和复习 Java 知识不错的实践;
|
||||
* **对于非 Java 初学者来说:** 本文档更适合回顾知识,准备面试,搞清面试应该把重心放在那些问题上。要搞清楚这个道理:提前知道那些面试常见,不是为了背下来应付面试,而是为了让你可以更有针对的学习重点。
|
||||
### 捐赠支持
|
||||
|
||||
Markdown 格式参考:[Github Markdown格式](https://guides.github.com/features/mastering-markdown/),表情素材来自:[EMOJI CHEAT SHEET](https://www.webpagefx.com/tools/emoji-cheat-sheet/)。
|
||||
项目的发展离不开你的支持,如果 JavaGuide 帮助到了你找到自己满意的 offer,请作者喝杯咖啡吧 ☕ 后续会继续完善更新!加油!
|
||||
|
||||
利用 docsify 生成文档部署在 Github pages: [docsify 官网介绍](https://docsify.js.org/#/)
|
||||
|
||||
### 关于转载
|
||||
|
||||
如果你需要转载本仓库的一些文章到自己的博客的话,记得注明原文地址就可以了。
|
||||
|
||||
### 如何对该开源文档进行贡献
|
||||
|
||||
1. 笔记内容大多是手敲,所以难免会有笔误,你可以帮我找错别字。
|
||||
2. 很多知识点我可能没有涉及到,所以你可以对其他知识点进行补充。
|
||||
3. 现有的知识点难免存在不完善或者错误,所以你可以对已有知识点的修改/补充。
|
||||
|
||||
### 为什么要做这个开源文档?
|
||||
|
||||
初始想法源于自己的个人那一段比较迷茫的学习经历。主要目的是为了通过这个开源平台来帮助一些在学习 Java 或者面试过程中遇到问题的小伙伴。
|
||||
|
||||
### 投稿
|
||||
|
||||
由于我个人能力有限,很多知识点我可能没有涉及到,所以你可以对其他知识点进行补充。大家也可以对自己的文章进行自荐,对于不错的文章不仅可以成功在本仓库展示出来更可以获得作者送出的 50 元左右的任意书籍进行奖励(当然你也可以直接折现50元)。
|
||||
[点击捐赠支持作者](https://www.yuque.com/snailclimb/dr6cvl/mr44yt#vu3ok)
|
||||
|
||||
### 联系我
|
||||
|
||||
添加我的微信备注“Github”,回复关键字 **“加群”** 即可入群。
|
||||
|
||||

|
||||
|
||||
### Contributor
|
||||
|
||||
下面是笔主收集的一些对本仓库提过有价值的pr或者issue的朋友,人数较多,如果你也对本仓库提过不错的pr或者issue的话,你可以加我的微信与我联系。下面的排名不分先后!
|
||||
|
||||
<a href="https://github.com/fanofxiaofeng">
|
||||
<img src="https://avatars0.githubusercontent.com/u/3983683?s=460&v=4" width="45px"></a>
|
||||
<a href="https://github.com/dongzl">
|
||||
<img src="https://avatars1.githubusercontent.com/u/5917359?s=460&v=4" width="45px"></a>
|
||||
<a href="https://github.com/Gene1994">
|
||||
<img src="https://avatars3.githubusercontent.com/u/24930369?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/spikesp">
|
||||
<img src="https://avatars0.githubusercontent.com/u/12581996?s=460&v=4" width="45px"></a>
|
||||
<a href="https://github.com/illusorycloud">
|
||||
<img src="https://avatars3.githubusercontent.com/u/31980412?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/LiWenGu">
|
||||
<img src="https://avatars0.githubusercontent.com/u/15909210?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/kinglaw1204">
|
||||
<img src="https://avatars1.githubusercontent.com/u/20039931?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/jun1st">
|
||||
<img src="https://avatars2.githubusercontent.com/u/14312378?s=460&v=4" width="45px">
|
||||
</a>"
|
||||
<a href="https://github.com/fantasygg">
|
||||
<img src="https://avatars3.githubusercontent.com/u/13445354?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/debugjoker">
|
||||
<img src="https://avatars3.githubusercontent.com/u/26218005?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/zhyank">
|
||||
<img src="https://avatars0.githubusercontent.com/u/17696240?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/Goose9527">
|
||||
<img src="https://avatars2.githubusercontent.com/u/43314997?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/yuechuanx">
|
||||
<img src="https://avatars3.githubusercontent.com/u/19339293?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<a href="https://github.com/cnLGMing">
|
||||
<img src="https://avatars2.githubusercontent.com/u/15910705?s=460&v=4" width="45px">
|
||||
</a>
|
||||
<img src="https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images-3@main/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/31603935587_.pic_hd.jpg" style="zoom:67%;" />
|
||||
|
||||
### 公众号
|
||||
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号“**JavaGuide**”。
|
||||
|
||||
**《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java面试突击"** 即可免费领取!
|
||||
**《Java 面试突击》:** 由本文档衍生的专为面试而生的《Java 面试突击》V3.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可领取!
|
||||
|
||||
**Java工程师必备学习资源:** 一些Java工程师常用学习资源[公众号](#公众号)后台回复关键字 **“1”** 即可免费无套路获取。
|
||||

|
||||
|
||||

|
||||
|
@ -1,10 +1,12 @@
|
||||
<p align="center">
|
||||
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3logo-透明.png" width=""/>
|
||||
<img src="./media/pictures/logo.png" width="200" height="200"/>
|
||||
</p>
|
||||
|
||||
|
||||
<h1 align="center">Java 学习/面试指南</h1>
|
||||
|
||||
[常用资源](https://shimo.im/docs/MuiACIg1HlYfVxrj/)
|
||||
[GitHub](<https://github.com/Snailclimb/JavaGuide>)
|
||||
[开始阅读](#java)
|
||||
|
||||
|
203
docs/HomePage.md
@ -1,203 +0,0 @@
|
||||
点击订阅[Java面试进阶指南](https://xiaozhuanlan.com/javainterview?rel=javaguide)(专为Java面试方向准备)。[为什么要弄这个专栏?](https://shimo.im/./9BJjNsNg7S4dCnz3/)
|
||||
|
||||
<h1 align="center">Java 学习/面试指南</h1>
|
||||
<p align="center">
|
||||
<a href="https://github.com/Snailclimb/JavaGuide" target="_blank">
|
||||
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-3/logo - 副本.png" width=""/>
|
||||
</a>
|
||||
|
||||
## Java
|
||||
|
||||
### 基础
|
||||
|
||||
* [Java 基础知识回顾](./java/Java基础知识.md)
|
||||
* [J2EE 基础知识回顾](./java/J2EE基础知识.md)
|
||||
|
||||
### 容器
|
||||
|
||||
* [常见面试题](./java/collection/Java集合框架常见面试题.md)
|
||||
* [ArrayList 源码学习](./java/collection/ArrayList.md)
|
||||
* [LinkedList 源码学习](./java/collection/LinkedList.md)
|
||||
* [HashMap(JDK1.8)源码学习](./java/collection/HashMap.md)
|
||||
|
||||
### 并发
|
||||
|
||||
* [Java 并发基础常见面试题总结](./java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md)
|
||||
* [Java 并发进阶常见面试题总结](./java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions.md)
|
||||
* [并发容器总结](./java/Multithread/并发容器总结.md)
|
||||
* [乐观锁与悲观锁](./essential-content-for-interview/面试必备之乐观锁与悲观锁.md)
|
||||
* [JUC 中的 Atomic 原子类总结](./java/Multithread/Atomic.md)
|
||||
* [AQS 原理以及 AQS 同步组件总结](./java/Multithread/AQS.md)
|
||||
|
||||
### JVM
|
||||
|
||||
* [一 Java内存区域](./java/jvm/Java内存区域.md)
|
||||
* [二 JVM垃圾回收](./java/jvm/JVM垃圾回收.md)
|
||||
* [三 JDK 监控和故障处理工具](./java/jvm/JDK监控和故障处理工具总结.md)
|
||||
* [四 类文件结构](./java/jvm/类文件结构.md)
|
||||
* [五 类加载过程](./java/jvm/类加载过程.md)
|
||||
* [六 类加载器](./java/jvm/类加载器.md)
|
||||
|
||||
### I/O
|
||||
|
||||
* [BIO,NIO,AIO 总结 ](./java/BIO-NIO-AIO.md)
|
||||
* [Java IO 与 NIO系列文章](./java/Java%20IO与NIO.md)
|
||||
|
||||
### Java 8
|
||||
|
||||
* [Java 8 新特性总结](./java/What's%20New%20in%20JDK8/Java8Tutorial.md)
|
||||
* [Java 8 学习资源推荐](./java/What's%20New%20in%20JDK8/Java8教程推荐.md)
|
||||
|
||||
### 编程规范
|
||||
|
||||
- [Java 编程规范](./java/Java编程规范.md)
|
||||
|
||||
## 网络
|
||||
|
||||
* [计算机网络常见面试题](./network/计算机网络.md)
|
||||
* [计算机网络基础知识总结](./network/干货:计算机网络知识总结.md)
|
||||
* [HTTPS中的TLS](./network/HTTPS中的TLS.md)
|
||||
|
||||
## 操作系统
|
||||
|
||||
### Linux相关
|
||||
|
||||
* [后端程序员必备的 Linux 基础知识](./operating-system/后端程序员必备的Linux基础知识.md)
|
||||
* [Shell 编程入门](./operating-system/Shell.md)
|
||||
|
||||
## 数据结构与算法
|
||||
|
||||
### 数据结构
|
||||
|
||||
- [数据结构知识学习与面试](./dataStructures-algorithms/数据结构.md)
|
||||
|
||||
### 算法
|
||||
|
||||
- [算法学习资源推荐](./dataStructures-algorithms/算法学习资源推荐.md)
|
||||
- [几道常见的子符串算法题总结 ](./dataStructures-algorithms/几道常见的子符串算法题.md)
|
||||
- [几道常见的链表算法题总结 ](./dataStructures-algorithms/几道常见的链表算法题.md)
|
||||
- [剑指offer部分编程题](./dataStructures-algorithms/剑指offer部分编程题.md)
|
||||
- [公司真题](./dataStructures-algorithms/公司真题.md)
|
||||
- [回溯算法经典案例之N皇后问题](./dataStructures-algorithms/Backtracking-NQueens.md)
|
||||
|
||||
## 数据库
|
||||
|
||||
### MySQL
|
||||
|
||||
* [MySQL 学习与面试](./database/MySQL.md)
|
||||
* [一千行MySQL学习笔记](./database/一千行MySQL命令.md)
|
||||
* [MySQL高性能优化规范建议](./database/MySQL高性能优化规范建议.md)
|
||||
* [搞定数据库索引就是这么简单](./database/MySQL%20Index.md)
|
||||
* [事务隔离级别(图文详解)](./database/事务隔离级别(图文详解).md)
|
||||
* [一条SQL语句在MySQL中如何执行的](./database/一条sql语句在mysql中如何执行的.md)
|
||||
|
||||
### Redis
|
||||
|
||||
* [Redis 总结](./database/Redis/Redis.md)
|
||||
* [Redlock分布式锁](./database/Redis/Redlock分布式锁.md)
|
||||
* [如何做可靠的分布式锁,Redlock真的可行么](./database/Redis/如何做可靠的分布式锁,Redlock真的可行么.md)
|
||||
|
||||
## 系统设计
|
||||
|
||||
### 设计模式
|
||||
|
||||
- [设计模式系列文章](./system-design/设计模式.md)
|
||||
|
||||
### 常用框架
|
||||
|
||||
#### Spring
|
||||
|
||||
- [Spring 学习与面试](./system-design/framework/Spring学习与面试.md)
|
||||
- [Spring中bean的作用域与生命周期](./system-design/framework/SpringBean.md)
|
||||
- [SpringMVC 工作原理详解](./system-design/framework/SpringMVC%20%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E8%AF%A6%E8%A7%A3.md)
|
||||
|
||||
#### ZooKeeper
|
||||
|
||||
- [ZooKeeper 相关概念总结](./system-design/framework/ZooKeeper.md)
|
||||
- [ZooKeeper 数据模型和常见命令](./system-design/framework/ZooKeeper数据模型和常见命令.md)
|
||||
|
||||
### 数据通信
|
||||
|
||||
- [数据通信(RESTful、RPC、消息队列)相关知识点总结](./system-design/data-communication/summary.md)
|
||||
- [Dubbo 总结:关于 Dubbo 的重要知识点](./system-design/data-communication/Dubbo.md)
|
||||
- [消息队列总结](./system-design/data-communication/message-queue.md)
|
||||
- [RabbitMQ 入门](./system-design/data-communication/RabbitMQ.md)
|
||||
- [RocketMQ的几个简单问题与答案](./system-design/data-communication/RocketMQ-Questions.md)
|
||||
|
||||
### 网站架构
|
||||
|
||||
- [一文读懂分布式应该学什么](./system-design/website-architecture/分布式.md)
|
||||
- [8 张图读懂大型网站技术架构](./system-design/website-architecture/8%20张图读懂大型网站技术架构.md)
|
||||
- [【面试精选】关于大型网站系统架构你不得不懂的10个问题](./system-design/website-architecture/【面试精选】关于大型网站系统架构你不得不懂的10个问题.md)
|
||||
|
||||
## 面试指南
|
||||
|
||||
### 备战面试
|
||||
|
||||
* [【备战面试1】程序员的简历就该这样写](./essential-content-for-interview/PreparingForInterview/程序员的简历之道.md)
|
||||
* [【备战面试2】初出茅庐的程序员该如何准备面试?](./essential-content-for-interview/PreparingForInterview/interviewPrepare.md)
|
||||
* [【备战面试3】7个大部分程序员在面试前很关心的问题](./essential-content-for-interview/PreparingForInterview/JavaProgrammerNeedKnow.md)
|
||||
* [【备战面试4】Github上开源的Java面试/学习相关的仓库推荐](./essential-content-for-interview/PreparingForInterview/JavaInterviewLibrary.md)
|
||||
* [【备战面试5】如果面试官问你“你有什么问题问我吗?”时,你该如何回答](./essential-content-for-interview/PreparingForInterview/如果面试官问你“你有什么问题问我吗?”时,你该如何回答.md)
|
||||
* [【备战面试6】美团面试常见问题总结(附详解答案)](./essential-content-for-interview/PreparingForInterview/美团面试常见问题总结.md)
|
||||
|
||||
### 常见面试题总结
|
||||
|
||||
* [第一周(2018-8-7)](./essential-content-for-interview/MostCommonJavaInterviewQuestions/第一周(2018-8-7).md) (为什么 Java 中只有值传递、==与equals、 hashCode与equals)
|
||||
* [第二周(2018-8-13)](./essential-content-for-interview/MostCommonJavaInterviewQuestions/第二周(2018-8-13).md)(String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的?、什么是反射机制?反射机制的应用场景有哪些?......)
|
||||
* [第三周(2018-08-22)](./java/collection/Java集合框架常见面试题.md) (Arraylist 与 LinkedList 异同、ArrayList 与 Vector 区别、HashMap的底层实现、HashMap 和 Hashtable 的区别、HashMap 的长度为什么是2的幂次方、HashSet 和 HashMap 区别、ConcurrentHashMap 和 Hashtable 的区别、ConcurrentHashMap线程安全的具体实现方式/底层具体实现、集合框架底层数据结构总结)
|
||||
* [第四周(2018-8-30).md](./essential-content-for-interview/MostCommonJavaInterviewQuestions/第四周(2018-8-30).md) (主要内容是几道面试常问的多线程基础题。)
|
||||
|
||||
### 面经
|
||||
|
||||
- [5面阿里,终获offer(2018年秋招)](./essential-content-for-interview/BATJrealInterviewExperience/5面阿里,终获offer.md)
|
||||
- [蚂蚁金服2019实习生面经总结(已拿口头offer)](./essential-content-for-interview/BATJrealInterviewExperience/蚂蚁金服实习生面经总结(已拿口头offer).md)
|
||||
- [2019年蚂蚁金服、头条、拼多多的面试总结](./essential-content-for-interview/BATJrealInterviewExperience/2019alipay-pinduoduo-toutiao.md)
|
||||
|
||||
## 工具
|
||||
|
||||
### Git
|
||||
|
||||
* [Git入门](./tools/Git.md)
|
||||
|
||||
### Docker
|
||||
|
||||
* [Docker 入门](./tools/Docker.md)
|
||||
* [一文搞懂 Docker 镜像的常用操作!](./tools/Docker-Image.md)
|
||||
|
||||
## 资料
|
||||
|
||||
### 书单
|
||||
|
||||
- [Java程序员必备书单](./data/java-recommended-books.md)
|
||||
|
||||
### Github榜单
|
||||
|
||||
- [Java 项目月榜单](./github-trending/JavaGithubTrending.md)
|
||||
|
||||
***
|
||||
|
||||
## 待办
|
||||
|
||||
- [x] [Java 8 新特性总结](./java/What's%20New%20in%20JDK8/Java8Tutorial.md)
|
||||
- [x] [Java 8 新特性详解](./java/What's%20New%20in%20JDK8/Java8教程推荐.md)
|
||||
- [ ] Java 多线程类别知识重构(---正在进行中---)
|
||||
- [x] [BIO,NIO,AIO 总结 ](./java/BIO-NIO-AIO.md)
|
||||
- [ ] Netty 总结(---正在进行中---)
|
||||
- [ ] 数据结构总结重构(---正在进行中---)
|
||||
|
||||
## 联系我
|
||||
|
||||
添加我的微信备注“Github”,回复关键字 **“加群”** 即可入群。
|
||||
|
||||

|
||||
|
||||
## 公众号
|
||||
|
||||
- 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
- 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本公众号后台回复 **"Java面试突击"** 即可免费领取!
|
||||
- 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-gold-cdn.xitu.io/2018/11/28/167598cd2e17b8ec?w=258&h=258&f=jpeg&s=27334" width=""/>
|
||||
</p>
|
@ -1,115 +0,0 @@
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [Java](#java)
|
||||
- [基础](#基础)
|
||||
- [并发](#并发)
|
||||
- [JVM](#jvm)
|
||||
- [Java8 新特性](#java8-新特性)
|
||||
- [代码优化](#代码优化)
|
||||
- [网络](#网络)
|
||||
- [操作系统](#操作系统)
|
||||
- [数据结构与算法](#数据结构与算法)
|
||||
- [数据库](#数据库)
|
||||
- [系统设计](#系统设计)
|
||||
- [设计模式](#设计模式)
|
||||
- [常用框架](#常用框架)
|
||||
- [网站架构](#网站架构)
|
||||
- [软件底层](#软件底层)
|
||||
- [其他](#其他)
|
||||
|
||||
<!-- /TOC -->
|
||||
## Java
|
||||
|
||||
### 基础
|
||||
|
||||
- [《Head First Java》](https://book.douban.com/subject/2000732/)(推荐,豆瓣评分 8.7,1.0K+人评价): 可以说是我的 Java 启蒙书籍了,特别适合新手读当然也适合我们用来温故 Java 知识点。
|
||||
- [《Java 核心技术卷 1+卷 2》](https://book.douban.com/subject/25762168/)(推荐): 很棒的两本书,建议有点 Java 基础之后再读,介绍的还是比较深入的,非常推荐。这两本书我一般也会用来巩固知识点,是两本适合放在自己身边的好书。
|
||||
- [《JAVA 网络编程 第 4 版》](https://book.douban.com/subject/26259017/): 可以系统的学习一下网络的一些概念以及网络编程在 Java 中的使用。
|
||||
- [《Java 编程思想 (第 4 版)》](https://book.douban.com/subject/2130190/)(推荐,豆瓣评分 9.1,3.2K+人评价):大部分人称之为Java领域的圣经,但我不推荐初学者阅读,有点劝退的味道。稍微有点基础后阅读更好。
|
||||
|
||||
### 并发
|
||||
|
||||
- [《Java 并发编程之美》](<https://book.douban.com/subject/30351286/>) (推荐):2018 年 10 月出版的一本书,个人感觉非常不错,对每个知识点的讲解都很棒。
|
||||
- [《Java 并发编程的艺术》](https://book.douban.com/subject/26591326/)(推荐,豆瓣评分 7.2,0.2K+人评价): 这本书不是很适合作为 Java 并发入门书籍,需要具备一定的 JVM 基础。我感觉有些东西讲的还是挺深入的,推荐阅读。
|
||||
- [《实战 Java 高并发程序设计》](https://book.douban.com/subject/26663605/)(推荐,豆瓣评分 8.3): 书的质量没的说,推荐大家好好看一下。
|
||||
- [《Java 高并发编程详解》](https://book.douban.com/subject/30255689/)(豆瓣评分 7.6): 2018 年 6 月出版的一本书,内容很详细,但可能又有点过于啰嗦,不过这只是我的感觉。
|
||||
|
||||
### JVM
|
||||
|
||||
- [《深入理解 Java 虚拟机(第 2 版)周志明》](https://book.douban.com/subject/24722612/)(推荐,豆瓣评分 8.9,1.0K+人评价):建议多刷几遍,书中的所有知识点可以通过 JAVA 运行时区域和 JAVA 的内存模型与线程两个大模块罗列完全。
|
||||
- [《实战 JAVA 虚拟机》](https://book.douban.com/subject/26354292/)(推荐,豆瓣评分 8.0,1.0K+人评价):作为入门的了解 Java 虚拟机的知识还是不错的。
|
||||
|
||||
### Java8 新特性
|
||||
|
||||
- [《Java 8 实战》](https://book.douban.com/subject/26772632/) (推荐,豆瓣评分 9.2 ):面向 Java 8 的技能升级,包括 Lambdas、流和函数式编程特性。实战系列的一贯风格让自己快速上手应用起来。Java 8 支持的 Lambda 是精简表达在语法上提供的支持。Java 8 提供了 Stream,学习和使用可以建立流式编程的认知。
|
||||
- [《Java 8 编程参考官方教程》](https://book.douban.com/subject/26556574/) (推荐,豆瓣评分 9.2):也还不错吧。
|
||||
|
||||
### 代码优化
|
||||
|
||||
- [《重构_改善既有代码的设计》](https://book.douban.com/subject/4262627/)(推荐):豆瓣 9.1 分,重构书籍的开山鼻祖。
|
||||
- [《Effective java 》](https://book.douban.com/subject/3360807/)(推荐,豆瓣评分 9.0,1.4K+人评价):本书介绍了在 Java 编程中 78 条极具实用价值的经验规则,这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对 Java 平台设计专家所使用的技术的全面描述,揭示了应该做什么,不应该做什么才能产生清晰、健壮和高效的代码。本书中的每条规则都以简短、独立的小文章形式出现,并通过例子代码加以进一步说明。本书内容全面,结构清晰,讲解详细。可作为技术人员的参考用书。
|
||||
- [《代码整洁之道》](https://book.douban.com/subject/5442024/)(推荐,豆瓣评分 9.1):虽然是用 Java 语言作为例子,全篇都是在阐述 Java 面向对象的思想,但是其中大部分内容其它语言也能应用到。
|
||||
- **阿里巴巴 Java 开发手册(详尽版)** [https://github.com/alibaba/p3c/blob/master/阿里巴巴 Java 开发手册(详尽版).pdf](https://github.com/alibaba/p3c/blob/master/%E9%98%BF%E9%87%8C%E5%B7%B4%E5%B7%B4Java%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C%EF%BC%88%E8%AF%A6%E5%B0%BD%E7%89%88%EF%BC%89.pdf)
|
||||
- **Google Java 编程风格指南:** <http://www.hawstein.com/posts/google-java-style.html>
|
||||
|
||||
|
||||
## 网络
|
||||
|
||||
- [《图解 HTTP》](https://book.douban.com/subject/25863515/)(推荐,豆瓣评分 8.1 , 1.6K+人评价): 讲漫画一样的讲 HTTP,很有意思,不会觉得枯燥,大概也涵盖也 HTTP 常见的知识点。因为篇幅问题,内容可能不太全面。不过,如果不是专门做网络方向研究的小伙伴想研究 HTTP 相关知识的话,读这本书的话应该来说就差不多了。
|
||||
- [《HTTP 权威指南》](https://book.douban.com/subject/10746113/) (推荐,豆瓣评分 8.6):如果要全面了解 HTTP 非此书不可!
|
||||
|
||||
## 操作系统
|
||||
|
||||
- [《鸟哥的 Linux 私房菜》](https://book.douban.com/subject/4889838/)(推荐,,豆瓣评分 9.1,0.3K+人评价):本书是最具知名度的 Linux 入门书《鸟哥的 Linux 私房菜基础学习篇》的最新版,全面而详细地介绍了 Linux 操作系统。全书分为 5 个部分:第一部分着重说明 Linux 的起源及功能,如何规划和安装 Linux 主机;第二部分介绍 Linux 的文件系统、文件、目录与磁盘的管理;第三部分介绍文字模式接口 shell 和管理系统的好帮手 shell 脚本,另外还介绍了文字编辑器 vi 和 vim 的使用方法;第四部分介绍了对于系统安全非常重要的 Linux 账号的管理,以及主机系统与程序的管理,如查看进程、任务分配和作业管理;第五部分介绍了系统管理员 (root) 的管理事项,如了解系统运行状况、系统服务,针对登录文件进行解析,对系统进行备份以及核心的管理等。
|
||||
|
||||
## 数据结构与算法
|
||||
|
||||
- [《大话数据结构》](https://book.douban.com/subject/6424904/)(推荐,豆瓣评分 7.9 , 1K+人评价):入门类型的书籍,读起来比较浅显易懂,适合没有数据结构基础或者说数据结构没学好的小伙伴用来入门数据结构。
|
||||
- [《数据结构与算法分析:C 语言描述》](https://book.douban.com/subject/1139426/)(推荐,豆瓣评分 8.9,1.6K+人评价):本书是《Data Structures and Algorithm Analysis in C》一书第 2 版的简体中译本。原书曾被评为 20 世纪顶尖的 30 部计算机著作之一,作者 Mark Allen Weiss 在数据结构和算法分析方面卓有建树,他的数据结构和算法分析的著作尤其畅销,并受到广泛好评.已被世界 500 余所大学用作教材。
|
||||
- [《算法图解》](https://book.douban.com/subject/26979890/)(推荐,豆瓣评分 8.4,0.6K+人评价):入门类型的书籍,读起来比较浅显易懂,适合没有算法基础或者说算法没学好的小伙伴用来入门。示例丰富,图文并茂,以让人容易理解的方式阐释了算法.读起来比较快,内容不枯燥!
|
||||
- [《算法 第四版》](https://book.douban.com/subject/10432347/)(推荐,豆瓣评分 9.3,0.4K+人评价):Java 语言描述,算法领域经典的参考书,全面介绍了关于算法和数据结构的必备知识,并特别针对排序、搜索、图处理和字符串处理进行了论述。书的内容非常多,可以说是 Java 程序员的必备书籍之一了。
|
||||
|
||||
## 数据库
|
||||
|
||||
- [《高性能 MySQL》](https://book.douban.com/subject/23008813/)(推荐,豆瓣评分 9.3,0.4K+人评价):mysql 领域的经典之作,拥有广泛的影响力。不但适合数据库管理员(dba)阅读,也适合开发人员参考学习。不管是数据库新手还是专家,相信都能从本书有所收获。
|
||||
- [《Redis 实战》](https://book.douban.com/subject/26612779/):如果你想了解 Redis 的一些概念性知识的话,这本书真的非常不错。
|
||||
- [《Redis 设计与实现》](https://book.douban.com/subject/25900156/)(推荐,豆瓣评分 8.5,0.5K+人评价):也还行吧!
|
||||
- [《MySQL 技术内幕-InnoDB 存储引擎》](<https://book.douban.com/subject/24708143/>)(推荐,豆瓣评分 8.7):了解 InnoDB 存储引擎底层原理必备的一本书,比较深入。
|
||||
|
||||
## 系统设计
|
||||
|
||||
### 设计模式
|
||||
|
||||
- [《设计模式 : 可复用面向对象软件的基础》 ](https://book.douban.com/subject/1052241/) (推荐,豆瓣评分 9.1):设计模式的经典!
|
||||
- [《Head First 设计模式(中文版)》](https://book.douban.com/subject/2243615/) (推荐,豆瓣评分 9.2):相当赞的一本设计模式入门书籍。用实际的编程案例讲解算法设计中会遇到的各种问题和需求变更(对的,连需求变更都考虑到了!),并以此逐步推导出良好的设计模式解决办法。
|
||||
|
||||
### 常用框架
|
||||
|
||||
- [《深入分析 Java Web 技术内幕》](https://book.douban.com/subject/25953851/): 感觉还行,涉及的东西也蛮多。
|
||||
- [《Netty 实战》](https://book.douban.com/subject/27038538/)(推荐,豆瓣评分 7.8,92 人评价):内容很细,如果想学 Netty 的话,推荐阅读这本书!
|
||||
- [《从 Paxos 到 Zookeeper》](https://book.douban.com/subject/26292004/)(推荐,豆瓣评分 7.8,0.3K 人评价):简要介绍几种典型的分布式一致性协议,以及解决分布式一致性问题的思路,其中重点讲解了 Paxos 和 ZAB 协议。同时,本书深入介绍了分布式一致性问题的工业解决方案——ZooKeeper,并着重向读者展示这一分布式协调框架的使用方法、内部实现及运维技巧,旨在帮助读者全面了解 ZooKeeper,并更好地使用和运维 ZooKeeper。
|
||||
- [《Spring 实战(第 4 版)》](https://book.douban.com/subject/26767354/)(推荐,豆瓣评分 8.3,0.3K+人评价):不建议当做入门书籍读,入门的话可以找点国人的书或者视频看。这本定位就相当于是关于 Spring 的新华字典,只有一些基本概念的介绍和示例,涵盖了 Spring 的各个方面,但都不够深入。就像作者在最后一页写的那样:“学习 Spring,这才刚刚开始”。
|
||||
- [《RabbitMQ 实战指南》](https://book.douban.com/subject/27591386/):《RabbitMQ 实战指南》从消息中间件的概念和 RabbitMQ 的历史切入,主要阐述 RabbitMQ 的安装、使用、配置、管理、运维、原理、扩展等方面的细节。如果你想浅尝 RabbitMQ 的使用,这本书是你最好的选择;如果你想深入 RabbitMQ 的原理,这本书也是你最好的选择;总之,如果你想玩转 RabbitMQ,这本书一定是最值得看的书之一
|
||||
- [《Spring Cloud 微服务实战》](https://book.douban.com/subject/27025912/):从时下流行的微服务架构概念出发,详细介绍了 Spring Cloud 针对微服务架构中几大核心要素的解决方案和基础组件。对于各个组件的介绍,《Spring Cloud 微服务实战》主要以示例与源码结合的方式来帮助读者更好地理解这些组件的使用方法以及运行原理。同时,在介绍的过程中,还包含了作者在实践中所遇到的一些问题和解决思路,可供读者在实践中作为参考。
|
||||
- [《第一本 Docker 书》](https://book.douban.com/subject/26780404/):Docker 入门书籍!
|
||||
|
||||
### 网站架构
|
||||
|
||||
- [《大型网站技术架构:核心原理与案例分析+李智慧》](https://book.douban.com/subject/25723064/)(推荐):这本书我读过,基本不需要你有什么基础啊~读起来特别轻松,但是却可以学到很多东西,非常推荐了。另外我写过这本书的思维导图,关注我的微信公众号:“Java 面试通关手册”回复“大型网站技术架构”即可领取思维导图。
|
||||
- [《亿级流量网站架构核心技术》](https://book.douban.com/subject/26999243/)(推荐):一书总结并梳理了亿级流量网站高可用和高并发原则,通过实例详细介绍了如何落地这些原则。本书分为四部分:概述、高可用原则、高并发原则、案例实战。从负载均衡、限流、降级、隔离、超时与重试、回滚机制、压测与预案、缓存、池化、异步化、扩容、队列等多方面详细介绍了亿级流量网站的架构核心技术,让读者看后能快速运用到实践项目中。
|
||||
|
||||
### 软件底层
|
||||
|
||||
- [《深入剖析 Tomcat》](https://book.douban.com/subject/10426640/)(推荐,豆瓣评分 8.4,0.2K+人评价):本书深入剖析 Tomcat 4 和 Tomcat 5 中的每个组件,并揭示其内部工作原理。通过学习本书,你将可以自行开发 Tomcat 组件,或者扩展已有的组件。 读完这本书,基本可以摆脱背诵面试题的尴尬。
|
||||
- [《深入理解 Nginx(第 2 版)》](https://book.douban.com/subject/26745255/):作者讲的非常细致,注释都写的都很工整,对于 Nginx 的开发人员非常有帮助。优点是细致,缺点是过于细致,到处都是代码片段,缺少一些抽象。
|
||||
|
||||
## 其他
|
||||
|
||||
- [《黑客与画家》](https://read.douban.com/ebook/387525/?dcs=subject-rec&dcm=douban&dct=2243615):这本书是硅谷创业之父,Y Combinator 创始人 Paul Graham 的文集。之所以叫这个名字,是因为作者认为黑客(并非负面的那个意思)与画家有着极大的相似性,他们都是在创造,而不是完成某个任务。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,145 +0,0 @@
|
||||
# N皇后
|
||||
[51. N皇后](https://leetcode-cn.com/problems/n-queens/)
|
||||
### 题目描述
|
||||
> n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
|
||||
>
|
||||

|
||||
>
|
||||
上图为 8 皇后问题的一种解法。
|
||||
>
|
||||
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
|
||||
>
|
||||
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
|
||||
|
||||
示例:
|
||||
|
||||
```
|
||||
输入: 4
|
||||
输出: [
|
||||
[".Q..", // 解法 1
|
||||
"...Q",
|
||||
"Q...",
|
||||
"..Q."],
|
||||
|
||||
["..Q.", // 解法 2
|
||||
"Q...",
|
||||
"...Q",
|
||||
".Q.."]
|
||||
]
|
||||
解释: 4 皇后问题存在两个不同的解法。
|
||||
```
|
||||
|
||||
### 问题分析
|
||||
约束条件为每个棋子所在的行、列、对角线都不能有另一个棋子。
|
||||
|
||||
使用一维数组表示一种解法,下标(index)表示行,值(value)表示该行的Q(皇后)在哪一列。
|
||||
每行只存储一个元素,然后递归到下一行,这样就不用判断行了,只需要判断列和对角线。
|
||||
### Solution1
|
||||
当result[row] = column时,即row行的棋子在column列。
|
||||
|
||||
对于[0, row-1]的任意一行(i 行),若 row 行的棋子和 i 行的棋子在同一列,则有result[i] == column;
|
||||
若 row 行的棋子和 i 行的棋子在同一对角线,等腰直角三角形两直角边相等,即 row - i == Math.abs(result[i] - column)
|
||||
|
||||
布尔类型变量 isValid 的作用是剪枝,减少不必要的递归。
|
||||
```
|
||||
public List<List<String>> solveNQueens(int n) {
|
||||
// 下标代表行,值代表列。如result[0] = 3 表示第1行的Q在第3列
|
||||
int[] result = new int[n];
|
||||
List<List<String>> resultList = new LinkedList<>();
|
||||
dfs(resultList, result, 0, n);
|
||||
return resultList;
|
||||
}
|
||||
|
||||
void dfs(List<List<String>> resultList, int[] result, int row, int n) {
|
||||
// 递归终止条件
|
||||
if (row == n) {
|
||||
List<String> list = new LinkedList<>();
|
||||
for (int x = 0; x < n; ++x) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int y = 0; y < n; ++y)
|
||||
sb.append(result[x] == y ? "Q" : ".");
|
||||
list.add(sb.toString());
|
||||
}
|
||||
resultList.add(list);
|
||||
return;
|
||||
}
|
||||
for (int column = 0; column < n; ++column) {
|
||||
boolean isValid = true;
|
||||
result[row] = column;
|
||||
/*
|
||||
* 逐行往下考察每一行。同列,result[i] == column
|
||||
* 同对角线,row - i == Math.abs(result[i] - column)
|
||||
*/
|
||||
for (int i = row - 1; i >= 0; --i) {
|
||||
if (result[i] == column || row - i == Math.abs(result[i] - column)) {
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isValid) dfs(resultList, result, row + 1, n);
|
||||
}
|
||||
}
|
||||
```
|
||||
### Solution2
|
||||
使用LinkedList表示一种解法,下标(index)表示行,值(value)表示该行的Q(皇后)在哪一列。
|
||||
|
||||
解法二和解法一的不同在于,相同列以及相同对角线的校验。
|
||||
将对角线抽象成【一次函数】这个简单的数学模型,根据一次函数的截距是常量这一特性进行校验。
|
||||
|
||||
这里,我将右上-左下对角线,简称为“\”对角线;左上-右下对角线简称为“/”对角线。
|
||||
|
||||
“/”对角线斜率为1,对应方程为y = x + b,其中b为截距。
|
||||
对于线上任意一点,均有y - x = b,即row - i = b;
|
||||
定义一个布尔类型数组anti_diag,将b作为下标,当anti_diag[b] = true时,表示相应对角线上已经放置棋子。
|
||||
但row - i有可能为负数,负数不能作为数组下标,row - i 的最小值为-n(当row = 0,i = n时),可以加上n作为数组下标,即将row -i + n 作为数组下标。
|
||||
row - i + n 的最大值为 2n(当row = n,i = 0时),故anti_diag的容量设置为 2n 即可。
|
||||
|
||||

|
||||
|
||||
“\”对角线斜率为-1,对应方程为y = -x + b,其中b为截距。
|
||||
对于线上任意一点,均有y + x = b,即row + i = b;
|
||||
同理,定义数组main_diag,将b作为下标,当main_diag[row + i] = true时,表示相应对角线上已经放置棋子。
|
||||
|
||||
有了两个校验对角线的数组,再来定义一个用于校验列的数组cols,这个太简单啦,不解释。
|
||||
|
||||
**解法二时间复杂度为O(n!),在校验相同列和相同对角线时,引入三个布尔类型数组进行判断。相比解法一,少了一层循环,用空间换时间。**
|
||||
|
||||
```
|
||||
List<List<String>> resultList = new LinkedList<>();
|
||||
|
||||
public List<List<String>> solveNQueens(int n) {
|
||||
boolean[] cols = new boolean[n];
|
||||
boolean[] main_diag = new boolean[2 * n];
|
||||
boolean[] anti_diag = new boolean[2 * n];
|
||||
LinkedList<Integer> result = new LinkedList<>();
|
||||
dfs(result, 0, cols, main_diag, anti_diag, n);
|
||||
return resultList;
|
||||
}
|
||||
|
||||
void dfs(LinkedList<Integer> result, int row, boolean[] cols, boolean[] main_diag, boolean[] anti_diag, int n) {
|
||||
if (row == n) {
|
||||
List<String> list = new LinkedList<>();
|
||||
for (int x = 0; x < n; ++x) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int y = 0; y < n; ++y)
|
||||
sb.append(result.get(x) == y ? "Q" : ".");
|
||||
list.add(sb.toString());
|
||||
}
|
||||
resultList.add(list);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (cols[i] || main_diag[row + i] || anti_diag[row - i + n])
|
||||
continue;
|
||||
result.add(i);
|
||||
cols[i] = true;
|
||||
main_diag[row + i] = true;
|
||||
anti_diag[row - i + n] = true;
|
||||
dfs(result, row + 1, cols, main_diag, anti_diag, n);
|
||||
result.removeLast();
|
||||
cols[i] = false;
|
||||
main_diag[row + i] = false;
|
||||
anti_diag[row - i + n] = false;
|
||||
}
|
||||
}
|
||||
```
|
297
docs/dataStructures-algorithms/data-structure/bloom-filter.md
Normal file
@ -0,0 +1,297 @@
|
||||
海量数据处理以及缓存穿透这两个场景让我认识了 布隆过滤器 ,我查阅了一些资料来了解它,但是很多现成资料并不满足我的需求,所以就决定自己总结一篇关于布隆过滤器的文章。希望通过这篇文章让更多人了解布隆过滤器,并且会实际去使用它!
|
||||
|
||||
下面我们将分为几个方面来介绍布隆过滤器:
|
||||
|
||||
1. 什么是布隆过滤器?
|
||||
2. 布隆过滤器的原理介绍。
|
||||
3. 布隆过滤器使用场景。
|
||||
4. 通过 Java 编程手动实现布隆过滤器。
|
||||
5. 利用Google开源的Guava中自带的布隆过滤器。
|
||||
6. Redis 中的布隆过滤器。
|
||||
|
||||
### 1.什么是布隆过滤器?
|
||||
|
||||
首先,我们需要了解布隆过滤器的概念。
|
||||
|
||||
布隆过滤器(Bloom Filter)是一个叫做 Bloom 的老哥于1970年提出的。我们可以把它看作由二进制向量(或者说位数组)和一系列随机映射函数(哈希函数)两部分组成的数据结构。相比于我们平时常用的的 List、Map 、Set 等数据结构,它占用空间更少并且效率更高,但是缺点是其返回的结果是概率性的,而不是非常准确的。理论情况下添加到集合中的元素越多,误报的可能性就越大。并且,存放在布隆过滤器的数据不容易删除。
|
||||
|
||||

|
||||
|
||||
位数组中的每个元素都只占用 1 bit ,并且每个元素只能是 0 或者 1。这样申请一个 100w 个元素的位数组只占用 1000000Bit / 8 = 125000 Byte = 125000/1024 kb ≈ 122kb 的空间。
|
||||
|
||||
总结:**一个名叫 Bloom 的人提出了一种来检索元素是否在给定大集合中的数据结构,这种数据结构是高效且性能很好的,但缺点是具有一定的错误识别率和删除难度。并且,理论情况下,添加到集合中的元素越多,误报的可能性就越大。**
|
||||
|
||||
### 2.布隆过滤器的原理介绍
|
||||
|
||||
**当一个元素加入布隆过滤器中的时候,会进行如下操作:**
|
||||
|
||||
1. 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
|
||||
2. 根据得到的哈希值,在位数组中把对应下标的值置为 1。
|
||||
|
||||
**当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行如下操作:**
|
||||
|
||||
1. 对给定元素再次进行相同的哈希计算;
|
||||
2. 得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
|
||||
|
||||
举个简单的例子:
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
如图所示,当字符串存储要加入到布隆过滤器中时,该字符串首先由多个哈希函数生成不同的哈希值,然后在对应的位数组的下表的元素设置为 1(当位数组初始化时 ,所有位置均为0)。当第二次存储相同字符串时,因为先前的对应位置已设置为 1,所以很容易知道此值已经存在(去重非常方便)。
|
||||
|
||||
如果我们需要判断某个字符串是否在布隆过滤器中时,只需要对给定字符串再次进行相同的哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
|
||||
|
||||
**不同的字符串可能哈希出来的位置相同,这种情况我们可以适当增加位数组大小或者调整我们的哈希函数。**
|
||||
|
||||
综上,我们可以得出:**布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。**
|
||||
|
||||
### 3.布隆过滤器使用场景
|
||||
|
||||
1. 判断给定数据是否存在:比如判断一个数字是否存在于包含大量数字的数字集中(数字集很大,5亿以上!)、 防止缓存穿透(判断请求的数据是否有效避免直接绕过缓存请求数据库)等等、邮箱的垃圾邮件过滤、黑名单功能等等。
|
||||
2. 去重:比如爬给定网址的时候对已经爬取过的 URL 去重。
|
||||
|
||||
### 4.通过 Java 编程手动实现布隆过滤器
|
||||
|
||||
我们上面已经说了布隆过滤器的原理,知道了布隆过滤器的原理之后就可以自己手动实现一个了。
|
||||
|
||||
如果你想要手动实现一个的话,你需要:
|
||||
|
||||
1. 一个合适大小的位数组保存数据
|
||||
2. 几个不同的哈希函数
|
||||
3. 添加元素到位数组(布隆过滤器)的方法实现
|
||||
4. 判断给定元素是否存在于位数组(布隆过滤器)的方法实现。
|
||||
|
||||
下面给出一个我觉得写的还算不错的代码(参考网上已有代码改进得到,对于所有类型对象皆适用):
|
||||
|
||||
```java
|
||||
import java.util.BitSet;
|
||||
|
||||
public class MyBloomFilter {
|
||||
|
||||
/**
|
||||
* 位数组的大小
|
||||
*/
|
||||
private static final int DEFAULT_SIZE = 2 << 24;
|
||||
/**
|
||||
* 通过这个数组可以创建 6 个不同的哈希函数
|
||||
*/
|
||||
private static final int[] SEEDS = new int[]{3, 13, 46, 71, 91, 134};
|
||||
|
||||
/**
|
||||
* 位数组。数组中的元素只能是 0 或者 1
|
||||
*/
|
||||
private BitSet bits = new BitSet(DEFAULT_SIZE);
|
||||
|
||||
/**
|
||||
* 存放包含 hash 函数的类的数组
|
||||
*/
|
||||
private SimpleHash[] func = new SimpleHash[SEEDS.length];
|
||||
|
||||
/**
|
||||
* 初始化多个包含 hash 函数的类的数组,每个类中的 hash 函数都不一样
|
||||
*/
|
||||
public MyBloomFilter() {
|
||||
// 初始化多个不同的 Hash 函数
|
||||
for (int i = 0; i < SEEDS.length; i++) {
|
||||
func[i] = new SimpleHash(DEFAULT_SIZE, SEEDS[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加元素到位数组
|
||||
*/
|
||||
public void add(Object value) {
|
||||
for (SimpleHash f : func) {
|
||||
bits.set(f.hash(value), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定元素是否存在于位数组
|
||||
*/
|
||||
public boolean contains(Object value) {
|
||||
boolean ret = true;
|
||||
for (SimpleHash f : func) {
|
||||
ret = ret && bits.get(f.hash(value));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态内部类。用于 hash 操作!
|
||||
*/
|
||||
public static class SimpleHash {
|
||||
|
||||
private int cap;
|
||||
private int seed;
|
||||
|
||||
public SimpleHash(int cap, int seed) {
|
||||
this.cap = cap;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算 hash 值
|
||||
*/
|
||||
public int hash(Object value) {
|
||||
int h;
|
||||
return (value == null) ? 0 : Math.abs(seed * (cap - 1) & ((h = value.hashCode()) ^ (h >>> 16)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
测试:
|
||||
|
||||
```java
|
||||
String value1 = "https://javaguide.cn/";
|
||||
String value2 = "https://github.com/Snailclimb";
|
||||
MyBloomFilter filter = new MyBloomFilter();
|
||||
System.out.println(filter.contains(value1));
|
||||
System.out.println(filter.contains(value2));
|
||||
filter.add(value1);
|
||||
filter.add(value2);
|
||||
System.out.println(filter.contains(value1));
|
||||
System.out.println(filter.contains(value2));
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
false
|
||||
false
|
||||
true
|
||||
true
|
||||
```
|
||||
|
||||
测试:
|
||||
|
||||
```java
|
||||
Integer value1 = 13423;
|
||||
Integer value2 = 22131;
|
||||
MyBloomFilter filter = new MyBloomFilter();
|
||||
System.out.println(filter.contains(value1));
|
||||
System.out.println(filter.contains(value2));
|
||||
filter.add(value1);
|
||||
filter.add(value2);
|
||||
System.out.println(filter.contains(value1));
|
||||
System.out.println(filter.contains(value2));
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```java
|
||||
false
|
||||
false
|
||||
true
|
||||
true
|
||||
```
|
||||
|
||||
### 5.利用Google开源的 Guava中自带的布隆过滤器
|
||||
|
||||
自己实现的目的主要是为了让自己搞懂布隆过滤器的原理,Guava 中布隆过滤器的实现算是比较权威的,所以实际项目中我们不需要手动实现一个布隆过滤器。
|
||||
|
||||
首先我们需要在项目中引入 Guava 的依赖:
|
||||
|
||||
```java
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>28.0-jre</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
实际使用如下:
|
||||
|
||||
我们创建了一个最多存放 最多 1500个整数的布隆过滤器,并且我们可以容忍误判的概率为百分之(0.01)
|
||||
|
||||
```java
|
||||
// 创建布隆过滤器对象
|
||||
BloomFilter<Integer> filter = BloomFilter.create(
|
||||
Funnels.integerFunnel(),
|
||||
1500,
|
||||
0.01);
|
||||
// 判断指定元素是否存在
|
||||
System.out.println(filter.mightContain(1));
|
||||
System.out.println(filter.mightContain(2));
|
||||
// 将元素添加进布隆过滤器
|
||||
filter.put(1);
|
||||
filter.put(2);
|
||||
System.out.println(filter.mightContain(1));
|
||||
System.out.println(filter.mightContain(2));
|
||||
```
|
||||
|
||||
在我们的示例中,当`mightContain()` 方法返回*true*时,我们可以99%确定该元素在过滤器中,当过滤器返回*false*时,我们可以100%确定该元素不存在于过滤器中。
|
||||
|
||||
**Guava 提供的布隆过滤器的实现还是很不错的(想要详细了解的可以看一下它的源码实现),但是它有一个重大的缺陷就是只能单机使用(另外,容量扩展也不容易),而现在互联网一般都是分布式的场景。为了解决这个问题,我们就需要用到 Redis 中的布隆过滤器了。**
|
||||
|
||||
### 6.Redis 中的布隆过滤器
|
||||
|
||||
#### 6.1介绍
|
||||
|
||||
Redis v4.0 之后有了 Module(模块/插件) 功能,Redis Modules 让 Redis 可以使用外部模块扩展其功能 。布隆过滤器就是其中的 Module。详情可以查看 Redis 官方对 Redis Modules 的介绍 :https://redis.io/modules
|
||||
|
||||
另外,官网推荐了一个 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
|
||||
- ......
|
||||
|
||||
RedisBloom 提供了多种语言的客户端支持,包括:Python、Java、JavaScript 和 PHP。
|
||||
|
||||
#### 6.2使用Docker安装
|
||||
|
||||
如果我们需要体验 Redis 中的布隆过滤器非常简单,通过 Docker 就可以了!我们直接在 Google 搜索**docker redis bloomfilter** 然后在排除广告的第一条搜素结果就找到了我们想要的答案(这是我平常解决问题的一种方式,分享一下),具体地址:https://hub.docker.com/r/redislabs/rebloom/ (介绍的很详细 )。
|
||||
|
||||
**具体操作如下:**
|
||||
|
||||
```
|
||||
➜ ~ docker run -p 6379:6379 --name redis-redisbloom redislabs/rebloom:latest
|
||||
➜ ~ docker exec -it redis-redisbloom bash
|
||||
root@21396d02c252:/data# redis-cli
|
||||
127.0.0.1:6379>
|
||||
```
|
||||
|
||||
#### 6.3常用命令一览
|
||||
|
||||
> 注意: key:布隆过滤器的名称,item : 添加的元素。
|
||||
|
||||
1. **`BF.ADD `**:将元素添加到布隆过滤器中,如果该过滤器尚不存在,则创建该过滤器。格式:`BF.ADD {key} {item}`。
|
||||
2. **`BF.MADD `** : 将一个或多个元素添加到“布隆过滤器”中,并创建一个尚不存在的过滤器。该命令的操作方式`BF.ADD`与之相同,只不过它允许多个输入并返回多个值。格式:`BF.MADD {key} {item} [item ...]` 。
|
||||
3. **`BF.EXISTS` ** : 确定元素是否在布隆过滤器中存在。格式:`BF.EXISTS {key} {item}`。
|
||||
4. **`BF.MEXISTS`** : 确定一个或者多个元素是否在布隆过滤器中存在格式:`BF.MEXISTS {key} {item} [item ...]`。
|
||||
|
||||
另外,`BF.RESERVE` 命令需要单独介绍一下:
|
||||
|
||||
这个命令的格式如下:
|
||||
|
||||
`BF.RESERVE {key} {error_rate} {capacity} [EXPANSION expansion] `。
|
||||
|
||||
下面简单介绍一下每个参数的具体含义:
|
||||
|
||||
1. key:布隆过滤器的名称
|
||||
2. error_rate :误报的期望概率。这应该是介于0到1之间的十进制值。例如,对于期望的误报率0.1%(1000中为1),error_rate应该设置为0.001。该数字越接近零,则每个项目的内存消耗越大,并且每个操作的CPU使用率越高。
|
||||
3. capacity: 过滤器的容量。当实际存储的元素个数超过这个值之后,性能将开始下降。实际的降级将取决于超出限制的程度。随着过滤器元素数量呈指数增长,性能将线性下降。
|
||||
|
||||
可选参数:
|
||||
|
||||
- expansion:如果创建了一个新的子过滤器,则其大小将是当前过滤器的大小乘以`expansion`。默认扩展值为2。这意味着每个后续子过滤器将是前一个子过滤器的两倍。
|
||||
|
||||
#### 6.4实际使用
|
||||
|
||||
```shell
|
||||
127.0.0.1:6379> BF.ADD myFilter java
|
||||
(integer) 1
|
||||
127.0.0.1:6379> BF.ADD myFilter javaguide
|
||||
(integer) 1
|
||||
127.0.0.1:6379> BF.EXISTS myFilter java
|
||||
(integer) 1
|
||||
127.0.0.1:6379> BF.EXISTS myFilter javaguide
|
||||
(integer) 1
|
||||
127.0.0.1:6379> BF.EXISTS myFilter github
|
||||
(integer) 0
|
||||
```
|
||||
|
BIN
docs/dataStructures-algorithms/data-structure/pictures/图/图.png
Normal file
After Width: | Height: | Size: 24 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:11:57.013Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="B6MeSWDkbwDCadNkhl8o" version="13.4.5" type="device"><diagram id="eB035yKR6LJ1ptgLsSbM" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 50 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:17:01.344Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="_-veHJdt8GyZRG2S5hoi" version="13.4.5" type="device"><diagram id="0kJCOVOw4mzpPHpbBMS6" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 54 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:16:36.629Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="nIl0iNm7MDMt_npkXUNt" version="13.4.5" type="device"><diagram id="3SB0d3gYCvH_tWMfFJ3L" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:25:55.080Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="8WiHXBkHtgRdnOqTx93g" version="13.4.5" type="device"><diagram id="4oGk1i_xgKgO_iptbDTg" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:27:27.506Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="wdYw9fv3CkE8DWJwPg9r" version="13.4.5" type="device"><diagram id="mifzlOfuH5t4TGWJTCWP" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 54 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:28:55.104Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="QBg4q7WOk0wTtNt3vV2g" version="13.4.5" type="device"><diagram id="PY_AcYCt6KOp1RRPQ6IG" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 55 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:06:03.875Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="J37IoMD33P3y1AJRJJDg" version="13.4.5" type="device"><diagram id="48qJXlv0dCf99pJ8nCJ8" name="Page-1">3Vrfc9o4EP5r/FiP9dPyI1C4PrQznWbuLu2bwAp4zljUVgL0rz8JS8YWJqEJhDQ8gLRarWR9n1baNQEaLTd/lXy1+CJTkQcwSjcB+hhACEBE9Y+RbGtJEkW1YF5mqVXaC26yX8IKndp9loqqo6ikzFW26gpnsijETHVkvCzluqt2J/PuqCs+FweCmxnPD6X/Zqla1FIG4738k8jmCzcyoEndsuRO2T5JteCpXLdEaBygUSmlqkvLzUjkZvHcutT9Jkdam4mVolCndPjnVrHB3+vP6MvP9bf1YvTrB5p8sFYeeH5vHziANNf2hlMzZbW160B/3pt5Du9koT5UO5QGWgGS1WbfqEtz8xs5G3oyUye0i9BYhHp2GkRdGa4XmRI3Kz4zLWvNIy1bqGWua0AXebWqkb3LNiI1k8jyfCRzWe4Mobs7QWczLa9UKf8TrZY0Tqaab27wB1EqsTm6fKABRbNZyKVQ5Var2A6QkLqLJTLBFtd1ixZWtGgxwsm4JeK8sbzHShcsXL8BHeyBzl/iIh2YPaBrhSxEd1n1c5fbW12JXOV7u/Jx06ltXW2TqVtnQZd3fUJia/tOpuL61PMS6cFO89Zez13elzPxyENbb6J4ORfqET3Uj2ULK9KDlZOVIucqe+hOtw9AO8JXmekHaahCEA6ThMSQxURXGOgSB0ZhFGEYA4hjTEHSNV8vgbXY3s7+IACEhLbMII+eKMToYA5ulHoBD0bZEbFZr+dzE13CrcD36FYI7roVCq7sVvAloEPvETocedAlV4aOXAI6/B6h8w/zq0NHLwEdeI/Q+bvu6vew+IX3sDPejlyA9NTtiF7zdoQZCiFrLi4QdC8uFLBu8zOvRzENY9Dcf+KkOwgGfa2vdDtib4cx5ETGHNmTr8MY5N2RSOLt5VNJgcEThi4MfPJC4F3w5SKsU4KvJsxrYr5Wr0cCvTOSDJ9IMnJVknnnShw9k2S+IYpel2QugHg5yxpmfW+1PMGyFrFOSSeckWWnpgbwVQ8/zwPF+Lksw08YOsIyDTvfttRWRqF6ZMJ+fEo7WUZdqC2el8J9acmXOsoTCXw9N4n+CAL7kZPv3U4+i8kT/vbSbhK+FY69PRd51YP4/TCsLwvqMcyEwtmM55/5VORfZZWpTBa6aSqVkkuNvlMY5NncNCjpReXVgq+MseVmbt7AhVNeZbOQl3Ugzks1KOa70aIQxCyOccIQ00FXjA0CQ0PxRsE4u5RXCxPd74x7Eb4gAO/ieJN+6ET+5tNE/u5FGezLBUwmSH/OkwsAiIaMdUDGFDSiFl8RoCEkh5yFaC8/e2YA9OVSr0kACDBkKGKQERjrb+oTAMcoYSABNGY00lxBfxghnKuwZEDsMDdESA8N0MWyQ+B4UrZa8SI4JbkX9SX3gjENdPiajIJxHAxxMIDBOAkGIBgOgjEx32wSjCfBcBQkg8iW2Ag0JdyUYFNq5+nr6R1JGmqIlEfDDq72uGzTxYq45fFMQyrKHoIvszTNj6UjS3lfpDs2Rj41z0AfSLsnBut5GdObXIS/Tx9d3b/7r4+c/T8o0Ph/</diagram></mxfile>
|
After Width: | Height: | Size: 29 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-19T10:20:44.307Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="ESmHvqxonDcUpBYq1nN6" version="13.4.5" type="device"><diagram id="hxIiUUJUZaj2I0l0lVWC" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 39 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T02:16:10.479Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="k1GW4w4a8eAJ490EksK_" version="13.4.5" type="device"><diagram id="iLyd2CJtKd0pi580Kj23" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 48 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T02:03:26.196Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="d1dYeaT7O6CDyL8oXn3e" version="13.4.5" type="device"><diagram id="FzY8Ofb7lh1C4Hv9kcNF" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 41 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T02:02:47.191Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="9FneORdGZP89w_YEhYEE" version="13.4.5" type="device"><diagram id="5t9a7hb9L9JXNoTOYt3Y" name="Page-1">3VlJl9soEP41HDNP+3KUbLnnvUnykvRhOrlhCUtKY+HGqC3n1wcksBbHiqfHjpfng6migKKWrwABc7KsHihcZR9IgjAwtKQC5hQYhq5rDv8TnG3D8TWtYaQ0T6RQy3jMfyDJVGJlnqB1T5ARglm+6jNjUhQoZj0epJRs+mILgvurrmCK9hiPMcT73H/zhGUN1zPclv83ytNMraw7ftOzhEpY7mSdwYRsOiwzAuaEEsKa1rKaICyMp+zSjJsd6N0pRlHBjhngf3yqXrzvwQspzRhZ0xn7uHgnZ1mzrdowSvj+JUkoy0hKCoijlhtSUhYJErNqnGpl3hOy4kydM78jxrbSmbBkhLMytsSyF1U5e+q0v4qp/rIlNa3kzDWxVUTB6PapS3RGCbIdVlNq3IIUTCpi1HSO8YRgQuvdmosFcuKY89eMkmfU6dED8eM9+3ZWRiMljdGIcU0Zr5CmiI05wWgEhek7K0g3PiCyRHxLXIAiDFn+2g9NKCM83cm1QcAbMg7+Q0wYl4yJNg6+dnp+HRO34FvvqlwrtX6FuJQrAcPBXP9w3vO481IKWKot/G5dmzjgAoa9qtpO3krFv6bm4CrNFXM/hjDmmC1iZZPlDD2uYG3fDS8b/UiA61UD5Iu8EhF1vFsT159r2phbXxFlqBp1hOw1HAnbsm5ZnqQ3nSogWVmnACjeyV1njWZlQYrTpuFboFm/HWi2j0xf56rS1z5H+hr3mL623U/fHX2p9HXO4TrzHl1n6X3XOeaFXef+UeS990Oxd5PI650jfa17TN/hweni6evvWfQsp6Txq8qBM9IV56m6lN7YDUcVjz91Tr6R6+rR3rwu2NXH36cuWXuH3nxzLb7GKLBPHQVy6CeSc53bU7p6HVa1whgUgUZROWoQSzs1/kd4Geco6/o9lvXhqfzi7yHG+IPIVT5dt2Dwluofl/S11l2/0seS86BGQCncdgRWAg3Wh0HFGV79/cF3kd/Ja+Pyar4D8rzRaPxWpNp+e/+cfaY/4uD5kfzzZZHPlrH6TtMFqsgGYQSCCEQWCGcg0EHkAK6bPwGRAby6IYFIRBIQ38hUVLSQxSPH901zH8UecpaVczGnFwHfA5EPPBf4fBVPLCqWs4HnAb/7ftQsJBFPajHdQSo9BlN161eYKhbzQ+BHohFYwJueZJOfprPfas/3yy0QuvV+Z8B3pC7c+MIULgjMjk1c0fDd2hcTEAa18KxuuELA2z+fcgRmfZTpJ7E85XQzXrIgztOCkzFPacT5ocDzPIY4kB3LPEnwoRrTB0MM5wiHMH5Oa/5w8RaLdEvSHZAJ69+JLpDeoNJY+5Vm91G1W2qGh4cjSg0n2w+wTa62n7HN6Cc=</diagram></mxfile>
|
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:05:17.571Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="lASJpg5Gj8Rj6XXFs8kq" version="13.4.5" type="device"><diagram id="Os7iT1nvRo9H4LHwW2Lk" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 46 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:46:44.567Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="WI8UacRvKQP8SDKfnp3P" version="13.4.5" type="device"><diagram id="30Cgl7Zjz6abKrNjlxG3" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 49 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:48:28.432Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="CvE3ne5Dy7j87GUfEagG" version="13.4.5" type="device"><diagram id="MGIOEjQTzljkyi59MuMZ" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:49:29.286Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="RE2yXWkCA_Scx2RjOj4p" version="13.4.5" type="device"><diagram id="y_rejXg0V4Tvyw0t4x3a" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:50:48.777Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="gXIhcKcSGzLmD1JHt_Ix" version="13.4.5" type="device"><diagram id="_B_lt8A41LRaqq57eL1I" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:51:41.770Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="-MVK6w1W0YCMoigiOuOH" version="13.4.5" type="device"><diagram id="u5kIgGnqsk5s8JGxCxMm" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:53:46.145Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="4-NnNN85JfE_xheUeO5_" version="13.4.5" type="device"><diagram id="pNHz1chyNOht0hOROlCL" name="Page-1">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</diagram></mxfile>
|
After Width: | Height: | Size: 54 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-20T05:41:52.303Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="LCfAV2g3_ip9GB3J-c8-" version="13.4.5" type="device"><diagram id="zlsS6F1qTQtopJuG0L2e" name="Page-1">3VpLk6M2EP41HMclifcRv5JDtmoqU6nNHhkjYzYYsUKewfn1kYzEy2Dj8TOew1hqdUsgfV833aDpk3X+G/XT1TcS4FhDIMg1faohBCGw+I+QbAuJC0AhCGkUSKVK8Bb9i6VQqW2iAGcNRUZIzKK0KVyQJMEL1pD5lJLPptqSxM1VUz/Ee4K3hR/vS79HAVsVUgfZlfx3HIUrtTK03GJk7StleSfZyg/IZ02kzzR9QglhRWudT3AsNk/tS2E37xktL4zihA0xCN0cvE1oDl+nKZn+lX3/Mwxf5CwZ26obxgG/f9kllK1ISBI/nlXS8WJDP7CYFPIOJZsk2PUA71UGfxCSSpWfmLGtPFl/wwgXrdg6lqM4j9jf0ly0f4j2yJS9aV4bmm5VJ2F0K4xewAhAJSgsLegoQWW86zWsXzGN1phhKoUZo+QfPCExobt90Odznf/xkWKHxLb0brzaRbKhC3xgt5EEsE9DzA7ouSU8OK8w4ddJt9yO4thn0UfzOnwJ8LDUk6Yepf62ppCSKGFZbeZXIeAKiqu2BKpkKnRaeDqir+tuXZ83iitQvdqtVKIdRk/Aq9zBDz/eyG3QkBXzvRy/N2Bs/doIXo2XJGEv2Q57HldAZppXg7wVil+g5uCX9K6E+8SIY+50BAE+VxHDb6m/O+tP7veaiPaztPBEyygXzBgvoziuIWu5xNZi0YW5wHbfASgX/8CU4fww6vZRIg1Kv6OO05D9z5obk6JVzYMpWReuamd7+tHpHUfX3uIk8ITP5r2EJLjlKBTpQYPwx8gu3Qs8wb1ckPEqjB2lvNl9mLXDMjsOS8lO8wx7VDZcd+S6po0c20Sm7gC7CR3E3SwwkA2RYRsWdPXmAsUuyDkPeAzDtkamVZunBVB9ZOjVRcDWKsUW7q1yKc9iPE4kLOH5o47Ow5FQGg2lxU3CnTkQ+xB0g38wqs/yS+Y1Qgp6xpBiwmZIKR/S7xVSrIfhbD28wC8xFj4CY+3zotVtCGtfg7D6MxJWbz2i8+z7voR1HoawzSD7P6asO5Cy9pmU/VJOiawmAA1wOKc04EH96+SU7jX8ifGM/qSdU97dnyi4XPbs4DOeXTsW3L0eAGHH2Z1SELigEx1amOvLVG6UphvOCDll/oxUhlw+jncPn5qmmwYc2bBMxO1mmo5ao47dXOXKaTrsqgDeCTVDQy+6J2j0VkiFbovQQ2GhO0cmuvbJn11APOdNQ+stQ/kc2PPod0GUDc3Jel4a3AZlqP0yAHwRZe2J+Ey3RZlxKZRdIME4VsW7IMoGF6p78ogbObOWD9KNrzozeGSiHpidmgC1ExqVEF01oYFdJc1zPeVABN/PTw6tNt8VwEY7g2q7t8HvUtARh3ttP2k9CsYe0EfeNRQ/D8T6C71Z6ieD8nvQld9rM1sbe5qHZGM812aO5kHNc4TEM4RwZmn8Aded7HSMnbKjjfkoVza18UxzyhLPO61m3rdyxcxiQlP8d7j5XBtPNNcDsuVMjLKlly1UturliOLGeyoSDOesSbJmRUGSsF5+kCI/jsKEdxecE+KTmLEoNUQLP/bkwDoKgriv1tEsygZ+tirrtdr5hYsSv6omqPo1OnVWLhDop05P5YJ3qw+yChxXn7Xps/8A</diagram></mxfile>
|
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 20 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-11-09T06:56:47.201Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="psfGgMNo4nzL4IA4JRTh" version="13.4.5" type="device"><diagram id="syluBulH61jp7zpPZQu4" name="Page-1">7Vpbc6M2FP41PCbDHfNobCedTne60+xMm77JIGN1BaJCvu2vrwTiToLt4Mt0sw8bdJCOpO87+o4koxizaP9MQbL+QgKIFV0N9ooxV3Rd01Sb/xGWQ25xVTU3hBQFslJleEE/oDQW1TYogGmjIiMEM5Q0jT6JY+izhg1QSnbNaiuCm70mIIQdw4sPcNf6JwrYOrdOdKey/wJRuC561mw3fxOBorKcSboGAdnVTMZCMWaUEJY/RfsZxAK8Ape83dMbb8uBURizYxq8vk42//y9/DZPbY8eovTLD8d6kF62AG/khOVg2aFAYLdGDL4kwBflHWdZMbw1izAvafwRpEmO+wrtIe/KWyGMZwQTmjU3VquV7vvcnjJKvsPam8Be2pbN38gxQMrg/s3JaSVkPNYgiSCjB15FNtAdibIMM82W5V1FWhlT6xphpRHIQAlL3xWW/EHCeQK0+jC0MA6mIkZ5KSYxbAJ7Lo4w6ET0IIo1lKwekAobhRgwtG267wNO9vCVIN7xMEmFi5RsqA9lq3ooDzhyW34YoCFkHT8ZjeWsz2fW+GS2RYjpuI/WONyak5tya94tt3wMGYRDan6rGDBai1JXR1rdHUdvhAAnBRxq1RJRIX0naCf9/VQRlXscNb6su42vm2lHmwZzrLhpO7pU3JhXiBv7M24GaDCMkeKm4+hCcVP0c9G4cT7jZmj5tncYZ+vNkVuVD+uNe4W4mQzHDSWbOBBHvrnKaR84Jt7kVGg2gStwHDoTtvPGaEdC9/8AqqU1UXVvjWrR2ZEa52OQpshvItlBTM3+8TdCyF6kI0LZmoQkBnhRWT1/Q7cZY8IRB5Ee/hL0PRpuUX7l5Qf1UXWswjLfS4bz0qFe+gop4shAKo3HCqqu5oLzHlTmLZW39FOcEc89IRhtRxfK2O0BW+rAsPR3ql9Gp7XuDV6ySdedBcDXO3sv5GXmryuKNAGMwlisGx5tIiY9oR7IB3gqX0QoCLKF0KdWTT1bkZjJO17dGunmz25lx64cOT0h3Y6Y8dRI7xJCkp+HD1Mz7ouP027rTswObQDPzhb1VKE+DuWJEVOCe8uMYKoDQn70raHVv9UbeyuunpQQ2lulqySEI24w73+T2fmpoEdGrrzL7Lm5W9jKVFUmE2XhKlNN8Wyx4M4QewGe4y5zRWnDDe0M7juXffuNhVzjSzOvqvs9N2YVX5biTRWXU6ouCWMk+qSsuva6HWWnXVZ9MFUfm0Nvdlpq5RrDNIvf2D58w2m2HF34RzXtiNukn5dYrc3GubR2HF2a1r77LBsL3QzQtkGv/e9GfOfiCVl9kLo45TUwXLHqLX8K5d/MS5qAuNeNoPwhzTgXXjQr2fd4WZiKN1fcmbKYKHxDOLGUxZPizRR3+hIDhGcYRUteGURCbuNlmtS65oDkvTdHxM3Z1JpWMZwPjPMCmBUIWGLW01kGBZ+4k1k4CE457/z/Z8TWm2WJz69gC57Fl1dnwcGtOSKFeTC3jpkpy++shONQSIl89kkkRCXf1NY0QzQi3CNiYh25oojBEmIP+N/DzHf7UM4b9wlR3orQANKeFk8gQlj08AwoiEgcFMOQwKiS6t/LoThqzqeP4vAb31Yac6My/CaioGHx8p1Mw/aHVC9jpLvf1l3LBXflvFh9G5crVvWFobH4Dw==</diagram></mxfile>
|
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 16 KiB |
153
docs/dataStructures-algorithms/data-structure/图.md
Normal file
@ -0,0 +1,153 @@
|
||||
# 图
|
||||
|
||||
> 开头还是求点赞,求转发!原创优质公众号,希望大家能让更多人看到我们的文章。
|
||||
>
|
||||
> 图片都是我们手绘的,可以说非常用心了!
|
||||
|
||||
图是一种较为复杂的非线性结构。 **为啥说其较为复杂呢?**
|
||||
|
||||
根据前面的内容,我们知道:
|
||||
|
||||
- 线性数据结构的元素满足唯一的线性关系,每个元素(除第一个和最后一个外)只有一个直接前趋和一个直接后继。
|
||||
- 树形数据结构的元素之间有着明显的层次关系。
|
||||
|
||||
但是,树形结构的元素之间的关系是任意的。
|
||||
|
||||
**何为图呢?** 简单来说,图就是由顶点的有穷非空集合和顶点之间的边组成的集合。通常表示为:**G(V,E)**,其中,G表示一个图,V表示顶点的集合,E表示边的集合。
|
||||
|
||||
下图所展示的就是图这种数据结构,并且还是一张有向图。
|
||||
|
||||

|
||||
|
||||
图在我们日常生活中的例子很多!比如我们在社交软件上好友关系就可以用图来表示。
|
||||
|
||||
## 图的基本概念
|
||||
|
||||
### 顶点
|
||||
图中的数据元素,我们称之为顶点,图至少有一个顶点(非空有穷集合)
|
||||
|
||||
对应到好友关系图,每一个用户就代表一个顶点。
|
||||
|
||||
### 边
|
||||
顶点之间的关系用边表示。
|
||||
|
||||
对应到好友关系图,两个用户是好友的话,那两者之间就存在一条边。
|
||||
|
||||
### 度
|
||||
度表示一个顶点包含多少条边,在有向图中,还分为出度和入度,出度表示从该顶点出去的边的条数,入度表示进入该顶点的边的条数。
|
||||
|
||||
对应到好友关系图,度就代表了某个人的好友数量。
|
||||
|
||||
### 无向图和有向图
|
||||
边表示的是顶点之间的关系,有的关系是双向的,比如同学关系,A是B的同学,那么B也肯定是A的同学,那么在表示A和B的关系时,就不用关注方向,用不带箭头的边表示,这样的图就是无向图。
|
||||
|
||||
有的关系是有方向的,比如父子关系,师生关系,微博的关注关系,A是B的爸爸,但B肯定不是A的爸爸,A关注B,B不一定关注A。在这种情况下,我们就用带箭头的边表示二者的关系,这样的图就是有向图。
|
||||
|
||||
### 无权图和带权图
|
||||
|
||||
对于一个关系,如果我们只关心关系的有无,而不关心关系有多强,那么就可以用无权图表示二者的关系。
|
||||
|
||||
对于一个关系,如果我们既关心关系的有无,也关心关系的强度,比如描述地图上两个城市的关系,需要用到距离,那么就用带权图来表示,带权图中的每一条边一个数值表示权值,代表关系的强度。
|
||||
|
||||

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

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

|
||||
|
||||
邻接矩阵存储的方式优点是简单直接(直接使用一个二维数组即可),并且,在获取两个定点之间的关系的时候也非常高效(直接获取指定位置的数组元素的值即可)。但是,这种存储方式的缺点也比较明显,那就是比较浪费空间,
|
||||
|
||||
### 邻接表存储
|
||||
|
||||
针对上面邻接矩阵比较浪费内存空间的问题,诞生了图的另外一种存储方法—**邻接表** 。
|
||||
|
||||
邻接链表使用一个链表来存储某个顶点的所有后继相邻顶点。对于图中每个顶点Vi,把所有邻接于Vi的顶点Vj链成一个单链表,这个单链表称为顶点Vi的 **邻接表**。如下图所示:
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||

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

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

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

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

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

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

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

|
||||
|
||||
### 深度优先搜索
|
||||
|
||||
深度优先搜索就是“一条路走到黑”,从源顶点开始,一直走到没有后继节点,才回溯到上一顶点,然后继续“一条路走到黑”,如下图所示:
|
||||
|
||||

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

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

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

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

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

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

|
||||
|
310
docs/dataStructures-algorithms/data-structure/线性数据结构.md
Normal file
@ -0,0 +1,310 @@
|
||||
# 线性数据结构
|
||||
|
||||
> 开头还是求点赞,求转发!原创优质公众号,希望大家能让更多人看到我们的文章。
|
||||
>
|
||||
> 图片都是我们手绘的,可以说非常用心了!
|
||||
|
||||
## 1. 数组
|
||||
|
||||
**数组(Array)** 是一种很常见的数据结构。它由相同类型的元素(element)组成,并且是使用一块连续的内存来存储。
|
||||
|
||||
我们直接可以利用元素的索引(index)可以计算出该元素对应的存储地址。
|
||||
|
||||
数组的特点是:**提供随机访问** 并且容量有限。
|
||||
|
||||
```java
|
||||
假如数组的长度为 n。
|
||||
访问:O(1)//访问特定位置的元素
|
||||
插入:O(n )//最坏的情况发生在插入发生在数组的首部并需要移动所有元素时
|
||||
删除:O(n)//最坏的情况发生在删除数组的开头发生并需要移动第一元素后面所有的元素时
|
||||
```
|
||||
|
||||

|
||||
|
||||
## 2. 链表
|
||||
|
||||
### 2.1. 链表简介
|
||||
|
||||
**链表(LinkedList)** 虽然是一种线性表,但是并不会按线性的顺序存储数据,使用的不是连续的内存空间来存储数据。
|
||||
|
||||
链表的插入和删除操作的复杂度为 O(1) ,只需要知道目标位置元素的上一个元素即可。但是,在查找一个节点或者访问特定位置的节点的时候复杂度为 O(n) 。
|
||||
|
||||
使用链表结构可以克服数组需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但链表不会节省空间,相比于数组会占用更多的空间,因为链表中每个节点存放的还有指向其他节点的指针。除此之外,链表不具有数组随机读取的优点。
|
||||
|
||||
### 2.2. 链表分类
|
||||
|
||||
**常见链表分类:**
|
||||
|
||||
1. 单链表
|
||||
2. 双向链表
|
||||
3. 循环链表
|
||||
4. 双向循环链表
|
||||
|
||||
```java
|
||||
假如链表中有n个元素。
|
||||
访问:O(n)//访问特定位置的元素
|
||||
插入删除:O(1)//必须要要知道插入元素的位置
|
||||
```
|
||||
|
||||
#### 2.2.1. 单链表
|
||||
|
||||
**单链表** 单向链表只有一个方向,结点只有一个后继指针 next 指向后面的节点。因此,链表这种数据结构通常在物理内存上是不连续的。我们习惯性地把第一个结点叫作头结点,链表通常有一个不保存任何值的 head 节点(头结点),通过头结点我们可以遍历整个链表。尾结点通常指向 null。
|
||||
|
||||

|
||||
|
||||
#### 2.2.2. 循环链表
|
||||
|
||||
**循环链表** 其实是一种特殊的单链表,和单链表不同的是循环链表的尾结点不是指向 null,而是指向链表的头结点。
|
||||
|
||||

|
||||
|
||||
#### 2.2.3. 双向链表
|
||||
|
||||
**双向链表** 包含两个指针,一个 prev 指向前一个节点,一个 next 指向后一个节点。
|
||||
|
||||

|
||||
|
||||
#### 2.2.4. 双向循环链表
|
||||
|
||||
**双向循环链表** 最后一个节点的 next 指向 head,而 head 的 prev 指向最后一个节点,构成一个环。
|
||||
|
||||

|
||||
|
||||
### 2.3. 应用场景
|
||||
|
||||
- 如果需要支持随机访问的话,链表没办法做到。如
|
||||
- 果需要存储的数据元素的个数不确定,并且需要经常添加和删除数据的话,使用链表比较合适。
|
||||
- 如果需要存储的数据元素的个数确定,并且不需要经常添加和删除数据的话,使用数组比较合适。
|
||||
|
||||
### 2.4. 数组 vs 链表
|
||||
|
||||
- 数据支持随机访问,而链表不支持。
|
||||
- 数组使用的是连续内存空间对 CPU 的缓存机制友好,链表则相反。
|
||||
- 数据的大小固定,而链表则天然支持动态扩容。如果声明的数组过小,需要另外申请一个更大的内存空间存放数组元素,然后将原数组拷贝进去,这个操作是比较耗时的!
|
||||
|
||||
## 3. 栈
|
||||
|
||||
### 3.1. 栈简介
|
||||
|
||||
**栈** (stack)只允许在有序的线性数据集合的一端(称为栈顶 top)进行加入数据(push)和移除数据(pop)。因而按照 **后进先出(LIFO, Last In First Out)** 的原理运作。**在栈中,push 和 pop 的操作都发生在栈顶。**
|
||||
|
||||
栈常用一维数组或链表来实现,用数组实现的栈叫作 **顺序栈** ,用链表实现的栈叫作 **链式栈** 。
|
||||
|
||||
```java
|
||||
假设堆栈中有n个元素。
|
||||
访问:O(n)//最坏情况
|
||||
插入删除:O(1)//顶端插入和删除元素
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 3.2. 栈的常见应用常见应用场景
|
||||
|
||||
当我们我们要处理的数据只涉及在一端插入和删除数据,并且满足 **后进先出(LIFO, Last In First Out)** 的特性时,我们就可以使用栈这个数据结构。
|
||||
|
||||
#### 3.2.1. 实现浏览器的回退和前进功能
|
||||
|
||||
我们只需要使用两个栈(Stack1 和 Stack2)和就能实现这个功能。比如你按顺序查看了 1,2,3,4 这四个页面,我们依次把 1,2,3,4 这四个页面压入 Stack1 中。当你想回头看 2 这个页面的时候,你点击回退按钮,我们依次把 4,3 这两个页面从 Stack1 弹出,然后压入 Stack2 中。假如你又想回到页面 3,你点击前进按钮,我们将 3 页面从 Stack2 弹出,然后压入到 Stack1 中。示例图如下:
|
||||
|
||||

|
||||
|
||||
#### 3.2.2. 检查符号是否成对出现
|
||||
|
||||
> 给定一个只包括 `'('`,`')'`,`'{'`,`'}'`,`'['`,`']'` 的字符串,判断该字符串是否有效。
|
||||
>
|
||||
> 有效字符串需满足:
|
||||
>
|
||||
> 1. 左括号必须用相同类型的右括号闭合。
|
||||
> 2. 左括号必须以正确的顺序闭合。
|
||||
>
|
||||
> 比如 "()"、"()[]{}"、"{[]}" 都是有效字符串,而 "(]" 、"([)]" 则不是。
|
||||
|
||||
这个问题实际是 Leetcode 的一道题目,我们可以利用栈 `Stack` 来解决这个问题。
|
||||
|
||||
1. 首先我们将括号间的对应规则存放在 `Map` 中,这一点应该毋容置疑;
|
||||
2. 创建一个栈。遍历字符串,如果字符是左括号就直接加入`stack`中,否则将`stack` 的栈顶元素与这个括号做比较,如果不相等就直接返回 false。遍历结束,如果`stack`为空,返回 `true`。
|
||||
|
||||
```java
|
||||
public boolean isValid(String s){
|
||||
// 括号之间的对应规则
|
||||
HashMap<Character, Character> mappings = new HashMap<Character, Character>();
|
||||
mappings.put(')', '(');
|
||||
mappings.put('}', '{');
|
||||
mappings.put(']', '[');
|
||||
Stack<Character> stack = new Stack<Character>();
|
||||
char[] chars = s.toCharArray();
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
if (mappings.containsKey(chars[i])) {
|
||||
char topElement = stack.empty() ? '#' : stack.pop();
|
||||
if (topElement != mappings.get(chars[i])) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
stack.push(chars[i]);
|
||||
}
|
||||
}
|
||||
return stack.isEmpty();
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.3. 反转字符串
|
||||
|
||||
将字符串中的每个字符先入栈再出栈就可以了。
|
||||
|
||||
#### 3.2.4. 维护函数调用
|
||||
|
||||
最后一个被调用的函数必须先完成执行,符合栈的 **后进先出(LIFO, Last In First Out)** 特性。
|
||||
|
||||
### 3.3. 栈的实现
|
||||
|
||||
栈既可以通过数组实现,也可以通过链表来实现。不管基于数组还是链表,入栈、出栈的时间复杂度都为 O(1)。
|
||||
|
||||
下面我们使用数组来实现一个栈,并且这个栈具有`push()`、`pop()`(返回栈顶元素并出栈)、`peek()` (返回栈顶元素不出栈)、`isEmpty()`、`size()`这些基本的方法。
|
||||
|
||||
> 提示:每次入栈之前先判断栈的容量是否够用,如果不够用就用`Arrays.copyOf()`进行扩容;
|
||||
|
||||
```java
|
||||
public class MyStack {
|
||||
private int[] storage;//存放栈中元素的数组
|
||||
private int capacity;//栈的容量
|
||||
private int count;//栈中元素数量
|
||||
private static final int GROW_FACTOR = 2;
|
||||
|
||||
//不带初始容量的构造方法。默认容量为8
|
||||
public MyStack() {
|
||||
this.capacity = 8;
|
||||
this.storage=new int[8];
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
//带初始容量的构造方法
|
||||
public MyStack(int initialCapacity) {
|
||||
if (initialCapacity < 1)
|
||||
throw new IllegalArgumentException("Capacity too small.");
|
||||
|
||||
this.capacity = initialCapacity;
|
||||
this.storage = new int[initialCapacity];
|
||||
this.count = 0;
|
||||
}
|
||||
|
||||
//入栈
|
||||
public void push(int value) {
|
||||
if (count == capacity) {
|
||||
ensureCapacity();
|
||||
}
|
||||
storage[count++] = value;
|
||||
}
|
||||
|
||||
//确保容量大小
|
||||
private void ensureCapacity() {
|
||||
int newCapacity = capacity * GROW_FACTOR;
|
||||
storage = Arrays.copyOf(storage, newCapacity);
|
||||
capacity = newCapacity;
|
||||
}
|
||||
|
||||
//返回栈顶元素并出栈
|
||||
private int pop() {
|
||||
if (count == 0)
|
||||
throw new IllegalArgumentException("Stack is empty.");
|
||||
count--;
|
||||
return storage[count];
|
||||
}
|
||||
|
||||
//返回栈顶元素不出栈
|
||||
private int peek() {
|
||||
if (count == 0){
|
||||
throw new IllegalArgumentException("Stack is empty.");
|
||||
}else {
|
||||
return storage[count-1];
|
||||
}
|
||||
}
|
||||
|
||||
//判断栈是否为空
|
||||
private boolean isEmpty() {
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
//返回栈中元素的个数
|
||||
private int size() {
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
验证
|
||||
|
||||
```java
|
||||
MyStack myStack = new MyStack(3);
|
||||
myStack.push(1);
|
||||
myStack.push(2);
|
||||
myStack.push(3);
|
||||
myStack.push(4);
|
||||
myStack.push(5);
|
||||
myStack.push(6);
|
||||
myStack.push(7);
|
||||
myStack.push(8);
|
||||
System.out.println(myStack.peek());//8
|
||||
System.out.println(myStack.size());//8
|
||||
for (int i = 0; i < 8; i++) {
|
||||
System.out.println(myStack.pop());
|
||||
}
|
||||
System.out.println(myStack.isEmpty());//true
|
||||
myStack.pop();//报错:java.lang.IllegalArgumentException: Stack is empty.
|
||||
```
|
||||
|
||||
## 4. 队列
|
||||
|
||||
### 4.1. 队列简介
|
||||
|
||||
**队列** 是 **先进先出( FIFO,First In, First Out)** 的线性表。在具体应用中通常用链表或者数组来实现,用数组实现的队列叫作 **顺序队列** ,用链表实现的队列叫作 **链式队列** 。**队列只允许在后端(rear)进行插入操作也就是 入队 enqueue,在前端(front)进行删除操作也就是出队 dequeue**
|
||||
|
||||
队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加。
|
||||
|
||||
```java
|
||||
假设队列中有n个元素。
|
||||
访问:O(n)//最坏情况
|
||||
插入删除:O(1)//后端插入前端删除元素
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 4.2. 队列分类
|
||||
|
||||
#### 4.2.1. 单队列
|
||||
|
||||
单队列就是常见的队列, 每次添加元素时,都是添加到队尾。单队列又分为 **顺序队列(数组实现)** 和 **链式队列(链表实现)**。
|
||||
|
||||
**顺序队列存在“假溢出”的问题也就是明明有位置却不能添加的情况。**
|
||||
|
||||
假设下图是一个顺序队列,我们将前两个元素 1,2 出队,并入队两个元素 7,8。当进行入队、出队操作的时候,front 和 rear 都会持续往后移动,当 rear 移动到最后的时候,我们无法再往队列中添加数据,即使数组中还有空余空间,这种现象就是 **”假溢出“** 。除了假溢出问题之外,如下图所示,当添加元素 8 的时候,rear 指针移动到数组之外(越界)。
|
||||
|
||||
> 为了避免当只有一个元素的时候,队头和队尾重合使处理变得麻烦,所以引入两个指针,front 指针指向对头元素,rear 指针指向队列最后一个元素的下一个位置,这样当 front 等于 rear 时,此队列不是还剩一个元素,而是空队列。——From 《大话数据结构》
|
||||
|
||||

|
||||
|
||||
#### 4.2.2. 循环队列
|
||||
|
||||
循环队列可以解决顺序队列的假溢出和越界问题。解决办法就是:从头开始,这样也就会形成头尾相接的循环,这也就是循环队列名字的由来。
|
||||
|
||||
还是用上面的图,我们将 rear 指针指向数组下标为 0 的位置就不会有越界问题了。当我们再向队列中添加元素的时候, rear 向后移动。
|
||||
|
||||

|
||||
|
||||
顺序队列中,我们说 `front==rear` 的时候队列为空,循环队列中则不一样,也可能为满,如上图所示。解决办法有两种:
|
||||
|
||||
1. 可以设置一个标志变量 `flag`,当 `front==rear` 并且 `flag=0` 的时候队列为空,当`front==rear` 并且 `flag=1` 的时候队列为满。
|
||||
2. 队列为空的时候就是 `front==rear` ,队列满的时候,我们保证数组还有一个空闲的位置,rear 就指向这个空闲位置,如下图所示,那么现在判断队列是否为满的条件就是: `(rear+1) % QueueSize= front` 。
|
||||
|
||||

|
||||
|
||||
### 4.3. 常见应用场景
|
||||
|
||||
当我们需要按照一定顺序来处理数据的时候可以考虑使用队列这个数据结构。
|
||||
|
||||
- **阻塞队列:** 阻塞队列可以看成在队列基础上加了阻塞操作的队列。当队列为空的时候,出队操作阻塞,当队列满的时候,入队操作阻塞。使用阻塞队列我们可以很容易实现“生产者 - 消费者“模型。
|
||||
- **线程池中的请求/任务队列:** 线程池中没有空闲线程时,新的任务请求线程资源时,线程池该如何处理呢?答案是将这些请求放在队列中,当有空闲线程的时候,会循环中反复从队列中获取任务来执行。队列分为无界队列(基于链表)和有界队列(基于数组)。无界队列的特点就是可以一直入列,除非系统资源耗尽,比如 :`FixedThreadPool` 使用无界队列 `LinkedBlockingQueue`。但是有界队列就不一样了,当队列满的话后面再有任务/请求就会拒绝,在 Java 中的体现就是会抛出`java.util.concurrent.RejectedExecutionException` 异常。
|
||||
- Linux 内核进程队列(按优先级排队)
|
||||
- 现实生活中的派对,播放器上的播放列表;
|
||||
- 消息队列
|
||||
- 等等......
|
@ -1,254 +0,0 @@
|
||||
# 网易 2018
|
||||
|
||||
下面三道编程题来自网易2018校招编程题,这三道应该来说是非常简单的编程题了,这些题目大家稍微有点编程和数学基础的话应该没什么问题。看答案之前一定要自己先想一下如果是自己做的话会怎么去做,然后再对照这我的答案看看,和你自己想的有什么区别?那一种方法更好?
|
||||
|
||||
## 问题
|
||||
|
||||
### 一 获得特定数量硬币问题
|
||||
|
||||
小易准备去魔法王国采购魔法神器,购买魔法神器需要使用魔法币,但是小易现在一枚魔法币都没有,但是小易有两台魔法机器可以通过投入x(x可以为0)个魔法币产生更多的魔法币。
|
||||
|
||||
魔法机器1:如果投入x个魔法币,魔法机器会将其变为2x+1个魔法币
|
||||
|
||||
魔法机器2:如果投入x个魔法币,魔法机器会将其变为2x+2个魔法币
|
||||
|
||||
小易采购魔法神器总共需要n个魔法币,所以小易只能通过两台魔法机器产生恰好n个魔法币,小易需要你帮他设计一个投入方案使他最后恰好拥有n个魔法币。
|
||||
|
||||
**输入描述:** 输入包括一行,包括一个正整数n(1 ≤ n ≤ 10^9),表示小易需要的魔法币数量。
|
||||
|
||||
**输出描述:** 输出一个字符串,每个字符表示该次小易选取投入的魔法机器。其中只包含字符'1'和'2'。
|
||||
|
||||
**输入例子1:** 10
|
||||
|
||||
**输出例子1:** 122
|
||||
|
||||
### 二 求“相反数”问题
|
||||
|
||||
为了得到一个数的"相反数",我们将这个数的数字顺序颠倒,然后再加上原先的数得到"相反数"。例如,为了得到1325的"相反数",首先我们将该数的数字顺序颠倒,我们得到5231,之后再加上原先的数,我们得到5231+1325=6556.如果颠倒之后的数字有前缀零,前缀零将会被忽略。例如n = 100, 颠倒之后是1.
|
||||
|
||||
**输入描述:** 输入包括一个整数n,(1 ≤ n ≤ 10^5)
|
||||
|
||||
**输出描述:** 输出一个整数,表示n的相反数
|
||||
|
||||
**输入例子1:** 1325
|
||||
|
||||
**输出例子1:** 6556
|
||||
|
||||
### 三 字符串碎片的平均长度
|
||||
|
||||
一个由小写字母组成的字符串可以看成一些同一字母的最大碎片组成的。例如,"aaabbaaac"是由下面碎片组成的:'aaa','bb','c'。牛牛现在给定一个字符串,请你帮助计算这个字符串的所有碎片的平均长度是多少。
|
||||
|
||||
**输入描述:** 输入包括一个字符串s,字符串s的长度length(1 ≤ length ≤ 50),s只含小写字母('a'-'z')
|
||||
|
||||
**输出描述:** 输出一个整数,表示所有碎片的平均长度,四舍五入保留两位小数。
|
||||
|
||||
**如样例所示:** s = "aaabbaaac"
|
||||
所有碎片的平均长度 = (3 + 2 + 3 + 1) / 4 = 2.25
|
||||
|
||||
**输入例子1:** aaabbaaac
|
||||
|
||||
**输出例子1:** 2.25
|
||||
|
||||
## 答案
|
||||
|
||||
### 一 获得特定数量硬币问题
|
||||
|
||||
#### 分析:
|
||||
|
||||
作为该试卷的第一题,这道题应该只要思路正确就很简单了。
|
||||
|
||||
解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
|
||||
|
||||
#### 示例代码
|
||||
|
||||
注意:由于用户的输入不确定性,一般是为了程序高可用性使需要将捕获用户输入异常然后友好提示用户输入类型错误并重新输入的。所以下面我给了两个版本,这两个版本都是正确的。这里只是给大家演示如何捕获输入类型异常,后面的题目中我给的代码没有异常处理的部分,参照下面两个示例代码,应该很容易添加。(PS:企业面试中没有明确就不用添加异常处理,当然你有的话也更好)
|
||||
|
||||
**不带输入异常处理判断的版本:**
|
||||
|
||||
```java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main2 {
|
||||
// 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("请输入要获得的硬币数量:");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
int coincount = scanner.nextInt();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (coincount >= 1) {
|
||||
// 偶数的情况
|
||||
if (coincount % 2 == 0) {
|
||||
coincount = (coincount - 2) / 2;
|
||||
sb.append("2");
|
||||
// 奇数的情况
|
||||
} else {
|
||||
coincount = (coincount - 1) / 2;
|
||||
sb.append("1");
|
||||
}
|
||||
}
|
||||
// 输出反转后的字符串
|
||||
System.out.println(sb.reverse());
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**带输入异常处理判断的版本(当输入的不是整数的时候会提示重新输入):**
|
||||
|
||||
```java
|
||||
import java.util.InputMismatchException;
|
||||
import java.util.Scanner;
|
||||
|
||||
|
||||
public class Main {
|
||||
// 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("请输入要获得的硬币数量:");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
boolean flag = true;
|
||||
while (flag) {
|
||||
try {
|
||||
int coincount = scanner.nextInt();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (coincount >= 1) {
|
||||
// 偶数的情况
|
||||
if (coincount % 2 == 0) {
|
||||
coincount = (coincount - 2) / 2;
|
||||
sb.append("2");
|
||||
// 奇数的情况
|
||||
} else {
|
||||
coincount = (coincount - 1) / 2;
|
||||
sb.append("1");
|
||||
}
|
||||
}
|
||||
// 输出反转后的字符串
|
||||
System.out.println(sb.reverse());
|
||||
flag=false;//程序结束
|
||||
} catch (InputMismatchException e) {
|
||||
System.out.println("输入数据类型不匹配,请您重新输入:");
|
||||
scanner.nextLine();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 二 求“相反数”问题
|
||||
|
||||
#### 分析:
|
||||
|
||||
解决本道题有几种不同的方法,但是最快速的方法就是利用reverse()方法反转字符串然后再将字符串转换成int类型的整数,这个方法是快速解决本题关键。我们先来回顾一下下面两个知识点:
|
||||
|
||||
**1)String转int;**
|
||||
|
||||
在 Java 中要将 String 类型转化为 int 类型时,需要使用 Integer 类中的 parseInt() 方法或者 valueOf() 方法进行转换.
|
||||
|
||||
```java
|
||||
String str = "123";
|
||||
int a = Integer.parseInt(str);
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```java
|
||||
String str = "123";
|
||||
int a = Integer.valueOf(str).intValue();
|
||||
```
|
||||
|
||||
**2)next()和nextLine()的区别**
|
||||
|
||||
在Java中输入字符串有两种方法,就是next()和nextLine().两者的区别就是:nextLine()的输入是碰到回车就终止输入,而next()方法是碰到空格,回车,Tab键都会被视为终止符。所以next()不会得到带空格的字符串,而nextLine()可以得到带空格的字符串。
|
||||
|
||||
#### 示例代码:
|
||||
|
||||
```java
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* 本题关键:①String转int;②next()和nextLine()的区别
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
System.out.println("请输入一个整数:");
|
||||
Scanner scanner = new Scanner(System.in);
|
||||
String s=scanner.next();
|
||||
//将字符串转换成数字
|
||||
int number1=Integer.parseInt(s);
|
||||
//将字符串倒序后转换成数字
|
||||
//因为Integer.parseInt()的参数类型必须是字符串所以必须加上toString()
|
||||
int number2=Integer.parseInt(new StringBuilder(s).reverse().toString());
|
||||
System.out.println(number1+number2);
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 三 字符串碎片的平均长度
|
||||
|
||||
#### 分析:
|
||||
|
||||
这道题的意思也就是要求:(字符串的总长度)/(相同字母团构成的字符串的个数)。
|
||||
|
||||
这样就很简单了,就变成了字符串的字符之间的比较。如果需要比较字符串的字符的话,我们可以利用charAt(i)方法:取出特定位置的字符与后一个字符比较,或者利用toCharArray()方法将字符串转换成字符数组采用同样的方法做比较。
|
||||
|
||||
#### 示例代码
|
||||
|
||||
**利用charAt(i)方法:**
|
||||
|
||||
```java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Scanner sc = new Scanner(System.in);
|
||||
while (sc.hasNext()) {
|
||||
String s = sc.next();
|
||||
//个数至少为一个
|
||||
float count = 1;
|
||||
for (int i = 0; i < s.length() - 1; i++) {
|
||||
if (s.charAt(i) != s.charAt(i + 1)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
System.out.println(s.length() / count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
**利用toCharArray()方法:**
|
||||
|
||||
```java
|
||||
import java.util.Scanner;
|
||||
|
||||
public class Main2 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Scanner sc = new Scanner(System.in);
|
||||
while (sc.hasNext()) {
|
||||
String s = sc.next();
|
||||
//个数至少为一个
|
||||
float count = 1;
|
||||
char [] stringArr = s.toCharArray();
|
||||
for (int i = 0; i < stringArr.length - 1; i++) {
|
||||
if (stringArr[i] != stringArr[i + 1]) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
System.out.println(s.length() / count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
```
|
@ -15,11 +15,13 @@
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
|
||||
## 说明
|
||||
|
||||
- 本文作者:wwwxmu
|
||||
- 原文地址:https://www.weiweiblog.cn/13string/
|
||||
- 作者的博客站点:https://www.weiweiblog.cn/ (推荐哦!)
|
||||
> 授权转载!
|
||||
>
|
||||
> - 本文作者:wwwxmu
|
||||
> - 原文地址:https://www.weiweiblog.cn/13string/
|
||||
|
||||
|
||||
|
||||
考虑到篇幅问题,我会分两次更新这个内容。本篇文章只是原文的一部分,我在原文的基础上增加了部分内容以及修改了部分代码和注释。另外,我增加了爱奇艺 2018 秋招 Java:`求给定合法括号序列的深度` 这道题。所有代码均编译成功,并带有注释,欢迎各位享用!
|
||||
|
||||
@ -135,17 +137,21 @@ public class Main {
|
||||
|
||||
}
|
||||
|
||||
private static boolean checkStrs(String[] strs) {
|
||||
if (strs != null) {
|
||||
// 遍历strs检查元素值
|
||||
for (int i = 0; i < strs.length; i++) {
|
||||
if (strs[i] == null || strs[i].length() == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private static boolean chechStrs(String[] strs) {
|
||||
boolean flag = false;
|
||||
if (strs != null) {
|
||||
// 遍历strs检查元素值
|
||||
for (int i = 0; i < strs.length; i++) {
|
||||
if (strs[i] != null && strs[i].length() != 0) {
|
||||
flag = true;
|
||||
} else {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
// 测试
|
||||
public static void main(String[] args) {
|
||||
@ -186,7 +192,7 @@ public class Main {
|
||||
我们上面已经知道了什么是回文串?现在我们考虑一下可以构成回文串的两种情况:
|
||||
|
||||
- 字符出现次数为双数的组合
|
||||
- 字符出现次数为双数的组合+一个只出现一次的字符
|
||||
- **字符出现次数为偶数的组合+单个字符中出现次数最多且为奇数次的字符** (参见 **[issue665](https://github.com/Snailclimb/JavaGuide/issues/665)** )
|
||||
|
||||
统计字符出现的次数即可,双数才能构成回文。因为允许中间一个数单独出现,比如“abcba”,所以如果最后有字母落单,总长度可以加 1。首先将字符串转变为字符数组。然后遍历该数组,判断对应字符是否在hashset中,如果不在就加进去,如果在就让count++,然后移除该字符!这样就能找到出现次数为双数的字符个数。
|
||||
|
||||
@ -459,7 +465,7 @@ public class Main {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return flag == 1 ? res : -res;
|
||||
return flag != 2 ? res : -res;
|
||||
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,7 @@ public class Solution {
|
||||
while (node1 != null) {
|
||||
node1 = node1.next;
|
||||
count++;
|
||||
if (k < 1 && node1 != null) {
|
||||
if (k < 1) {
|
||||
node2 = node2.next;
|
||||
}
|
||||
k--;
|
||||
@ -324,7 +324,7 @@ public class Solution {
|
||||
**进阶——一次遍历法:**
|
||||
|
||||
|
||||
> **链表中倒数第N个节点也就是正数第(L-N+1)个节点。
|
||||
> 链表中倒数第N个节点也就是正数第(L-N+1)个节点。
|
||||
|
||||
其实这种方法就和我们上面第四题找“链表中倒数第k个节点”所用的思想是一样的。**基本思路就是:** 定义两个节点 node1、node2;node1 节点先跑,node1节点 跑到第 n+1 个节点的时候,node2 节点开始跑.当node1 节点跑到最后一个节点时,node2 节点所在的位置就是第 (L-n ) 个节点(L代表总链表长度,也就是倒数第 n+1 个节点)
|
||||
|
||||
|
@ -48,15 +48,6 @@ n<=39
|
||||
}
|
||||
```
|
||||
|
||||
#### **运行时间对比:**
|
||||
|
||||
假设n为40我们分别使用迭代法和递归法计算,计算结果如下:
|
||||
|
||||
1. 迭代法
|
||||

|
||||
2. 递归法
|
||||

|
||||
|
||||
### 二 跳台阶问题
|
||||
|
||||
#### **题目描述:**
|
||||
|
@ -1,5 +1,5 @@
|
||||
下面只是简单地总结,给了一些参考文章,后面会对这部分内容进行重构。
|
||||
<!-- MarkdownTOC -->
|
||||
> 注意!!!这部分内容会进行重构,以下内容仅作为参考。
|
||||
> <!-- MarkdownTOC -->
|
||||
|
||||
- [Queue](#queue)
|
||||
- [什么是队列](#什么是队列)
|
||||
@ -38,16 +38,12 @@ Java 集合中的 Queue 继承自 Collection 接口 ,Deque, LinkedList, Priori
|
||||
Queue 用来存放 等待处理元素 的集合,这种场景一般用于缓冲、并发访问。
|
||||
除了继承 Collection 接口的一些方法,Queue 还添加了额外的 添加、删除、查询操作。
|
||||
|
||||
### 推荐文章
|
||||
|
||||
- [Java 集合深入理解(9):Queue 队列](https://blog.csdn.net/u011240877/article/details/52860924)
|
||||
|
||||
## Set
|
||||
|
||||
### 什么是 Set
|
||||
Set 继承于 Collection 接口,是一个不允许出现重复元素,并且无序的集合,主要 HashSet 和 TreeSet 两大实现类。
|
||||
|
||||
在判断重复元素的时候,Set 集合会调用 hashCode()和 equal()方法来实现。
|
||||
在判断重复元素的时候,HashSet 集合会调用 hashCode()和 equal()方法来实现;TreeSet 集合会调用compareTo方法来实现。
|
||||
|
||||
### 补充:有序集合与无序集合说明
|
||||
- 有序集合:集合里的元素可以根据 key 或 index 访问 (List、Map)
|
||||
@ -60,11 +56,6 @@ Set 继承于 Collection 接口,是一个不允许出现重复元素,并且
|
||||
|
||||
**TreeSet** 是红黑树结构,每一个元素都是树中的一个节点,插入的元素都会进行排序;
|
||||
|
||||
|
||||
### 推荐文章
|
||||
|
||||
- [Java集合--Set(基础)](https://www.jianshu.com/p/b48c47a42916)
|
||||
|
||||
## List
|
||||
|
||||
### 什么是List
|
||||
@ -79,106 +70,102 @@ Set 继承于 Collection 接口,是一个不允许出现重复元素,并且
|
||||
|
||||
**Vector** 是矢量队列,和ArrayList一样,它也是一个动态数组,由数组实现。但是ArrayList是非线程安全的,而Vector是线程安全的。
|
||||
|
||||
**Stack** 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。相关阅读:[java数据结构与算法之栈(Stack)设计与实现](https://blog.csdn.net/javazejian/article/details/53362993)
|
||||
|
||||
### ArrayList 和 LinkedList 源码学习
|
||||
|
||||
- [ArrayList 源码学习](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/ArrayList.md)
|
||||
- [LinkedList 源码学习](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/LinkedList.md)
|
||||
|
||||
### 推荐阅读
|
||||
|
||||
- [java 数据结构与算法之顺序表与链表深入分析](https://blog.csdn.net/javazejian/article/details/52953190)
|
||||
|
||||
|
||||
## Map
|
||||
|
||||
|
||||
- [集合框架源码学习之 HashMap(JDK1.8)](https://juejin.im/post/5ab0568b5188255580020e56)
|
||||
- [ConcurrentHashMap 实现原理及源码分析](https://link.juejin.im/?target=http%3A%2F%2Fwww.cnblogs.com%2Fchengxiao%2Fp%2F6842045.html)
|
||||
**Stack** 是栈,它继承于Vector。它的特性是:先进后出(FILO, First In Last Out)。
|
||||
|
||||
## 树
|
||||
* ### 1 二叉树
|
||||
|
||||
[二叉树](https://baike.baidu.com/item/%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科)
|
||||
|
||||
(1)[完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
|
||||
|
||||
(2)[满二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
|
||||
|
||||
(3)[平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/10421057)——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
|
||||
### 1 二叉树
|
||||
|
||||
* ### 2 完全二叉树
|
||||
[二叉树](https://baike.baidu.com/item/%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科)
|
||||
|
||||
[完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科)
|
||||
(1)[完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
|
||||
|
||||
完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。
|
||||
* ### 3 满二叉树
|
||||
(2)[满二叉树](https://baike.baidu.com/item/%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91)——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
|
||||
|
||||
[满二叉树](https://baike.baidu.com/item/%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科,国内外的定义不同)
|
||||
(3)[平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/10421057)——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
|
||||
|
||||
国内教程定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
|
||||
* ### 堆
|
||||
|
||||
[数据结构之堆的定义](https://blog.csdn.net/qq_33186366/article/details/51876191)
|
||||
### 2 完全二叉树
|
||||
|
||||
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
|
||||
* ### 4 二叉查找树(BST)
|
||||
[完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科)
|
||||
|
||||
[浅谈算法和数据结构: 七 二叉查找树](http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html)
|
||||
完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。
|
||||
|
||||
二叉查找树的特点:
|
||||
### 3 满二叉树
|
||||
|
||||
1. 若任意节点的左子树不空,则左子树上所有结点的 值均小于它的根结点的值;
|
||||
2. 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
|
||||
3. 任意节点的左、右子树也分别为二叉查找树;
|
||||
4. 没有键值相等的节点(no duplicate nodes)。
|
||||
[满二叉树](https://baike.baidu.com/item/%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科,国内外的定义不同)
|
||||
|
||||
* ### 5 平衡二叉树(Self-balancing binary search tree)
|
||||
|
||||
[ 平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科,平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等)
|
||||
* ### 6 红黑树
|
||||
|
||||
- 红黑树特点:
|
||||
1. 每个节点非红即黑;
|
||||
2. 根节点总是黑色的;
|
||||
3. 每个叶子节点都是黑色的空节点(NIL节点);
|
||||
4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
|
||||
5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
|
||||
|
||||
- 红黑树的应用:
|
||||
|
||||
TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。
|
||||
|
||||
- 为什么要用红黑树
|
||||
|
||||
简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。详细了解可以查看 [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐)
|
||||
|
||||
- 推荐文章:
|
||||
- [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐)
|
||||
- [寻找红黑树的操作手册](http://dandanlove.com/2018/03/18/red-black-tree/)(文章排版以及思路真的不错)
|
||||
- [红黑树深入剖析及Java实现](https://zhuanlan.zhihu.com/p/24367771)(美团点评技术团队)
|
||||
* ### 7 B-,B+,B*树
|
||||
|
||||
[二叉树学习笔记之B树、B+树、B*树 ](https://yq.aliyun.com/articles/38345)
|
||||
国内教程定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
|
||||
|
||||
[《B-树,B+树,B*树详解》](https://blog.csdn.net/aqzwss/article/details/53074186)
|
||||
### 堆
|
||||
|
||||
[《B-树,B+树与B*树的优缺点比较》](https://blog.csdn.net/bigtree_3721/article/details/73632405)
|
||||
|
||||
B-树(或B树)是一种平衡的多路查找(又称排序)树,在文件系统中有所应用。主要用作文件的索引。其中的B就表示平衡(Balance)
|
||||
1. B+ 树的叶子节点链表结构相比于 B- 树便于扫库,和范围检索。
|
||||
2. B+树支持range-query(区间查询)非常方便,而B树不支持。这是数据库选用B+树的最主要原因。
|
||||
3. B\*树 是B+树的变体,B\*树分配新结点的概率比B+树要低,空间使用率更高;
|
||||
* ### 8 LSM 树
|
||||
[数据结构之堆的定义](https://blog.csdn.net/qq_33186366/article/details/51876191)
|
||||
|
||||
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
|
||||
|
||||
### 4 二叉查找树(BST)
|
||||
|
||||
[浅谈算法和数据结构: 七 二叉查找树](https://www.yycoding.xyz/post/2014/3/24/introduce-binary-search-tree)
|
||||
|
||||
二叉查找树的特点:
|
||||
|
||||
1. 若任意节点的左子树不空,则左子树上所有结点的 值均小于它的根结点的值;
|
||||
2. 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
|
||||
3. 任意节点的左、右子树也分别为二叉查找树;
|
||||
4. 没有键值相等的节点(no duplicate nodes)。
|
||||
|
||||
### 5 平衡二叉树(Self-balancing binary search tree)
|
||||
|
||||
[ 平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科,平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等)
|
||||
|
||||
### 6 红黑树
|
||||
|
||||
红黑树特点:
|
||||
|
||||
1. 每个节点非红即黑;
|
||||
2. 根节点总是黑色的;
|
||||
3. 每个叶子节点都是黑色的空节点(NIL节点);
|
||||
4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
|
||||
5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
|
||||
|
||||
|
||||
红黑树的应用:
|
||||
|
||||
[[HBase] LSM树 VS B+树](https://blog.csdn.net/dbanote/article/details/8897599)
|
||||
TreeMap、TreeSet以及JDK1.8的HashMap底层都用到了红黑树。
|
||||
|
||||
**为什么要用红黑树?**
|
||||
|
||||
|
||||
简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。详细了解可以查看 [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐)
|
||||
|
||||
推荐文章:
|
||||
|
||||
- [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐)
|
||||
- [寻找红黑树的操作手册](http://dandanlove.com/2018/03/18/red-black-tree/)(文章排版以及思路真的不错)
|
||||
- [红黑树深入剖析及Java实现](https://zhuanlan.zhihu.com/p/24367771)(美团点评技术团队)
|
||||
|
||||
### 7 B-,B+,B*树
|
||||
|
||||
[二叉树学习笔记之B树、B+树、B*树 ](https://yq.aliyun.com/articles/38345)
|
||||
|
||||
[《B-树,B+树,B*树详解》](https://blog.csdn.net/aqzwss/article/details/53074186)
|
||||
|
||||
[《B-树,B+树与B*树的优缺点比较》](https://blog.csdn.net/bigtree_3721/article/details/73632405)
|
||||
|
||||
B-树(或B树)是一种平衡的多路查找(又称排序)树,在文件系统中有所应用。主要用作文件的索引。其中的B就表示平衡(Balance)
|
||||
|
||||
1. B+ 树的叶子节点链表结构相比于 B- 树便于扫库,和范围检索。
|
||||
2. B+树支持range-query(区间查询)非常方便,而B树不支持。这是数据库选用B+树的最主要原因。
|
||||
3. B\*树 是B+树的变体,B\*树分配新结点的概率比B+树要低,空间使用率更高;
|
||||
|
||||
### 8 LSM 树
|
||||
|
||||
[[HBase] LSM树 VS B+树](https://blog.csdn.net/dbanote/article/details/8897599)
|
||||
|
||||
B+树最大的性能问题是会产生大量的随机IO
|
||||
B+树最大的性能问题是会产生大量的随机IO
|
||||
|
||||
为了克服B+树的弱点,HBase引入了LSM树的概念,即Log-Structured Merge-Trees。
|
||||
|
||||
[LSM树由来、设计思想以及应用到HBase的索引](http://www.cnblogs.com/yanghuahui/p/3483754.html)
|
||||
为了克服B+树的弱点,HBase引入了LSM树的概念,即Log-Structured Merge-Trees。
|
||||
|
||||
[LSM树由来、设计思想以及应用到HBase的索引](http://www.cnblogs.com/yanghuahui/p/3483754.html)
|
||||
|
||||
|
||||
## 图
|
||||
|
@ -1,52 +0,0 @@
|
||||
我比较推荐大家可以刷一下 Leetcode ,我自己平时没事也会刷一下,我觉得刷 Leetcode 不仅是为了能让你更从容地面对面试中的手撕算法问题,更可以提高你的编程思维能力、解决问题的能力以及你对某门编程语言 API 的熟练度。当然牛客网也有一些算法题,我下面也整理了一些。
|
||||
|
||||
## LeetCode
|
||||
|
||||
- [LeetCode(中国)官网](https://leetcode-cn.com/)
|
||||
|
||||
- [如何高效地使用 LeetCode](https://leetcode-cn.com/articles/%E5%A6%82%E4%BD%95%E9%AB%98%E6%95%88%E5%9C%B0%E4%BD%BF%E7%94%A8-leetcode/)
|
||||
|
||||
|
||||
## 牛客网
|
||||
|
||||
- [牛客网官网](https://www.nowcoder.com)
|
||||
- [剑指offer编程题](https://www.nowcoder.com/ta/coding-interviews)
|
||||
|
||||
- [2017校招真题](https://www.nowcoder.com/ta/2017test)
|
||||
- [华为机试题](https://www.nowcoder.com/ta/huawei)
|
||||
|
||||
|
||||
## 公司真题
|
||||
|
||||
- [ 网易2018校园招聘编程题真题集合](https://www.nowcoder.com/test/6910869/summary)
|
||||
- [ 网易2018校招内推编程题集合](https://www.nowcoder.com/test/6291726/summary)
|
||||
- [2017年校招全国统一模拟笔试(第五场)编程题集合](https://www.nowcoder.com/test/5986669/summary)
|
||||
- [2017年校招全国统一模拟笔试(第四场)编程题集合](https://www.nowcoder.com/test/5507925/summary)
|
||||
- [2017年校招全国统一模拟笔试(第三场)编程题集合](https://www.nowcoder.com/test/5217106/summary)
|
||||
- [2017年校招全国统一模拟笔试(第二场)编程题集合](https://www.nowcoder.com/test/4546329/summary)
|
||||
- [ 2017年校招全国统一模拟笔试(第一场)编程题集合](https://www.nowcoder.com/test/4236887/summary)
|
||||
- [百度2017春招笔试真题编程题集合](https://www.nowcoder.com/test/4998655/summary)
|
||||
- [网易2017春招笔试真题编程题集合](https://www.nowcoder.com/test/4575457/summary)
|
||||
- [网易2017秋招编程题集合](https://www.nowcoder.com/test/2811407/summary)
|
||||
- [网易有道2017内推编程题](https://www.nowcoder.com/test/2385858/summary)
|
||||
- [ 滴滴出行2017秋招笔试真题-编程题汇总](https://www.nowcoder.com/test/3701760/summary)
|
||||
- [腾讯2017暑期实习生编程题](https://www.nowcoder.com/test/1725829/summary)
|
||||
- [今日头条2017客户端工程师实习生笔试题](https://www.nowcoder.com/test/1649301/summary)
|
||||
- [今日头条2017后端工程师实习生笔试题](https://www.nowcoder.com/test/1649268/summary)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,13 +1,86 @@
|
||||
## 为什么要使用索引?
|
||||
|
||||
# 思维导图-索引篇
|
||||
1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
|
||||
2. 可以大大加快 数据的检索速度(大大减少的检索的数据量), 这也是创建索引的最主要的原因。
|
||||
3. 帮助服务器避免排序和临时表。
|
||||
4. 将随机IO变为顺序IO
|
||||
5. 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
|
||||
|
||||
> 系列思维导图源文件(数据库+架构)以及思维导图制作软件—XMind8 破解安装,公众号后台回复:**“思维导图”** 免费领取!(下面的图片不是很清楚,原图非常清晰,另外提供给大家源文件也是为了大家根据自己需要进行修改)
|
||||
## 索引这么多优点,为什么不对表中的每一个列创建一个索引呢?
|
||||
|
||||

|
||||
1. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
|
||||
2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
|
||||
3. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
|
||||
|
||||
> **下面是我补充的一些内容**
|
||||
## 使用索引的注意事项?
|
||||
|
||||
# 为什么索引能提高查询速度
|
||||
1. 在经常需要搜索的列上,可以加快搜索的速度;
|
||||
|
||||
2. 在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
|
||||
|
||||
3. 在经常需要排序的列上创 建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
|
||||
|
||||
4. 对于中到大型表索引都是非常有效的,但是特大型表的话维护开销会很大,不适合建索引
|
||||
|
||||
5. 在经常用在连接的列上,这 些列主要是一些外键,可以加快连接的速度;
|
||||
|
||||
6. 避免 where 子句中对字段施加函数,这会造成无法命中索引。
|
||||
|
||||
7. 在使用InnoDB时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。
|
||||
|
||||
8. ~~将打算加索引的列设置为 NOT NULL ,否则将导致引擎放弃使用索引而进行全表扫描。~~
|
||||
|
||||
订正,来自[issue758](https://github.com/Snailclimb/JavaGuide/issues/758) 。**将某一列设置为default null,where 是可以走索引,另外索引列是否设置 null 是不影响性能的。** 但是,还是不建议列上允许为空。最好限制not null,因为null需要更多的存储空间并且null值无法参与某些运算。
|
||||
|
||||
《高性能MySQL》第四章如是说:And, in case you’re wondering, allowing NULL values in the index really doesn’t impact performance 。NULL 值的索引查找流程参考:https://juejin.im/post/5d5defc2518825591523a1db ,相关阅读:[MySQL中IS NULL、IS NOT NULL、!=不能用索引?胡扯!](https://juejin.im/post/5d5defc2518825591523a1db) 。
|
||||
|
||||
9. 删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗 MySQL 5.7 可以通过查询 sys 库的 schema_unused_indexes 视图来查询哪些索引从未被使用
|
||||
|
||||
10. 在使用 limit offset 查询缓慢时,可以借助索引来提高性能
|
||||
|
||||
## Mysql索引主要使用的两种数据结构
|
||||
|
||||
### 哈希索引
|
||||
|
||||
对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
|
||||
|
||||
### BTree索引
|
||||
|
||||
## MyISAM和InnoDB实现BTree索引方式的区别
|
||||
|
||||
### MyISAM
|
||||
|
||||
B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。
|
||||
|
||||
### InnoDB
|
||||
|
||||
其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”,而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,在走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。 PS:整理自《Java工程师修炼之道》
|
||||
|
||||
## 覆盖索引介绍
|
||||
|
||||
### 什么是覆盖索引
|
||||
|
||||
如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道InnoDB存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
|
||||
|
||||
### 覆盖索引使用实例
|
||||
|
||||
现在我创建了索引(username,age),我们执行下面的 sql 语句
|
||||
|
||||
```sql
|
||||
select username , age from user where username = 'Java' and age = 22
|
||||
```
|
||||
|
||||
在查询数据的时候:要查询出的列在叶子节点都存在!所以,就不用回表。
|
||||
|
||||
## 选择索引和编写利用这些索引的查询的3个原则
|
||||
|
||||
1. 单行访问是很慢的。特别是在机械硬盘存储中(SSD的随机I/O要快很多,不过这一点仍然成立)。如果服务器从存储中读取一个数据块只是为了获取其中一行,那么就浪费了很多工作。最好读取的块中能包含尽可能多所需要的行。使用索引可以创建位置引,用以提升效率。
|
||||
2. 按顺序访问范围数据是很快的,这有两个原因。第一,顺序 I/O 不需要多次磁盘寻道,所以比随机I/O要快很多(特别是对机械硬盘)。第二,如果服务器能够按需要顺序读取数据,那么就不再需要额外的排序操作,并且GROUPBY查询也无须再做排序和将行按组进行聚合计算了。
|
||||
3. 索引覆盖查询是很快的。如果一个索引包含了查询需要的所有列,那么存储引擎就
|
||||
不需要再回表查找行。这避免了大量的单行访问,而上面的第1点已经写明单行访
|
||||
问是很慢的。
|
||||
|
||||
## 为什么索引能提高查询速度
|
||||
|
||||
> 以下内容整理自:
|
||||
> 地址: https://juejin.im/post/5b55b842f265da0f9e589e79
|
||||
@ -48,7 +121,7 @@ MySQL的基本存储结构是页(记录都存在页里边):
|
||||
|
||||
其实底层结构就是B+树,B+树作为树的一种实现,能够让我们很快地查找出对应的记录。
|
||||
|
||||
# 关于索引其他重要的内容补充
|
||||
## 关于索引其他重要的内容补充
|
||||
|
||||
> 以下内容整理自:《Java工程师修炼之道》
|
||||
|
||||
@ -61,16 +134,16 @@ MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索
|
||||
select * from user where name=xx and city=xx ; //可以命中索引
|
||||
select * from user where name=xx ; // 可以命中索引
|
||||
select * from user where city=xx ; // 无法命中索引
|
||||
```
|
||||
```
|
||||
这里需要注意的是,查询的时候如果两个条件都用上了,但是顺序不同,如 `city= xx and name =xx`,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
|
||||
|
||||
由于最左前缀原则,在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。ORDER BY子句也遵循此规则。
|
||||
|
||||
### 注意避免冗余索引
|
||||
|
||||
冗余索引指的是索引的功能相同,能够命中 就肯定能命中 ,那么 就是冗余索引如(name,city )和(name )这两个索引就是冗余索引,能够命中后者的查询肯定是能够命中前者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
|
||||
冗余索引指的是索引的功能相同,能够命中索引(a, b)就肯定能命中索引(a) ,那么索引(a)就是冗余索引。如(name,city )和(name )这两个索引就是冗余索引,能够命中前者的查询肯定是能够命中后者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
|
||||
|
||||
MySQLS.7 版本后,可以通过查询 sys 库的 `schema_redundant_indexes` 表来查看冗余索引
|
||||
MySQL 5.7 版本后,可以通过查询 sys 库的 `schema_redundant_indexes` 表来查看冗余索引
|
||||
|
||||
### Mysql如何为表字段添加索引???
|
||||
|
||||
@ -84,19 +157,19 @@ ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
|
||||
```
|
||||
ALTER TABLE `table_name` ADD UNIQUE ( `column` )
|
||||
```
|
||||
|
||||
|
||||
3.添加INDEX(普通索引)
|
||||
|
||||
```
|
||||
ALTER TABLE `table_name` ADD INDEX index_name ( `column` )
|
||||
```
|
||||
|
||||
|
||||
4.添加FULLTEXT(全文索引)
|
||||
|
||||
```
|
||||
ALTER TABLE `table_name` ADD FULLTEXT ( `column`)
|
||||
```
|
||||
|
||||
|
||||
5.添加多列索引
|
||||
|
||||
```
|
||||
@ -104,7 +177,7 @@ ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3`
|
||||
```
|
||||
|
||||
|
||||
# 参考
|
||||
## 参考
|
||||
|
||||
- 《Java工程师修炼之道》
|
||||
- 《MySQL高性能书籍_第3版》
|
||||
|
@ -1,174 +1,284 @@
|
||||
<!-- TOC -->
|
||||
## MySQL 基础
|
||||
|
||||
- [书籍推荐](#书籍推荐)
|
||||
- [文字教程推荐](#文字教程推荐)
|
||||
- [视频教程推荐](#视频教程推荐)
|
||||
- [常见问题总结](#常见问题总结)
|
||||
### 关系型数据库介绍
|
||||
|
||||
<!-- /TOC -->
|
||||
顾名思义,关系型数据库就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系(一对一、一对多、多对多)。
|
||||
|
||||
## 书籍推荐
|
||||
关系型数据库中,我们的数据都被存放在了各种表中(比如用户表),表中的每一列就存放着一条数据(比如一个用户的信息)。
|
||||
|
||||
- 《SQL基础教程(第2版)》 (入门级)
|
||||
- 《高性能MySQL : 第3版》 (进阶)
|
||||

|
||||
|
||||
## 文字教程推荐
|
||||
大部分关系型数据库都使用 SQL 来操作数据库中的数据。并且,大部分关系型数据库都支持事务的四大特性(ACID)。
|
||||
|
||||
[MySQL 教程(菜鸟教程)](http://www.runoob.com/mysql/mysql-tutorial.html)
|
||||
**有哪些常见的关系型数据库呢?**
|
||||
|
||||
[MySQL教程(易百教程)](https://www.yiibai.com/mysql/)
|
||||
MySQL、PostgreSQL、Oracle、SQL Server、SQLite(微信本地的聊天记录的存储就是用的 SQLite) ......。
|
||||
|
||||
## 视频教程推荐
|
||||
### MySQL 介绍
|
||||
|
||||
**基础入门:** [与MySQL的零距离接触-慕课网](https://www.imooc.com/learn/122)
|
||||

|
||||
|
||||
**Mysql开发技巧:** [MySQL开发技巧(一)](https://www.imooc.com/learn/398) [MySQL开发技巧(二)](https://www.imooc.com/learn/427) [MySQL开发技巧(三)](https://www.imooc.com/learn/449)
|
||||
**MySQL 是一种关系型数据库,主要用于持久化存储我们的系统中的一些数据比如用户信息。**
|
||||
|
||||
**Mysql5.7新特性及相关优化技巧:** [MySQL5.7版本新特性](https://www.imooc.com/learn/533) [性能优化之MySQL优化](https://www.imooc.com/learn/194)
|
||||
由于 MySQL 是开源免费并且比较成熟的数据库,因此,MySQL 被大量使用在各种系统中。任何人都可以在 GPL(General Public License) 的许可下下载并根据个性化的需要对其进行修改。MySQL 的默认端口号是**3306**。
|
||||
|
||||
[MySQL集群(PXC)入门](https://www.imooc.com/learn/993) [MyCAT入门及应用](https://www.imooc.com/learn/951)
|
||||
## 存储引擎
|
||||
|
||||
## 常见问题总结
|
||||
### 存储引擎相关的命令
|
||||
|
||||
- ### ①存储引擎
|
||||
**查看 MySQL 提供的所有存储引擎**
|
||||
|
||||
[MySQL常见的两种存储引擎:MyISAM与InnoDB的爱恨情仇](https://juejin.im/post/5b1685bef265da6e5c3c1c34)
|
||||
|
||||
- ### ②字符集及校对规则
|
||||
```sql
|
||||
mysql> show engines;
|
||||
```
|
||||
|
||||
字符集指的是一种从二进制编码到某类字符符号的映射。校对规则则是指某种字符集下的排序规则。Mysql中每一种字符集都会对应一系列的校对规则。
|
||||

|
||||
|
||||
Mysql采用的是类似继承的方式指定字符集的默认值,每个数据库以及每张数据表都有自己的默认值,他们逐层继承。比如:某个库中所有表的默认字符集将是该数据库所指定的字符集(这些表在没有指定字符集的情况下,才会采用默认字符集) PS:整理自《Java工程师修炼之道》
|
||||
|
||||
详细内容可以参考: [MySQL字符集及校对规则的理解](https://www.cnblogs.com/geaozhang/p/6724393.html#mysqlyuzifuji)
|
||||
从上图我们可以查看出 MySQL 当前默认的存储引擎是 InnoDB,并且在 5.7 版本所有的存储引擎中只有 InnoDB 是事务性存储引擎,也就是说只有 InnoDB 支持事务。
|
||||
|
||||
- ### ③索引相关的内容(数据库使用中非常关键的技术,合理正确的使用索引可以大大提高数据库的查询性能)
|
||||
|
||||
Mysql索引使用的数据结构主要有**BTree索引** 和 **哈希索引** 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
|
||||
|
||||
Mysql的BTree索引使用的是B数中的B+Tree,但对于主要的两种存储引擎的实现方式是不同的。
|
||||
**查看 MySQL 当前默认的存储引擎**
|
||||
|
||||
**MyISAM:** B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“非聚簇索引”。
|
||||
|
||||
**InnoDB:** 其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。**在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。** **因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。** PS:整理自《Java工程师修炼之道》
|
||||
|
||||
详细内容可以参考:
|
||||
|
||||
[干货:mysql索引的数据结构](https://www.jianshu.com/p/1775b4ff123a)
|
||||
|
||||
[MySQL优化系列(三)--索引的使用、原理和设计优化](https://blog.csdn.net/Jack__Frost/article/details/72571540)
|
||||
|
||||
[数据库两大神器【索引和锁】](https://juejin.im/post/5b55b842f265da0f9e589e79#comment)
|
||||
|
||||
- ### ④查询缓存的使用
|
||||
我们也可以通过下面的命令查看默认的存储引擎。
|
||||
|
||||
my.cnf加入以下配置,重启Mysql开启查询缓存
|
||||
```
|
||||
query_cache_type=1
|
||||
query_cache_size=600000
|
||||
```
|
||||
|
||||
Mysql执行以下命令也可以开启查询缓存
|
||||
|
||||
```
|
||||
set global query_cache_type=1;
|
||||
set global query_cache_size=600000;
|
||||
```
|
||||
如上,**开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果**。这里的查询条件包括查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息。因此任何两个查询在任何字符上的不同都会导致缓存不命中。此外,如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、Mysql库中的系统表,其查询结果也不会被缓存。
|
||||
|
||||
缓存建立之后,Mysql的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。
|
||||
|
||||
**缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。** 因此,开启缓存查询要谨慎,尤其对于写密集的应用来说更是如此。如果开启,要注意合理控制缓存空间大小,一般来说其大小设置为几十MB比较合适。此外,**还可以通过sql_cache和sql_no_cache来控制某个查询语句是否需要缓存:**
|
||||
```
|
||||
select sql_no_cache count(*) from usr;
|
||||
```
|
||||
|
||||
- ### ⑤事务机制
|
||||
|
||||
**关系性数据库需要遵循ACID规则,具体内容如下:**
|
||||
```sql
|
||||
mysql> show variables like '%storage_engine%';
|
||||
```
|
||||
|
||||

|
||||
**查看表的存储引擎**
|
||||
|
||||
1. **原子性:** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
|
||||
2. **一致性:** 执行事务前后,数据库从一个一致性状态转换到另一个一致性状态。
|
||||
3. **隔离性:** 并发访问数据库时,一个用户的事物不被其他事务所干扰,各并发事务之间数据库是独立的;
|
||||
4. **持久性:** 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库 发生故障也不应该对其有任何影响。
|
||||
|
||||
**为了达到上述事务特性,数据库定义了几种不同的事务隔离级别:**
|
||||
```sql
|
||||
show table status like "table_name" ;
|
||||
```
|
||||
|
||||
- **READ_UNCOMMITTED(未提交读):** 最低的隔离级别,允许读取尚未提交的数据变更,**可能会导致脏读、幻读或不可重复读**
|
||||
- **READ_COMMITTED(提交读):** 允许读取并发事务已经提交的数据,**可以阻止脏读,但是幻读或不可重复读仍有可能发生**
|
||||
- **REPEATABLE_READ(可重复读):** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,**可以阻止脏读和不可重复读,但幻读仍有可能发生。**
|
||||
- **SERIALIZABLE(串行):** 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不可重复读以及幻读**。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
|
||||

|
||||
|
||||
这里需要注意的是:**Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.**
|
||||
### MyISAM 和 InnoDB 的区别
|
||||
|
||||
事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVCC(多版本并发控制),通过行的创建时间和行的过期时间来支持并发一致性读和回滚等特性。
|
||||

|
||||
|
||||
详细内容可以参考: [可能是最漂亮的Spring事务管理详解](https://blog.csdn.net/qq_34337272/article/details/80394121)
|
||||
MySQL 5.5 之前,MyISAM 引擎是 MySQL 的默认存储引擎,可谓是风光一时。
|
||||
|
||||
- ### ⑥锁机制与InnoDB锁算法
|
||||
**MyISAM和InnoDB存储引擎使用的锁:**
|
||||
虽然,MyISAM 的性能还行,各种特性也还不错(比如全文索引、压缩、空间函数等)。但是,MyISAM 不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。
|
||||
|
||||
- MyISAM采用表级锁(table-level locking)。
|
||||
- InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁
|
||||
5.5 版本之后,MySQL 引入了 InnoDB(事务性数据库引擎),MySQL 5.5 版本后默认的存储引擎为 InnoDB。小伙子,一定要记好这个 InnoDB ,你每次使用 MySQL 数据库都是用的这个存储引擎吧?
|
||||
|
||||
**表级锁和行级锁对比:**
|
||||
言归正传!咱们下面还是来简单对比一下两者:
|
||||
|
||||
- **表级锁:** Mysql中锁定 **粒度最大** 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。
|
||||
- **行级锁:** Mysql中锁定 **粒度最小** 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
|
||||
**1.是否支持行级锁**
|
||||
|
||||
详细内容可以参考:
|
||||
[Mysql锁机制简单了解一下](https://blog.csdn.net/qq_34337272/article/details/80611486)
|
||||
|
||||
**InnoDB存储引擎的锁的算法有三种:**
|
||||
- Record lock:单个行记录上的锁
|
||||
- Gap lock:间隙锁,锁定一个范围,不包括记录本身
|
||||
- Next-key lock:record+gap 锁定一个范围,包含记录本身
|
||||
|
||||
**相关知识点:**
|
||||
1. innodb对于行的查询使用next-key lock
|
||||
2. Next-locking keying为了解决Phantom Problem幻读问题
|
||||
3. 当查询的索引含有唯一属性时,将next-key lock降级为record key
|
||||
4. Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生
|
||||
5. 有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1
|
||||
MyISAM 只有表级锁(table-level locking),而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。
|
||||
|
||||
- ### ⑦大表优化
|
||||
也就说,MyISAM 一锁就是锁住了整张表,这在并发写的情况下是多么滴憨憨啊!这也是为什么 InnoDB 在并发写的时候,性能更牛皮了!
|
||||
|
||||
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,一些常见的优化措施如下:
|
||||
|
||||
1. **限定数据的范围:** 务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内;
|
||||
2. **读/写分离:** 经典的数据库拆分方案,主库负责写,从库负责读;
|
||||
3 . **垂直分区:**
|
||||
|
||||
**根据数据库里面数据表的相关性进行拆分。** 例如,用户表中既有用户的登录信息又有用户的基本信息,可以将用户表拆分成两个单独的表,甚至放到单独的库做分库。
|
||||
**2.是否支持事务**
|
||||
|
||||
**简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。** 如下图所示,这样来说大家应该就更容易理解了。
|
||||

|
||||
|
||||
**垂直拆分的优点:** 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。
|
||||
MyISAM 不提供事务支持。
|
||||
|
||||
**垂直拆分的缺点:** 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂;
|
||||
InnoDB 提供事务支持,具有提交(commit)和回滚(rollback)事务的能力。
|
||||
|
||||
4. **水平分区:**
|
||||
|
||||
|
||||
**保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。 水平拆分可以支撑非常大的数据量。**
|
||||
|
||||
水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放。举个例子:我们可以将用户信息表拆分成多个用户信息表,这样就可以避免单一表数据量过大对性能造成影响。
|
||||
|
||||

|
||||
|
||||
水平拆分可以支持非常大的数据量。需要注意的一点是:分表仅仅是解决了单一表数据过大的问题,但由于表的数据还是在同一台机器上,其实对于提升MySQL并发能力没有什么意义,所以 **水平拆分最好分库** 。
|
||||
|
||||
水平拆分能够 **支持非常大的数据量存储,应用端改造也少**,但 **分片事务难以解决** ,跨节点Join性能较差,逻辑复杂。《Java工程师修炼之道》的作者推荐 **尽量不要对数据进行分片,因为拆分会带来逻辑、部署、运维的各种复杂度** ,一般的数据表在优化得当的情况下支撑千万以下的数据量是没有太大问题的。如果实在要分片,尽量选择客户端分片架构,这样可以减少一次和中间件的网络I/O。
|
||||
|
||||
**下面补充一下数据库分片的两种常见方案:**
|
||||
- **客户端代理:** **分片逻辑在应用端,封装在jar包中,通过修改或者封装JDBC层来实现。** 当当网的 **Sharding-JDBC** 、阿里的TDDL是两种比较常用的实现。
|
||||
- **中间件代理:** **在应用和数据中间加了一个代理层。分片逻辑统一维护在中间件服务中。** 我们现在谈的 **Mycat** 、360的Atlas、网易的DDB等等都是这种架构的实现。
|
||||
|
||||
|
||||
详细内容可以参考:
|
||||
[MySQL大表优化方案](https://segmentfault.com/a/1190000006158186)
|
||||
|
||||
**3.是否支持外键**
|
||||
|
||||
MyISAM 不支持,而 InnoDB 支持。
|
||||
|
||||
🌈 拓展一下:
|
||||
|
||||
一般我们也是不建议在数据库层面使用外键的,应用层面可以解决。不过,这样会对数据的一致性造成威胁。具体要不要使用外键还是要根据你的项目来决定。
|
||||
|
||||
**4.是否支持数据库异常崩溃后的安全恢复**
|
||||
|
||||
MyISAM 不支持,而 InnoDB 支持。
|
||||
|
||||
使用 InnoDB 的数据库在异常崩溃后,数据库重新启动的时候会保证数据库恢复到崩溃前的状态。这个恢复的过程依赖于 `redo log` 。
|
||||
|
||||
🌈 拓展一下:
|
||||
|
||||
- MySQL InnoDB 引擎使用 **redo log(重做日志)** 保证事务的**持久性**,使用 **undo log(回滚日志)** 来保证事务的**原子性**。
|
||||
- MySQL InnoDB 引擎通过 **锁机制**、**MVCC** 等手段来保证事务的隔离性( 默认支持的隔离级别是 **`REPEATABLE-READ`** )。
|
||||
- 保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。
|
||||
|
||||
**5.是否支持 MVCC**
|
||||
|
||||
MyISAM 不支持,而 InnoDB 支持。
|
||||
|
||||
讲真,这个对比有点废话,毕竟 MyISAM 连行级锁都不支持。
|
||||
|
||||
MVCC 可以看作是行级锁的一个升级,可以有效减少加锁操作,提供性能。
|
||||
|
||||
### 关于 MyISAM 和 InnoDB 的选择问题
|
||||
|
||||
大多数时候我们使用的都是 InnoDB 存储引擎,在某些读密集的情况下,使用 MyISAM 也是合适的。不过,前提是你的项目不介意 MyISAM 不支持事务、崩溃恢复等缺点(可是~我们一般都会介意啊!)。
|
||||
|
||||
《MySQL 高性能》上面有一句话这样写到:
|
||||
|
||||
> 不要轻易相信“MyISAM 比 InnoDB 快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知场景中,InnoDB 的速度都可以让 MyISAM 望尘莫及,尤其是用到了聚簇索引,或者需要访问的数据都可以放入内存的应用。
|
||||
|
||||
一般情况下我们选择 InnoDB 都是没有问题的,但是某些情况下你并不在乎可扩展能力和并发能力,也不需要事务支持,也不在乎崩溃后的安全恢复问题的话,选择 MyISAM 也是一个不错的选择。但是一般情况下,我们都是需要考虑到这些问题的。
|
||||
|
||||
因此,对于咱们日常开发的业务系统来说,你几乎找不到什么理由再使用 MyISAM 作为自己的 MySQL 数据库的存储引擎。
|
||||
|
||||
## 锁机制与 InnoDB 锁算法
|
||||
|
||||
**MyISAM 和 InnoDB 存储引擎使用的锁:**
|
||||
|
||||
- MyISAM 采用表级锁(table-level locking)。
|
||||
- InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁
|
||||
|
||||
**表级锁和行级锁对比:**
|
||||
|
||||
- **表级锁:** MySQL 中锁定 **粒度最大** 的一种锁,对当前操作的整张表加锁,实现简单,资源消耗也比较少,加锁快,不会出现死锁。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都支持表级锁。
|
||||
- **行级锁:** MySQL 中锁定 **粒度最小** 的一种锁,只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。
|
||||
|
||||
**InnoDB 存储引擎的锁的算法有三种:**
|
||||
|
||||
- Record lock:单个行记录上的锁
|
||||
- Gap lock:间隙锁,锁定一个范围,不包括记录本身
|
||||
- Next-key lock:record+gap 锁定一个范围,包含记录本身
|
||||
|
||||
## 查询缓存
|
||||
|
||||
执行查询语句的时候,会先查询缓存。不过,MySQL 8.0 版本后移除,因为这个功能不太实用
|
||||
|
||||
`my.cnf` 加入以下配置,重启 MySQL 开启查询缓存
|
||||
|
||||
```properties
|
||||
query_cache_type=1
|
||||
query_cache_size=600000
|
||||
```
|
||||
|
||||
MySQL 执行以下命令也可以开启查询缓存
|
||||
|
||||
```properties
|
||||
set global query_cache_type=1;
|
||||
set global query_cache_size=600000;
|
||||
```
|
||||
|
||||
如上,**开启查询缓存后在同样的查询条件以及数据情况下,会直接在缓存中返回结果**。这里的查询条件包括查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息。因此任何两个查询在任何字符上的不同都会导致缓存不命中。此外,如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、MySQL 库中的系统表,其查询结果也不会被缓存。
|
||||
|
||||
缓存建立之后,MySQL 的查询缓存系统会跟踪查询中涉及的每张表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。
|
||||
|
||||
**缓存虽然能够提升数据库的查询性能,但是缓存同时也带来了额外的开销,每次查询后都要做一次缓存操作,失效后还要销毁。** 因此,开启查询缓存要谨慎,尤其对于写密集的应用来说更是如此。如果开启,要注意合理控制缓存空间大小,一般来说其大小设置为几十 MB 比较合适。此外,**还可以通过 sql_cache 和 sql_no_cache 来控制某个查询语句是否需要缓存:**
|
||||
|
||||
```sql
|
||||
select sql_no_cache count(*) from usr;
|
||||
```
|
||||
|
||||
## 事务
|
||||
|
||||
### 何为事务?
|
||||
|
||||
一言蔽之,**事务是逻辑上的一组操作,要么都执行,要么都不执行。**
|
||||
|
||||
**可以简单举一个例子不?**
|
||||
|
||||
事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账 1000 元,这个转账会涉及到两个关键操作就是:
|
||||
|
||||
1. 将小明的余额减少 1000 元
|
||||
2. 将小红的余额增加 1000 元。
|
||||
|
||||
事务会把这两个操作就可以看成逻辑上的一个整体,这个整体包含的操作要么都成功,要么都要失败。
|
||||
|
||||
这样就不会出现小明余额减少而小红的余额却并没有增加的情况。
|
||||
|
||||
### 何为数据库事务?
|
||||
|
||||
数据库事务在我们日常开发中接触的最多了。如果你的项目属于单体架构的话,你接触到的往往就是数据库事务了。
|
||||
|
||||
平时,我们在谈论事务的时候,如果没有特指**分布式事务**,往往指的就是**数据库事务**。
|
||||
|
||||
**那数据库事务有什么作用呢?**
|
||||
|
||||
简单来说:数据库事务可以保证多个对数据库的操作(也就是 SQL 语句)构成一个逻辑上的整体。构成这个逻辑上的整体的这些数据库操作遵循:**要么全部执行成功,要么全部不执行** 。
|
||||
|
||||
```sql
|
||||
# 开启一个事务
|
||||
START TRANSACTION;
|
||||
# 多条 SQL 语句
|
||||
SQL1,SQL2...
|
||||
## 提交事务
|
||||
COMMIT;
|
||||
```
|
||||
|
||||

|
||||
|
||||
另外,关系型数据库(例如:`MySQL`、`SQL Server`、`Oracle` 等)事务都有 **ACID** 特性:
|
||||
|
||||

|
||||
|
||||
### 何为 ACID 特性呢?
|
||||
|
||||
1. **原子性**(`Atomicity`) : 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
|
||||
2. **一致性**(`Consistency`): 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
|
||||
3. **隔离性**(`Isolation`): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
|
||||
4. **持久性**(`Durabilily`): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
|
||||
|
||||
**数据事务的实现原理呢?**
|
||||
|
||||
我们这里以 MySQL 的 InnoDB 引擎为例来简单说一下。
|
||||
|
||||
MySQL InnoDB 引擎使用 **redo log(重做日志)** 保证事务的**持久性**,使用 **undo log(回滚日志)** 来保证事务的**原子性**。
|
||||
|
||||
MySQL InnoDB 引擎通过 **锁机制**、**MVCC** 等手段来保证事务的隔离性( 默认支持的隔离级别是 **`REPEATABLE-READ`** )。
|
||||
|
||||
保证了事务的持久性、原子性、隔离性之后,一致性才能得到保障。
|
||||
|
||||
### 并发事务带来哪些问题?
|
||||
|
||||
在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
|
||||
|
||||
- **脏读(Dirty read):** 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
|
||||
- **丢失修改(Lost to modify):** 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 修改 A=A-1,事务 2 也修改 A=A-1,最终结果 A=19,事务 1 的修改被丢失。
|
||||
- **不可重复读(Unrepeatableread):** 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
|
||||
- **幻读(Phantom read):** 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
|
||||
|
||||
**不可重复读和幻读区别:**
|
||||
|
||||
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。
|
||||
|
||||
### 事务隔离级别有哪些?
|
||||
|
||||
SQL 标准定义了四个隔离级别:
|
||||
|
||||
- **READ-UNCOMMITTED(读取未提交):** 最低的隔离级别,允许读取尚未提交的数据变更,**可能会导致脏读、幻读或不可重复读**。
|
||||
- **READ-COMMITTED(读取已提交):** 允许读取并发事务已经提交的数据,**可以阻止脏读,但是幻读或不可重复读仍有可能发生**。
|
||||
- **REPEATABLE-READ(可重复读):** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,**可以阻止脏读和不可重复读,但幻读仍有可能发生**。
|
||||
- **SERIALIZABLE(可串行化):** 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不可重复读以及幻读**。
|
||||
|
||||
---
|
||||
|
||||
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|
||||
| :--------------: | :--: | :--------: | :--: |
|
||||
| READ-UNCOMMITTED | √ | √ | √ |
|
||||
| READ-COMMITTED | × | √ | √ |
|
||||
| REPEATABLE-READ | × | × | √ |
|
||||
| SERIALIZABLE | × | × | × |
|
||||
|
||||
### MySQL 的默认隔离级别是什么?
|
||||
|
||||
MySQL InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)**。我们可以通过`SELECT @@tx_isolation;`命令来查看,MySQL 8.0 该命令改为`SELECT @@transaction_isolation;`
|
||||
|
||||
```sql
|
||||
mysql> SELECT @@tx_isolation;
|
||||
+-----------------+
|
||||
| @@tx_isolation |
|
||||
+-----------------+
|
||||
| REPEATABLE-READ |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
~~这里需要注意的是:与 SQL 标准不同的地方在于 InnoDB 存储引擎在 **REPEATABLE-READ(可重读)** 事务隔离级别下使用的是 Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说 InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)** 已经可以完全保证事务的隔离性要求,即达到了 SQL 标准的 **SERIALIZABLE(可串行化)** 隔离级别。~~
|
||||
|
||||
🐛 问题更正:**MySQL InnoDB 的 REPEATABLE-READ(可重读)并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是 Next-Key Locks。**
|
||||
|
||||
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是 **READ-COMMITTED(读取提交内容)** ,但是你要知道的是 InnoDB 存储引擎默认使用 **REPEAaTABLE-READ(可重读)** 并不会有任何性能损失。
|
||||
|
||||
InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到 **SERIALIZABLE(可串行化)** 隔离级别。
|
||||
|
||||
🌈 拓展一下(以下内容摘自《MySQL 技术内幕:InnoDB 存储引擎(第 2 版)》7.7 章):
|
||||
|
||||
> InnoDB 存储引擎提供了对 XA 事务的支持,并通过 XA 事务来支持分布式事务的实现。分布式事务指的是允许多个独立的事务资源(transactional resources)参与到一个全局的事务中。事务资源通常是关系型数据库系统,但也可以是其他类型的资源。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的 ACID 要求又有了提高。另外,在使用分布式事务时,InnoDB 存储引擎的事务隔离级别必须设置为 SERIALIZABLE。
|
||||
|
||||
## 参考
|
||||
|
||||
- 《高性能 MySQL》
|
||||
|
||||
- https://www.omnisci.com/technical-glossary/relational-database
|
@ -35,7 +35,7 @@
|
||||
- [2. 避免数据类型的隐式转换](#2-避免数据类型的隐式转换)
|
||||
- [3. 充分利用表上已经存在的索引](#3-充分利用表上已经存在的索引)
|
||||
- [4. 数据库设计时,应该要对以后扩展进行考虑](#4-数据库设计时应该要对以后扩展进行考虑)
|
||||
- [5. 程序连接不同的数据库使用不同的账号,进制跨库查询](#5-程序连接不同的数据库使用不同的账号进制跨库查询)
|
||||
- [5. 程序连接不同的数据库使用不同的账号,禁止跨库查询](#5-程序连接不同的数据库使用不同的账号禁止跨库查询)
|
||||
- [6. 禁止使用 SELECT * 必须使用 SELECT <字段列表> 查询](#6-禁止使用-select--必须使用-select-字段列表-查询)
|
||||
- [7. 禁止使用不含字段列表的 INSERT 语句](#7-禁止使用不含字段列表的-insert-语句)
|
||||
- [8. 避免使用子查询,可以把子查询优化为 join 操作](#8-避免使用子查询可以把子查询优化为-join-操作)
|
||||
@ -76,6 +76,8 @@ Innodb 支持事务,支持行级锁,更好的恢复性,高并发下性能
|
||||
|
||||
兼容性更好,统一字符集可以避免由于字符集转换产生的乱码,不同的字符集进行比较前需要进行转换会造成索引失效,如果数据库中有存储 emoji 表情的需要,字符集需要采用 utf8mb4 字符集。
|
||||
|
||||
参考文章:[MySQL 字符集不一致导致索引失效的一个真实案例](https://blog.csdn.net/horses/article/details/107243447)
|
||||
|
||||
### 3. 所有表和字段都需要添加注释
|
||||
|
||||
使用 comment 从句添加表和列的备注,从一开始就进行数据字典的维护
|
||||
|
108
docs/database/Redis/3种常用的缓存读写策略.md
Normal file
@ -0,0 +1,108 @@
|
||||
看到很多小伙伴简历上写了“**熟练使用缓存**”,但是被我问到“**缓存常用的3种读写策略**”的时候却一脸懵逼。
|
||||
|
||||
在我看来,造成这个问题的原因是我们在学习 Redis 的时候,可能只是简单了写一些 Demo,并没有去关注缓存的读写策略,或者说压根不知道这回事。
|
||||
|
||||
但是,搞懂3种常见的缓存读写策略对于实际工作中使用缓存以及面试中被问到缓存都是非常有帮助的!
|
||||
|
||||
下面我会简单介绍一下自己对于这 3 种缓存读写策略的理解。
|
||||
|
||||
另外,**这3 种缓存读写策略各有优劣,不存在最佳,需要我们根据具体的业务场景选择更适合的。**
|
||||
|
||||
*个人能力有限。如果文章有任何需要补充/完善/修改的地方,欢迎在评论区指出,共同进步!——爱你们的 Guide 哥*
|
||||
|
||||
### Cache Aside Pattern(旁路缓存模式)
|
||||
|
||||
**Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。**
|
||||
|
||||
Cache Aside Pattern 中服务端需要同时维系 DB 和 cache,并且是以 DB 的结果为准。
|
||||
|
||||
下面我们来看一下这个策略模式下的缓存读写步骤。
|
||||
|
||||
**写** :
|
||||
|
||||
- 先更新 DB
|
||||
- 然后直接删除 cache 。
|
||||
|
||||
简单画了一张图帮助大家理解写的步骤。
|
||||
|
||||

|
||||
|
||||
**读** :
|
||||
|
||||
- 从 cache 中读取数据,读取到就直接返回
|
||||
- cache中读取不到的话,就从 DB 中读取数据返回
|
||||
- 再把数据放到 cache 中。
|
||||
|
||||
简单画了一张图帮助大家理解读的步骤。
|
||||
|
||||

|
||||
|
||||
|
||||
你仅仅了解了上面这些内容的话是远远不够的,我们还要搞懂其中的原理。
|
||||
|
||||
比如说面试官很可能会追问:“**在写数据的过程中,可以先删除 cache ,后更新 DB 么?**”
|
||||
|
||||
**答案:** 那肯定是不行的!因为这样可能会造成**数据库(DB)和缓存(Cache)数据不一致**的问题。为什么呢?比如说请求1 先写数据A,请求2随后读数据A的话就很有可能产生数据不一致性的问题。这个过程可以简单描述为:
|
||||
|
||||
> 请求1先把cache中的A数据删除 -> 请求2从DB中读取数据->请求1再把DB中的A数据更新。
|
||||
|
||||
当你这样回答之后,面试官可能会紧接着就追问:“**在写数据的过程中,先更新DB,后删除cache就没有问题了么?**”
|
||||
|
||||
**答案:** 理论上来说还是可能会出现数据不一致性的问题,不过概率非常小,因为缓存的写入速度是比数据库的写入速度快很多!
|
||||
|
||||
比如请求1先读数据 A,请求2随后写数据A,并且数据A不在缓存中的话也有可能产生数据不一致性的问题。这个过程可以简单描述为:
|
||||
|
||||
> 请求1从DB读数据A->请求2写更新数据 A 到数据库并把删除cache中的A数据->请求1将数据A写入cache。
|
||||
|
||||
现在我们再来分析一下 **Cache Aside Pattern 的缺陷**。
|
||||
|
||||
**缺陷1:首次请求数据一定不在 cache 的问题**
|
||||
|
||||
解决办法:可以将热点数据可以提前放入cache 中。
|
||||
|
||||
**缺陷2:写操作比较频繁的话导致cache中的数据会被频繁被删除,这样会影响缓存命中率 。**
|
||||
|
||||
解决办法:
|
||||
|
||||
- 数据库和缓存数据强一致场景 :更新DB的时候同样更新cache,不过我们需要加一个锁/分布式锁来保证更新cache的时候不存在线程安全问题。
|
||||
- 可以短暂地允许数据库和缓存数据不一致的场景 :更新DB的时候同样更新cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。
|
||||
|
||||
### Read/Write Through Pattern(读写穿透)
|
||||
|
||||
Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 DB,从而减轻了应用程序的职责。
|
||||
|
||||
这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 并没有提供 cache 将数据写入DB的功能。
|
||||
|
||||
**写(Write Through):**
|
||||
|
||||
- 先查 cache,cache 中不存在,直接更新 DB。
|
||||
- cache 中存在,则先更新 cache,然后 cache 服务自己更新 DB(**同步更新 cache 和 DB**)。
|
||||
|
||||
简单画了一张图帮助大家理解写的步骤。
|
||||
|
||||

|
||||
|
||||
**读(Read Through):**
|
||||
|
||||
- 从 cache 中读取数据,读取到就直接返回 。
|
||||
- 读取不到的话,先从 DB 加载,写入到 cache 后返回响应。
|
||||
|
||||
简单画了一张图帮助大家理解读的步骤。
|
||||
|
||||

|
||||
|
||||
Read-Through Pattern 实际只是在 Cache-Aside Pattern 之上进行了封装。在 Cache-Aside Pattern 下,发生读请求的时候,如果 cache 中不存在对应的数据,是由客户端自己负责把数据写入 cache,而 Read Through Pattern 则是 cache 服务自己来写入缓存的,这对客户端是透明的。
|
||||
|
||||
和 Cache Aside Pattern 一样, Read-Through Pattern 也有首次请求数据一定不再 cache 的问题,对于热点数据可以提前放入缓存中。
|
||||
|
||||
### Write Behind Pattern(异步缓存写入)
|
||||
|
||||
Write Behind Pattern 和 Read/Write Through Pattern 很相似,两者都是由 cache 服务来负责 cache 和 DB 的读写。
|
||||
|
||||
但是,两个又有很大的不同:**Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。**
|
||||
|
||||
很明显,这种方式对数据一致性带来了更大的挑战,比如cache数据可能还没异步更新DB的话,cache服务可能就就挂掉了。
|
||||
|
||||
这种策略在我们平时开发过程中也非常非常少见,但是不代表它的应用场景少,比如消息队列中消息的异步写入磁盘、MySQL 的 InnoDB Buffer Pool 机制都用到了这种策略。
|
||||
|
||||
Write Behind Pattern 下 DB 的写性能非常高,非常适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量。
|
@ -1,299 +0,0 @@
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [redis 简介](#redis-简介)
|
||||
- [为什么要用 redis/为什么要用缓存](#为什么要用-redis为什么要用缓存)
|
||||
- [为什么要用 redis 而不用 map/guava 做缓存?](#为什么要用-redis-而不用-mapguava-做缓存)
|
||||
- [redis 和 memcached 的区别](#redis-和-memcached-的区别)
|
||||
- [redis 常见数据结构以及使用场景分析](#redis-常见数据结构以及使用场景分析)
|
||||
- [1.String](#1string)
|
||||
- [2.Hash](#2hash)
|
||||
- [3.List](#3list)
|
||||
- [4.Set](#4set)
|
||||
- [5.Sorted Set](#5sorted-set)
|
||||
- [redis 设置过期时间](#redis-设置过期时间)
|
||||
- [redis 内存淘汰机制(MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?)](#redis-内存淘汰机制mysql里有2000w数据redis中只存20w的数据如何保证redis中的数据都是热点数据)
|
||||
- [redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)](#redis-持久化机制怎么保证-redis-挂掉之后再重启数据可以进行恢复)
|
||||
- [redis 事务](#redis-事务)
|
||||
- [缓存雪崩和缓存穿透问题解决方案](#缓存雪崩和缓存穿透问题解决方案)
|
||||
- [如何解决 Redis 的并发竞争 Key 问题](#如何解决-redis-的并发竞争-key-问题)
|
||||
- [如何保证缓存与数据库双写时的数据一致性?](#如何保证缓存与数据库双写时的数据一致性)
|
||||
- [参考:](#参考)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
### redis 简介
|
||||
|
||||
简单来说 redis 就是一个数据库,不过与传统数据库不同的是 redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向。另外,redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持不同的业务场景。除此之外,redis 支持事务 、持久化、LUA脚本、LRU驱动事件、多种集群方案。
|
||||
|
||||
### 为什么要用 redis/为什么要用缓存
|
||||
|
||||
主要从“高性能”和“高并发”这两点来看待这个问题。
|
||||
|
||||
**高性能:**
|
||||
|
||||
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
|
||||
|
||||

|
||||
|
||||
|
||||
**高并发:**
|
||||
|
||||
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
### 为什么要用 redis 而不用 map/guava 做缓存?
|
||||
|
||||
|
||||
>下面的内容来自 segmentfault 一位网友的提问,地址:https://segmentfault.com/q/1010000009106416
|
||||
|
||||
缓存分为本地缓存和分布式缓存。以 Java 为例,使用自带的 map 或者 guava 实现的是本地缓存,最主要的特点是轻量以及快速,生命周期随着 jvm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
|
||||
|
||||
使用 redis 或 memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。缺点是需要保持 redis 或 memcached服务的高可用,整个程序架构上较为复杂。
|
||||
|
||||
|
||||
### redis 和 memcached 的区别
|
||||
|
||||
对于 redis 和 memcached 我总结了下面四点。现在公司一般都是用 redis 来实现缓存,而且 redis 自身也越来越强大了!
|
||||
|
||||
1. **redis支持更丰富的数据类型(支持更复杂的应用场景)**:Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。
|
||||
2. **Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中。**
|
||||
3. **集群模式**:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 redis 目前是原生支持 cluster 模式的.
|
||||
4. **Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的多路 IO 复用模型。**
|
||||
|
||||
|
||||
> 来自网络上的一张图,这里分享给大家!
|
||||
|
||||

|
||||
|
||||
|
||||
### redis 常见数据结构以及使用场景分析
|
||||
|
||||
#### 1.String
|
||||
|
||||
> **常用命令:** set,get,decr,incr,mget 等。
|
||||
|
||||
|
||||
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。
|
||||
常规key-value缓存应用;
|
||||
常规计数:微博数,粉丝数等。
|
||||
|
||||
#### 2.Hash
|
||||
> **常用命令:** hget,hset,hgetall 等。
|
||||
|
||||
hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。比如下面我就用 hash 类型存放了我本人的一些信息:
|
||||
|
||||
```
|
||||
key=JavaUser293847
|
||||
value={
|
||||
“id”: 1,
|
||||
“name”: “SnailClimb”,
|
||||
“age”: 22,
|
||||
“location”: “Wuhan, Hubei”
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### 3.List
|
||||
> **常用命令:** lpush,rpush,lpop,rpop,lrange等
|
||||
|
||||
list 就是链表,Redis list 的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的 list 结构来实现。
|
||||
|
||||
Redis list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
|
||||
|
||||
另外可以通过 lrange 命令,就是从某个元素开始读取多少个元素,可以基于 list 实现分页查询,这个很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西(一页一页的往下走),性能高。
|
||||
|
||||
#### 4.Set
|
||||
|
||||
> **常用命令:**
|
||||
sadd,spop,smembers,sunion 等
|
||||
|
||||
set 对外提供的功能与list类似是一个列表的功能,特殊之处在于 set 是可以自动排重的。
|
||||
|
||||
当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。
|
||||
|
||||
比如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程,具体命令如下:
|
||||
|
||||
```
|
||||
sinterstore key1 key2 key3 将交集存在key1内
|
||||
```
|
||||
|
||||
#### 5.Sorted Set
|
||||
> **常用命令:** zadd,zrange,zrem,zcard等
|
||||
|
||||
|
||||
和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。
|
||||
|
||||
**举例:** 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 Sorted Set 结构进行存储。
|
||||
|
||||
|
||||
### redis 设置过期时间
|
||||
|
||||
Redis中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的 token 或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。
|
||||
|
||||
我们 set key 的时候,都可以给一个 expire time,就是过期时间,通过过期时间我们可以指定这个 key 可以存活的时间。
|
||||
|
||||
如果假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?
|
||||
|
||||
**定期删除+惰性删除。**
|
||||
|
||||
通过名字大概就能猜出这两个删除方式的意思了。
|
||||
|
||||
- **定期删除**:redis默认是每隔 100ms 就**随机抽取**一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
|
||||
- **惰性删除** :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈!
|
||||
|
||||
|
||||
但是仅仅通过设置过期时间还是有问题的。我们想一下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了。怎么解决这个问题呢? **redis 内存淘汰机制。**
|
||||
|
||||
|
||||
|
||||
### redis 内存淘汰机制(MySQL里有2000w数据,Redis中只存20w的数据,如何保证Redis中的数据都是热点数据?)
|
||||
|
||||
redis 配置文件 redis.conf 中有相关注释,我这里就不贴了,大家可以自行查阅或者通过这个网址查看: [http://download.redis.io/redis-stable/redis.conf](http://download.redis.io/redis-stable/redis.conf)
|
||||
|
||||
**redis 提供 6种数据淘汰策略:**
|
||||
|
||||
1. **volatile-lru**:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
|
||||
2. **volatile-ttl**:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
|
||||
3. **volatile-random**:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
|
||||
4. **allkeys-lru**:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
|
||||
5. **allkeys-random**:从数据集(server.db[i].dict)中任意选择数据淘汰
|
||||
6. **no-eviction**:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
|
||||
|
||||
4.0版本后增加以下两种:
|
||||
|
||||
7. **volatile-lfu**:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
|
||||
8. **allkeys-lfu**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的key
|
||||
|
||||
|
||||
|
||||
**备注: 关于 redis 设置过期时间以及内存淘汰机制,我这里只是简单的总结一下,后面会专门写一篇文章来总结!**
|
||||
|
||||
|
||||
### redis 持久化机制(怎么保证 redis 挂掉之后再重启数据可以进行恢复)
|
||||
|
||||
很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了防止系统故障而将数据备份到一个远程位置。
|
||||
|
||||
Redis不同于Memcached的很重一点就是,Redis支持持久化,而且支持两种不同的持久化操作。**Redis的一种持久化方式叫快照(snapshotting,RDB),另一种方式是只追加文件(append-only file,AOF)**。这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。
|
||||
|
||||
**快照(snapshotting)持久化(RDB)**
|
||||
|
||||
Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。
|
||||
|
||||
快照持久化是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:
|
||||
|
||||
```conf
|
||||
|
||||
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
|
||||
|
||||
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
|
||||
|
||||
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
|
||||
```
|
||||
|
||||
**AOF(append-only file)持久化**
|
||||
|
||||
与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:
|
||||
|
||||
```conf
|
||||
appendonly yes
|
||||
```
|
||||
|
||||
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。
|
||||
|
||||
在Redis的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:
|
||||
|
||||
```conf
|
||||
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
|
||||
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
|
||||
appendfsync no #让操作系统决定何时进行同步
|
||||
```
|
||||
|
||||
为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec选项 ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
|
||||
|
||||
**Redis 4.0 对于持久化机制的优化**
|
||||
|
||||
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 `aof-use-rdb-preamble` 开启)。
|
||||
|
||||
如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
|
||||
|
||||
**补充内容:AOF 重写**
|
||||
|
||||
AOF重写可以产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态一样,但体积更小。
|
||||
|
||||
AOF重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有AOF文件进行任何读入、分析或者写入操作。
|
||||
|
||||
在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新AOF文件的末尾,使得新旧两个AOF文件所保存的数据库状态一致。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操作
|
||||
|
||||
**更多内容可以查看我的这篇文章:**
|
||||
|
||||
- [Redis持久化](Redis持久化.md)
|
||||
|
||||
|
||||
### redis 事务
|
||||
|
||||
Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。
|
||||
|
||||
在传统的关系式数据库中,常常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),并且当 Redis 运行在某种特定的持久化模式下时,事务也具有持久性(Durability)。
|
||||
|
||||
### 缓存雪崩和缓存穿透问题解决方案
|
||||
|
||||
**缓存雪崩**
|
||||
|
||||
简介:缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
|
||||
|
||||
解决办法(中华石杉老师在他的视频中提到过,视频地址在最后一个问题中有提到):
|
||||
|
||||
- 事前:尽量保证整个 redis 集群的高可用性,发现机器宕机尽快补上。选择合适的内存淘汰策略。
|
||||
- 事中:本地ehcache缓存 + hystrix限流&降级,避免MySQL崩掉
|
||||
- 事后:利用 redis 持久化机制保存的数据尽快恢复缓存
|
||||
|
||||

|
||||
|
||||
|
||||
**缓存穿透**
|
||||
|
||||
简介:一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。
|
||||
|
||||
解决办法: 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
|
||||
|
||||
参考:
|
||||
|
||||
- [https://blog.csdn.net/zeb_perfect/article/details/54135506](https://blog.csdn.net/zeb_perfect/article/details/54135506)
|
||||
|
||||
### 如何解决 Redis 的并发竞争 Key 问题
|
||||
|
||||
所谓 Redis 的并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同!
|
||||
|
||||
推荐一种方案:分布式锁(zookeeper 和 redis 都可以实现分布式锁)。(如果不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能)
|
||||
|
||||
基于zookeeper临时有序节点可以实现的分布式锁。大致思想为:每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。 判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。完成业务流程后,删除对应的子节点释放锁。
|
||||
|
||||
在实践中,当然是从以可靠性为主。所以首推Zookeeper。
|
||||
|
||||
参考:
|
||||
|
||||
- https://www.jianshu.com/p/8bddd381de06
|
||||
|
||||
### 如何保证缓存与数据库双写时的数据一致性?
|
||||
|
||||
你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?
|
||||
|
||||
一般来说,就是如果你的系统不是严格要求缓存+数据库必须一致性的话,缓存可以稍微的跟数据库偶尔有不一致的情况,最好不要做这个方案,读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况
|
||||
|
||||
串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
|
||||
|
||||
**参考:**
|
||||
|
||||
- Java工程师面试突击第1季(可能是史上最好的Java面试突击课程)-中华石杉老师。视频地址见下面!
|
||||
- 链接: https://pan.baidu.com/s/18pp6g1xKVGCfUATf_nMrOA
|
||||
- 密码:5i58
|
||||
|
||||
### 参考:
|
||||
|
||||
- redis设计与实现(第二版)
|
||||
|
@ -1,5 +1,4 @@
|
||||
|
||||
|
||||
非常感谢《redis实战》真本书,本文大多内容也参考了书中的内容。非常推荐大家看一下《redis实战》这本书,感觉书中的很多理论性东西还是很不错的。
|
||||
|
||||
为什么本文的名字要加上春夏秋冬又一春,哈哈 ,这是一部韩国的电影,我感觉电影不错,所以就用在文章名字上了,没有什么特别的含义,然后下面的有些配图也是电影相关镜头。
|
||||
@ -10,12 +9,10 @@
|
||||
|
||||
Redis不同于Memcached的很重一点就是,**Redis支持持久化**,而且支持两种不同的持久化操作。Redis的一种持久化方式叫**快照(snapshotting,RDB)**,另一种方式是**只追加文件(append-only file,AOF)**.这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。
|
||||
|
||||
|
||||
## 快照(snapshotting)持久化
|
||||
|
||||
Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。
|
||||
|
||||
|
||||

|
||||
|
||||
**快照持久化是Redis默认采用的持久化方式**,在redis.conf配置文件中默认有此下配置:
|
||||
@ -46,6 +43,7 @@ save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生
|
||||
|
||||
## **AOF(append-only file)持久化**
|
||||
与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:
|
||||
|
||||
```
|
||||
appendonly yes
|
||||
```
|
||||
|
1
docs/database/Redis/images/redis-all/redis-list.drawio
Normal file
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-07-27T06:27:52.340Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="dsqbsLB26jrO3YkREjOb" version="13.4.5" type="device"><diagram id="tTBfL0GptSJyUtbD2ZoE" name="Page-1">7VlNc5swFPw1PiaDENjmmNjpx6Gdjt1J2lNHAzKoEYgRcmzn11cYyRgJT1w3DnScU3grIaHd5b2HM4CTdP2Rozz5wiJMB64TrQdwOnBdEEAo/5TIpkICx6mAmJNITaqBOXnGCtTTliTCRWOiYIwKkjfBkGUZDkUDQ5yzVXPagtHmrjmKsQXMQ0Rt9IFEIqnQsTuq8U+YxIneGQyDaiRFerI6SZGgiK32IHg3gBPOmKiu0vUE05I8zUt134cDo7sH4zgTx9wwu/+V/H4g/myezsD35/vZ56/ulVrlCdGlOrB6WLHRDHC2zCJcLgIG8HaVEIHnOQrL0ZXUXGKJSKkaLh6xCBMVLFgmlKJgJGO1F+YCrw8eAuyokZ7CLMWCb+QUdcOVp9hUdnKhile1OL62WLInzEhhSPkh3i1dUyYvFGt/waBrMbi9BD3lUfPm2bwFLbT556INttPWc/sB035d0+i10wj7TaM77hmNw5fTIM6im7KeyCikqChI2ORMHp1vfsjA0cHPMrh2fR1P1/uj042KjiAbR1aRMqiWVRHxGIuXUr0tSSNxHqacY4oEeWo+RpsOaodvjMgHrPM2gKbk4+YaBVvyEKvb9suYuZLpHdcwRUWEtdDWF7tzn26V0f9ula4s4A0N4cbOtX+aB3zfWupNPaCb01cxAXjPF0cofGq68AJjIT8wXXdusxzRY/fbLJ0VDbPbOj1j2PXnrVOG/Z3A82WRWFaQXZUwujHB2SOeMMq4RDKW4VJLQqkBIUrirHSQVA9L/Lbs0Yj8lL1RAymJonKb1vavbhCP9M0/dYA+OFDF95zltTjLLPav1gECu5PmOcvf5aloD7qWx7fkoRf8+siiYggEuhbI/oSil/v+2Pp0/gLZ3y18u20/BTqDJp5O8VoSp2tJxvYrgxcXpIjV351PERnWv7RXPV39/wp49wc=</diagram></mxfile>
|
BIN
docs/database/Redis/images/redis-all/redis-list.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/database/Redis/images/redis-all/redis-rollBack.png
Normal file
After Width: | Height: | Size: 275 KiB |
BIN
docs/database/Redis/images/redis-all/redis-vs-memcached.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
docs/database/Redis/images/redis-all/redis4.0-more-thread.png
Normal file
After Width: | Height: | Size: 412 KiB |
BIN
docs/database/Redis/images/redis-all/redis事件处理器.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
docs/database/Redis/images/redis-all/redis事务.png
Normal file
After Width: | Height: | Size: 336 KiB |
BIN
docs/database/Redis/images/redis-all/redis过期时间.png
Normal file
After Width: | Height: | Size: 399 KiB |
BIN
docs/database/Redis/images/redis-all/try-redis.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
docs/database/Redis/images/redis-all/what-is-redis.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
docs/database/Redis/images/redis-all/使用缓存之后.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
docs/database/Redis/images/redis-all/加入布隆过滤器后的缓存处理流程.png
Normal file
After Width: | Height: | Size: 45 KiB |
BIN
docs/database/Redis/images/redis-all/单体架构.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/database/Redis/images/redis-all/缓存的处理流程.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
docs/database/Redis/images/redis-all/缓存穿透情况.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
docs/database/Redis/images/redis-all/集中式缓存架构.png
Normal file
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1 @@
|
||||
<mxfile host="Electron" modified="2020-12-19T10:09:54.115Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/13.4.5 Chrome/83.0.4103.122 Electron/9.1.0 Safari/537.36" etag="ouO_wanaVWn605JgN_9b" version="13.4.5" type="device"><diagram id="3s-LqK6m4lkifnhmqHE2" name="Page-1">7Vpbc+o2EP41ekzHl/j2iAmk02nmdA6dpn3qCFsYTWSLY0Qg/fWVbMnYluE4B4jTQh4Se6WVVt+32pU2BvY43T3mcLV8ojEiwDLiHbAfgGWZluPzP0LyVkoCwygFSY5j2WkvmOF/kBSqbhsco3WjI6OUMLxqCiOaZShiDRnMc7ptdltQ0px1BROkCWYRJLr0GcdsWUp9y9vLf0Y4WaqZTTcoW1KoOsuVrJcwptuayJ4Ae5xTysqndDdGRICncCn1pgdaK8NylLE+Ck/Pf/xthHPraf7NePliLeLZ1+c7OcorJBu5YGkse1MI5HSTxUgMYgI73C4xQ7MVjETrlnPOZUuWEtm8wISMKaF5oWsvFgsrirh8zXL6gmotsTt3HVe0KFiEekRTzLs/GPw5IXC9ls/SSpQztDu4fLMClXsjoili+RvvIhVs5VHSEX3fKd+3e1pNxdWyRqkrZVB6UlINvQebP0i834G9db3YW0Njb2vYx5BBDX++ZNYEuQlmRjN0BHlIcJJxWcRxQrwxFChiHlxGsiHFcSzm6qR2T75RTaxiUME3zZgMmFbQIq1O5jkIvG8RqN5rBLod/NmX4u9e4y8TVN3468dflYWH4s+5mtjnGJ8t77jXi/3gece75Z13EWi3845OYFfcUjyfnT//lndO4W/wvBNcTexzg8+Wd9Ql4BrBHzzxmPpN/5Z5jjDoWZ/sxmPq5YJb6nkHgYOnHlOvOfxfw5/vf7rcoxcMrgb84XOPftu/5Z4jDAbtcunguUcvGdxyzzsIHD736HUHjTyUxSPxDzNBgIBGANKIeC38mgjbXIJ2mP0pe4vnvwSiPzny7WEnAS5e3hpoo1j7L1wLa24q3eQROrbIbk5qmDsdmCtZjghk+LVpRhcRcobfKOYGHiw1BSrjqCFK86XWns4eA7VqGQzmCWLaQIVfVMs+wVX0Esd/zFWGcgG/vevbzPV1gXYG/3AX6FElublAF3Nu+9Lxo1GgXUDQBrqwC6h1NFzAJUV2t/bK7reN+HwhBJN7EPrAD8DEAf4U+GPxwNcQmGASgMAG4QRMfDAywcivqdVdSgmF59ytC9cZ8Q62vdrVNdyknJDP8wACp3N4UnadClNlfymM8esJk8pB5vnpY3DrCluU9PSTlBSd9RBVv7IcOi/Vdrptn+f8ZPt+w/s9Vz8/OeZHHqAsvXz2PZcSFN5JNoQ/ELRgh/1hvYJZH68yne7twPcf3w5jsQt49uD37ckUhGMQjGYZxGRMcDrnnWEqSM7m61XTFcvZj3toKRXmnGDnBTCrBQQHjMYFFHzhngxFoVetu/z9iNlyM6/w+QW+wkfxkdkPwcGlJSK99/HF92e9vNDOxEKJ8hExEzsrEK8EzhEJYfSSFGO3AwxXrl3VjOKn0qJ5jPIOjSlMMREzPMIcpjSLlRkSGENS/aUyxTNKPiOcJb/TlTwiSMGvwgsakpAyRtOm7KsMA2eKQZ6qWakM7OhlFKsrBt1fLAbpFeDbsazXscw0vnep6nsuMw1v2LO5pVeRs41Q+9jAM2R1RefgcuUV/rr/QrfkcP+dsz35Fw==</diagram></mxfile>
|
BIN
docs/database/Redis/images/缓存读写策略/cache-aside-read.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
docs/database/Redis/images/缓存读写策略/cache-aside-write.png
Normal file
After Width: | Height: | Size: 57 KiB |
1
docs/database/Redis/images/缓存读写策略/read-through.drawio
Normal file
BIN
docs/database/Redis/images/缓存读写策略/read-through.png
Normal file
After Width: | Height: | Size: 55 KiB |
1
docs/database/Redis/images/缓存读写策略/write-through.drawio
Normal file
BIN
docs/database/Redis/images/缓存读写策略/write-through.png
Normal file
After Width: | Height: | Size: 62 KiB |
796
docs/database/Redis/redis-all.md
Normal file
@ -0,0 +1,796 @@
|
||||
点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java 面试突击》以及 Java 工程师必备学习资源。
|
||||
|
||||
|
||||
<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->
|
||||
|
||||
<!-- code_chunk_output -->
|
||||
|
||||
- [1. 简单介绍一下 Redis 呗!](#1-简单介绍一下-redis-呗)
|
||||
- [2. 分布式缓存常见的技术选型方案有哪些?](#2-分布式缓存常见的技术选型方案有哪些)
|
||||
- [3. 说一下 Redis 和 Memcached 的区别和共同点](#3-说一下-redis-和-memcached-的区别和共同点)
|
||||
- [4. 缓存数据的处理流程是怎样的?](#4-缓存数据的处理流程是怎样的)
|
||||
- [5. 为什么要用 Redis/为什么要用缓存?](#5-为什么要用-redis为什么要用缓存)
|
||||
- [6. Redis 常见数据结构以及使用场景分析](#6-redis-常见数据结构以及使用场景分析)
|
||||
- [6.1. string](#61-string)
|
||||
- [6.2. list](#62-list)
|
||||
- [6.3. hash](#63-hash)
|
||||
- [6.4. set](#64-set)
|
||||
- [6.5. sorted set](#65-sorted-set)
|
||||
- [6.6 bitmap](#66-bitmap)
|
||||
- [7. Redis 单线程模型详解](#7-redis-单线程模型详解)
|
||||
- [8. Redis 没有使用多线程?为什么不使用多线程?](#8-redis-没有使用多线程为什么不使用多线程)
|
||||
- [9. Redis6.0 之后为何引入了多线程?](#9-redis60-之后为何引入了多线程)
|
||||
- [10. Redis 给缓存数据设置过期时间有啥用?](#10-redis-给缓存数据设置过期时间有啥用)
|
||||
- [11. Redis 是如何判断数据是否过期的呢?](#11-redis-是如何判断数据是否过期的呢)
|
||||
- [12. 过期的数据的删除策略了解么?](#12-过期的数据的删除策略了解么)
|
||||
- [13. Redis 内存淘汰机制了解么?](#13-redis-内存淘汰机制了解么)
|
||||
- [14. Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)](#14-redis-持久化机制怎么保证-redis-挂掉之后再重启数据可以进行恢复)
|
||||
- [15. Redis 事务](#15-redis-事务)
|
||||
- [16. 缓存穿透](#16-缓存穿透)
|
||||
- [16.1. 什么是缓存穿透?](#161-什么是缓存穿透)
|
||||
- [16.2. 缓存穿透情况的处理流程是怎样的?](#162-缓存穿透情况的处理流程是怎样的)
|
||||
- [16.3. 有哪些解决办法?](#163-有哪些解决办法)
|
||||
- [17. 缓存雪崩](#17-缓存雪崩)
|
||||
- [17.1. 什么是缓存雪崩?](#171-什么是缓存雪崩)
|
||||
- [17.2. 有哪些解决办法?](#172-有哪些解决办法)
|
||||
- [18. 如何保证缓存和数据库数据的一致性?](#18-如何保证缓存和数据库数据的一致性)
|
||||
- [19. 参考](#19-参考)
|
||||
- [20. 公众号](#20-公众号)
|
||||
|
||||
<!-- /code_chunk_output -->
|
||||
|
||||
|
||||
|
||||
### 1. 简单介绍一下 Redis 呗!
|
||||
|
||||
简单来说 **Redis 就是一个使用 C 语言开发的数据库**,不过与传统数据库不同的是 **Redis 的数据是存在内存中的** ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。
|
||||
|
||||
另外,**Redis 除了做缓存之外,Redis 也经常用来做分布式锁,甚至是消息队列。**
|
||||
|
||||
**Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案。**
|
||||
|
||||
### 2. 分布式缓存常见的技术选型方案有哪些?
|
||||
|
||||
分布式缓存的话,使用的比较多的主要是 **Memcached** 和 **Redis**。不过,现在基本没有看过还有项目使用 **Memcached** 来做缓存,都是直接用 **Redis**。
|
||||
|
||||
Memcached 是分布式缓存最开始兴起的那会,比较常用的。后来,随着 Redis 的发展,大家慢慢都转而使用更加强大的 Redis 了。
|
||||
|
||||
分布式缓存主要解决的是单机缓存的容量受服务器限制并且无法保存通用的信息。因为,本地缓存只在当前服务里有效,比如如果你部署了两个相同的服务,他们两者之间的缓存数据是无法共同的。
|
||||
|
||||
### 3. 说一下 Redis 和 Memcached 的区别和共同点
|
||||
|
||||
现在公司一般都是用 Redis 来实现缓存,而且 Redis 自身也越来越强大了!不过,了解 Redis 和 Memcached 的区别和共同点,有助于我们在做相应的技术选型的时候,能够做到有理有据!
|
||||
|
||||
**共同点** :
|
||||
|
||||
1. 都是基于内存的数据库,一般都用来当做缓存使用。
|
||||
2. 都有过期策略。
|
||||
3. 两者的性能都非常高。
|
||||
|
||||
**区别** :
|
||||
|
||||
1. **Redis 支持更丰富的数据类型(支持更复杂的应用场景)**。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。
|
||||
2. **Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memecache 把数据全部存在内存之中。**
|
||||
3. **Redis 有灾难恢复机制。** 因为可以把缓存中的数据持久化到磁盘上。
|
||||
4. **Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。**
|
||||
5. **Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的.**
|
||||
6. **Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。** (Redis 6.0 引入了多线程 IO )
|
||||
7. **Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。**
|
||||
8. **Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。**
|
||||
|
||||
相信看了上面的对比之后,我们已经没有什么理由可以选择使用 Memcached 来作为自己项目的分布式缓存了。
|
||||
|
||||
### 4. 缓存数据的处理流程是怎样的?
|
||||
|
||||
作为暖男一号,我给大家画了一个草图。
|
||||
|
||||

|
||||
|
||||
简单来说就是:
|
||||
|
||||
1. 如果用户请求的数据在缓存中就直接返回。
|
||||
2. 缓存中不存在的话就看数据库中是否存在。
|
||||
3. 数据库中存在的话就更新缓存中的数据。
|
||||
4. 数据库中不存在的话就返回空数据。
|
||||
|
||||
### 5. 为什么要用 Redis/为什么要用缓存?
|
||||
|
||||
_简单,来说使用缓存主要是为了提升用户体验以及应对更多的用户。_
|
||||
|
||||
下面我们主要从“高性能”和“高并发”这两点来看待这个问题。
|
||||
|
||||

|
||||
|
||||
**高性能** :
|
||||
|
||||
对照上面 👆 我画的图。我们设想这样的场景:
|
||||
|
||||
假如用户第一次访问数据库中的某些数据的话,这个过程是比较慢,毕竟是从硬盘中读取的。但是,如果说,用户访问的数据属于高频数据并且不会经常改变的话,那么我们就可以很放心地将该用户访问的数据存在缓存中。
|
||||
|
||||
**这样有什么好处呢?** 那就是保证用户下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。
|
||||
|
||||
不过,要保持数据库和缓存中的数据的一致性。 如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
|
||||
|
||||
**高并发:**
|
||||
|
||||
一般像 MySQL 这类的数据库的 QPS 大概都在 1w 左右(4 核 8g) ,但是使用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 redis 的情况,redis 集群的话会更高)。
|
||||
|
||||
> QPS(Query Per Second):服务器每秒可以执行的查询次数;
|
||||
|
||||
所以,直接操作缓存能够承受的数据库请求数量是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。进而,我们也就提高的系统整体的并发。
|
||||
|
||||
### 6. Redis 常见数据结构以及使用场景分析
|
||||
|
||||
你可以自己本机安装 redis 或者通过 redis 官网提供的[在线 redis 环境](https://try.redis.io/)。
|
||||
|
||||

|
||||
|
||||
#### 6.1. string
|
||||
|
||||
1. **介绍** :string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 **简单动态字符串**(simple dynamic string,**SDS**)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。
|
||||
2. **常用命令:** `set,get,strlen,exists,dect,incr,setex` 等等。
|
||||
3. **应用场景** :一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等。
|
||||
|
||||
下面我们简单看看它的使用!
|
||||
|
||||
**普通字符串的基本操作:**
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> set key value #设置 key-value 类型的值
|
||||
OK
|
||||
127.0.0.1:6379> get key # 根据 key 获得对应的 value
|
||||
"value"
|
||||
127.0.0.1:6379> exists key # 判断某个 key 是否存在
|
||||
(integer) 1
|
||||
127.0.0.1:6379> strlen key # 返回 key 所储存的字符串值的长度。
|
||||
(integer) 5
|
||||
127.0.0.1:6379> del key # 删除某个 key 对应的值
|
||||
(integer) 1
|
||||
127.0.0.1:6379> get key
|
||||
(nil)
|
||||
```
|
||||
|
||||
**批量设置** :
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> mset key1 value1 key2 value2 # 批量设置 key-value 类型的值
|
||||
OK
|
||||
127.0.0.1:6379> mget key1 key2 # 批量获取多个 key 对应的 value
|
||||
1) "value1"
|
||||
2) "value2"
|
||||
```
|
||||
|
||||
**计数器(字符串的内容为整数的时候可以使用):**
|
||||
|
||||
```bash
|
||||
|
||||
127.0.0.1:6379> set number 1
|
||||
OK
|
||||
127.0.0.1:6379> incr number # 将 key 中储存的数字值增一
|
||||
(integer) 2
|
||||
127.0.0.1:6379> get number
|
||||
"2"
|
||||
127.0.0.1:6379> decr number # 将 key 中储存的数字值减一
|
||||
(integer) 1
|
||||
127.0.0.1:6379> get number
|
||||
"1"
|
||||
```
|
||||
|
||||
**过期**:
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> expire key 60 # 数据在 60s 后过期
|
||||
(integer) 1
|
||||
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
|
||||
OK
|
||||
127.0.0.1:6379> ttl key # 查看数据还有多久过期
|
||||
(integer) 56
|
||||
```
|
||||
|
||||
#### 6.2. list
|
||||
|
||||
1. **介绍** :**list** 即是 **链表**。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 **LinkedList**,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 **双向链表**,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
|
||||
2. **常用命令:** `rpush,lpop,lpush,rpop,lrange、llen` 等。
|
||||
3. **应用场景:** 发布与订阅或者说消息队列、慢查询。
|
||||
|
||||
下面我们简单看看它的使用!
|
||||
|
||||
**通过 `rpush/lpop` 实现队列:**
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> rpush myList value1 # 向 list 的头部(右边)添加元素
|
||||
(integer) 1
|
||||
127.0.0.1:6379> rpush myList value2 value3 # 向list的头部(最右边)添加多个元素
|
||||
(integer) 3
|
||||
127.0.0.1:6379> lpop myList # 将 list的尾部(最左边)元素取出
|
||||
"value1"
|
||||
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
|
||||
1) "value2"
|
||||
2) "value3"
|
||||
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
|
||||
1) "value2"
|
||||
2) "value3"
|
||||
```
|
||||
|
||||
**通过 `rpush/rpop` 实现栈:**
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> rpush myList2 value1 value2 value3
|
||||
(integer) 3
|
||||
127.0.0.1:6379> rpop myList2 # 将 list的头部(最右边)元素取出
|
||||
"value3"
|
||||
```
|
||||
|
||||
我专门花了一个图方便小伙伴们来理解:
|
||||
|
||||

|
||||
|
||||
**通过 `lrange` 查看对应下标范围的列表元素:**
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> rpush myList value1 value2 value3
|
||||
(integer) 3
|
||||
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
|
||||
1) "value1"
|
||||
2) "value2"
|
||||
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
|
||||
1) "value1"
|
||||
2) "value2"
|
||||
3) "value3"
|
||||
```
|
||||
|
||||
通过 `lrange` 命令,你可以基于 list 实现分页查询,性能非常高!
|
||||
|
||||
**通过 `llen` 查看链表长度:**
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> llen myList
|
||||
(integer) 3
|
||||
```
|
||||
|
||||
#### 6.3. hash
|
||||
|
||||
1. **介绍** :hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。不过,Redis 的 hash 做了更多优化。另外,hash 是一个 string 类型的 field 和 value 的映射表,**特别适合用于存储对象**,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。
|
||||
2. **常用命令:** `hset,hmset,hexists,hget,hgetall,hkeys,hvals` 等。
|
||||
3. **应用场景:** 系统中对象数据的存储。
|
||||
|
||||
下面我们简单看看它的使用!
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> hset userInfoKey name "guide" description "dev" age "24"
|
||||
OK
|
||||
127.0.0.1:6379> hexists userInfoKey name # 查看 key 对应的 value中指定的字段是否存在。
|
||||
(integer) 1
|
||||
127.0.0.1:6379> hget userInfoKey name # 获取存储在哈希表中指定字段的值。
|
||||
"guide"
|
||||
127.0.0.1:6379> hget userInfoKey age
|
||||
"24"
|
||||
127.0.0.1:6379> hgetall userInfoKey # 获取在哈希表中指定 key 的所有字段和值
|
||||
1) "name"
|
||||
2) "guide"
|
||||
3) "description"
|
||||
4) "dev"
|
||||
5) "age"
|
||||
6) "24"
|
||||
127.0.0.1:6379> hkeys userInfoKey # 获取 key 列表
|
||||
1) "name"
|
||||
2) "description"
|
||||
3) "age"
|
||||
127.0.0.1:6379> hvals userInfoKey # 获取 value 列表
|
||||
1) "guide"
|
||||
2) "dev"
|
||||
3) "24"
|
||||
127.0.0.1:6379> hset userInfoKey name "GuideGeGe" # 修改某个字段对应的值
|
||||
127.0.0.1:6379> hget userInfoKey name
|
||||
"GuideGeGe"
|
||||
```
|
||||
|
||||
#### 6.4. set
|
||||
|
||||
1. **介绍 :** set 类似于 Java 中的 `HashSet` 。Redis 中的 set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。
|
||||
2. **常用命令:** `sadd,spop,smembers,sismember,scard,sinterstore,sunion` 等。
|
||||
3. **应用场景:** 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景
|
||||
|
||||
下面我们简单看看它的使用!
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> sadd mySet value1 value2 # 添加元素进去
|
||||
(integer) 2
|
||||
127.0.0.1:6379> sadd mySet value1 # 不允许有重复元素
|
||||
(integer) 0
|
||||
127.0.0.1:6379> smembers mySet # 查看 set 中所有的元素
|
||||
1) "value1"
|
||||
2) "value2"
|
||||
127.0.0.1:6379> scard mySet # 查看 set 的长度
|
||||
(integer) 2
|
||||
127.0.0.1:6379> sismember mySet value1 # 检查某个元素是否存在set 中,只能接收单个元素
|
||||
(integer) 1
|
||||
127.0.0.1:6379> sadd mySet2 value2 value3
|
||||
(integer) 2
|
||||
127.0.0.1:6379> sinterstore mySet3 mySet mySet2 # 获取 mySet 和 mySet2 的交集并存放在 mySet3 中
|
||||
(integer) 1
|
||||
127.0.0.1:6379> smembers mySet3
|
||||
1) "value2"
|
||||
```
|
||||
|
||||
#### 6.5. sorted set
|
||||
|
||||
1. **介绍:** 和 set 相比,sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。
|
||||
2. **常用命令:** `zadd,zcard,zscore,zrange,zrevrange,zrem` 等。
|
||||
3. **应用场景:** 需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> zadd myZset 3.0 value1 # 添加元素到 sorted set 中 3.0 为权重
|
||||
(integer) 1
|
||||
127.0.0.1:6379> zadd myZset 2.0 value2 1.0 value3 # 一次添加多个元素
|
||||
(integer) 2
|
||||
127.0.0.1:6379> zcard myZset # 查看 sorted set 中的元素数量
|
||||
(integer) 3
|
||||
127.0.0.1:6379> zscore myZset value1 # 查看某个 value 的权重
|
||||
"3"
|
||||
127.0.0.1:6379> zrange myZset 0 -1 # 顺序输出某个范围区间的元素,0 -1 表示输出所有元素
|
||||
1) "value3"
|
||||
2) "value2"
|
||||
3) "value1"
|
||||
127.0.0.1:6379> zrange myZset 0 1 # 顺序输出某个范围区间的元素,0 为 start 1 为 stop
|
||||
1) "value3"
|
||||
2) "value2"
|
||||
127.0.0.1:6379> zrevrange myZset 0 1 # 逆序输出某个范围区间的元素,0 为 start 1 为 stop
|
||||
1) "value1"
|
||||
2) "value2"
|
||||
```
|
||||
|
||||
#### 6.6 bitmap
|
||||
|
||||
1. **介绍 :** bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。
|
||||
2. **常用命令:** `setbit` 、`getbit` 、`bitcount`、`bitop`
|
||||
3. **应用场景:** 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。
|
||||
|
||||
```bash
|
||||
# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位
|
||||
127.0.0.1:6379> setbit mykey 7 1
|
||||
(integer) 0
|
||||
127.0.0.1:6379> setbit mykey 7 0
|
||||
(integer) 1
|
||||
127.0.0.1:6379> getbit mykey 7
|
||||
(integer) 0
|
||||
127.0.0.1:6379> setbit mykey 6 1
|
||||
(integer) 0
|
||||
127.0.0.1:6379> setbit mykey 8 1
|
||||
(integer) 0
|
||||
# 通过 bitcount 统计被被设置为 1 的位的数量。
|
||||
127.0.0.1:6379> bitcount mykey
|
||||
(integer) 2
|
||||
```
|
||||
|
||||
针对上面提到的一些场景,这里进行进一步说明。
|
||||
|
||||
**使用场景一:用户行为分析**
|
||||
很多网站为了分析你的喜好,需要研究你点赞过的内容。
|
||||
|
||||
```bash
|
||||
# 记录你喜欢过 001 号小姐姐
|
||||
127.0.0.1:6379> setbit beauty_girl_001 uid 1
|
||||
```
|
||||
|
||||
**使用场景二:统计活跃用户**
|
||||
|
||||
使用时间作为 key,然后用户 ID 为 offset,如果当日活跃过就设置为 1
|
||||
|
||||
那么我该如果计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只有有一天在线就称为活跃),有请下一个 redis 的命令
|
||||
|
||||
```bash
|
||||
# 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
|
||||
# BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数
|
||||
BITOP operation destkey key [key ...]
|
||||
```
|
||||
|
||||
初始化数据:
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> setbit 20210308 1 1
|
||||
(integer) 0
|
||||
127.0.0.1:6379> setbit 20210308 2 1
|
||||
(integer) 0
|
||||
127.0.0.1:6379> setbit 20210309 1 1
|
||||
(integer) 0
|
||||
```
|
||||
|
||||
统计 20210308~20210309 总活跃用户数: 1
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> bitop and desk1 20210308 20210309
|
||||
(integer) 1
|
||||
127.0.0.1:6379> bitcount desk1
|
||||
(integer) 1
|
||||
```
|
||||
|
||||
统计 20210308~20210309 在线活跃用户数: 2
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> bitop or desk2 20210308 20210309
|
||||
(integer) 1
|
||||
127.0.0.1:6379> bitcount desk2
|
||||
(integer) 2
|
||||
```
|
||||
|
||||
**使用场景三:用户在线状态**
|
||||
|
||||
对于获取或者统计用户在线状态,使用 bitmap 是一个节约空间效率又高的一种方法。
|
||||
|
||||
只需要一个 key,然后用户 ID 为 offset,如果在线就设置为 1,不在线就设置为 0。
|
||||
|
||||
### 7. Redis 单线程模型详解
|
||||
|
||||
**Redis 基于 Reactor 模式来设计开发了自己的一套高效的事件处理模型** (Netty 的线程模型也基于 Reactor 模式,Reactor 模式不愧是高性能 IO 的基石),这套事件处理模型对应的是 Redis 中的文件事件处理器(file event handler)。由于文件事件处理器(file event handler)是单线程方式运行的,所以我们一般都说 Redis 是单线程模型。
|
||||
|
||||
**既然是单线程,那怎么监听大量的客户端连接呢?**
|
||||
|
||||
Redis 通过**IO 多路复用程序** 来监听来自客户端的大量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否发生。
|
||||
|
||||
这样的好处非常明显: **I/O 多路复用技术的使用让 Redis 不需要额外创建多余的线程来监听客户端的大量连接,降低了资源的消耗**(和 NIO 中的 `Selector` 组件很像)。
|
||||
|
||||
另外, Redis 服务器是一个事件驱动程序,服务器需要处理两类事件: 1. 文件事件; 2. 时间事件。
|
||||
|
||||
时间事件不需要多花时间了解,我们接触最多的还是 **文件事件**(客户端进行读取写入等操作,涉及一系列网络通信)。
|
||||
|
||||
《Redis 设计与实现》有一段话是如是介绍文件事件的,我觉得写得挺不错。
|
||||
|
||||
> Redis 基于 Reactor 模式开发了自己的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。文件事件处理器使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并根据 套接字目前执行的任务来为套接字关联不同的事件处理器。
|
||||
>
|
||||
> 当被监听的套接字准备好执行连接应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。
|
||||
>
|
||||
> **虽然文件事件处理器以单线程方式运行,但通过使用 I/O 多路复用程序来监听多个套接字**,文件事件处理器既实现了高性能的网络通信模型,又可以很好地与 Redis 服务器中其他同样以单线程方式运行的模块进行对接,这保持了 Redis 内部单线程设计的简单性。
|
||||
|
||||
可以看出,文件事件处理器(file event handler)主要是包含 4 个部分:
|
||||
|
||||
- 多个 socket(客户端连接)
|
||||
- IO 多路复用程序(支持多个客户端连接的关键)
|
||||
- 文件事件分派器(将 socket 关联到相应的事件处理器)
|
||||
- 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
|
||||
|
||||

|
||||
|
||||
<p style="text-align:right; font-size:14px; color:gray">《Redis设计与实现:12章》</p>
|
||||
|
||||
### 8. Redis 没有使用多线程?为什么不使用多线程?
|
||||
|
||||
虽然说 Redis 是单线程模型,但是, 实际上,**Redis 在 4.0 之后的版本中就已经加入了对多线程的支持。**
|
||||
|
||||

|
||||
|
||||
不过,Redis 4.0 增加的多线程主要是针对一些大键值对的删除操作的命令,使用这些命令就会使用主处理之外的其他线程来“异步处理”。
|
||||
|
||||
大体上来说,**Redis 6.0 之前主要还是单线程处理。**
|
||||
|
||||
**那,Redis6.0 之前 为什么不使用多线程?**
|
||||
|
||||
我觉得主要原因有下面 3 个:
|
||||
|
||||
1. 单线程编程容易并且更容易维护;
|
||||
2. Redis 的性能瓶颈不再 CPU ,主要在内存和网络;
|
||||
3. 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。
|
||||
|
||||
### 9. Redis6.0 之后为何引入了多线程?
|
||||
|
||||
**Redis6.0 引入多线程主要是为了提高网络 IO 读写性能**,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。
|
||||
|
||||
虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了, 执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。
|
||||
|
||||
Redis6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis 配置文件 `redis.conf` :
|
||||
|
||||
```bash
|
||||
io-threads-do-reads yes
|
||||
```
|
||||
|
||||
开启多线程后,还需要设置线程数,否则是不生效的。同样需要修改 redis 配置文件 `redis.conf` :
|
||||
|
||||
```bash
|
||||
io-threads 4 #官网建议4核的机器建议设置为2或3个线程,8核的建议设置为6个线程
|
||||
```
|
||||
|
||||
推荐阅读:
|
||||
|
||||
1. [Redis 6.0 新特性-多线程连环 13 问!](https://mp.weixin.qq.com/s/FZu3acwK6zrCBZQ_3HoUgw)
|
||||
2. [为什么 Redis 选择单线程模型](https://draveness.me/whys-the-design-redis-single-thread/)
|
||||
|
||||
### 10. Redis 给缓存数据设置过期时间有啥用?
|
||||
|
||||
一般情况下,我们设置保存的缓存数据的时候都会设置一个过期时间。为什么呢?
|
||||
|
||||
因为内存是有限的,如果缓存中的所有数据都是一直保存的话,分分钟直接 Out of memory。
|
||||
|
||||
Redis 自带了给缓存数据设置过期时间的功能,比如:
|
||||
|
||||
```bash
|
||||
127.0.0.1:6379> exp key 60 # 数据在 60s 后过期
|
||||
(integer) 1
|
||||
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
|
||||
OK
|
||||
127.0.0.1:6379> ttl key # 查看数据还有多久过期
|
||||
(integer) 56
|
||||
```
|
||||
|
||||
注意:**Redis 中除了字符串类型有自己独有设置过期时间的命令 `setex` 外,其他方法都需要依靠 `expire` 命令来设置过期时间 。另外, `persist` 命令可以移除一个键的过期时间: **
|
||||
|
||||
**过期时间除了有助于缓解内存的消耗,还有什么其他用么?**
|
||||
|
||||
很多时候,我们的业务场景就是需要某个数据只在某一时间段内存在,比如我们的短信验证码可能只在 1 分钟内有效,用户登录的 token 可能只在 1 天内有效。
|
||||
|
||||
如果使用传统的数据库来处理的话,一般都是自己判断过期,这样更麻烦并且性能要差很多。
|
||||
|
||||
### 11. Redis 是如何判断数据是否过期的呢?
|
||||
|
||||
Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
|
||||
|
||||

|
||||
|
||||
过期字典是存储在 redisDb 这个结构里的:
|
||||
|
||||
```c
|
||||
typedef struct redisDb {
|
||||
...
|
||||
|
||||
dict *dict; //数据库键空间,保存着数据库中所有键值对
|
||||
dict *expires // 过期字典,保存着键的过期时间
|
||||
...
|
||||
} redisDb;
|
||||
```
|
||||
|
||||
### 12. 过期的数据的删除策略了解么?
|
||||
|
||||
如果假设你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢?
|
||||
|
||||
常用的过期数据的删除策略就两个(重要!自己造缓存轮子的时候需要格外考虑的东西):
|
||||
|
||||
1. **惰性删除** :只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
|
||||
2. **定期删除** : 每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
|
||||
|
||||
定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 **定期删除+惰性/懒汉式删除** 。
|
||||
|
||||
但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。
|
||||
|
||||
怎么解决这个问题呢?答案就是: **Redis 内存淘汰机制。**
|
||||
|
||||
### 13. Redis 内存淘汰机制了解么?
|
||||
|
||||
> 相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?
|
||||
|
||||
Redis 提供 6 种数据淘汰策略:
|
||||
|
||||
1. **volatile-lru(least recently used)**:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
|
||||
2. **volatile-ttl**:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
|
||||
3. **volatile-random**:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
|
||||
4. **allkeys-lru(least recently used)**:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
|
||||
5. **allkeys-random**:从数据集(server.db[i].dict)中任意选择数据淘汰
|
||||
6. **no-eviction**:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
|
||||
|
||||
4.0 版本后增加以下两种:
|
||||
|
||||
7. **volatile-lfu(least frequently used)**:从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
|
||||
8. **allkeys-lfu(least frequently used)**:当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key
|
||||
|
||||
### 14. Redis 持久化机制(怎么保证 Redis 挂掉之后再重启数据可以进行恢复)
|
||||
|
||||
很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后恢复数据),或者是为了防止系统故障而将数据备份到一个远程位置。
|
||||
|
||||
Redis 不同于 Memcached 的很重要一点就是,Redis 支持持久化,而且支持两种不同的持久化操作。**Redis 的一种持久化方式叫快照(snapshotting,RDB),另一种方式是只追加文件(append-only file, AOF)**。这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。
|
||||
|
||||
**快照(snapshotting)持久化(RDB)**
|
||||
|
||||
Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。
|
||||
|
||||
快照持久化是 Redis 默认采用的持久化方式,在 Redis.conf 配置文件中默认有此下配置:
|
||||
|
||||
```conf
|
||||
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
|
||||
|
||||
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
|
||||
|
||||
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
|
||||
```
|
||||
|
||||
**AOF(append-only file)持久化**
|
||||
|
||||
与快照持久化相比,AOF 持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:
|
||||
|
||||
```conf
|
||||
appendonly yes
|
||||
```
|
||||
|
||||
开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入硬盘中的 AOF 文件。AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof。
|
||||
|
||||
在 Redis 的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:
|
||||
|
||||
```conf
|
||||
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
|
||||
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
|
||||
appendfsync no #让操作系统决定何时进行同步
|
||||
```
|
||||
|
||||
为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件,Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis 还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
|
||||
|
||||
**相关 issue** :[783:Redis 的 AOF 方式](https://github.com/Snailclimb/JavaGuide/issues/783)
|
||||
|
||||
**拓展:Redis 4.0 对于持久化机制的优化**
|
||||
|
||||
Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 `aof-use-rdb-preamble` 开启)。
|
||||
|
||||
如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。
|
||||
|
||||
**补充内容:AOF 重写**
|
||||
|
||||
AOF 重写可以产生一个新的 AOF 文件,这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。
|
||||
|
||||
AOF 重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。
|
||||
|
||||
在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作
|
||||
|
||||
### 15. Redis 事务
|
||||
|
||||
Redis 可以通过 **MULTI,EXEC,DISCARD 和 WATCH** 等命令来实现事务(transaction)功能。
|
||||
|
||||
```bash
|
||||
> MULTI
|
||||
OK
|
||||
> INCR foo
|
||||
QUEUED
|
||||
> INCR bar
|
||||
QUEUED
|
||||
> EXEC
|
||||
1) (integer) 1
|
||||
2) (integer) 1
|
||||
```
|
||||
|
||||
使用 [MULTI](https://redis.io/commands/multi)命令后可以输入多个命令。Redis 不会立即执行这些命令,而是将它们放到队列,当调用了[EXEC](https://redis.io/commands/exec)命令将执行所有命令。
|
||||
|
||||
Redis 官网相关介绍 [https://redis.io/topics/transactions](https://redis.io/topics/transactions) 如下:
|
||||
|
||||

|
||||
|
||||
但是,Redis 的事务和我们平时理解的关系型数据库的事务不同。我们知道事务具有四大特性: **1. 原子性**,**2. 隔离性**,**3. 持久性**,**4. 一致性**。
|
||||
|
||||
1. **原子性(Atomicity):** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
|
||||
2. **隔离性(Isolation):** 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
|
||||
3. **持久性(Durability):** 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
|
||||
4. **一致性(Consistency):** 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
|
||||
|
||||
**Redis 是不支持 roll back 的,因而不满足原子性的(而且不满足持久性)。**
|
||||
|
||||
Redis 官网也解释了自己为啥不支持回滚。简单来说就是 Redis 开发者们觉得没必要支持回滚,这样更简单便捷并且性能更好。Redis 开发者觉得即使命令执行错误也应该在开发过程中就被发现而不是生产过程中。
|
||||
|
||||

|
||||
|
||||
你可以将 Redis 中的事务就理解为 :**Redis 事务提供了一种将多个命令请求打包的功能。然后,再按顺序执行打包的所有命令,并且不会被中途打断。**
|
||||
|
||||
**相关 issue** :[issue452: 关于 Redis 事务不满足原子性的问题](https://github.com/Snailclimb/JavaGuide/issues/452) ,推荐阅读:[https://zhuanlan.zhihu.com/p/43897838](https://zhuanlan.zhihu.com/p/43897838) 。
|
||||
|
||||
### 16. 缓存穿透
|
||||
|
||||
#### 16.1. 什么是缓存穿透?
|
||||
|
||||
缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。
|
||||
|
||||
#### 16.2. 缓存穿透情况的处理流程是怎样的?
|
||||
|
||||
如下图所示,用户的请求最终都要跑到数据库中查询一遍。
|
||||
|
||||

|
||||
|
||||
#### 16.3. 有哪些解决办法?
|
||||
|
||||
最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。
|
||||
|
||||
**1)缓存无效 key**
|
||||
|
||||
如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间,具体命令如下: `SET key value EX 10086` 。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。
|
||||
|
||||
另外,这里多说一嘴,一般情况下我们是这样设计 key 的: `表名:列名:主键名:主键值` 。
|
||||
|
||||
如果用 Java 代码展示的话,差不多是下面这样的:
|
||||
|
||||
```java
|
||||
public Object getObjectInclNullById(Integer id) {
|
||||
// 从缓存中获取数据
|
||||
Object cacheValue = cache.get(id);
|
||||
// 缓存为空
|
||||
if (cacheValue == null) {
|
||||
// 从数据库中获取
|
||||
Object storageValue = storage.get(key);
|
||||
// 缓存空对象
|
||||
cache.set(key, storageValue);
|
||||
// 如果存储数据为空,需要设置一个过期时间(300秒)
|
||||
if (storageValue == null) {
|
||||
// 必须设置过期时间,否则有被攻击的风险
|
||||
cache.expire(key, 60 * 5);
|
||||
}
|
||||
return storageValue;
|
||||
}
|
||||
return cacheValue;
|
||||
}
|
||||
```
|
||||
|
||||
**2)布隆过滤器**
|
||||
|
||||
布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。我们需要的就是判断 key 是否合法,有没有感觉布隆过滤器就是我们想要找的那个“人”。
|
||||
|
||||
具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。
|
||||
|
||||
加入布隆过滤器之后的缓存处理流程图如下。
|
||||
|
||||

|
||||
|
||||
但是,需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是: **布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。**
|
||||
|
||||
_为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理来说!_
|
||||
|
||||
我们先来看一下,**当一个元素加入布隆过滤器中的时候,会进行哪些操作:**
|
||||
|
||||
1. 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
|
||||
2. 根据得到的哈希值,在位数组中把对应下标的值置为 1。
|
||||
|
||||
我们再来看一下,**当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行哪些操作:**
|
||||
|
||||
1. 对给定元素再次进行相同的哈希计算;
|
||||
2. 得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
|
||||
|
||||
然后,一定会出现这样一种情况:**不同的字符串可能哈希出来的位置相同。** (可以适当增加位数组大小或者调整我们的哈希函数来降低概率)
|
||||
|
||||
更多关于布隆过滤器的内容可以看我的这篇原创:[《不了解布隆过滤器?一文给你整的明明白白!》](https://github.com/Snailclimb/JavaGuide/blob/master/docs/dataStructures-algorithms/data-structure/bloom-filter.md) ,强烈推荐,个人感觉网上应该找不到总结的这么明明白白的文章了。
|
||||
|
||||
### 17. 缓存雪崩
|
||||
|
||||
#### 17.1. 什么是缓存雪崩?
|
||||
|
||||
我发现缓存雪崩这名字起的有点意思,哈哈。
|
||||
|
||||
实际上,缓存雪崩描述的就是这样一个简单的场景:**缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。** 这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。
|
||||
|
||||
举个例子:系统的缓存模块出了问题比如宕机导致不可用。造成系统的所有访问,都要走数据库。
|
||||
|
||||
还有一种缓存雪崩的场景是:**有一些被大量访问数据(热点缓存)在某一时刻大面积失效,导致对应的请求直接落到了数据库上。** 这样的情况,有下面几种解决办法:
|
||||
|
||||
举个例子 :秒杀开始 12 个小时之前,我们统一存放了一批商品到 Redis 中,设置的缓存过期时间也是 12 个小时,那么秒杀开始的时候,这些秒杀的商品的访问直接就失效了。导致的情况就是,相应的请求直接就落到了数据库上,就像雪崩一样可怕。
|
||||
|
||||
#### 17.2. 有哪些解决办法?
|
||||
|
||||
**针对 Redis 服务不可用的情况:**
|
||||
|
||||
1. 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
|
||||
2. 限流,避免同时处理大量的请求。
|
||||
|
||||
**针对热点缓存失效的情况:**
|
||||
|
||||
1. 设置不同的失效时间比如随机设置缓存的失效时间。
|
||||
2. 缓存永不失效。
|
||||
|
||||
### 18. 如何保证缓存和数据库数据的一致性?
|
||||
|
||||
细说的话可以扯很多,但是我觉得其实没太大必要(小声 BB:很多解决方案我也没太弄明白)。我个人觉得引入缓存之后,如果为了短时间的不一致性问题,选择让系统设计变得更加复杂的话,完全没必要。
|
||||
|
||||
下面单独对 **Cache Aside Pattern(旁路缓存模式)** 来聊聊。
|
||||
|
||||
Cache Aside Pattern 中遇到写请求是这样的:更新 DB,然后直接删除 cache 。
|
||||
|
||||
如果更新数据库成功,而删除缓存这一步失败的情况的话,简单说两个解决方案:
|
||||
|
||||
1. **缓存失效时间变短(不推荐,治标不治本)** :我们让缓存数据的过期时间变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不适用。
|
||||
2. **增加 cache 更新重试机制(常用)**: 如果 cache 服务当前不可用导致缓存删除失败的话,我们就隔一段时间进行重试,重试次数可以自己定。如果多次重试还是失败的话,我们可以把当前更新失败的 key 存入队列中,等缓存服务可用之后,再将 缓存中对应的 key 删除即可。
|
||||
|
||||
### 19. 参考
|
||||
|
||||
- 《Redis 开发与运维》
|
||||
- 《Redis 设计与实现》
|
||||
- Redis 命令总结:http://Redisdoc.com/string/set.html
|
||||
- 通俗易懂的 Redis 数据结构基础教程:[https://juejin.im/post/5b53ee7e5188251aaa2d2e16](https://juejin.im/post/5b53ee7e5188251aaa2d2e16)
|
||||
- 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)
|
||||
|
||||
### 20. 公众号
|
||||
|
||||
如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
|
||||
|
||||
**《Java 面试突击》:** 由本文档衍生的专为面试而生的《Java 面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"Java 面试突击"** 即可免费领取!
|
||||
|
||||
**Java 工程师必备学习资源:** 一些 Java 工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
|
||||
|
||||

|
269
docs/database/Redis/redis集群以及应用场景.md
Normal file
@ -0,0 +1,269 @@
|
||||
相关阅读:
|
||||
|
||||
- [史上最全Redis高可用技术解决方案大全](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247484850&idx=1&sn=3238360bfa8105cf758dcf7354af2814&chksm=cea24a79f9d5c36fb2399aafa91d7fb2699b5006d8d037fe8aaf2e5577ff20ae322868b04a87&token=1082669959&lang=zh_CN&scene=21#wechat_redirect)
|
||||
- [Raft协议实战之Redis Sentinel的选举Leader源码解析](http://weizijun.cn/2015/04/30/Raft%E5%8D%8F%E8%AE%AE%E5%AE%9E%E6%88%98%E4%B9%8BRedis%20Sentinel%E7%9A%84%E9%80%89%E4%B8%BELeader%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/)
|
||||
|
||||
目录:
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [Redis 集群以及应用](#redis-集群以及应用)
|
||||
- [集群](#集群)
|
||||
- [主从复制](#主从复制)
|
||||
- [主从链(拓扑结构)](#主从链拓扑结构)
|
||||
- [复制模式](#复制模式)
|
||||
- [问题点](#问题点)
|
||||
- [哨兵机制](#哨兵机制)
|
||||
- [拓扑图](#拓扑图)
|
||||
- [节点下线](#节点下线)
|
||||
- [Leader选举](#Leader选举)
|
||||
- [故障转移](#故障转移)
|
||||
- [读写分离](#读写分离)
|
||||
- [定时任务](#定时任务)
|
||||
- [分布式集群(Cluster)](#分布式集群cluster)
|
||||
- [拓扑图](#拓扑图)
|
||||
- [通讯](#通讯)
|
||||
- [集中式](#集中式)
|
||||
- [Gossip](#gossip)
|
||||
- [寻址分片](#寻址分片)
|
||||
- [hash取模](#hash取模)
|
||||
- [一致性hash](#一致性hash)
|
||||
- [hash槽](#hash槽)
|
||||
- [使用场景](#使用场景)
|
||||
- [热点数据](#热点数据)
|
||||
- [会话维持 Session](#会话维持-session)
|
||||
- [分布式锁 SETNX](#分布式锁-setnx)
|
||||
- [表缓存](#表缓存)
|
||||
- [消息队列 list](#消息队列-list)
|
||||
- [计数器 string](#计数器-string)
|
||||
- [缓存设计](#缓存设计)
|
||||
- [更新策略](#更新策略)
|
||||
- [更新一致性](#更新一致性)
|
||||
- [缓存粒度](#缓存粒度)
|
||||
- [缓存穿透](#缓存穿透)
|
||||
- [解决方案](#解决方案)
|
||||
- [缓存雪崩](#缓存雪崩)
|
||||
- [出现后应对](#出现后应对)
|
||||
- [请求过程](#请求过程)
|
||||
|
||||
<!-- /MarkdownTOC -->
|
||||
|
||||
# Redis 集群以及应用
|
||||
|
||||
## 集群
|
||||
|
||||
### 主从复制
|
||||
|
||||
#### 主从链(拓扑结构)
|
||||
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
#### 复制模式
|
||||
- 全量复制:Master 全部同步到 Slave
|
||||
- 部分复制:Slave 数据丢失进行备份
|
||||
|
||||
#### 问题点
|
||||
- 同步故障
|
||||
- 复制数据延迟(不一致)
|
||||
- 读取过期数据(Slave 不能删除数据)
|
||||
- 从节点故障
|
||||
- 主节点故障
|
||||
- 配置不一致
|
||||
- maxmemory 不一致:丢失数据
|
||||
- 优化参数不一致:内存不一致.
|
||||
- 避免全量复制
|
||||
- 选择小主节点(分片)、低峰期间操作.
|
||||
- 如果节点运行 id 不匹配(如主节点重启、运行 id 发送变化),此时要执行全量复制,应该配合哨兵和集群解决.
|
||||
- 主从复制挤压缓冲区不足产生的问题(网络中断,部分复制无法满足),可增大复制缓冲区( rel_backlog_size 参数).
|
||||
- 复制风暴
|
||||
|
||||
### 哨兵机制
|
||||
|
||||
#### 拓扑图
|
||||
|
||||

|
||||
|
||||
#### 节点下线
|
||||
|
||||
- 主观下线
|
||||
- 即 Sentinel 节点对 Redis 节点失败的偏见,超出超时时间认为 Master 已经宕机。
|
||||
- Sentinel 集群的每一个 Sentinel 节点会定时对 Redis 集群的所有节点发心跳包检测节点是否正常。如果一个节点在 `down-after-milliseconds` 时间内没有回复 Sentinel 节点的心跳包,则该 Redis 节点被该 Sentinel 节点主观下线。
|
||||
- 客观下线
|
||||
- 所有 Sentinel 节点对 Redis 节点失败要达成共识,即超过 quorum 个统一。
|
||||
- 当节点被一个 Sentinel 节点记为主观下线时,并不意味着该节点肯定故障了,还需要 Sentinel 集群的其他 Sentinel 节点共同判断为主观下线才行。
|
||||
- 该 Sentinel 节点会询问其它 Sentinel 节点,如果 Sentinel 集群中超过 quorum 数量的 Sentinel 节点认为该 Redis 节点主观下线,则该 Redis 客观下线。
|
||||
|
||||
#### Leader选举
|
||||
|
||||
- 选举出一个 Sentinel 作为 Leader:集群中至少有三个 Sentinel 节点,但只有其中一个节点可完成故障转移.通过以下命令可以进行失败判定或领导者选举。
|
||||
- 选举流程
|
||||
1. 每个主观下线的 Sentinel 节点向其他 Sentinel 节点发送命令,要求设置它为领导者.
|
||||
2. 收到命令的 Sentinel 节点如果没有同意通过其他 Sentinel 节点发送的命令,则同意该请求,否则拒绝。
|
||||
3. 如果该 Sentinel 节点发现自己的票数已经超过 Sentinel 集合半数且超过 quorum,则它成为领导者。
|
||||
4. 如果此过程有多个 Sentinel 节点成为领导者,则等待一段时间再重新进行选举。
|
||||
|
||||
#### 故障转移
|
||||
|
||||
- 转移流程
|
||||
1. Sentinel 选出一个合适的 Slave 作为新的 Master(slaveof no one 命令)。
|
||||
2. 向其余 Slave 发出通知,让它们成为新 Master 的 Slave( parallel-syncs 参数)。
|
||||
3. 等待旧 Master 复活,并使之称为新 Master 的 Slave。
|
||||
4. 向客户端通知 Master 变化。
|
||||
- 从 Slave 中选择新 Master 节点的规则(slave 升级成 master 之后)
|
||||
1. 选择 slave-priority 最高的节点。
|
||||
2. 选择复制偏移量最大的节点(同步数据最多)。
|
||||
3. 选择 runId 最小的节点。
|
||||
|
||||
>Sentinel 集群运行过程中故障转移完成,所有 Sentinel 又会恢复平等。Leader 仅仅是故障转移操作出现的角色。
|
||||
|
||||
#### 读写分离
|
||||
|
||||
#### 定时任务
|
||||
|
||||
- 每 1s 每个 Sentinel 对其他 Sentinel 和 Redis 执行 ping,进行心跳检测。
|
||||
- 每 2s 每个 Sentinel 通过 Master 的 Channel 交换信息(pub - sub)。
|
||||
- 每 10s 每个 Sentinel 对 Master 和 Slave 执行 info,目的是发现 Slave 节点、确定主从关系。
|
||||
|
||||
### 分布式集群(Cluster)
|
||||
|
||||
#### 拓扑图
|
||||
|
||||

|
||||
|
||||
#### 通讯
|
||||
|
||||
##### 集中式
|
||||
|
||||
> 将集群元数据(节点信息、故障等等)几种存储在某个节点上。
|
||||
- 优势
|
||||
1. 元数据的更新读取具有很强的时效性,元数据修改立即更新
|
||||
- 劣势
|
||||
1. 数据集中存储
|
||||
|
||||
##### Gossip
|
||||
|
||||

|
||||
|
||||
- [Gossip 协议](https://www.jianshu.com/p/8279d6fd65bb)
|
||||
|
||||
#### 寻址分片
|
||||
|
||||
##### hash取模
|
||||
|
||||
- hash(key)%机器数量
|
||||
- 问题
|
||||
1. 机器宕机,造成数据丢失,数据读取失败
|
||||
1. 伸缩性
|
||||
|
||||
##### 一致性hash
|
||||
|
||||
- 
|
||||
|
||||
- 问题
|
||||
1. 一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。
|
||||
- 解决方案
|
||||
- 可以通过引入虚拟节点机制解决:即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。
|
||||
|
||||
##### hash槽
|
||||
|
||||
- CRC16(key)%16384
|
||||
-
|
||||

|
||||
|
||||
## 使用场景
|
||||
|
||||
### 热点数据
|
||||
|
||||
存取数据优先从 Redis 操作,如果不存在再从文件(例如 MySQL)中操作,从文件操作完后将数据存储到 Redis 中并返回。同时有个定时任务后台定时扫描 Redis 的 key,根据业务规则进行淘汰,防止某些只访问一两次的数据一直存在 Redis 中。
|
||||
>例如使用 Zset 数据结构,存储 Key 的访问次数/最后访问时间作为 Score,最后做排序,来淘汰那些最少访问的 Key。
|
||||
|
||||
如果企业级应用,可以参考:[阿里云的 Redis 混合存储版][1]
|
||||
|
||||
### 会话维持 Session
|
||||
|
||||
会话维持 Session 场景,即使用 Redis 作为分布式场景下的登录中心存储应用。每次不同的服务在登录的时候,都会去统一的 Redis 去验证 Session 是否正确。但是在微服务场景,一般会考虑 Redis + JWT 做 Oauth2 模块。
|
||||
>其中 Redis 存储 JWT 的相关信息主要是留出口子,方便以后做统一的防刷接口,或者做登录设备限制等。
|
||||
|
||||
### 分布式锁 SETNX
|
||||
|
||||
命令格式:`SETNX key value`:当且仅当 key 不存在,将 key 的值设为 value。若给定的 key 已经存在,则 SETNX 不做任何动作。
|
||||
|
||||
1. 超时时间设置:获取锁的同时,启动守护线程,使用 expire 进行定时更新超时时间。如果该业务机器宕机,守护线程也挂掉,这样也会自动过期。如果该业务不是宕机,而是真的需要这么久的操作时间,那么增加超时时间在业务上也是可以接受的,但是肯定有个最大的阈值。
|
||||
2. 但是为了增加高可用,需要使用多台 Redis,就增加了复杂性,就可以参考 Redlock:[Redlock分布式锁](Redlock分布式锁.md#怎么在单节点上实现分布式锁)
|
||||
|
||||
### 表缓存
|
||||
|
||||
Redis 缓存表的场景有黑名单、禁言表等。访问频率较高,即读高。根据业务需求,可以使用后台定时任务定时刷新 Redis 的缓存表数据。
|
||||
|
||||
### 消息队列 list
|
||||
|
||||
主要使用了 List 数据结构。
|
||||
List 支持在头部和尾部操作,因此可以实现简单的消息队列。
|
||||
1. 发消息:在 List 尾部塞入数据。
|
||||
2. 消费消息:在 List 头部拿出数据。
|
||||
|
||||
同时可以使用多个 List,来实现多个队列,根据不同的业务消息,塞入不同的 List,来增加吞吐量。
|
||||
|
||||
### 计数器 string
|
||||
|
||||
主要使用了 INCR、DECR、INCRBY、DECRBY 方法。
|
||||
|
||||
INCR key:给 key 的 value 值增加一
|
||||
DECR key:给 key 的 value 值减去一
|
||||
|
||||
## 缓存设计
|
||||
|
||||
### 更新策略
|
||||
|
||||
- LRU、LFU、FIFO 算法自动清除:一致性最差,维护成本低。
|
||||
- 超时自动清除(key expire):一致性较差,维护成本低。
|
||||
- 主动更新:代码层面控制生命周期,一致性最好,维护成本高。
|
||||
|
||||
在 Redis 根据在 redis.conf 的参数 `maxmemory` 来做更新淘汰策略:
|
||||
1. noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。大多数写命令都会导致占用更多的内存(有极少数会例外, 如 DEL 命令)。
|
||||
2. allkeys-lru: 所有 key 通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
|
||||
3. volatile-lru: 只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
|
||||
4. allkeys-random: 所有key通用; 随机删除一部分 key。
|
||||
5. volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
|
||||
6. volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。
|
||||
|
||||
### 更新一致性
|
||||
|
||||
- 读请求:先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
|
||||
- 写请求:先删除缓存,然后再更新数据库(避免大量地写、却又不经常读的数据导致缓存频繁更新)。
|
||||
|
||||
### 缓存粒度
|
||||
|
||||
- 通用性:全量属性更好。
|
||||
- 占用空间:部分属性更好。
|
||||
- 代码维护成本。
|
||||
|
||||
### 缓存穿透
|
||||
|
||||
> 当大量的请求无命中缓存、直接请求到后端数据库(业务代码的 bug、或恶意攻击),同时后端数据库也没有查询到相应的记录、无法添加缓存。
|
||||
> 这种状态会一直维持,流量一直打到存储层上,无法利用缓存、还会给存储层带来巨大压力。
|
||||
|
||||
#### 解决方案
|
||||
|
||||
1. 请求无法命中缓存、同时数据库记录为空时在缓存添加该 key 的空对象(设置过期时间),缺点是可能会在缓存中添加大量的空值键(比如遭到恶意攻击或爬虫),而且缓存层和存储层数据短期内不一致;
|
||||
2. 使用布隆过滤器在缓存层前拦截非法请求、自动为空值添加黑名单(同时可能要为误判的记录添加白名单).但需要考虑布隆过滤器的维护(离线生成/ 实时生成)。
|
||||
|
||||
### 缓存雪崩
|
||||
|
||||
> 缓存崩溃时请求会直接落到数据库上,很可能由于无法承受大量的并发请求而崩溃,此时如果只重启数据库,或因为缓存重启后没有数据,新的流量进来很快又会把数据库击倒。
|
||||
|
||||
#### 出现后应对
|
||||
|
||||
- 事前:Redis 高可用,主从 + 哨兵,Redis Cluster,避免全盘崩溃。
|
||||
- 事中:本地 ehcache 缓存 + hystrix 限流 & 降级,避免数据库承受太多压力。
|
||||
- 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
|
||||
|
||||
#### 请求过程
|
||||
|
||||
1. 用户请求先访问本地缓存,无命中后再访问 Redis,如果本地缓存和 Redis 都没有再查数据库,并把数据添加到本地缓存和 Redis;
|
||||
2. 由于设置了限流,一段时间范围内超出的请求走降级处理(返回默认值,或给出友情提示)。
|
||||
|
@ -104,7 +104,7 @@ SHOW VARIABLES -- 显示系统变量信息
|
||||
-- 查看所有表
|
||||
SHOW TABLES[ LIKE 'pattern']
|
||||
SHOW TABLES FROM 库名
|
||||
-- 查看表机构
|
||||
-- 查看表结构
|
||||
SHOW CREATE TABLE 表名 (信息更详细)
|
||||
DESC 表名 / DESCRIBE 表名 / EXPLAIN 表名 / SHOW COLUMNS FROM 表名 [LIKE 'PATTERN']
|
||||
SHOW TABLE STATUS [FROM db_name] [LIKE 'pattern']
|
||||
@ -242,7 +242,7 @@ SET NAMES GBK; -- 相当于完成以上三个设置
|
||||
utf8 最大为21844个字符,gbk 最大为32766个字符,latin1 最大为65532个字符
|
||||
varchar 是变长的,需要利用存储空间保存 varchar 的长度,如果数据小于255个字节,则采用一个字节来保存长度,反之需要两个字节来保存。
|
||||
varchar 的最大有效长度由最大行大小和使用的字符集确定。
|
||||
最大有效长度是65532字节,因为在varchar存字符串时,第一个字节是空的,不存在任何数据,然后还需两个字节来存放字符串的长度,所以有效长度是64432-1-2=65532字节。
|
||||
最大有效长度是65532字节,因为在varchar存字符串时,第一个字节是空的,不存在任何数据,然后还需两个字节来存放字符串的长度,所以有效长度是65535-1-2=65532字节。
|
||||
例:若一个表定义为 CREATE TABLE tb(c1 int, c2 char(30), c3 varchar(N)) charset=utf8; 问N的最大值是多少? 答:(65535-1-2-4-30*3)/3
|
||||
-- b. blob, text ----------
|
||||
blob 二进制字符串(字节字符串)
|
||||
@ -363,7 +363,7 @@ set(val1, val2, val3...)
|
||||
字段不能再分,就满足第一范式。
|
||||
-- 2NF, 第二范式
|
||||
满足第一范式的前提下,不能出现部分依赖。
|
||||
消除符合主键就可以避免部分依赖。增加单列关键字。
|
||||
消除复合主键就可以避免部分依赖。增加单列关键字。
|
||||
-- 3NF, 第三范式
|
||||
满足第二范式的前提下,不能出现传递依赖。
|
||||
某个字段依赖于主键,而有其他字段依赖于该字段。这就是传递依赖。
|
||||
@ -590,7 +590,7 @@ CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW view_name
|
||||
```mysql
|
||||
事务是指逻辑上的一组操作,组成这组操作的各个单元,要不全成功要不全失败。
|
||||
- 支持连续SQL的集体成功或集体撤销。
|
||||
- 事务是数据库在数据晚自习方面的一个功能。
|
||||
- 事务是数据库在数据完整性方面的一个功能。
|
||||
- 需要利用 InnoDB 或 BDB 存储引擎,对自动提交的特性支持完成。
|
||||
- InnoDB被称为事务安全型引擎。
|
||||
-- 事务开启
|
||||
|
@ -1,9 +1,9 @@
|
||||
> 本文由 [SnailClimb](https://github.com/Snailclimb) 和 [BugSpeak](https://github.com/BugSpeak) 共同完成。
|
||||
> 本文由 [SnailClimb](https://github.com/Snailclimb) 和 [guang19](https://github.com/guang19) 共同完成。
|
||||
<!-- TOC -->
|
||||
|
||||
- [事务隔离级别(图文详解)](#事务隔离级别图文详解)
|
||||
- [什么是事务?](#什么是事务)
|
||||
- [事物的特性(ACID)](#事物的特性acid)
|
||||
- [事务的特性(ACID)](#事务的特性acid)
|
||||
- [并发事务带来的问题](#并发事务带来的问题)
|
||||
- [事务隔离级别](#事务隔离级别)
|
||||
- [实际情况演示](#实际情况演示)
|
||||
@ -24,14 +24,13 @@
|
||||
|
||||
事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
|
||||
|
||||
### 事物的特性(ACID)
|
||||
### 事务的特性(ACID)
|
||||
|
||||

|
||||
|
||||
<div align="center">
|
||||
<img src="https://user-gold-cdn.xitu.io/2018/5/20/1637b08b98619455?w=312&h=305&f=png&s=22430" width="200px"/>
|
||||
</div>
|
||||
|
||||
1. **原子性:** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
|
||||
2. **一致性:** 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
|
||||
2. **一致性:** 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的;
|
||||
3. **隔离性:** 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
|
||||
4. **持久性:** 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
|
||||
|
||||
@ -70,7 +69,7 @@
|
||||
| REPEATABLE-READ | × | × | √ |
|
||||
| SERIALIZABLE | × | × | × |
|
||||
|
||||
MySQL InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)**。我们可以通过`SELECT @@tx_isolation;`命令来查看
|
||||
MySQL InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)**。我们可以通过`SELECT @@tx_isolation;`命令来查看,MySQL 8.0 该命令改为`SELECT @@transaction_isolation;`
|
||||
|
||||
```sql
|
||||
mysql> SELECT @@tx_isolation;
|
||||
@ -81,11 +80,11 @@ mysql> SELECT @@tx_isolation;
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 **REPEATABLE-READ(可重读)**事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)** 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的**SERIALIZABLE(可串行化)**隔离级别。
|
||||
这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 **REPEATABLE-READ(可重读)** 事务隔离级别下,允许应用使用 Next-Key Lock 锁算法来避免幻读的产生。这与其他数据库系统(如 SQL Server)是不同的。所以说虽然 InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)** ,但是可以通过应用加锁读(例如 `select * from table for update` 语句)来保证不会产生幻读,而这个加锁度使用到的机制就是 Next-Key Lock 锁算法。从而达到了 SQL 标准的 **SERIALIZABLE(可串行化)** 隔离级别。
|
||||
|
||||
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是**READ-COMMITTED(读取提交内容):**,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。
|
||||
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是**READ-COMMITTED(读取提交内容):**,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)** 并不会有任何性能损失。
|
||||
|
||||
InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
|
||||
InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到**SERIALIZABLE(可串行化)** 隔离级别。
|
||||
|
||||
### 实际情况演示
|
||||
|
||||
|
160
docs/database/关于数据库存储时间的一点思考.md
Normal file
@ -0,0 +1,160 @@
|
||||
我们平时开发中不可避免的就是要存储时间,比如我们要记录操作表中这条记录的时间、记录转账的交易时间、记录出发时间等等。你会发现这个时间这个东西与我们开发的联系还是非常紧密的,用的好与不好会给我们的业务甚至功能带来很大的影响。所以,我们有必要重新出发,好好认识一下这个东西。
|
||||
|
||||
这是一篇短小精悍的文章,仔细阅读一定能学到不少东西!
|
||||
|
||||
### 1.切记不要用字符串存储日期
|
||||
|
||||
我记得我在大学的时候就这样干过,而且现在很多对数据库不太了解的新手也会这样干,可见,这种存储日期的方式的优点还是有的,就是简单直白,容易上手。
|
||||
|
||||
但是,这是不正确的做法,主要会有下面两个问题:
|
||||
|
||||
1. 字符串占用的空间更大!
|
||||
2. 字符串存储的日期比较效率比较低(逐个字符进行比对),无法用日期相关的 API 进行计算和比较。
|
||||
|
||||
### 2.Datetime 和 Timestamp 之间抉择
|
||||
|
||||
Datetime 和 Timestamp 是 MySQL 提供的两种比较相似的保存时间的数据类型。他们两者究竟该如何选择呢?
|
||||
|
||||
**通常我们都会首选 Timestamp。** 下面说一下为什么这样做!
|
||||
|
||||
#### 2.1 DateTime 类型没有时区信息的
|
||||
|
||||
**DateTime 类型是没有时区信息的(时区无关)** ,DateTime 类型保存的时间都是当前会话所设置的时区对应的时间。这样就会有什么问题呢?当你的时区更换之后,比如你的服务器更换地址或者更换客户端连接时区设置的话,就会导致你从数据库中读出的时间错误。不要小看这个问题,很多系统就是因为这个问题闹出了很多笑话。
|
||||
|
||||
**Timestamp 和时区有关**。Timestamp 类型字段的值会随着服务器时区的变化而变化,自动换算成相应的时间,说简单点就是在不同时区,查询到同一个条记录此字段的值会不一样。
|
||||
|
||||
下面实际演示一下!
|
||||
|
||||
建表 SQL 语句:
|
||||
|
||||
```sql
|
||||
CREATE TABLE `time_zone_test` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
`date_time` datetime DEFAULT NULL,
|
||||
`time_stamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
```
|
||||
|
||||
插入数据:
|
||||
|
||||
```sql
|
||||
INSERT INTO time_zone_test(date_time,time_stamp) VALUES(NOW(),NOW());
|
||||
```
|
||||
|
||||
查看数据:
|
||||
|
||||
```sql
|
||||
select date_time,time_stamp from time_zone_test;
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
```
|
||||
+---------------------+---------------------+
|
||||
| date_time | time_stamp |
|
||||
+---------------------+---------------------+
|
||||
| 2020-01-11 09:53:32 | 2020-01-11 09:53:32 |
|
||||
+---------------------+---------------------+
|
||||
```
|
||||
|
||||
现在我们运行
|
||||
|
||||
修改当前会话的时区:
|
||||
|
||||
```sql
|
||||
set time_zone='+8:00';
|
||||
```
|
||||
|
||||
再次查看数据:
|
||||
|
||||
```
|
||||
+---------------------+---------------------+
|
||||
| date_time | time_stamp |
|
||||
+---------------------+---------------------+
|
||||
| 2020-01-11 09:53:32 | 2020-01-11 17:53:32 |
|
||||
+---------------------+---------------------+
|
||||
```
|
||||
|
||||
**扩展:一些关于 MySQL 时区设置的一个常用 sql 命令**
|
||||
|
||||
```sql
|
||||
# 查看当前会话时区
|
||||
SELECT @@session.time_zone;
|
||||
# 设置当前会话时区
|
||||
SET time_zone = 'Europe/Helsinki';
|
||||
SET time_zone = "+00:00";
|
||||
# 数据库全局时区设置
|
||||
SELECT @@global.time_zone;
|
||||
# 设置全局时区
|
||||
SET GLOBAL time_zone = '+8:00';
|
||||
SET GLOBAL time_zone = 'Europe/Helsinki';
|
||||
```
|
||||
|
||||
#### 2.2 DateTime 类型耗费空间更大
|
||||
|
||||
Timestamp 只需要使用 4 个字节的存储空间,但是 DateTime 需要耗费 8 个字节的存储空间。但是,这样同样造成了一个问题,Timestamp 表示的时间范围更小。
|
||||
|
||||
- DateTime :1000-01-01 00:00:00 ~ 9999-12-31 23:59:59
|
||||
- Timestamp: 1970-01-01 00:00:01 ~ 2037-12-31 23:59:59
|
||||
|
||||
> Timestamp 在不同版本的 MySQL 中有细微差别。
|
||||
|
||||
### 3 再看 MySQL 日期类型存储空间
|
||||
|
||||
下图是 MySQL 5.6 版本中日期类型所占的存储空间:
|
||||
|
||||

|
||||
|
||||
可以看出 5.6.4 之后的 MySQL 多出了一个需要 0 ~ 3 字节的小数位。Datatime 和 Timestamp 会有几种不同的存储空间占用。
|
||||
|
||||
为了方便,本文我们还是默认 Timestamp 只需要使用 4 个字节的存储空间,但是 DateTime 需要耗费 8 个字节的存储空间。
|
||||
|
||||
### 4.数值型时间戳是更好的选择吗?
|
||||
|
||||
很多时候,我们也会使用 int 或者 bigint 类型的数值也就是时间戳来表示时间。
|
||||
|
||||
这种存储方式的具有 Timestamp 类型的所具有一些优点,并且使用它的进行日期排序以及对比等操作的效率会更高,跨系统也很方便,毕竟只是存放的数值。缺点也很明显,就是数据的可读性太差了,你无法直观的看到具体时间。
|
||||
|
||||
时间戳的定义如下:
|
||||
|
||||
> 时间戳的定义是从一个基准时间开始算起,这个基准时间是「1970-1-1 00:00:00 +0:00」,从这个时间开始,用整数表示,以秒计时,随着时间的流逝这个时间整数不断增加。这样一来,我只需要一个数值,就可以完美地表示时间了,而且这个数值是一个绝对数值,即无论的身处地球的任何角落,这个表示时间的时间戳,都是一样的,生成的数值都是一样的,并且没有时区的概念,所以在系统的中时间的传输中,都不需要进行额外的转换了,只有在显示给用户的时候,才转换为字符串格式的本地时间。
|
||||
|
||||
数据库中实际操作:
|
||||
|
||||
```sql
|
||||
mysql> select UNIX_TIMESTAMP('2020-01-11 09:53:32');
|
||||
+---------------------------------------+
|
||||
| UNIX_TIMESTAMP('2020-01-11 09:53:32') |
|
||||
+---------------------------------------+
|
||||
| 1578707612 |
|
||||
+---------------------------------------+
|
||||
1 row in set (0.00 sec)
|
||||
|
||||
mysql> select FROM_UNIXTIME(1578707612);
|
||||
+---------------------------+
|
||||
| FROM_UNIXTIME(1578707612) |
|
||||
+---------------------------+
|
||||
| 2020-01-11 09:53:32 |
|
||||
+---------------------------+
|
||||
1 row in set (0.01 sec)
|
||||
```
|
||||
|
||||
### 5.总结
|
||||
|
||||
MySQL 中时间到底怎么存储才好?Datetime?Timestamp? 数值保存的时间戳?
|
||||
|
||||
好像并没有一个银弹,很多程序员会觉得数值型时间戳是真的好,效率又高还各种兼容,但是很多人又觉得它表现的不够直观。这里插一嘴,《高性能 MySQL 》这本神书的作者就是推荐 Timestamp,原因是数值表示时间不够直观。下面是原文:
|
||||
|
||||
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/高性能mysql-不推荐用数值时间戳.jpg" style="zoom:50%;" />
|
||||
|
||||
每种方式都有各自的优势,根据实际场景才是王道。下面再对这三种方式做一个简单的对比,以供大家实际开发中选择正确的存放时间的数据类型:
|
||||
|
||||
<img src="https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/总结-常用日期存储方式.jpg" style="zoom:50%;" />
|
||||
|
||||
如果还有什么问题欢迎给我留言!如果文章有什么问题的话,也劳烦指出,Guide 哥感激不尽!
|
||||
|
||||
后面的文章我会介绍:
|
||||
|
||||
- [ ] Java8 对日期的支持以及为啥不能用 SimpleDateFormat。
|
||||
- [ ] SpringBoot 中如何实际使用(JPA 为例)
|