diff --git a/notes/MySQL.md b/notes/MySQL.md index 7f2421b7..51e7e745 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -19,10 +19,6 @@ * [水平切分](#水平切分) * [切分的选择](#切分的选择) * [存在的问题](#存在的问题) -* [六、分库与分表带来的分布式困境与应对之策](#六分库与分表带来的分布式困境与应对之策) - * [事务问题](#事务问题) - * [查询问题](#查询问题) - * [ID 唯一性](#id-唯一性) * [六、故障转移和故障恢复](#六故障转移和故障恢复) * [参考资料](#参考资料) @@ -58,11 +54,15 @@ MyISAM 设计简单,数据以紧密格式存储。对于只读数据,或者 ## 比较 -1. 事务:InnoDB 是事务型的。 -2. 备份:InnoDB 支持在线热备份。 -3. 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 -4. 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。 -5. 其它特性:MyISAM 支持压缩表和空间数据索引。 +- 事务:InnoDB 是事务型的。 + +- 备份:InnoDB 支持在线热备份。 + +- 崩溃恢复:MyISAM 崩溃后发生损坏的概率比 InnoDB 高很多,而且恢复的速度也更慢。 + +- 并发:MyISAM 只支持表级锁,而 InnoDB 还支持行级锁。 + +- 其它特性:MyISAM 支持压缩表和空间数据索引。 # 二、数据类型 @@ -102,7 +102,7 @@ MySQL 提供了两种相似的日期时间类型:DATATIME 和 TIMESTAMP。 和 UNIX 时间戳相同,保存从 1970 年 1 月 1 日午夜(格林威治时间)以来的秒数,使用 4 个字节,只能表示从 1970 年 到 2038 年。 -它和时区有关。 +它和时区有关,也就是说一个时间戳在不同的时区所代表的具体时间是不同的。 MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP() 函数把日期转换为 UNIX 时间戳。 @@ -124,7 +124,7 @@ MySQL 提供了 FROM_UNIXTIME() 函数把 UNIX 时间戳转换为日期,并提

-《高性能 MySQL》一书使用 B-Tree 进行描述,其实从技术上来说这种索引是 B+Tree。 +《高性能 MySQL》一书使用 B-Tree 进行描述,其实从技术上来说这种索引是 B+Tree,因为只有叶子节点存储数据值。 B+Tree 索引是大多数 MySQL 存储引擎的默认索引类型。 @@ -147,15 +147,15 @@ InnoDB 引擎有一个特殊的功能叫“自适应哈希索引”,当某个 限制: - 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行。不过,访问内存中的行的速度很快,所以大部分情况下这一点对性能影响并不明显; -- 无法用于分组与排序; +- 无法用于排序与分组; - 只支持精确查找,无法用于部分查找和范围查找; - 如果哈希冲突很多,查找速度会变得很慢。 ### 3. 空间数据索引(R-Tree) -MyISAM 存储引擎支持空间索引,可以用于地理数据存储。 +MyISAM 存储引擎支持空间数据索引,可以用于地理数据存储。 -空间索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。 +空间数据索引会从所有维度来索引数据,可以有效地使用任意维度来进行组合查询。 必须使用 GIS 相关的函数来维护数据。 @@ -163,7 +163,7 @@ MyISAM 存储引擎支持空间索引,可以用于地理数据存储。 MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而不是直接比较索引中的值。 -使用 MATCH AGAINST,而不是普通的 WHERE。 +查找条件使用 MATCH AGAINST,而不是普通的 WHERE。 ## 索引的优点 @@ -171,7 +171,7 @@ MyISAM 存储引擎支持全文索引,用于查找文本中的关键词,而 - 帮助服务器避免进行排序和创建临时表(B+Tree 索引是有序的,可以用来做 ORDER BY 和 GROUP BY 操作); -- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,也就将相邻的列值都存储在一起)。 +- 将随机 I/O 变为顺序 I/O(B+Tree 索引是有序的,也就将相邻的数据都存储在一起)。 ## 索引优化 @@ -256,9 +256,7 @@ customer_id_selectivity: 0.0373

-为了描述 B-Tree,首先定义一条数据记录为一个二元组 [key, data]。 - -B-Tree 是满足下列条件的数据结构: +定义一条数据记录为一个二元组 [key, data],B-Tree 是满足下列条件的数据结构: - 所有叶节点具有相同的深度,也就是说 B-Tree 是平衡的; - 一个节点中的 key 从左到右非递减排列; @@ -283,17 +281,25 @@ B-Tree 是满足下列条件的数据结构: 一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 基础上进行了优化,在叶子节点增加了顺序访问指针,做这个优化的目的是为了提高区间访问的性能。 -### 4. 为什么使用 B-Tree 和 B+Tree +### 4. 为什么使用 B+Tree 和 B-Tree -红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用 B-/+Tree 作为索引结构。 +红黑树等平衡树也可以用来实现索引,但是文件系统及数据库系统普遍采用 B+Tree B-Tree 作为索引结构,主要有以下两个原因: -页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页(在许多操作系统中,页的大小通常为 4k),主存和磁盘以页为单位交换数据。 +**(一)更少的检索次数** -一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次 I/O 就可以完全载入。 +红黑树和 B+Tree B-Tree 检索数据的时间复杂度等于树高 h,而树高大致为 O(h)=O(logdN),其中 d 为每个节点的出度。 -B-Tree 中一次检索最多需要 h-1 次 I/O(根节点常驻内存),渐进复杂度为 O(h)=O(logdN)。一般实际应用中,出度 d 是非常大的数字,通常超过 100,因此 h 非常小(通常不超过 3)。而红黑树这种结构,h 明显要深的多。并且于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,效率明显比 B-Tree 差很多。 +红黑树的出度为 2,而 B+Tree 与 B-Tree 的出度一般都非常大。红黑树的树高 h 很明显比 B+Tree B-Tree 大非常多,因此检索的次数也就更多。 -B+Tree 更适合外存索引,原因和内节点出度 d 有关。由于 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,拥有更好的性能。 +B+Tree 相比于 B-Tree 更适合外存索引,因为 B+Tree 内节点去掉了 data 域,因此可以拥有更大的出度,检索效率会更高。 + +**(二)利用计算机预读特性** + +为了减少磁盘 I/O,磁盘往往不是严格按需读取,而是每次都会预读。这样做的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的旋转时间,因此速度会非常快。 + +操作系统一般将内存和磁盘分割成固态大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点,并且可以利用预读特性,临近的节点也能够被预先载入。 + +更多内容请参考:[MySQL 索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) # 四、查询性能优化 @@ -372,25 +378,6 @@ do { 最显而易见的就是数据的定位问题和数据的增删改查的重复执行问题,这些都可以通过应用程序解决,但必然引起额外的逻辑运算。 -# 六、分库与分表带来的分布式困境与应对之策 - -

- -## 事务问题 - -使用分布式事务。 - -## 查询问题 - -使用汇总表。 - -## ID 唯一性 - -- 使用全局唯一 ID:GUID。 -- 为每个分片指定一个 ID 范围。 -- 分布式 ID 生成器 (如 Twitter 的 [Snowflake](https://twitter.github.io/twitter-server/) 算法)。 - - # 六、故障转移和故障恢复 故障转移也叫做切换,当主库出现故障时就切换到备库,使备库成为主库。故障恢复顾名思义就是从故障中恢复过来,并且保证数据的正确性。 @@ -415,7 +402,6 @@ do { - BaronScbwartz, PeterZaitsev, VadimTkacbenko, 等. 高性能 MySQL[M]. 电子工业出版社, 2013. - [How Sharding Works](https://medium.com/@jeeyoungk/how-sharding-works-b4dec46b3f6) -- [MySQL 索引背后的数据结构及算法原理 ](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) - [20+ 条 MySQL 性能优化的最佳经验 ](https://www.jfox.info/20-tiao-mysql-xing-nen-you-hua-de-zui-jia-jing-yan.html) - [数据库为什么分库分表?mysql的分库分表方案](https://www.i3geek.com/archives/1108) - [How Sharding Works](https://medium.com/@jeeyoungk/how-sharding-works-b4dec46b3f6) diff --git a/notes/Redis.md b/notes/Redis.md index b92fc7ae..0256407d 100644 --- a/notes/Redis.md +++ b/notes/Redis.md @@ -55,7 +55,7 @@ Redis 支持很多特性,例如将内存中的数据持久化到硬盘中, | 数据类型 | 可以存储的值 | 操作 | | :--: | :--: | :--: | | STRING | 字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作
对整数和浮点数执行自增或者自减操作 | -| LIST | 链表 | 从两端压入或者弹出元素
读取单个或者多个元素
进行修剪,只保留一个范围内的元素 | +| LIST | 列表 | 从两端压入或者弹出元素
读取单个或者多个元素
进行修剪,只保留一个范围内的元素 | | SET | 无序集合 | 添加、获取、移除单个元素
检查一个元素是否存在于集合中
计算交集、并集、差集
从集合里面随机获取元素 | | HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对
获取所有键值对
检查某个键是否存在| | ZSET | 有序集合 | 添加、获取、删除元素
根据分值范围或者成员来获取元素
计算一个键的排名 | @@ -223,8 +223,8 @@ Redis 可以为每个键设置过期时间,当键过期时,会自动删除 发布与订阅有一些问题,很少使用它,而是使用替代的解决方案。问题如下: -1. 如果订阅者读取消息的速度很慢,会使得消息不断积压在发布者的输出缓存区中,造成内存占用过多; -2. 如果订阅者在执行订阅的过程中网络出现问题,那么就会丢失断线期间发送的所有消息。 +- 如果订阅者读取消息的速度很慢,会使得消息不断积压在发布者的输出缓存区中,造成内存占用过多; +- 如果订阅者在执行订阅的过程中网络出现问题,那么就会丢失断线期间发送的所有消息。 # 五、事务 @@ -272,11 +272,11 @@ always 选项会严重减低服务器的性能;everysec 选项比较合适, ## 从服务器连接主服务器的过程 -1. 主服务器创建快照文件,发送给从服务器,并在发送期间使用缓冲区记录执行的写命令。快照文件发送完毕之后,开始向从服务器发送存储在缓冲区中的写命令; +- 主服务器创建快照文件,发送给从服务器,并在发送期间使用缓冲区记录执行的写命令。快照文件发送完毕之后,开始向从服务器发送存储在缓冲区中的写命令; -2. 从服务器丢弃所有旧数据,载入主服务器发来的快照文件,之后从服务器开始接受主服务器发来的写命令; +- 从服务器丢弃所有旧数据,载入主服务器发来的快照文件,之后从服务器开始接受主服务器发来的写命令; -3. 主服务器每执行一次写命令,就向从服务器发送相同的写命令。 +- 主服务器每执行一次写命令,就向从服务器发送相同的写命令。 ## 主从链 @@ -431,7 +431,7 @@ Redis 这种内存数据库能支持计数器频繁的读写操作。 如果使用 Redis 来缓存数据时,要保证所有数据都是热点数据,可以将内存最大使用量设置为热点数据占用的内存量,然后启用 allkeys-lru 淘汰策略,将最近最少使用的数据淘汰。 -作为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法(LRU、TTL)实际实现上并非针对所有 key,而是抽样一小部分 key 从中选出被淘汰 key。抽样数量可通过 maxmemory-samples 配置。 +作为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法(LRU、TTL)实际实现上并非针对所有 key,而是抽样一小部分 key 从中选出被淘汰 key,抽样数量可通过 maxmemory-samples 配置。 # 十四、一个简单的论坛系统分析 diff --git a/notes/设计模式.md b/notes/设计模式.md index 400ede5e..0fbd186e 100644 --- a/notes/设计模式.md +++ b/notes/设计模式.md @@ -47,7 +47,7 @@ 以下实现中,私有静态变量 uniqueInstance 被延迟化实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。 -这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 if(uniqueInstance == null) ,那么就会多次实例化 uniqueInstance。 +这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 if (uniqueInstance == null) ,并且此时 uniqueInstance 为 null,那么多个线程会执行 uniqueInstance = new Singleton(); 语句,这将导致实例化多次 uniqueInstance。 ```java public class Singleton { @@ -350,8 +350,8 @@ public class Client { public static void main(String[] args) { AbstractFactory abstractFactory = new ConcreteFactory1(); AbstractProductA productA = abstractFactory.createProductA(); - abstractFactory = new ConcreteFactory2(); - productA = abstractFactory.createProductA(); + AbstractProductB productB = abstractFactory.createProductB(); + // do something with productA and productB } } ``` diff --git a/notes/面向对象思想.md b/notes/面向对象思想.md index 8cd324cb..a25a8b00 100644 --- a/notes/面向对象思想.md +++ b/notes/面向对象思想.md @@ -136,7 +136,7 @@ public class Person { } public void work() { - if(18 <= age && age <= 50) { + if (18 <= age && age <= 50) { System.out.println(name + " is working very hard!"); } else { System.out.println(name + " can't work any more!"); @@ -163,9 +163,9 @@ Animal animal = new Cat(); 运行时多态有三个条件: -1. 继承 -2. 覆盖 -3. 向上转型 +- 继承 +- 覆盖(重写) +- 向上转型 下面的代码中,乐器类(Instrument)有两个子类:Wind 和 Percussion,它们都覆盖了父类的 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Wind 和 Percussion 对象。在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。 @@ -236,9 +236,9 @@ public class Music { 和关联关系不同的是,依赖关系是在运行过程中起作用的。A 类和 B 类是依赖关系主要有三种形式: -1. A 类是 B 类中的(某中方法的)局部变量; -2. A 类是 B 类方法当中的一个参数; -3. A 类向 B 类发送消息,从而影响 B 类发生变化; +- A 类是 B 类中的(某中方法的)局部变量; +- A 类是 B 类方法当中的一个参数; +- A 类向 B 类发送消息,从而影响 B 类发生变化;