diff --git a/README.md b/README.md
index 026e338b11f..681e1596f5c 100644
--- a/README.md
+++ b/README.md
@@ -48,15 +48,10 @@
### 基础
-**知识点/面试题** : (必看:+1: )
-
-1. **[Java 基础知识](docs/java/basis/Java基础知识.md)**
-2. [Java 基础知识疑难点/易错点](docs/java/basis/Java基础知识疑难点.md)
+**知识点/面试题** : (必看:+1: ):[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 有啥区别?](docs/java/basis/IO模型.md)
@@ -156,25 +151,24 @@ JVM 这部分内容主要参考 [JVM 虚拟机规范-Java8 ](https://docs.oracle
**总结:**
1. [数据库基础知识总结](docs/database/数据库基础知识.md)
-2. **[MySQL知识点总结](docs/database/mysql/MySQL总结.md)** (必看 :+1:)
-3. [阿里巴巴开发手册数据库部分的一些最佳实践](docs/database/mysql/阿里巴巴开发手册数据库部分的一些最佳实践.md)
-4. [一千行 MySQL 学习笔记](docs/database/mysql/一千行MySQL学习笔记.md)
-5. [MySQL 高性能优化规范建议](docs/database/mysql/MySQL高性能优化规范建议.md)
+2. **[MySQL知识点总结](docs/database/mysql/mysql知识点&面试题总结.md)** (必看 :+1:)
+4. [一千行 MySQL 学习笔记](docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md)
+5. [MySQL 高性能优化规范建议](docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md)
**重要知识点:**
-1. [MySQL数据库索引总结](docs/database/mysql/MySQL数据库索引.md)
-2. [事务隔离级别(图文详解)](docs/database/mysql/事务隔离级别(图文详解).md)
-3. [MySQL三大日志(binlog、redo log和undo log)详解](docs/database/mysql/MySQL三大日志.md)
-4. [InnoDB存储引擎对MVCC的实现](docs/database/mysql/InnoDB对MVCC的实现.md)
-5. [一条 SQL 语句在 MySQL 中如何执行的?](docs/database/mysql/一条sql语句在mysql中如何执行的.md)
+1. [MySQL数据库索引总结](docs/database/mysql/mysql-index.md)
+2. [事务隔离级别(图文详解)](docs/database/mysql/transaction-isolation-level.md)
+3. [MySQL三大日志(binlog、redo log和undo log)详解](docs/database/mysql/mysql-logs.md)
+4. [InnoDB存储引擎对MVCC的实现](docs/database/mysql/innodb-implementation-of-mvcc.md)
+5. [一条 SQL 语句在 MySQL 中如何被执行的?](docs/database/mysql/how-sql-executed-in-mysql.md)
6. [字符集详解:为什么不建议在MySQL中使用 utf8 ?](docs/database/字符集.md)
-7. [关于数据库中如何存储时间的一点思考](docs/database/mysql/关于数据库存储时间的一点思考.md)
+7. [关于数据库中如何存储时间的一点思考](docs/database/mysql/some-thoughts-on-database-storage-time.md)
### Redis
-2. [Redis 常见问题总结](docs/database/Redis/redis-all.md)
-3. [面试/工作必备!3种常用的缓存读写策略!](docs/database/Redis/3种常用的缓存读写策略.md)
+1. [Redis 常见问题总结](docs/database/redis/redis-all.md)
+2. [3种常用的缓存读写策略](docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md)
## 搜索引擎
diff --git a/docs/database/MySQL Index.md b/docs/database/MySQL Index.md
deleted file mode 100644
index 32d8838886f..00000000000
--- a/docs/database/MySQL Index.md
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-## Mysql索引主要使用的两种数据结构
-
-### 哈希索引
-
-对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
-
-### BTree索引
-
-
-
-## 覆盖索引介绍
-
-### 什么是覆盖索引
-
-如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称之为“覆盖索引”。我们知道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
-> 作者 :Java3y
-
-### 先从 MySQL 的基本存储结构说起
-
-MySQL的基本存储结构是页(记录都存在页里边):
-
-
-
-
-
- - **各个数据页可以组成一个双向链表**
- - **每个数据页中的记录又可以组成一个单向链表**
- - 每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
- - 以其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。
-
-所以说,如果我们写select * from user where indexname = 'xxx'这样没有进行任何优化的sql语句,默认会这样做:
-
-1. **定位到记录所在的页:需要遍历双向链表,找到所在的页**
-2. **从所在的页内中查找相应的记录:由于不是根据主键查询,只能遍历所在页的单链表了**
-
-很明显,在数据量很大的情况下这样查找会很慢!这样的时间复杂度为O(n)。
-
-
-### 使用索引之后
-
-索引做了些什么可以让我们查询加快速度呢?其实就是将无序的数据变成有序(相对):
-
-
-
-要找到id为8的记录简要步骤:
-
-
-
-很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过 **“目录”** 就可以很快地定位到对应的页上了!(二分查找,时间复杂度近似为O(logn))
-
-其实底层结构就是B+树,B+树作为树的一种实现,能够让我们很快地查找出对应的记录。
-
-## 关于索引其他重要的内容补充
-
-> 以下内容整理自:《Java工程师修炼之道》
-
-
-### 最左前缀原则
-
-MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。如User表的name和city加联合索引就是(name,city),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:
-
-```
-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子句也遵循此规则。
-
-### 注意避免冗余索引
-
-冗余索引指的是索引的功能相同,能够命中索引(a, b)就肯定能命中索引(a) ,那么索引(a)就是冗余索引。如(name,city)和(name)这两个索引就是冗余索引,能够命中前者的查询肯定是能够命中后者。在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
-
-MySQL 5.7 版本后,可以通过查询 sys 库的 `schema_redundant_indexes` 表来查看冗余索引。
-
diff --git "a/docs/database/Redis/Redis\346\214\201\344\271\205\345\214\226.md" "b/docs/database/Redis/Redis\346\214\201\344\271\205\345\214\226.md"
deleted file mode 100644
index 2da52eec186..00000000000
--- "a/docs/database/Redis/Redis\346\214\201\344\271\205\345\214\226.md"
+++ /dev/null
@@ -1,111 +0,0 @@
-
-非常感谢《redis实战》真本书,本文大多内容也参考了书中的内容。非常推荐大家看一下《redis实战》这本书,感觉书中的很多理论性东西还是很不错的。
-
-为什么本文的名字要加上春夏秋冬又一春,哈哈 ,这是一部韩国的电影,我感觉电影不错,所以就用在文章名字上了,没有什么特别的含义,然后下面的有些配图也是电影相关镜头。
-
-
-
-**很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置。**
-
-Redis不同于Memcached的很重要一点就是,**Redis支持持久化**,而且支持两种不同的持久化操作。Redis的一种持久化方式叫**快照(snapshotting,RDB)**,另一种方式是**只追加文件(append-only file,AOF)**。这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。
-
-## 快照(snapshotting)持久化
-
-Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。
-
-
-
-**快照持久化是Redis默认采用的持久化方式**,在redis.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命令创建快照。
-```
-
-根据配置,快照将被写入dbfilename选项指定的文件里面,并存储在dir选项指定的路径上面。如果在新的快照文件创建完毕之前,Redis、系统或者硬件这三者中的任意一个崩溃了,那么Redis将丢失最近一次创建快照写入的所有数据。
-
-举个例子:假设Redis的上一个快照是2:35开始创建的,并且已经创建成功。下午3:06时,Redis又开始创建新的快照,并且在下午3:08快照创建完毕之前,有35个键进行了更新。如果在下午3:06到3:08期间,系统发生了崩溃,导致Redis无法完成新快照的创建工作,那么Redis将丢失下午2:35之后写入的所有数据。另一方面,如果系统恰好在新的快照文件创建完毕之后崩溃,那么Redis将丢失35个键的更新数据。
-
-**创建快照的办法有如下几种:**
-
-- **BGSAVE命令:** 客户端向Redis发送 **BGSAVE命令** 来创建一个快照。对于支持BGSAVE命令的平台来说(基本上所有平台支持,除了Windows平台),Redis会调用fork来创建一个子进程,然后子进程负责将快照写入硬盘,而父进程则继续处理命令请求。
-- **SAVE命令:** 客户端还可以向Redis发送 **SAVE命令** 来创建一个快照,接到SAVE命令的Redis服务器在快照创建完毕之前不会再响应任何其他命令。SAVE命令不常用,我们通常只会在没有足够内存去执行BGSAVE命令的情况下,又或者即使等待持久化操作执行完毕也无所谓的情况下,才会使用这个命令。
-- **save选项:** 如果用户设置了save选项(一般会默认设置),比如 **save 60 10000**,那么从Redis最近一次创建快照之后开始算起,当“60秒之内有10000次写入”这个条件被满足时,Redis就会自动触发BGSAVE命令。
-- **SHUTDOWN命令:** 当Redis通过SHUTDOWN命令接收到关闭服务器的请求时,或者接收到标准TERM信号时,会执行一个SAVE命令,阻塞所有客户端,不再执行客户端发送的任何命令,并在SAVE命令执行完毕之后关闭服务器。
-- **一个Redis服务器连接到另一个Redis服务器:** 当一个Redis服务器连接到另一个Redis服务器,并向对方发送SYNC命令来开始一次复制操作的时候,如果主服务器目前没有执行BGSAVE操作,或者主服务器并非刚刚执行完BGSAVE操作,那么主服务器就会执行BGSAVE命令
-
-如果系统真的发生崩溃,用户将丢失最近一次生成快照之后更改的所有数据。因此,快照持久化只适用于即使丢失一部分数据也不会造成一些大问题的应用程序。不能接受这个缺点的话,可以考虑AOF持久化。
-
-## **AOF(append-only file)持久化**
-与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:
-
-```
-appendonly yes
-```
-
-开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。
-
-
-
-**在Redis的配置文件中存在三种同步方式,它们分别是:**
-
-```
-appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
-appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
-appendfsync no #让操作系统决定何时进行同步
-```
-
-**appendfsync always** 可以实现将数据丢失减到最少,不过这种方式需要对硬盘进行大量的写入而且每次只写入一个命令,十分影响Redis的速度。另外使用固态硬盘的用户谨慎使用appendfsync always选项,因为这会明显降低固态硬盘的使用寿命。
-
-为了兼顾数据和写入性能,用户可以考虑 **appendfsync everysec选项** ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
-
-**appendfsync no** 选项一般不推荐,这种方案会使Redis丢失不定量的数据而且如果用户的硬盘处理写入操作的速度不够的话,那么当缓冲区被等待写入的数据填满时,Redis的写入操作将被阻塞,这会导致Redis的请求速度变慢。
-
-**虽然AOF持久化非常灵活地提供了多种不同的选项来满足不同应用程序对数据安全的不同要求,但AOF持久化也有缺陷——AOF文件的体积太大。**
-
-## 重写/压缩AOF
-
-AOF虽然在某个角度可以将数据丢失降低到最小而且对性能影响也很小,但是极端的情况下,体积不断增大的AOF文件很可能会用完硬盘空间。另外,如果AOF体积过大,那么还原操作执行时间就可能会非常长。
-
-为了解决AOF体积过大的问题,用户可以向Redis发送 **BGREWRITEAOF命令** ,这个命令会通过移除AOF文件中的冗余命令来重写(rewrite)AOF文件来减小AOF文件的体积。BGREWRITEAOF命令和BGSAVE创建快照原理十分相似,所以AOF文件重写也需要用到子进程,这样会导致性能问题和内存占用问题,和快照持久化一样。更糟糕的是,如果不加以控制的话,AOF文件的体积可能会比快照文件大好几倍。
-
-**文件重写流程:**
-
-
-和快照持久化可以通过设置save选项来自动执行BGSAVE一样,AOF持久化也可以通过设置
-
-```
-auto-aof-rewrite-percentage
-```
-
-选项和
-
-```
-auto-aof-rewrite-min-size
-```
-
-选项自动执行BGREWRITEAOF命令。举例:假设用户对Redis设置了如下配置选项并且启用了AOF持久化。那么当AOF文件体积大于64mb,并且AOF的体积比上一次重写之后的体积大了至少一倍(100%)的时候,Redis将执行BGREWRITEAOF命令。
-
-```
-auto-aof-rewrite-percentage 100
-auto-aof-rewrite-min-size 64mb
-```
-
-无论是AOF持久化还是快照持久化,将数据持久化到硬盘上都是非常有必要的,但除了进行持久化外,用户还必须对持久化得到的文件进行备份(最好是备份到不同的地方),这样才能尽量避免数据丢失事故发生。如果条件允许的话,最好能将快照文件和重新重写的AOF文件备份到不同的服务器上面。
-
-随着负载量的上升,或者数据的完整性变得越来越重要时,用户可能需要使用到复制特性。
-
-## Redis 4.0 对于持久化机制的优化
-Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 `aof-use-rdb-preamble` 开启)。
-
-如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分就是压缩格式不再是 AOF 格式,可读性较差。
-
-参考:
-
-《Redis实战》
-
-[深入学习Redis(2):持久化](https://www.cnblogs.com/kismetv/p/9137897.html)
-
diff --git "a/docs/database/Redis/Redlock\345\210\206\345\270\203\345\274\217\351\224\201.md" "b/docs/database/Redis/Redlock\345\210\206\345\270\203\345\274\217\351\224\201.md"
deleted file mode 100644
index 86a15ff6faf..00000000000
--- "a/docs/database/Redis/Redlock\345\210\206\345\270\203\345\274\217\351\224\201.md"
+++ /dev/null
@@ -1,47 +0,0 @@
-这篇文章主要是对 Redis 官方网站刊登的 [Distributed locks with Redis](https://redis.io/topics/distlock) 部分内容的总结和翻译。
-
-## 什么是 RedLock
-
-Redis 官方站这篇文章提出了一种权威的基于 Redis 实现分布式锁的方式名叫 *Redlock*,此种方式比原先的单节点的方法更安全。它可以保证以下特性:
-
-1. 安全特性:互斥访问,即永远只有一个 client 能拿到锁
-2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client crash 了或者出现了网络分区
-3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务
-
-## 怎么在单节点上实现分布式锁
-
-> SET resource_name my_random_value NX PX 30000
-
-主要依靠上述命令,该命令仅当 Key 不存在时(NX保证)set 值,并且设置过期时间 3000ms (PX保证),值 my_random_value 必须是所有 client 和所有锁请求发生期间唯一的,释放锁的逻辑是:
-
-```lua
-if redis.call("get",KEYS[1]) == ARGV[1] then
- return redis.call("del",KEYS[1])
-else
- return 0
-end
-```
-
-上述实现可以避免释放另一个client创建的锁,如果只有 del 命令的话,那么如果 client1 拿到 lock1 之后因为某些操作阻塞了很长时间,此时 Redis 端 lock1 已经过期了并且已经被重新分配给了 client2,那么 client1 此时再去释放这把锁就会造成 client2 原本获取到的锁被 client1 无故释放了,但现在为每个 client 分配一个 unique 的 string 值可以避免这个问题。至于如何去生成这个 unique string,方法很多随意选择一种就行了。
-
-## Redlock 算法
-
-算法很易懂,起 5 个 master 节点,分布在不同的机房尽量保证可用性。为了获得锁,client 会进行如下操作:
-
-1. 得到当前的时间,微秒单位
-2. 尝试顺序地在 5 个实例上申请锁,当然需要使用相同的 key 和 random value,这里一个 client 需要合理设置与 master 节点沟通的 timeout 大小,避免长时间和一个 fail 了的节点浪费时间
-3. 当 client 在大于等于 3 个 master 上成功申请到锁的时候,且它会计算申请锁消耗了多少时间,这部分消耗的时间采用获得锁的当下时间减去第一步获得的时间戳得到,如果锁的持续时长(lock validity time)比流逝的时间多的话,那么锁就真正获取到了。
-4. 如果锁申请到了,那么锁真正的 lock validity time 应该是 origin(lock validity time) - 申请锁期间流逝的时间
-5. 如果 client 申请锁失败了,那么它就会在少部分申请成功锁的 master 节点上执行释放锁的操作,重置状态
-
-## 失败重试
-
-如果一个 client 申请锁失败了,那么它需要稍等一会在重试避免多个 client 同时申请锁的情况,最好的情况是一个 client 需要几乎同时向 5 个 master 发起锁申请。另外就是如果 client 申请锁失败了它需要尽快在它曾经申请到锁的 master 上执行 unlock 操作,便于其他 client 获得这把锁,避免这些锁过期造成的时间浪费,当然如果这时候网络分区使得 client 无法联系上这些 master,那么这种浪费就是不得不付出的代价了。
-
-## 放锁
-
-放锁操作很简单,就是依次释放所有节点上的锁就行了
-
-## 性能、崩溃恢复和 fsync
-
-如果我们的节点没有持久化机制,client 从 5 个 master 中的 3 个处获得了锁,然后其中一个重启了,这是注意 **整个环境中又出现了 3 个 master 可供另一个 client 申请同一把锁!** 违反了互斥性。如果我们开启了 AOF 持久化那么情况会稍微好转一些,因为 Redis 的过期机制是语义层面实现的,所以在 server 挂了的时候时间依旧在流逝,重启之后锁状态不会受到污染。但是考虑断电之后呢,AOF部分命令没来得及刷回磁盘直接丢失了,除非我们配置刷回策略为 fsnyc = always,但这会损伤性能。解决这个问题的方法是,当一个节点重启之后,我们规定在 max TTL 期间它是不可用的,这样它就不会干扰原本已经申请到的锁,等到它 crash 前的那部分锁都过期了,环境不存在历史锁了,那么再把这个节点加进来正常工作。
diff --git a/docs/database/Redis/redis-all.md b/docs/database/Redis/redis-all.md
index 55dc910f54b..8457f28f354 100644
--- a/docs/database/Redis/redis-all.md
+++ b/docs/database/Redis/redis-all.md
@@ -595,7 +595,7 @@ AOF 重写可以产生一个新的 AOF 文件,这个新的 AOF 文件和原有
AOF 重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有 AOF 文件进行任何读入、分析或者写入操作。
-在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。
+在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新的 AOF 文件保存的数据库状态与现有的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。
### Redis 事务
diff --git "a/docs/database/Redis/redis\351\233\206\347\276\244\344\273\245\345\217\212\345\272\224\347\224\250\345\234\272\346\231\257.md" "b/docs/database/Redis/redis\351\233\206\347\276\244\344\273\245\345\217\212\345\272\224\347\224\250\345\234\272\346\231\257.md"
deleted file mode 100644
index cd54f067f2c..00000000000
--- "a/docs/database/Redis/redis\351\233\206\347\276\244\344\273\245\345\217\212\345\272\224\347\224\250\345\234\272\346\231\257.md"
+++ /dev/null
@@ -1,269 +0,0 @@
-相关阅读:
-
-- [史上最全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/)
-
-目录:
-
-
-
-- [Redis 集群以及应用](#redis-集群以及应用)
- - [集群](#集群)
- - [主从复制](#主从复制)
- - [主从链(拓扑结构)](#主从链拓扑结构)
- - [复制模式](#复制模式)
- - [问题点](#问题点)
- - [哨兵机制](#哨兵机制)
- - [拓扑图](#拓扑图)
- - [节点下线](#节点下线)
- - [Leader选举](#Leader选举)
- - [故障转移](#故障转移)
- - [读写分离](#读写分离)
- - [定时任务](#定时任务)
- - [分布式集群(Cluster)](#分布式集群cluster)
- - [拓扑图](#拓扑图)
- - [通讯](#通讯)
- - [集中式](#集中式)
- - [Gossip](#gossip)
- - [寻址分片](#寻址分片)
- - [hash取模](#hash取模)
- - [一致性hash](#一致性hash)
- - [hash槽](#hash槽)
- - [使用场景](#使用场景)
- - [热点数据](#热点数据)
- - [会话维持 Session](#会话维持-session)
- - [分布式锁 SETNX](#分布式锁-setnx)
- - [表缓存](#表缓存)
- - [消息队列 list](#消息队列-list)
- - [计数器 string](#计数器-string)
- - [缓存设计](#缓存设计)
- - [更新策略](#更新策略)
- - [更新一致性](#更新一致性)
- - [缓存粒度](#缓存粒度)
- - [缓存穿透](#缓存穿透)
- - [解决方案](#解决方案)
- - [缓存雪崩](#缓存雪崩)
- - [出现后应对](#出现后应对)
- - [请求过程](#请求过程)
-
-
-
-# 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. 由于设置了限流,一段时间范围内超出的请求走降级处理(返回默认值,或给出友情提示)。
-
diff --git "a/docs/database/Redis/\345\246\202\344\275\225\345\201\232\345\217\257\351\235\240\347\232\204\345\210\206\345\270\203\345\274\217\351\224\201\357\274\214Redlock\347\234\237\347\232\204\345\217\257\350\241\214\344\271\210.md" "b/docs/database/Redis/\345\246\202\344\275\225\345\201\232\345\217\257\351\235\240\347\232\204\345\210\206\345\270\203\345\274\217\351\224\201\357\274\214Redlock\347\234\237\347\232\204\345\217\257\350\241\214\344\271\210.md"
deleted file mode 100644
index 043df96566d..00000000000
--- "a/docs/database/Redis/\345\246\202\344\275\225\345\201\232\345\217\257\351\235\240\347\232\204\345\210\206\345\270\203\345\274\217\351\224\201\357\274\214Redlock\347\234\237\347\232\204\345\217\257\350\241\214\344\271\210.md"
+++ /dev/null
@@ -1,91 +0,0 @@
-本文是对 [Martin Kleppmann](https://martin.kleppmann.com/) 的文章 [How to do distributed locking](https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html) 部分内容的翻译和总结,上次写 Redlock 的原因就是看到了 Martin 的这篇文章,写得很好,特此翻译和总结。感兴趣的同学可以翻看原文,相信会收获良多。
-
-开篇作者认为现在 Redis 逐渐被使用到数据管理领域,这个领域需要更强的数据一致性和耐久性,这使得他感到担心,因为这不是 Redis 最初设计的初衷(事实上这也是很多业界程序员的误区,越来越把 Redis 当成数据库在使用),其中基于 Redis 的分布式锁就是令人担心的其一。
-
-Martin 指出首先你要明确你为什么使用分布式锁,为了性能还是正确性?为了帮你区分这二者,在这把锁 fail 了的时候你可以询问自己以下问题:
-1. **要性能的:** 拥有这把锁使得你不会重复劳动(例如一个 job 做了两次),如果这把锁 fail 了,两个节点同时做了这个 Job,那么这个 Job 增加了你的成本。
-2. **要正确性的:** 拥有锁可以防止并发操作污染你的系统或者数据,如果这把锁 fail 了两个节点同时操作了一份数据,结果可能是数据不一致、数据丢失、file 冲突等,会导致严重的后果。
-
-上述二者都是需求锁的正确场景,但是你必须清楚自己是因为什么原因需要分布式锁。
-
-如果你只是为了性能,那没必要用 Redlock,它成本高且复杂,你只用一个 Redis 实例也够了,最多加个从防止主挂了。当然,你使用单节点的 Redis 那么断电或者一些情况下,你会丢失锁,但是你的目的只是加速性能且断电这种事情不会经常发生,这并不是什么大问题。并且如果你使用了单节点 Redis,那么很显然你这个应用需要的锁粒度是很模糊粗糙的,也不会是什么重要的服务。
-
-那么是否 Redlock 对于要求正确性的场景就合适呢?Martin 列举了若干场景证明 Redlock 这种算法是不可靠的。
-
-## 用锁保护资源
-这节里 Martin 先将 Redlock 放在了一边而是仅讨论总体上一个分布式锁是怎么工作的。在分布式环境下,锁比 mutex 这类复杂,因为涉及到不同节点、网络通信并且他们随时可能无征兆的 fail 。
-Martin 假设了一个场景,一个 client 要修改一个文件,它先申请得到锁,然后修改文件写回,放锁。另一个 client 再申请锁 ... 代码流程如下:
-
-```java
-// THIS CODE IS BROKEN
-function writeData(filename, data) {
- var lock = lockService.acquireLock(filename);
- if (!lock) {
- throw 'Failed to acquire lock';
- }
-
- try {
- var file = storage.readFile(filename);
- var updated = updateContents(file, data);
- storage.writeFile(filename, updated);
- } finally {
- lock.release();
- }
-}
-```
-
-可惜即使你的锁服务非常完美,上述代码还是可能跪,下面的流程图会告诉你为什么:
-
-
-
-上述图中,得到锁的 client1 在持有锁的期间 pause 了一段时间,例如 GC 停顿。锁有过期时间(一般叫租约,为了防止某个 client 崩溃之后一直占有锁),但是如果 GC 停顿太长超过了锁租约时间,此时锁已经被另一个 client2 所得到,原先的 client1 还没有感知到锁过期,那么奇怪的结果就会发生,曾经 HBase 就发生过这种 Bug。即使你在 client1 写回之前检查一下锁是否过期也无助于解决这个问题,因为 GC 可能在任何时候发生,即使是你非常不便的时候(在最后的检查与写操作期间)。
-如果你认为自己的程序不会有长时间的 GC 停顿,还有其他原因会导致你的进程 pause。例如进程可能读取尚未进入内存的数据,所以它得到一个 page fault 并且等待 page 被加载进缓存;还有可能你依赖于网络服务;或者其他进程占用 CPU;或者其他人意外发生 SIGSTOP 等。
-
-... .... 这里 Martin 又增加了一节列举各种进程 pause 的例子,为了证明上面的代码是不安全的,无论你的锁服务多完美。
-
-## 使用 Fencing (栅栏)使得锁变安全
-修复问题的方法也很简单:你需要在每次写操作时加入一个 fencing token。这个场景下,fencing token 可以是一个递增的数字(lock service 可以做到),每次有 client 申请锁就递增一次:
-
-
-
-client1 申请锁同时拿到 token33,然后它进入长时间的停顿锁也过期了。client2 得到锁和 token34 写入数据,紧接着 client1 活过来之后尝试写入数据,自身 token33 比 34 小因此写入操作被拒绝。注意这需要存储层来检查 token,但这并不难实现。如果你使用 Zookeeper 作为 lock service 的话那么你可以使用 zxid 作为递增数字。
-但是对于 Redlock 你要知道,没什么生成 fencing token 的方式,并且怎么修改 Redlock 算法使其能产生 fencing token 呢?好像并不那么显而易见。因为产生 token 需要单调递增,除非在单节点 Redis 上完成但是这又没有高可靠性,你好像需要引进一致性协议来让 Redlock 产生可靠的 fencing token。
-
-## 使用时间来解决一致性
-Redlock 无法产生 fencing token 早该成为在需求正确性的场景下弃用它的理由,但还有一些值得讨论的地方。
-
-学术界有个说法,算法对时间不做假设:因为进程可能pause一段时间、数据包可能因为网络延迟延后到达、时钟可能根本就是错的。而可靠的算法依旧要在上述假设下做正确的事情。
-
-对于 failure detector 来说,timeout 只能作为猜测某个节点 fail 的依据,因为网络延迟、本地时钟不正确等其他原因的限制。考虑到 Redis 使用 gettimeofday,而不是单调的时钟,会受到系统时间的影响,可能会突然前进或者后退一段时间,这会导致一个 key 更快或更慢地过期。
-
-可见,Redlock 依赖于许多时间假设,它假设所有 Redis 节点都能对同一个 Key 在其过期前持有差不多的时间、跟过期时间相比网络延迟很小、跟过期时间相比进程 pause 很短。
-
-## 用不可靠的时间打破 Redlock
-这节 Martin 举了个因为时间问题,Redlock 不可靠的例子。
-
-1. client1 从 ABC 三个节点处申请到锁,DE由于网络原因请求没有到达
-2. C节点的时钟往前推了,导致 lock 过期
-3. client2 在CDE处获得了锁,AB由于网络原因请求未到达
-4. 此时 client1 和 client2 都获得了锁
-
-**在 Redlock 官方文档中也提到了这个情况,不过是C崩溃的时候,Redlock 官方本身也是知道 Redlock 算法不是完全可靠的,官方为了解决这种问题建议使用延时启动,相关内容可以看之前的[这篇文章](https://zhuanlan.zhihu.com/p/40915772)。但是 Martin 这里分析得更加全面,指出延时启动不也是依赖于时钟的正确性的么?**
-
-接下来 Martin 又列举了进程 Pause 时而不是时钟不可靠时会发生的问题:
-
-1. client1 从 ABCDE 处获得了锁
-2. 当获得锁的 response 还没到达 client1 时 client1 进入 GC 停顿
-3. 停顿期间锁已经过期了
-4. client2 在 ABCDE 处获得了锁
-5. client1 GC 完成收到了获得锁的 response,此时两个 client 又拿到了同一把锁
-
-**同时长时间的网络延迟也有可能导致同样的问题。**
-
-## Redlock 的同步性假设
-这些例子说明了,仅有在你假设了一个同步性系统模型的基础上,Redlock 才能正常工作,也就是系统能满足以下属性:
-
-1. 网络延时边界,即假设数据包一定能在某个最大延时之内到达
-2. 进程停顿边界,即进程停顿一定在某个最大时间之内
-3. 时钟错误边界,即不会从一个坏的 NTP 服务器处取得时间
-
-## 结论
-Martin 认为 Redlock 实在不是一个好的选择,对于需求性能的分布式锁应用它太重了且成本高;对于需求正确性的应用来说它不够安全。因为它对高危的时钟或者说其他上述列举的情况进行了不可靠的假设,如果你的应用只需要高性能的分布式锁不要求多高的正确性,那么单节点 Redis 够了;如果你的应用想要保住正确性,那么不建议 Redlock,建议使用一个合适的一致性协调系统,例如 Zookeeper,且保证存在 fencing token。
diff --git "a/docs/database/mysql/\344\270\200\345\215\203\350\241\214MySQL\345\255\246\344\271\240\347\254\224\350\256\260.md" b/docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md
similarity index 100%
rename from "docs/database/mysql/\344\270\200\345\215\203\350\241\214MySQL\345\255\246\344\271\240\347\254\224\350\256\260.md"
rename to docs/database/mysql/a-thousand-lines-of-mysql-study-notes.md
diff --git "a/docs/database/mysql/\344\270\200\346\235\241sql\350\257\255\345\217\245\345\234\250mysql\344\270\255\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md" b/docs/database/mysql/how-sql-executed-in-mysql.md
similarity index 100%
rename from "docs/database/mysql/\344\270\200\346\235\241sql\350\257\255\345\217\245\345\234\250mysql\344\270\255\345\246\202\344\275\225\346\211\247\350\241\214\347\232\204.md"
rename to docs/database/mysql/how-sql-executed-in-mysql.md
diff --git "a/docs/database/mysql/InnoDB\345\257\271MVCC\347\232\204\345\256\236\347\216\260.md" b/docs/database/mysql/innodb-implementation-of-mvcc.md
similarity index 100%
rename from "docs/database/mysql/InnoDB\345\257\271MVCC\347\232\204\345\256\236\347\216\260.md"
rename to docs/database/mysql/innodb-implementation-of-mvcc.md
diff --git "a/docs/database/mysql/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md" b/docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md
similarity index 100%
rename from "docs/database/mysql/MySQL\351\253\230\346\200\247\350\203\275\344\274\230\345\214\226\350\247\204\350\214\203\345\273\272\350\256\256.md"
rename to docs/database/mysql/mysql-high-performance-optimization-specification-recommendations.md
diff --git "a/docs/database/mysql/MySQL\346\225\260\346\215\256\345\272\223\347\264\242\345\274\225.md" b/docs/database/mysql/mysql-index.md
similarity index 100%
rename from "docs/database/mysql/MySQL\346\225\260\346\215\256\345\272\223\347\264\242\345\274\225.md"
rename to docs/database/mysql/mysql-index.md
diff --git "a/docs/database/mysql/MySQL\344\270\211\345\244\247\346\227\245\345\277\227.md" b/docs/database/mysql/mysql-logs.md
similarity index 100%
rename from "docs/database/mysql/MySQL\344\270\211\345\244\247\346\227\245\345\277\227.md"
rename to docs/database/mysql/mysql-logs.md
diff --git "a/docs/database/mysql/MySQL\346\200\273\347\273\223.md" "b/docs/database/mysql/mysql\347\237\245\350\257\206\347\202\271&\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
similarity index 100%
rename from "docs/database/mysql/MySQL\346\200\273\347\273\223.md"
rename to "docs/database/mysql/mysql\347\237\245\350\257\206\347\202\271&\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223.md"
diff --git "a/docs/database/mysql/\345\205\263\344\272\216\346\225\260\346\215\256\345\272\223\345\255\230\345\202\250\346\227\266\351\227\264\347\232\204\344\270\200\347\202\271\346\200\235\350\200\203.md" b/docs/database/mysql/some-thoughts-on-database-storage-time.md
similarity index 100%
rename from "docs/database/mysql/\345\205\263\344\272\216\346\225\260\346\215\256\345\272\223\345\255\230\345\202\250\346\227\266\351\227\264\347\232\204\344\270\200\347\202\271\346\200\235\350\200\203.md"
rename to docs/database/mysql/some-thoughts-on-database-storage-time.md
diff --git "a/docs/database/mysql/\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253(\345\233\276\346\226\207\350\257\246\350\247\243).md" b/docs/database/mysql/transaction-isolation-level.md
similarity index 100%
rename from "docs/database/mysql/\344\272\213\345\212\241\351\232\224\347\246\273\347\272\247\345\210\253(\345\233\276\346\226\207\350\257\246\350\247\243).md"
rename to docs/database/mysql/transaction-isolation-level.md
diff --git "a/docs/database/mysql/\351\230\277\351\207\214\345\267\264\345\267\264\345\274\200\345\217\221\346\211\213\345\206\214\346\225\260\346\215\256\345\272\223\351\203\250\345\210\206\347\232\204\344\270\200\344\272\233\346\234\200\344\275\263\345\256\236\350\267\265.md" "b/docs/database/mysql/\351\230\277\351\207\214\345\267\264\345\267\264\345\274\200\345\217\221\346\211\213\345\206\214\346\225\260\346\215\256\345\272\223\351\203\250\345\210\206\347\232\204\344\270\200\344\272\233\346\234\200\344\275\263\345\256\236\350\267\265.md"
deleted file mode 100644
index b3031d50b0d..00000000000
--- "a/docs/database/mysql/\351\230\277\351\207\214\345\267\264\345\267\264\345\274\200\345\217\221\346\211\213\345\206\214\346\225\260\346\215\256\345\272\223\351\203\250\345\210\206\347\232\204\344\270\200\344\272\233\346\234\200\344\275\263\345\256\236\350\267\265.md"
+++ /dev/null
@@ -1,41 +0,0 @@
-# 阿里巴巴Java开发手册数据库部分的一些最佳实践总结
-
-## 模糊查询
-
-对于模糊查询阿里巴巴开发手册这样说到:
-
-> 【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
->
-> 说明:索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。
-
-## 外键和级联
-
-对于外键和级联,阿里巴巴开发手册这样说到:
-
->【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
->
->说明:以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风 险;外键影响数据库的插入速度
-
-为什么不要用外键呢?大部分人可能会这样回答:
-
-> 1. **增加了复杂性:** a.每次做DELETE 或者UPDATE都必须考虑外键约束,会导致开发的时候很痛苦,测试数据极为不方便;b.外键的主从关系是定的,假如那天需求有变化,数据库中的这个字段根本不需要和其他表有关联的话就会增加很多麻烦。
-> 2. **增加了额外工作**: 数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的的一致性和正确性,这样会不得不消耗资源;(个人觉得这个不是不用外键的原因,因为即使你不使用外键,你在应用层面也还是要保证的。所以,我觉得这个影响可以忽略不计。)
-> 3. 外键还会因为需要请求对其他表内部加锁而容易出现死锁情况;
-> 4. **对分库分表不友好** :因为分库分表下外键是无法生效的。
-> 5. ......
-
-我个人觉得上面这种回答不是特别的全面,只是说了外键存在的一个常见的问题。实际上,我们知道外键也是有很多好处的,比如:
-
-1. 保证了数据库数据的一致性和完整性;
-2. 级联操作方便,减轻了程序代码量;
-3. ......
-
-所以说,不要一股脑的就抛弃了外键这个概念,既然它存在就有它存在的道理,如果系统不涉及分库分表,并发量不是很高的情况还是可以考虑使用外键的。
-
-我个人是不太喜欢外键约束,比较喜欢在应用层去进行相关操作。
-
-## 关于@Transactional注解
-
-对于`@Transactional`事务注解,阿里巴巴开发手册这样说到:
-
->【参考】@Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
diff --git "a/docs/database/Redis/3\347\247\215\345\270\270\347\224\250\347\232\204\347\274\223\345\255\230\350\257\273\345\206\231\347\255\226\347\225\245.md" b/docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md
similarity index 100%
rename from "docs/database/Redis/3\347\247\215\345\270\270\347\224\250\347\232\204\347\274\223\345\255\230\350\257\273\345\206\231\347\255\226\347\225\245.md"
rename to docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md
diff --git "a/docs/database/\346\225\260\346\215\256\345\272\223\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/docs/database/\346\225\260\346\215\256\345\272\223\345\237\272\347\241\200\347\237\245\350\257\206.md"
index 92f16b970a1..fdb1615c7fd 100644
--- "a/docs/database/\346\225\260\346\215\256\345\272\223\345\237\272\347\241\200\347\237\245\350\257\206.md"
+++ "b/docs/database/\346\225\260\346\215\256\345\272\223\345\237\272\347\241\200\347\237\245\350\257\206.md"
@@ -1,38 +1,70 @@
-数据库知识基础,这部分内容一定要理解记忆。虽然这部分内容只是理论知识,但是非常重要,这是后面学习 MySQL 数据库的基础。PS:这部分内容由于涉及太多概念性内容,所以参考了维基百科和百度百科相应的介绍。
+---
+title: 数据库基础知识
+category: 数据库
+tag:
+ + 数据库基础
+---
-## 什么是数据库,数据库管理系统,数据库系统,数据库管理员?
+数据库知识基础,这部分内容一定要理解记忆。虽然这部分内容只是理论知识,但是非常重要,这是后面学习 MySQL 数据库的基础。PS: 这部分内容由于涉及太多概念性内容,所以参考了维基百科和百度百科相应的介绍。
-- **数据库** :数据库(DataBase 简称 DB)就是信息的集合或者说数据库是由数据库管理系统管理的数据的集合。
-- **数据库管理系统** : 数据库管理系统(Database Management System 简称 DBMS)是一种操纵和管理数据库的大型软件,通常用于建立、使用和维护数据库。
-- **数据库系统** : 数据库系统(Data Base System,简称 DBS)通常由软件、数据库和数据管理员(DBA)组成。
-- **数据库管理员** : 数据库管理员(Database Administrator,简称 DBA)负责全面管理和控制数据库系统。
+## 什么是数据库, 数据库管理系统, 数据库系统, 数据库管理员?
+
+* **数据库** : 数据库(DataBase 简称 DB)就是信息的集合或者说数据库是由数据库管理系统管理的数据的集合。
+* **数据库管理系统** : 数据库管理系统(Database Management System 简称 DBMS)是一种操纵和管理数据库的大型软件,通常用于建立、使用和维护数据库。
+* **数据库系统** : 数据库系统(Data Base System,简称 DBS)通常由软件、数据库和数据管理员(DBA)组成。
+* **数据库管理员** : 数据库管理员(Database Administrator, 简称 DBA)负责全面管理和控制数据库系统。
数据库系统基本构成如下图所示:

-## 什么是元组,码,候选码,主码,外码,主属性,非主属性?
+## 什么是元组, 码, 候选码, 主码, 外码, 主属性, 非主属性?
-- **元组** : 元组(tuple)是关系数据库中的基本概念,关系是一张表,表中的每行(即数据库中的每条记录)就是一个元组,每列就是一个属性。 在二维表里,元组也称为行。
-- **码** :码就是能唯一标识实体的属性,对应表中的列。
-- **候选码** : 若关系中的某一属性或属性组的值能唯一的标识一个元组,而其任何、子集都不能再标识,则称该属性组为候选码。例如:在学生实体中,“学号”是能唯一的区分学生实体的,同时又假设“姓名”、“班级”的属性组合足以区分学生实体,那么{学号}和{姓名,班级}都是候选码。
-- **主码** : 主码也叫主键。主码是从候选码中选出来的。 一个实体集中只能有一个主码,但可以有多个候选码。
-- **外码** : 外码也叫外键。如果一个关系中的一个属性是另外一个关系中的主码则这个属性为外码。
-- **主属性** : 候选码中出现过的属性称为主属性。比如关系 工人(工号,身份证号,姓名,性别,部门).显然工号和身份证号都能够唯一标示这个关系,所以都是候选码。工号、身份证号这两个属性就是主属性。如果主码是一个属性组,那么属性组中的属性都是主属性。
-- **非主属性:** 不包含在任何一个候选码中的属性称为非主属性。比如在关系——学生(学号,姓名,年龄,性别,班级)中,主码是“学号”,那么其他的“姓名”、“年龄”、“性别”、“班级”就都可以称为非主属性。
+* **元组** : 元组(tuple)是关系数据库中的基本概念,关系是一张表,表中的每行(即数据库中的每条记录)就是一个元组,每列就是一个属性。 在二维表里,元组也称为行。
+* **码** :码就是能唯一标识实体的属性,对应表中的列。
+* **候选码** : 若关系中的某一属性或属性组的值能唯一的标识一个元组,而其任何、子集都不能再标识,则称该属性组为候选码。例如:在学生实体中,“学号”是能唯一的区分学生实体的,同时又假设“姓名”、“班级”的属性组合足以区分学生实体,那么{学号}和{姓名,班级}都是候选码。
+* **主码** : 主码也叫主键。主码是从候选码中选出来的。 一个实体集中只能有一个主码,但可以有多个候选码。
+* **外码** : 外码也叫外键。如果一个关系中的一个属性是另外一个关系中的主码则这个属性为外码。
+* **主属性** : 候选码中出现过的属性称为主属性。比如关系 工人(工号,身份证号,姓名,性别,部门). 显然工号和身份证号都能够唯一标示这个关系,所以都是候选码。工号、身份证号这两个属性就是主属性。如果主码是一个属性组,那么属性组中的属性都是主属性。
+* **非主属性:** 不包含在任何一个候选码中的属性称为非主属性。比如在关系——学生(学号,姓名,年龄,性别,班级)中,主码是“学号”,那么其他的“姓名”、“年龄”、“性别”、“班级”就都可以称为非主属性。
## 主键和外键有什么区别?
-- **主键(主码)** :主键用于唯一标识一个元组,不能有重复,不允许为空。一个表只能有一个主键。
-- **外键(外码)** :外键用来和其他表建立联系用,外键是另一表的主键,外键是可以有重复的,可以是空值。一个表可以有多个外键。
+* **主键(主码)** :主键用于唯一标识一个元组,不能有重复,不允许为空。一个表只能有一个主键。
+* **外键(外码)** :外键用来和其他表建立联系用,外键是另一表的主键,外键是可以有重复的,可以是空值。一个表可以有多个外键。
+
+## 为什么不推荐使用外键与级联?
+
+对于外键和级联,阿里巴巴开发手册这样说到:
+
+> 【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
+>
+> 说明: 以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群; 级联更新是强阻塞,存在数据库更新风暴的风 险; 外键影响数据库的插入速度
+
+为什么不要用外键呢?大部分人可能会这样回答:
+
+> 1. **增加了复杂性:** a. 每次做DELETE 或者UPDATE都必须考虑外键约束,会导致开发的时候很痛苦, 测试数据极为不方便; b. 外键的主从关系是定的,假如那天需求有变化,数据库中的这个字段根本不需要和其他表有关联的话就会增加很多麻烦。
+> 2. **增加了额外工作**: 数据库需要增加维护外键的工作,比如当我们做一些涉及外键字段的增,删,更新操作之后,需要触发相关操作去检查,保证数据的的一致性和正确性,这样会不得不消耗资源;(个人觉得这个不是不用外键的原因,因为即使你不使用外键,你在应用层面也还是要保证的。所以,我觉得这个影响可以忽略不计。)
+> 3. 外键还会因为需要请求对其他表内部加锁而容易出现死锁情况;
+> 4. **对分库分表不友好** :因为分库分表下外键是无法生效的。
+> 5. ......
+
+我个人觉得上面这种回答不是特别的全面,只是说了外键存在的一个常见的问题。实际上,我们知道外键也是有很多好处的,比如:
+
+1. 保证了数据库数据的一致性和完整性;
+2. 级联操作方便,减轻了程序代码量;
+3. ......
+
+所以说,不要一股脑的就抛弃了外键这个概念,既然它存在就有它存在的道理,如果系统不涉及分库分表,并发量不是很高的情况还是可以考虑使用外键的。
+
## 什么是 ER 图?
> 我们做一个项目的时候一定要试着画 ER 图来捋清数据库设计,这个也是面试官问你项目的时候经常会被问道的。
-**E-R 图**也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。 它是描述现实世界关系概念模型的有效方法。 是表示概念关系模型的一种方式。
+**E-R 图** 也称实体-联系图(Entity Relationship Diagram),提供了表示实体类型、属性和联系的方法,用来描述现实世界的概念模型。 它是描述现实世界关系概念模型的有效方法。 是表示概念关系模型的一种方式。
-下图是一个学生选课的 ER 图,每个学生可以选若干门课程,同一门课程也可以被若干人选择,所以它们之间的关系是多对多(M:N)。另外,还有其他两种关系是:1 对 1(1:1)、1 对多(1:N)。
+下图是一个学生选课的 ER 图,每个学生可以选若干门课程,同一门课程也可以被若干人选择,所以它们之间的关系是多对多(M: N)。另外,还有其他两种关系是:1 对 1(1:1)、1 对多(1: N)。

@@ -54,20 +86,20 @@
一些重要的概念:
-- **函数依赖(functional dependency)** :若在一张表中,在属性(或属性组)X 的值确定的情况下,必定能确定属性 Y 的值,那么就可以说 Y 函数依赖于 X,写作 X → Y。
-- **部分函数依赖(partial functional dependency)** :如果 X→Y,并且存在 X 的一个真子集 X0,使得 X0→Y,则称 Y 对 X 部分函数依赖。比如学生基本信息表 R 中(学号,身份证号,姓名)当然学号属性取值是唯一的,在 R 关系中,(学号,身份证号)->(姓名),(学号)->(姓名),(身份证号)->(姓名);所以姓名部分函数依赖与(学号,身份证号);
-- **完全函数依赖(Full functional dependency)** :在一个关系中,若某个非主属性数据项依赖于全部关键字称之为完全函数依赖。比如学生基本信息表 R(学号,班级,姓名)假设不同的班级学号有相同的,班级内学号不能相同,在 R 关系中,(学号,班级)->(姓名),但是(学号)->(姓名)不成立,(班级)->(姓名)不成立,所以姓名完全函数依赖与(学号,班级);
-- **传递函数依赖** : 在关系模式 R(U)中,设 X,Y,Z 是 U 的不同的属性子集,如果 X 确定 Y、Y 确定 Z,且有 X 不包含 Y,Y 不确定 X,(X∪Y)∩Z=空集合,则称 Z 传递函数依赖(transitive functional dependency) 于 X。传递函数依赖会导致数据冗余和异常。传递函数依赖的 Y 和 Z 子集往往同属于某一个事物,因此可将其合并放到一个表中。比如在关系 R(学号 ,姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖。。
+* **函数依赖(functional dependency)** :若在一张表中,在属性(或属性组)X 的值确定的情况下,必定能确定属性 Y 的值,那么就可以说 Y 函数依赖于 X,写作 X → Y。
+* **部分函数依赖(partial functional dependency)** :如果 X→Y,并且存在 X 的一个真子集 X0,使得 X0→Y,则称 Y 对 X 部分函数依赖。比如学生基本信息表 R 中(学号,身份证号,姓名)当然学号属性取值是唯一的,在 R 关系中,(学号,身份证号)->(姓名),(学号)->(姓名),(身份证号)->(姓名);所以姓名部分函数依赖与(学号,身份证号);
+* **完全函数依赖(Full functional dependency)** :在一个关系中,若某个非主属性数据项依赖于全部关键字称之为完全函数依赖。比如学生基本信息表 R(学号,班级,姓名)假设不同的班级学号有相同的,班级内学号不能相同,在 R 关系中,(学号,班级)->(姓名),但是(学号)->(姓名)不成立,(班级)->(姓名)不成立,所以姓名完全函数依赖与(学号,班级);
+* **传递函数依赖** : 在关系模式 R(U)中,设 X,Y,Z 是 U 的不同的属性子集,如果 X 确定 Y、Y 确定 Z,且有 X 不包含 Y,Y 不确定 X,(X∪Y)∩Z=空集合,则称 Z 传递函数依赖(transitive functional dependency) 于 X。传递函数依赖会导致数据冗余和异常。传递函数依赖的 Y 和 Z 子集往往同属于某一个事物,因此可将其合并放到一个表中。比如在关系 R(学号 , 姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖。。
**3NF(第三范式)**
-3NF 在 2NF 的基础之上,消除了非主属性对于码的传递函数依赖 。符合 3NF 要求的数据库设计,**基本**上解决了数据冗余过大,插入异常,修改异常,删除异常的问题。比如在关系 R(学号 ,姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖,所以该表的设计,不符合 3NF 的要求。
+3NF 在 2NF 的基础之上,消除了非主属性对于码的传递函数依赖 。符合 3NF 要求的数据库设计,**基本**上解决了数据冗余过大,插入异常,修改异常,删除异常的问题。比如在关系 R(学号 , 姓名, 系名,系主任)中,学号 → 系名,系名 → 系主任,所以存在非主属性系主任对于学号的传递函数依赖,所以该表的设计,不符合 3NF 的要求。
**总结**
-- 1NF:属性不可再分。
-- 2NF:1NF 的基础之上,消除了非主属性对于码的部分函数依赖。
-- 3NF:3NF 在 2NF 的基础之上,消除了非主属性对于码的传递函数依赖 。
+* 1NF:属性不可再分。
+* 2NF:1NF 的基础之上,消除了非主属性对于码的部分函数依赖。
+* 3NF:3NF 在 2NF 的基础之上,消除了非主属性对于码的传递函数依赖 。
## 什么是存储过程?
@@ -77,15 +109,15 @@
阿里巴巴 Java 开发手册里要求禁止使用存储过程。
-
+
## drop、delete 与 truncate 区别?
### 用法不同
-- drop(丢弃数据): `drop table 表名` ,直接将表都删除掉,在删除表的时候使用。
-- truncate (清空数据) : `truncate table 表名` ,只删除表中的数据,再插入数据的时候自增长 id 又从 1 开始,在清空表中数据的时候使用。
-- delete(删除数据) : `delete from 表名 where 列名=值`,删除某一列的数据,如果不加 where 子句和`truncate table 表名`作用类似。
+* drop(丢弃数据): `drop table 表名` ,直接将表都删除掉,在删除表的时候使用。
+* truncate (清空数据) : `truncate table 表名` ,只删除表中的数据,再插入数据的时候自增长 id 又从 1 开始,在清空表中数据的时候使用。
+* delete(删除数据) : `delete from 表名 where 列名=值`,删除某一列的数据,如果不加 where 子句和`truncate table 表名`作用类似。
truncate 和不带 where 子句的 delete、以及 drop 都会删除表内的数据,但是 **truncate 和 delete 只删除数据不删除表的结构(定义),执行 drop 语句,此表的结构也会删除,也就是执行 drop 之后对应的表不复存在。**
@@ -95,8 +127,8 @@ truncate 和 drop 属于 DDL(数据定义语言)语句,操作立即生效,
**DML 语句和 DDL 语句区别:**
-- DML 是数据库操作语言(Data Manipulation Language)的缩写,是指对数据库中表记录的操作,主要包括表记录的插入(insert)、更新(update)、删除(delete)和查询(select),是开发人员日常使用最频繁的操作。
-- DDL (Data Definition Language)是数据定义语言的缩写,简单来说,就是对数据库内部的对象进行创建、删除、修改的操作语言。它和 DML 语言的最大区别是 DML 只是对表内部数据的操作,而不涉及到表的定义、结构的修改,更不会涉及到其他对象。DDL 语句更多的被数据库管理员(DBA)所使用,一般的开发人员很少使用。
+* DML 是数据库操作语言(Data Manipulation Language)的缩写,是指对数据库中表记录的操作,主要包括表记录的插入(insert)、更新(update)、删除(delete)和查询(select),是开发人员日常使用最频繁的操作。
+* DDL (Data Definition Language)是数据定义语言的缩写,简单来说,就是对数据库内部的对象进行创建、删除、修改的操作语言。它和 DML 语言的最大区别是 DML 只是对表内部数据的操作,而不涉及到表的定义、结构的修改,更不会涉及到其他对象。DDL 语句更多的被数据库管理员(DBA)所使用,一般的开发人员很少使用。
### 执行速度不同
@@ -113,6 +145,7 @@ truncate 和 drop 属于 DDL(数据定义语言)语句,操作立即生效,
## 参考
--
+