diff --git a/cs_learn/cs_learn.md b/cs_learn/cs_learn.md index 1e08c316..b0663aa7 100644 --- a/cs_learn/cs_learn.md +++ b/cs_learn/cs_learn.md @@ -317,7 +317,7 @@ MySQL 入门的话是了解 SQL 语法,进阶的话是深入底层实现原理 我先介绍下 MySQL 的重点知识,也是面试常面的知识点: - **基本语法**:select/delete/insert/update、limit、join 等 -- **索引**:B+树,聚族索引,二级索引,组合索引,最左匹配原则,索引失效、慢查询 +- **索引**:B+ 树,聚族索引,二级索引,组合索引,最左匹配原则,索引失效、慢查询 - **事务**:事务四大特性 ACID,事务隔离级别,MVCC - **锁**:全局锁、表级锁、行级锁、快照读、当前读、乐观锁、悲观锁、死锁 - **日志**:重做日志 (redo log)、回滚日志 (undo log)、二进制日志 (binlog) @@ -370,7 +370,7 @@ Redis 官网也有一整套的命令详解,遇到需要或者不会的地方 所以最好的看书方式是带着问题去翻阅,比如: - 带着程序是如何在计算机里跑起来的问题,去学计算机组成原理; -- 带着输入一条 url 到网页显示,期间发生了什么的问题,去学习计算机网络; +- 带着输入一条 URL 到网页显示,期间发生了什么的问题,去学习计算机网络; - 带着进程、内存、磁盘是如何被操作系统管理点,去学习操作系统; - 带着如何实现一个高并发网络模型,去学习网络编程; - …… diff --git a/cs_learn/feel_cs.md b/cs_learn/feel_cs.md index 31abd1ba..def1d08b 100644 --- a/cs_learn/feel_cs.md +++ b/cs_learn/feel_cs.md @@ -6,7 +6,7 @@ ![图片](https://img-blog.csdnimg.cn/img_convert/d3692e2197fb0d020ca4f13a4d3a1b42.png) -他从那时起,就日复一日的学习,并在 Github 有做笔记的习惯,你看他的提交记录,每天都有,一天都没拉下,就这样坚持了一年。 +他从那时起,就日复一日的学习,并在 GitHub 有做笔记的习惯,你看他的提交记录,每天都有,一天都没拉下,就这样坚持了一年。 这个一年没有间断过的坚持,我是真的被震撼到,虽然我也经常肝文章,但是我也做不到每天都是学习的状态,总会想偷懒几天,毕竟学习真的是反人性的哈哈。 diff --git a/cs_learn/look_book.md b/cs_learn/look_book.md index d21dee01..7b29476b 100644 --- a/cs_learn/look_book.md +++ b/cs_learn/look_book.md @@ -74,7 +74,7 @@ 这两本书让我大概知道了如果一个服务端要服务多个客户端时,不是就简单写个 socket 编程就完事,而是还要结合 IO 多路复用 + 多线程的思想,也就是 Reactor 的设计理念,知道了这些事情后,后面我在看很多开源框架的网络模型时候,发现大多数基于 Reactor 的思想来实现的。 -有了网络编程总体的视角后,在需要深入理解 socket api 中各种属性设置(超时、非阻塞 IO、阻塞 IO 等)和异常处理就要回归 APUE 这本书。 +有了网络编程总体的视角后,在需要深入理解 socket API 中各种属性设置(超时、非阻塞 IO、阻塞 IO 等)和异常处理就要回归 APUE 这本书。 到这里我才知道 UNP 和 APUE 为什么会被称为网络编程圣经级别的书,原因是书里各种细节和异常都写的很全,也很细致,可以应对工作中很多问题。 diff --git a/mysql/base/how_select.md b/mysql/base/how_select.md index 76d5e64b..1b5e4342 100644 --- a/mysql/base/how_select.md +++ b/mysql/base/how_select.md @@ -22,7 +22,7 @@ select * from product where id = 1; 可以看到,MySQL 的架构共分为两层:**Server 层和存储引擎层**, - **Server 层负责建立连接、分析和执行 SQL**。MySQL 大多数的核心功能模块都在这实现,主要包括连接器,查询缓存、解析器、预处理器、优化器、执行器等。另外,所有的内置函数(如日期、时间、数学和加密函数等)和所有跨存储引擎的功能(如存储过程、触发器、视图等。)都在 Server 层实现。 -- **存储引擎层负责数据的存储和提取**。支持 InnoDB、MyISAM、Memory 等多个存储引擎,不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB,从 MySQL 5.5 版本开始,InnoDB 成为了 MySQL 的默认存储引擎。我们常说的索引数据结构,就是由存储引擎层实现的,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+树,且是默认使用,也就是说在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引。 +- **存储引擎层负责数据的存储和提取**+。支持 InnoDB、MyISAM、Memory 等多个存储引擎,不同的存储引擎共用一个 Server 层。现在最常用的存储引擎是 InnoDB,从 MySQL 5.5 版本开始,InnoDB 成为了 MySQL 的默认存储引擎。我们常说的索引数据结构,就是由存储引擎层实现的,不同的存储引擎支持的索引类型也不相同,比如 InnoDB 支持索引类型是 B+ 树,且是默认使用,也就是说在数据表中创建的主键索引和二级索引默认使用的是 B+ 树索引。 好了,现在我们对 Server 层和存储引擎层有了一个简单认识,接下来,就详细说一条 SQL 查询语句的执行流程,依次看看每一个功能模块的作用。 @@ -31,7 +31,7 @@ select * from product where id = 1; 如果你在 Linux 操作系统里要使用 MySQL,那你第一步肯定是要先连接 MySQL 服务,然后才能执行 SQL 语句,普遍我们都是使用下面这条命令进行连接: ```shell -# -h 指定 MySQL 服务的 IP 地址,如果是连接本地的 MySQL服务,可以不用这个参数; +# -h 指定 MySQL 服务的 IP 地址,如果是连接本地的 MySQL 服务,可以不用这个参数; # -u 指定用户名,管理员角色名为 root; # -p 指定密码,如果命令行中不填写密码(为了密码安全,建议不要在命令行写密码),就需要在交互对话里面输入密码 mysql -h$ip -u$user -p @@ -273,7 +273,7 @@ select * from product where name = 'iphone'; 这条查询语句的查询条件没有用到索引,所以优化器决定选用访问类型为 ALL 进行查询,也就是全表扫描的方式查询,那么这时执行器与存储引擎的执行流程是这样的: - 执行器第一次查询,会调用 read_first_record 函数指针指向的函数,因为优化器选择的访问类型为 all,这个函数指针被指向为 InnoDB 引擎全扫描的接口,**让存储引擎读取表中的第一条记录**; -- 执行器会判断读到的这条记录的 name 是不是 iphone,如果不是则跳过;如果是则将记录发给客户端(是的没错,Server 层每从存储引擎读到一条记录就会发送给客户端,之所以客户端显示的时候是直接显示所有记录的,是因为客户端是等查询语句查询完成后,才会显示出所有的记录)。 +- 执行器会判断读到的这条记录的 name 是不是 `iphone`,如果不是则跳过;如果是则将记录发给客户端(是的没错,Server 层每从存储引擎读到一条记录就会发送给客户端,之所以客户端显示的时候是直接显示所有记录的,是因为客户端是等查询语句查询完成后,才会显示出所有的记录)。 - 执行器查询的过程是一个 while 循环,所以还会再查一次,会调用 read_record 函数指针指向的函数,因为优化器选择的访问类型为 all,read_record 函数指针指向的还是 InnoDB 引擎全扫描的接口,所以接着向存储引擎层要求继续读刚才那条记录的下一条记录,存储引擎把下一条记录取出后就将其返回给执行器(Server 层),执行器继续判断条件,不符合查询条件即跳过该记录,否则发送到客户端; - 一直重复上述过程,直到存储引擎把表中的所有记录读完,然后向执行器(Server 层)返回了读取完毕的信息; - 执行器收到存储引擎报告的查询完毕的信息,退出循环,停止查询。 diff --git a/mysql/base/row_format.md b/mysql/base/row_format.md index 8dc3941b..055d7110 100644 --- a/mysql/base/row_format.md +++ b/mysql/base/row_format.md @@ -210,8 +210,8 @@ CREATE TABLE `t_user` ( 如果存在允许 NULL 值的列,则每个列对应一个二进制位(bit),二进制位按照列的顺序逆序排列。 -- 二进制位的值为`1`时,代表该列的值为 NULL。 -- 二进制位的值为`0`时,代表该列的值不为 NULL。 +- 二进制位的值为 `1` 时,代表该列的值为 NULL。 +- 二进制位的值为 `0` 时,代表该列的值不为 NULL。 另外,NULL 值列表必须用整数个字节的位表示(1 字节 8 位),如果使用的二进制位个数不足整数个字节,则在字节的高位补 `0`。 @@ -249,7 +249,7 @@ NULL 值列表也不是必须的。 **当数据表的字段都定义成 NOT NULL 的时候,这时候表里的行格式就不会有 NULL 值列表了**。 -所以在设计数据库表的时候,通常都是建议将字段设置为 NOT NULL,这样可以至少节省 1 字节的空间(NULL 值列表至少占用 1 字节空间)。 +所以在设计数据库表的时候,通常都是建议将字段设置为 NOT NULL,这样可以至少节省 1 字节的空间(NULL 值列表至少占用 1 字节空间)。 > 「NULL 值列表」是固定 1 字节空间吗?如果这样的话,一条记录有 9 个字段值都是 NULL,这时候怎么表示? @@ -261,9 +261,9 @@ NULL 值列表也不是必须的。 记录头信息中包含的内容很多,我就不一一列举了,这里说几个比较重要的: -- delete_mask:标识此条数据是否被删除。从这里可以知道,我们执行 detele 删除记录的时候,并不会真正的删除记录,只是将这个记录的 delete_mask 标记为 1。 -- next_record:下一条记录的位置。从这里可以知道,记录与记录之间是通过链表组织的。在前面我也提到了,指向的是下一条记录的「记录头信息」和「真实数据」之间的位置,这样的好处是向左读就是记录头信息,向右读就是真实数据,比较方便。 -- record_type:表示当前记录的类型,0 表示普通记录,1 表示 B+树非叶子节点记录,2 表示最小记录,3 表示最大记录 +- `delete_mask`:标识此条数据是否被删除。从这里可以知道,我们执行 delete 删除记录的时候,并不会真正的删除记录,只是将这个记录的 `delete_mask` 标记为 1。 +- `next_record`:下一条记录的位置。从这里可以知道,记录与记录之间是通过链表组织的。在前面我也提到了,指向的是下一条记录的「记录头信息」和「真实数据」之间的位置,这样的好处是向左读就是记录头信息,向右读就是真实数据,比较方便。 +- `record_type`:表示当前记录的类型,0 表示普通记录,1 表示 B+ 树非叶子节点记录,2 表示最小记录,3 表示最大记录 ### 记录的真实数据 diff --git a/mysql/index/2000w.md b/mysql/index/2000w.md index 8695304f..ee74d0f2 100644 --- a/mysql/index/2000w.md +++ b/mysql/index/2000w.md @@ -39,7 +39,7 @@ select (@i:=@i+1) as rownum, person_name from person, (select @i:=100) as init; set @i=1; ``` -运行下面的 sql,连续执行 20 次,就是 2 的 20 次方约等于 100w 的数据;执行 23 次就是 2 的 23 次方约等于 800w , 如此下去即可实现千万测试数据的插入。 +运行下面的 SQL,连续执行 20 次,就是 2 的 20 次方约等于 100w 的数据;执行 23 次就是 2 的 23 次方约等于 800w,如此下去即可实现千万测试数据的插入。 如果不想翻倍翻倍的增加数据,而是想少量,少量的增加,有个技巧,就是在 SQL 的后面增加 where 条件,如 id > 某一个值去控制增加的数据量即可。 @@ -86,7 +86,7 @@ CREATE TABLE person( ) comment '人员信息表'; ``` -看看上面的建表 sql。id 是主键,本身就是唯一的,也就是说主键的大小可以限制表的上限: +看看上面的建表 SQL。id 是主键,本身就是唯一的,也就是说主键的大小可以限制表的上限: - 如果主键声明 `int` 类型,也就是 32 位,那么支持 2^32-1 ~~21 亿; - 如果主键声明 `bigint` 类型,那就是 2^62-1(36893488147419103232),难以想象这个的多大了,一般还没有到这个限制之前,可能数据库已经爆满了!! @@ -224,7 +224,7 @@ CREATE TABLE person( 同样,还是按照 z = 3 的值来计算,那 Total = (1280 ^2) *3 = 4915200(近 500w) -所以,在保持相同的层级(相似查询性能)的情况下,在行数据大小不同的情况下,其实这个最大建议值也是不同的,而且影响查询性能的还有很多其他因素,比如,数据库版本,服务器配置,sql 的编写等等。 +所以,在保持相同的层级(相似查询性能)的情况下,在行数据大小不同的情况下,其实这个最大建议值也是不同的,而且影响查询性能的还有很多其他因素,比如,数据库版本,服务器配置,SQL 的编写等等。 MySQL 为了提高性能,会将表的索引装载到内存中,在 InnoDB buffer size 足够的情况下,其能完成全加载进内存,查询不会有问题。 diff --git a/mysql/index/index_interview.md b/mysql/index/index_interview.md index 342637fa..54873a62 100644 --- a/mysql/index/index_interview.md +++ b/mysql/index/index_interview.md @@ -32,7 +32,7 @@ ## 索引的分类 -你知道索引有哪些吗?大家肯定都能霹雳啪啦地说出聚簇索引、主键索引、二级索引、普通索引、唯一索引、hash 索引、B+树索引等等。 +你知道索引有哪些吗?大家肯定都能霹雳啪啦地说出聚簇索引、主键索引、二级索引、普通索引、唯一索引、Hash 索引、B+ 树索引等等。 然后再问你,你能将这些索引分一下类吗?可能大家就有点模糊了。其实,要对这些索引进行分类,要清楚这些索引的使用和实现方式,然后再针对有相同特点的索引归为一类。 diff --git a/mysql/index/index_lose.md b/mysql/index/index_lose.md index 8f3212c8..eaf7fc99 100644 --- a/mysql/index/index_lose.md +++ b/mysql/index/index_lose.md @@ -62,7 +62,7 @@ InnoDB 存储引擎根据索引类型不同,分为聚簇索引(上图就是 select * from t_user where id=1; ``` -在我们使用「二级索引」字段作为条件查询的时候,如果要查询的数据都在「聚簇索引」的叶子节点里,那么需要检索两颗 B+树: +在我们使用「二级索引」字段作为条件查询的时候,如果要查询的数据都在「聚簇索引」的叶子节点里,那么需要检索两颗 B+ 树: - 先在「二级索引」的 B+ 树找到对应的叶子节点,获取主键值; - 然后用上一步获取的主键值,在「聚簇索引」中的 B+ 树检索到对应的叶子节点,然后获取要查询的数据。 diff --git a/mysql/index/why_index_chose_bpuls_tree.md b/mysql/index/why_index_chose_bpuls_tree.md index 1f51b3e8..fe25d7d9 100644 --- a/mysql/index/why_index_chose_bpuls_tree.md +++ b/mysql/index/why_index_chose_bpuls_tree.md @@ -183,7 +183,7 @@ B 树进行单个索引查询时,最快可以在 O(1) 的时间代价内就查 但是 B 树的查询波动会比较大,因为每个节点既存索引又存记录,所以有时候访问到了非叶子节点就可以找到索引,而有时需要访问到叶子节点才能找到索引。 -**B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比既存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少**。 +**B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比既存索引又存记录的 B 树,B+ 树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少**。 ### 2、插入和删除效率 @@ -218,7 +218,7 @@ B 树和 B+ 树等值查询原理基本一致,先从根节点查找,然后 而 B 树没有将所有叶子节点用链表串联起来的结构,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。 -因此,存在大量范围检索的场景,适合使用 B+树,比如数据库。而对于大量的单个索引查询的场景,可以考虑 B 树,比如 nosql 的 MongoDB。 +因此,存在大量范围检索的场景,适合使用 B+ 树,比如数据库。而对于大量的单个索引查询的场景,可以考虑 B 树,比如 NoSQL 的 MongoDB。 ### MySQL 中的 B+ 树 @@ -255,7 +255,7 @@ B 树和 B+ 都是通过多叉树的方式,会将树的高度变矮,所以 但是 MySQL 默认的存储引擎 InnoDB 采用的是 B+ 作为索引的数据结构,原因有: -- B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比既存索引又存记录的 B 树,B+树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少。 +- B+ 树的非叶子节点不存放实际的记录数据,仅存放索引,因此数据量相同的情况下,相比既存索引又存记录的 B 树,B+ 树的非叶子节点可以存放更多的索引,因此 B+ 树可以比 B 树更「矮胖」,查询底层节点的磁盘 I/O 次数会更少。 - B+ 树有大量的冗余节点(所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删除的效率都更高,比如删除根节点的时候,不会像 B 树那样会发生复杂的树的变化; - B+ 树叶子节点之间用链表连接了起来,有利于范围查询,而 B 树要实现范围查询,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。 diff --git a/mysql/lock/how_to_lock.md b/mysql/lock/how_to_lock.md index 7bea7ea2..d4b16677 100644 --- a/mysql/lock/how_to_lock.md +++ b/mysql/lock/how_to_lock.md @@ -541,7 +541,7 @@ Empty set (0.00 sec) 我们先要清楚,什么情况下插入语句会发生阻塞。 -**插入语句在插入一条记录之前,需要先定位到该记录在 B+树 的位置,如果插入的位置的下一条记录的索引上有间隙锁,才会发生阻塞**。 +**插入语句在插入一条记录之前,需要先定位到该记录在 B+ 树 的位置,如果插入的位置的下一条记录的索引上有间隙锁,才会发生阻塞**。 在分析二级索引的间隙锁是否可以成功插入记录时,我们要先要知道二级索引树是如何存放记录的? diff --git a/mysql/log/how_update.md b/mysql/log/how_update.md index ab2160f8..8ed9d43b 100644 --- a/mysql/log/how_update.md +++ b/mysql/log/how_update.md @@ -477,9 +477,9 @@ binlog 已经写入了,之后就会被从库(或者用这个 binlog 恢复 也就是说,事务没提交的时候,redo log 也是可能被持久化到磁盘的。 -有的同学可能会问,如果 mysql 崩溃了,还没提交事务的 redo log 已经被持久化磁盘了,mysql 重启后,数据不就不一致了? +有的同学可能会问,如果 MySQL 崩溃了,还没提交事务的 redo log 已经被持久化磁盘了,MySQL 重启后,数据不就不一致了? -放心,这种情况 mysql 重启会进行回滚操作,因为事务没提交的时候,binlog 是还没持久化到磁盘的。 +放心,这种情况 MySQL 重启会进行回滚操作,因为事务没提交的时候,binlog 是还没持久化到磁盘的。 所以,redo log 可以在事务没提交之前持久化到磁盘,但是 binlog 必须在事务提交之后,才可以持久化到磁盘。 @@ -597,7 +597,7 @@ commit 阶段队列的作用是承接 sync 阶段的事务,完成最后的引 - 如果一样的话就不进行后续更新流程; - 如果不一样的话就把更新前的记录和更新后的记录都当作参数传给 InnoDB 层,让 InnoDB 真正的执行更新记录的操作; 3. 开启事务,InnoDB 层更新记录前,首先要记录相应的 undo log,因为这是更新操作,需要把被更新的列的旧值记下来,也就是要生成一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面,不过在内存修改该 Undo 页面后,需要记录对应的 redo log。 -4. InnoDB 层开始更新记录,先生成对应redo log,并存入redo log buffer里面,当事务提交时,将redo log写入redo log file,并更新buffer pool中的数据页,将其放入flush 链表并标记脏页和记录redo log对应的lsn到该页的oldest_modification,这个时候更新就算完成了。为了减少磁盘 I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 **WAL 技术**,MySQL 的写操作并不是立刻写到磁盘上,而是先写 redo 日志,然后在合适的时间再将修改的行数据写到磁盘上。 +4. InnoDB 层开始更新记录,先生成对应 redo log,并存入 redo log buffer 里面,当事务提交时,将 redo log 写入 redo log file,并更新 buffer pool 中的数据页,将其放入 flush 链表并标记脏页和记录 redo log 对应的 lsn 到该页的 oldest_modification,这个时候更新就算完成了。为了减少磁盘 I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 5. 至此,一条记录更新完了。 6. 在一条更新语句执行完成后,然后开始记录该语句对应的 binlog,此时记录的 binlog 会被保存到 binlog cache,并没有刷新到硬盘上的 binlog 文件,在事务提交时才会统一将该事务运行过程中的所有 binlog 刷新到硬盘。 7. 事务提交(为了方便说明,这里不说组提交的过程,只说两阶段提交): diff --git a/network/2_http/http2.md b/network/2_http/http2.md index d79db823..4440ae25 100644 --- a/network/2_http/http2.md +++ b/network/2_http/http2.md @@ -212,7 +212,7 @@ HTTP/2 **二进制帧**的结构如下图: 你可以从上图中看到: - 1 个 TCP 连接包含一个或者多个 Stream,Stream 是 HTTP/2 并发的关键技术; -- 1 个 Stream 里包含 2个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成; +- 1 个 Stream 里包含 2 个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成; - Message 里包含一条或者多个 Frame,Frame 是 HTTP/2 最小单位,以二进制压缩格式存放 HTTP/1 中的内容(头部和包体); 因此,我们可以得出个结论:多个 Stream 跑在一条 TCP 连接,同一个 HTTP 请求与响应是跑在同一个 Stream 中,HTTP 消息可以由多个 Frame 构成,一个 Frame 可以由多个 TCP 报文构成。 diff --git a/network/2_http/http_interview.md b/network/2_http/http_interview.md index 52940970..fca6ea29 100644 --- a/network/2_http/http_interview.md +++ b/network/2_http/http_interview.md @@ -1035,11 +1035,11 @@ HTTP/3 现在普及的进度非常的缓慢,不知道未来 UDP 是否能够 ## 读者问答 -> 读者问:“https 和 http 相比,就是传输的内容多了对称加密,可以这么理解吗?” +> 读者问:“HTTPS 和 HTTP 相比,就是传输的内容多了对称加密,可以这么理解吗?” -1. 建立连接时候:https 比 http 多了 TLS 的握手过程; +1. 建立连接时候:HTTPS 比 HTTP 多了 TLS 的握手过程; -2. 传输内容的时候:https 会把数据进行加密,通常是对称加密数据; +2. 传输内容的时候:HTTPS 会把数据进行加密,通常是对称加密数据; > 读者问:“我看文中 TLS 和 SSL 没有做区分,这两个需要区分吗?” diff --git a/network/2_http/http_rpc.md b/network/2_http/http_rpc.md index 1574f49d..f812b0a5 100644 --- a/network/2_http/http_rpc.md +++ b/network/2_http/http_rpc.md @@ -156,19 +156,19 @@ fd = socket(AF_INET,SOCK_STREAM,0); **Header** 是用于标记一些特殊信息,其中最重要的是**消息体长度**。 -**Body** 则是放我们真正需要传输的内容,而这些内容只能是二进制 01 串,毕竟计算机只认识这玩意。所以 TCP 传字符串和数字都问题不大,因为字符串可以转成编码再变成 01 串,而数字本身也能直接转为二进制。但结构体呢,我们得想个办法将它也转为二进制 01 串,这样的方案现在也有很多现成的,比如 **Json,Protobuf。** +**Body** 则是放我们真正需要传输的内容,而这些内容只能是二进制 01 串,毕竟计算机只认识这玩意。所以 TCP 传字符串和数字都问题不大,因为字符串可以转成编码再变成 01 串,而数字本身也能直接转为二进制。但结构体呢,我们得想个办法将它也转为二进制 01 串,这样的方案现在也有很多现成的,比如 **JSON,Protobuf。** 这个将结构体转为二进制数组的过程就叫**序列化**,反过来将二进制数组复原成结构体的过程叫**反序列化**。 ![序列化和反序列化](https://img-blog.csdnimg.cn/img_convert/dba2bc3af0938d2c087f85acc191fd3f.png) -对于主流的 HTTP/1.1,虽然它现在叫**超文本**协议,支持音频视频,但 HTTP 设计初是用于做网页**文本**展示的,所以它传的内容以字符串为主。Header 和 Body 都是如此。在 Body 这块,它使用 **Json** 来**序列化**结构体数据。 +对于主流的 HTTP/1.1,虽然它现在叫**超文本**协议,支持音频视频,但 HTTP 设计初是用于做网页**文本**展示的,所以它传的内容以字符串为主。Header 和 Body 都是如此。在 Body 这块,它使用 **JSON** 来**序列化**结构体数据。 我们可以随便截个图直观看下。 ![HTTP 报文](https://img-blog.csdnimg.cn/img_convert/324cbe84c303a3b975e50329f5cdbf8b.png) -可以看到这里面的内容非常多的**冗余**,显得**非常啰嗦**。最明显的,像 `Header` 里的那些信息,其实如果我们约定好头部的第几位是 Content-Type,就**不需要每次都真的把"Content-Type"这个字段都传过来**,类似的情况其实在 `body` 的 Json 结构里也特别明显。 +可以看到这里面的内容非常多的**冗余**,显得**非常啰嗦**。最明显的,像 `Header` 里的那些信息,其实如果我们约定好头部的第几位是 Content-Type,就**不需要每次都真的把"Content-Type"这个字段都传过来**,类似的情况其实在 `body` 的 JSON 结构里也特别明显。 而 RPC,因为它定制化程度更高,可以采用体积更小的 Protobuf 或其他序列化协议去保存结构体数据,同时也不需要像 HTTP 那样考虑各种浏览器行为,比如 302 重定向跳转啥的。**因此性能也会更好一些,这也是在公司内部微服务中抛弃 HTTP,选择使用 RPC 的最主要原因。** diff --git a/network/3_tcp/quic.md b/network/3_tcp/quic.md index 93fce6ac..85f04c3a 100644 --- a/network/3_tcp/quic.md +++ b/network/3_tcp/quic.md @@ -200,7 +200,7 @@ QUIC 实现了两种级别的流量控制,分别为 Stream 和 Connection 两 ### Stream 级别的流量控制 -最开始,接收方的接收窗口初始状态如下(网上的讲 QUIC 流量控制的资料太少了,下面的例子我是参考 google 文档的:[Flow control in QUIC](https://docs.google.com/document/d/1F2YfdDXKpy20WVKJueEf4abn_LVZHhMUMS5gX6Pgjl4/mobilebasic)): +最开始,接收方的接收窗口初始状态如下(网上的讲 QUIC 流量控制的资料太少了,下面的例子我是参考 Google 文档的:[Flow control in QUIC](https://docs.google.com/document/d/1F2YfdDXKpy20WVKJueEf4abn_LVZHhMUMS5gX6Pgjl4/mobilebasic)): ![](https://img-blog.csdnimg.cn/f1070a6eccd24559904815297b07f789.png) diff --git a/network/3_tcp/tcp_drop.md b/network/3_tcp/tcp_drop.md index 57783fa9..aad60d58 100644 --- a/network/3_tcp/tcp_drop.md +++ b/network/3_tcp/tcp_drop.md @@ -220,7 +220,7 @@ net.ipv4.tcp_wmem = 4096 16384 4194304 ![recv_buffer 丢包](https://img-blog.csdnimg.cn/img_convert/2df66c2e1d9f1245813e8d1de7482e0c.png) -我们可以通过下面的命令里的`TCPRcvQDrop`查看到有没有发生过这种丢包现象。 +我们可以通过下面的命令里的 `TCPRcvQDrop` 查看到有没有发生过这种丢包现象。 ```shell cat /proc/net/netstat @@ -228,7 +228,7 @@ TcpExt: SyncookiesSent TCPRcvQDrop SyncookiesFailed TcpExt: 0 157 60116 ``` -但是说个伤心的事情,我们一般也看不到这个`TCPRcvQDrop`,因为这个是`5.9版本`里引入的打点,而我们的服务器用的一般是`2.x~3.x`左右版本。你可以通过下面的命令查看下你用的是什么版本的 linux 内核。 +但是说个伤心的事情,我们一般也看不到这个 `TCPRcvQDrop`,因为这个是 `5.9版本` 里引入的打点,而我们的服务器用的一般是 `2.x~3.x` 左右版本。你可以通过下面的命令查看下你用的是什么版本的 Linux 内核。 ```shell # cat /proc/version diff --git a/network/3_tcp/tcp_feature.md b/network/3_tcp/tcp_feature.md index 31e78e28..e070d625 100644 --- a/network/3_tcp/tcp_feature.md +++ b/network/3_tcp/tcp_feature.md @@ -664,7 +664,7 @@ https://coolshell.cn/articles/11564.html > 会去确认): > 1.拥塞避免这一段,蓝色字体:每当收到一个 > ACK 时,cwnd 增加 1/cwnd。是否应该是 -> 1/ssthresh?否则不符合线性增长。 +> 1/ssthresh?否则不符合线性增长。 > 2.快速重传的拥塞发生算法,步骤一和步骤 2 是 > 否写反了?否则快速恢复算法中最后一步【如果 > 收到新数据的 ACK 后,设置 cwnd 为 diff --git a/network/3_tcp/tcp_queue.md b/network/3_tcp/tcp_queue.md index 761540b1..652a2518 100644 --- a/network/3_tcp/tcp_queue.md +++ b/network/3_tcp/tcp_queue.md @@ -79,7 +79,7 @@ 实验环境: -- 客户端和服务端都是 CentOs 6.5,Linux 内核版本 2.6.32 +- 客户端和服务端都是 CentOS 6.5,Linux 内核版本 2.6.32 - 服务端 IP 192.168.3.200,客户端 IP 192.168.3.100 - 服务端是 Nginx 服务,端口为 8088 @@ -208,7 +208,7 @@ tcp_abort_on_overflow 共有两个值分别是 0 和 1,其分别表示: 实验环境: -- 客户端和服务端都是 CentOs 6.5,Linux 内核版本 2.6.32 +- 客户端和服务端都是 CentOS 6.5,Linux 内核版本 2.6.32 - 服务端 IP 192.168.3.200,客户端 IP 192.168.3.100 - 服务端是 Nginx 服务,端口为 8088 diff --git a/network/3_tcp/tcp_tcpdump.md b/network/3_tcp/tcp_tcpdump.md index 5a055641..1508298b 100644 --- a/network/3_tcp/tcp_tcpdump.md +++ b/network/3_tcp/tcp_tcpdump.md @@ -180,7 +180,7 @@ TCP 三次握手的过程相信大家都背的滚瓜烂熟,那么你有没有 ![实验环境](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost/计算机网络/TCP-Wireshark/21.jpg) -- 客户端和服务端都是 CentOs 6.5 Linux,Linux 内核版本 2.6.32 +- 客户端和服务端都是 CentOS 6.5 Linux,Linux 内核版本 2.6.32 - 服务端 192.168.12.36,apache web 服务 - 客户端 192.168.12.37 diff --git a/network/4_ip/ip_base.md b/network/4_ip/ip_base.md index 9f83f438..4bc31ca2 100644 --- a/network/4_ip/ip_base.md +++ b/network/4_ip/ip_base.md @@ -74,7 +74,7 @@ IP 地址(IPv4 地址)由 `32` 位正整数来表示,IP 地址在计算机 因此,让 43 亿台计算机全部连网其实是不可能的,更何况 IP 地址是由「网络标识」和「主机标识」这两个部分组成的,所以实际能够连接到网络的计算机个数更是少了很多。 -> 可能有的小伙伴提出了疑问,现在不仅电脑配了 IP,手机、IPad 等电子设备都配了 IP 呀,照理来说肯定会超过 43 亿啦,那是怎么能够支持这么多 IP 的呢? +> 可能有的小伙伴提出了疑问,现在不仅电脑配了 IP,手机、iPad 等电子设备都配了 IP 呀,照理来说肯定会超过 43 亿啦,那是怎么能够支持这么多 IP 的呢? 因为会根据一种可以更换 IP 地址的技术 `NAT`,使得可连接计算机数超过 43 亿台。 `NAT` 技术后续会进一步讨论和说明。 diff --git a/network/4_ip/ping.md b/network/4_ip/ping.md index 4c8774e3..01d909ca 100644 --- a/network/4_ip/ping.md +++ b/network/4_ip/ping.md @@ -255,7 +255,7 @@ ICMP 数据包内包含多个字段,最重要的是两个: ## traceroute —— 差错报文类型的使用 -有一款充分利用 ICMP **差错报文类型**的应用叫做 `traceroute`(在 UNIX、MacOS 中是这个命令,而在 Windows 中对等的命令叫做 tracert)。 +有一款充分利用 ICMP **差错报文类型**的应用叫做 `traceroute`(在 UNIX、macOS 中是这个命令,而在 Windows 中对等的命令叫做 `tracert`)。 *1. traceroute 作用一* diff --git a/network/4_ip/ping_lo.md b/network/4_ip/ping_lo.md index 03a69a01..d9f135e5 100644 --- a/network/4_ip/ping_lo.md +++ b/network/4_ip/ping_lo.md @@ -109,7 +109,7 @@ ping 和其他应用层软件都属于**应用层**。 那么我们横向对比一下,比方说聊天软件,如果用的是 TCP 的方式去发送消息。 -为了发送消息,那就得先知道往哪发。linux 里万物皆文件,那你要发消息的目的地,也是个文件,这里就引出了 socket 的概念。 +为了发送消息,那就得先知道往哪发。Linux 里万物皆文件,那你要发消息的目的地,也是个文件,这里就引出了 socket 的概念。 要使用 `socket` , 那么首先需要创建它。 diff --git a/network/5_learn/draw.md b/network/5_learn/draw.md index 583987bd..167555c4 100644 --- a/network/5_learn/draw.md +++ b/network/5_learn/draw.md @@ -13,7 +13,7 @@ 咳咳,没问题,直接坦白讲,我用的是一个在线的画图网址,地址是: - *https://draw.io* -用它的原因是使用方便和简单,当然最重要的是它完全免费,没有什么限制,甚至还能直接把图片保存到 GoogleDrive、OneDrive 和 Github,我就是保存到 Github,然后用 Github 作为我的图床。 +用它的原因是使用方便和简单,当然最重要的是它完全免费,没有什么限制,甚至还能直接把图片保存到 GoogleDrive、OneDrive 和 GitHub,我就是保存到 GitHub,然后用 GitHub 作为我的图床。 既然要认识它,那就先来看看它长什么样子,它主要分为三个区域,从左往右的顺序是「图形选择区域、绘图区域、属性设置区域」。 diff --git a/os/10_learn/draw.md b/os/10_learn/draw.md index cca6b4c5..0b1f40c6 100644 --- a/os/10_learn/draw.md +++ b/os/10_learn/draw.md @@ -13,7 +13,7 @@ 咳咳,没问题,直接坦白讲,我用的是一个在线的画图网址,地址是: - *https://draw.io* -用它的原因是使用方便和简单,当然最重要的是它完全免费,没有什么限制,甚至还能直接把图片保存到 GoogleDrive、OneDrive 和 Github,我就是保存到 Github,然后用 Github 作为我的图床。 +用它的原因是使用方便和简单,当然最重要的是它完全免费,没有什么限制,甚至还能直接把图片保存到 GoogleDrive、OneDrive 和 GitHub,我就是保存到 GitHub,然后用 GitHub 作为我的图床。 既然要认识它,那就先来看看它长什么样子,它主要分为三个区域,从左往右的顺序是「图形选择区域、绘图区域、属性设置区域」。 diff --git a/os/2_os_structure/linux_vs_windows.md b/os/2_os_structure/linux_vs_windows.md index 2e415c39..5614511d 100644 --- a/os/2_os_structure/linux_vs_windows.md +++ b/os/2_os_structure/linux_vs_windows.md @@ -16,7 +16,7 @@ Windows 基本占领了电脑时代的市场,商业上取得了很大成就, 什么是内核呢? -计算机是由各种外部硬件设备组成的,比如内存、cpu、硬盘等,如果每个应用都要和这些硬件设备对接通信协议,那这样太累了,所以这个中间人就由内核来负责,**让内核作为应用连接硬件设备的桥梁**,应用程序只需关心与内核交互,不用关心硬件的细节。 +计算机是由各种外部硬件设备组成的,比如内存、CPU、硬盘等,如果每个应用都要和这些硬件设备对接通信协议,那这样太累了,所以这个中间人就由内核来负责,**让内核作为应用连接硬件设备的桥梁**,应用程序只需关心与内核交互,不用关心硬件的细节。 ![内核](https://cdn.jsdelivr.net/gh/xiaolincoder/ImageHost4@main/操作系统/内核/Kernel_Layout.png) @@ -33,7 +33,7 @@ Windows 基本占领了电脑时代的市场,商业上取得了很大成就, 内核是怎么工作的? -内核具有很高的权限,可以控制 cpu、内存、硬盘等硬件,而应用程序具有的权限很小,因此大多数操作系统,把内存分成了两个区域: +内核具有很高的权限,可以控制 CPU、内存、硬盘等硬件,而应用程序具有的权限很小,因此大多数操作系统,把内存分成了两个区域: - 内核空间,这个内存空间只有内核程序可以访问; - 用户空间,这个内存空间专门给应用程序使用; diff --git a/os/3_memory/alloc_mem.md b/os/3_memory/alloc_mem.md index 668fdce0..3e48148d 100644 --- a/os/3_memory/alloc_mem.md +++ b/os/3_memory/alloc_mem.md @@ -291,7 +291,7 @@ int main() { ### 实验二:有开启 Swap 机制 -我用我的 mac book pro 笔记本做测试,我的笔记本是 64 位操作系统,物理内存是 8 GB,目前 Swap 分区大小为 1 GB(注意这个大小不是固定不变的,Swap 分区总大小是会动态变化的,当没有使用 Swap 分区时,Swap 分区总大小是 0;当使用了 Swap 分区,Swap 分区总大小会增加至 1 GB;当 Swap 分区已使用的大小超过 1 GB 时;Swap 分区总大小就会增加到至 2 GB;当 Swap 分区已使用的大小超过 2 GB 时;Swap 分区总大小就增加至 3GB,如此往复。这个估计是 macos 自己实现的,Linux 的分区则是固定大小的,Swap 分区不会根据使用情况而自动增长)。 +我用我的 mac book pro 笔记本做测试,我的笔记本是 64 位操作系统,物理内存是 8 GB,目前 Swap 分区大小为 1 GB(注意这个大小不是固定不变的,Swap 分区总大小是会动态变化的,当没有使用 Swap 分区时,Swap 分区总大小是 0;当使用了 Swap 分区,Swap 分区总大小会增加至 1 GB;当 Swap 分区已使用的大小超过 1 GB 时;Swap 分区总大小就会增加到至 2 GB;当 Swap 分区已使用的大小超过 2 GB 时;Swap 分区总大小就增加至 3GB,如此往复。这个估计是 macOS 自己实现的,Linux 的分区则是固定大小的,Swap 分区不会根据使用情况而自动增长)。 ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost/操作系统/内存管理/swap分区大小.png) @@ -337,7 +337,7 @@ int main() { ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/ImageHost/操作系统/内存管理/被kill掉.png) -当系统多次尝试回收内存,还是无法满足所需使用的内存大小,进程就会被系统 kill 掉了,意味着发生了 OOM(*PS:我没有在 macos 系统找到像 linux 系统里的 /var/log/message 系统日志文件,所以无法通过查看日志确认是否发生了 OOM*)。 +当系统多次尝试回收内存,还是无法满足所需使用的内存大小,进程就会被系统 kill 掉了,意味着发生了 OOM(*PS:我没有在 macOS 系统找到像 Linux 系统里的 /var/log/message 系统日志文件,所以无法通过查看日志确认是否发生了 OOM*)。 ## 总结 diff --git a/os/3_memory/mem_reclaim.md b/os/3_memory/mem_reclaim.md index c7bdf898..1a131239 100644 --- a/os/3_memory/mem_reclaim.md +++ b/os/3_memory/mem_reclaim.md @@ -60,8 +60,8 @@ OOM Killer 机制会根据算法选择一个占用物理内存较高的进程, 活跃和非活跃的内存页,按照类型的不同,又分别分为文件页和匿名页。可以从 /proc/meminfo 中,查询它们的大小,比如: ```shell -# grep表示只保留包含active的指标(忽略大小写) -# sort表示按照字母顺序排序 +# grep 表示只保留包含 active 的指标(忽略大小写) +# sort 表示按照字母顺序排序 [root@xiaolin ~]# cat /proc/meminfo | grep -i active | sort Active: 901456 kB Active(anon): 227252 kB diff --git a/reader_nb/1_reader.md b/reader_nb/1_reader.md index e5086ebf..78601784 100644 --- a/reader_nb/1_reader.md +++ b/reader_nb/1_reader.md @@ -110,13 +110,13 @@ hr 经历的违约事件数不胜数,并不会对你做特殊处理。就业 我自己是有较扎实的 C 知识,所以学 C++ 并不是那么困难,而且我比较喜欢 C++ 这门语言,喜欢它的严谨和多样性,所以我看的 C++ 书籍可能比较多。 -从《C++ Primer》到《Effective C++》系列再到《C++沉思录》这些前前后后可能看了十几本左右,越往后看越体会到 C++ 的复杂性,越看越感觉难…… +从《C++ Primer》到《Effective C++》系列再到《C++ 沉思录》这些前前后后可能看了十几本左右,越往后看越体会到 C++ 的复杂性,越看越感觉难…… 校招的话 C++ 一般不会问的很难,甚至如果面试官是搞 PHP 或者 Java 的可能都不问你语言相关。 所以我**不建议你直接啃《C++ Primer》,把它当作一本词典来用会更好**,新人上来一直看这么厚一本书很容易直接被劝退。 -我推荐你从《C++ 新经典》 -->《Essential C++》 --> 《C++新经典 对象模型》这样的顺序学习,基本上校招应付面试是绰绰有余了。 +我推荐你从《C++ 新经典》 -->《Essential C++》 --> 《C++ 新经典 对象模型》这样的顺序学习,基本上校招应付面试是绰绰有余了。 如果想要在面试中在 C++ 相关做到侃侃而谈的话可以看看 Effective 系列的书籍,以及啃一啃《深入理解 C++ 对象模型》这本书,我过年的时候把它放在家里了,是我最后悔的事。因为到现在为止,我看这本书都蛮吃力的,需要慢慢的看,细细的看,而且很多地方都不理解。 @@ -158,11 +158,11 @@ hr 经历的违约事件数不胜数,并不会对你做特殊处理。就业 《深入理解计算机系统》的话,可以跳着看,有几章节是用纯汇编演示的,懂汇编的同学可以看一看,不懂的同学粗略看一看也没关系。 -到这基本校招问的知识你已经具备了,如果想要实践的话,github 上也有一些和操作系统相关的项目可以看一看。 +到这基本校招问的知识你已经具备了,如果想要实践的话,GitHub 上也有一些和操作系统相关的项目可以看一看。 #### 计算机网络 -计算机网络我自己是先看了谢希仁的《计算机网络》,发现讲的太杂了,并不是很深入,又看了《TCP/IP 详解:卷 1》,又发现讲的太深入了,根本看不懂,最后又看了《计算机网络自顶向下方法》和《Http 图解》,面试的时候就够用了。 +计算机网络我自己是先看了谢希仁的《计算机网络》,发现讲的太杂了,并不是很深入,又看了《TCP/IP 详解:卷 1》,又发现讲的太深入了,根本看不懂,最后又看了《计算机网络自顶向下方法》和《HTTP 图解》,面试的时候就够用了。 我建议你直接看《计算机网络自顶向下方法》就好了,然后直接看小林哥总结的计算机网络相关的知识就可以了,应付面试时绰绰有余了。 @@ -236,9 +236,9 @@ Linux 系统的话纯看书可能事半功倍,最好在 Linux 环境下做两 在有扎实的基础前提下,在面试的时候要学会引导面试官的面试方向。 -假如你对 http 协议研究的比较通透,在问到和网络协议相关的问题时,可以试着把问题导向 http 方面的知识。 +假如你对 HTTP 协议研究的比较通透,在问到和网络协议相关的问题时,可以试着把问题导向 HTTP 方面的知识。 -比如你在回答完上一个问题时,发现这个问题和 http 中的某些知识点吻合,可以在说完之后顺嘴提一句这个和 http 中 xxx 有点像,然后面试官可能就顺着问你 http 中的知识了。 +比如你在回答完上一个问题时,发现这个问题和 HTTP 中的某些知识点吻合,可以在说完之后顺嘴提一句这个和 HTTP 中 xxx 有点像,然后面试官可能就顺着问你 HTTP 中的知识了。 因为面试官其实在面试的时候,除了部门硬性要求掌握的技术,并没有一个具体的侧重点,所以在这种情况下,大概率就顺着你所说的问下去了。 diff --git a/reader_nb/2_reader.md b/reader_nb/2_reader.md index 3c9c753b..9c356882 100644 --- a/reader_nb/2_reader.md +++ b/reader_nb/2_reader.md @@ -190,7 +190,7 @@ emmm 据个人面试经验,简历上的闪光点排名及说明如下表: 由于我主攻 C++,这里我说一下我主要在准备秋招时看了哪些资料吧。 -首先对于 C++的基础学习,我看了《C++ Primer Plus》这本书,看完基本就入门了,看完之后看了《Effective C++》以及侯捷的《STL 源码剖析》(由于时间关系没有看完)。看完书之后就是对语言的熟悉了,后期刷题基本上都用 C++ 去刷。 +首先对于 C++ 的基础学习,我看了《C++ Primer Plus》这本书,看完基本就入门了,看完之后看了《Effective C++》以及侯捷的《STL 源码剖析》(由于时间关系没有看完)。看完书之后就是对语言的熟悉了,后期刷题基本上都用 C++ 去刷。 其他常考的 C++ 知识,我可能没有总结资料,需要你们自己搜,比如: @@ -201,11 +201,11 @@ emmm 据个人面试经验,简历上的闪光点排名及说明如下表: - 虚表(常考); - sizeof; - 多态; -- C++内存管理; +- C++ 内存管理; - C++11 特性; -- C++编译的 4 个阶段; +- C++ 编译的 4 个阶段; - 动态库与静态库; -- C++结构体和类的内存对齐问题; +- C++ 结构体和类的内存对齐问题; - 智能指针; - ……. @@ -221,8 +221,8 @@ emmm 据个人面试经验,简历上的闪光点排名及说明如下表: - join 用法、split 用法; - 类、类中 self; - 运算符 and,or,not; -- is 和“==”的区别; -- 位运算和 C++的不同; +- is 和 `==` 的区别; +- 位运算和 C++ 的不同; - …… 以上我提到的最好看看,但不能作为唯一参考,真实考察的会比我上面提到的多。多搜搜面经和真题,查漏补缺,学会记录,好记性不如烂笔头。 @@ -239,7 +239,7 @@ emmm 据个人面试经验,简历上的闪光点排名及说明如下表: 包括操作系统,我也是先在 b 站系统的学习了(看的是王道考研的操作系统),才去看小林的图解系统对一些常考的问题以及怎么与实际问题结合进行了学习和查漏补缺。 -另外,在公司大多都是在 linux 环境下进行开发工作,面试官多多少少会问一些 linux 指令,大家可以熟悉一些常用的指令。 +另外,在公司大多都是在 Linux 环境下进行开发工作,面试官多多少少会问一些 Linux 指令,大家可以熟悉一些常用的指令。 这里我根据我个人的面试经历总结一下上述几个方面常考的一些题。 diff --git a/reader_nb/3_reader.md b/reader_nb/3_reader.md index ad09e5ad..70b193c4 100644 --- a/reader_nb/3_reader.md +++ b/reader_nb/3_reader.md @@ -144,7 +144,7 @@ Java 基础那时我已经看了很多遍视频了,觉得不能再这样下去 而且要从容(面试官不开摄像头除外),语言逻辑一定要顺畅,突出重点,带有引导性的去和面试官聊。 -举个例子,当时某厂面试官问我 http 和 https 有什么区别,我提了一点 https 多了一次 SSL 握手过程,紧接着就问我 http 和 https 三次握手(多亏了小林的计网模块)。 +举个例子,当时某厂面试官问我 HTTP 和 HTTPS 有什么区别,我提了一点 HTTPS 多了一次 SSL 握手过程,紧接着就问我 HTTP 和 HTTPS 三次握手(多亏了小林的计网模块)。 ## Choose diff --git a/reader_nb/4_reader.md b/reader_nb/4_reader.md index 399a3de6..9daf8962 100644 --- a/reader_nb/4_reader.md +++ b/reader_nb/4_reader.md @@ -36,7 +36,7 @@ Hello,这里是伞兵一号。 ![图片](https://img-blog.csdnimg.cn/img_convert/07efce5886cb4f1f4815fe4bf417b46e.png) -阿里阿里云 C++研发。 +阿里阿里云 C++ 研发。 ![图片](https://img-blog.csdnimg.cn/img_convert/62bf3127e81fe1d893b40ddfe7f21a1a.png) @@ -195,7 +195,7 @@ Momenta Deep Learning. 这里插一句,到现在实际上挺感慨的,性能不错的电脑也有了,也用奖学金买了 switch 和 PS,可是玩游戏的时间却越来越少了。 -到了大学,一开始自己捣鼓了几个月 unity,做出来个 FPS 游戏,自己通过 youtube 和官方文档好不容易把重力系统捣鼓出来,又加了加技能,特殊弹药和 BOSS 战,虽然啥也不懂,但是慢慢捣鼓出来,很开心。 +到了大学,一开始自己捣鼓了几个月 unity,做出来个 FPS 游戏,自己通过 YouTube 和官方文档好不容易把重力系统捣鼓出来,又加了加技能,特殊弹药和 BOSS 战,虽然啥也不懂,但是慢慢捣鼓出来,很开心。 后面听说了 ACM 比赛,开始自学刘汝佳老师的《算法竞赛入门经典》(所谓的「入门经典」,我到现在还没刷完),之后进入了 ACM 实验室,进入了天坑。 @@ -245,9 +245,9 @@ Momenta Deep Learning. 我在学习过程由衷的觉得机械工业出版社出版的多本经典书籍让我受益良多,比如龙书《编译原理》,上文提到的《计算机组成原理:硬件/软件接口》,还有《计算机网络自顶向下方法》《现代操作系统》《汇编语言基于 x86 处理器》等等,都是经典中的经典。 -还有 effective 三部曲,《代码大全》《深度探索 C++对象模型》《STL 源码解析》等好书,无一不让人读起来大呼过瘾(当然我还没全读完,最近准备去补一下 CSAPP,英语版好贵啊!) +还有 effective 三部曲,《代码大全》《深度探索 C++ 对象模型》《STL 源码解析》等好书,无一不让人读起来大呼过瘾(当然我还没全读完,最近准备去补一下 CSAPP,英语版好贵啊!) -英语对于程序员来说也是重中之重,有很多时候要去外网求解,比如 stackoverflow 上和暴躁老哥激情对喷,到 github 上和大家同性交友等等。 +英语对于程序员来说也是重中之重,有很多时候要去外网求解,比如 stackoverflow 上和暴躁老哥激情对喷,到 GitHub 上和大家同性交友等等。 不过我学习英语的道路比较畸形,小时候上了几年补习班背了几千词,之后再也没有背过英语。 @@ -263,7 +263,7 @@ Momenta Deep Learning. 学习 Linux 也可以使用 WSL 去学习,相对安装 Linux 系统来说要简单许多,同时也不要忘记了学习代码风格,代码风格太差会影响你找 BUG 的能力和耗费的精力,也有可能造成一行代码报五个错的奇葩情况。 -还要学会如何使用搜索引擎,查找文档可以去 Microsoft Document、IBM Knowledge Center、cppreference 等,找具体问题可以去 Github 和 StackOverflow 等,学会如何提问也是一种很重要的东西。 +还要学会如何使用搜索引擎,查找文档可以去 Microsoft Document、IBM Knowledge Center、cppreference 等,找具体问题可以去 GitHub 和 StackOverflow 等,学会如何提问也是一种很重要的东西。 对语言不必要求什么都学,我认为到毕业时会使用一门 OOP 语言和一门脚本语言即可,语言实际上是一种触类旁通的东西,重点在于思维的培养。 diff --git a/reader_nb/5_reader.md b/reader_nb/5_reader.md index 07619de9..8f321cd9 100644 --- a/reader_nb/5_reader.md +++ b/reader_nb/5_reader.md @@ -204,7 +204,7 @@ 在家这半年主要做了两件事: - 第一是将之前学的操作系统、计算机网络、数据结构和算法、C++ 进行总结,用 xmind 将每一个内容的知识点脉络整理出来,用 typora 将比较难得知识点记录下来。 -- 第二就是进行实战训练,学习编程技能,比如 linux 的基础 shell 命令、vim、gdb、gcc、make、git 的使用,学完这些后,开始跟着 acwing 刷题,刷完算法基础课后,学习 linux 系统编程和 linux 网络编程。 +- 第二就是进行实战训练,学习编程技能,比如 Linux 的基础 shell 命令、VIM、gdb、gcc、make、git 的使用,学完这些后,开始跟着 acwing 刷题,刷完算法基础课后,学习 Linux 系统编程和 Linux 网络编程。 ### 编程基础技能 @@ -228,13 +228,13 @@ 在我遇到的面试中,但凡是要求手撕算法的,都能在算法基础课里找到一个类型的题。 -### linux 系统编程 & 网络编程 +### Linux 系统编程 & 网络编程 -我将 linux 系统编程、网络编程看做是操作系统、计算机网络的技能体现,既然是技能,就一定要练。 +我将 Linux 系统编程、网络编程看做是操作系统、计算机网络的技能体现,既然是技能,就一定要练。 这两门课我是看 B 站黑马程序员的视频学的,在 B 站输入关键词就能找到。这两门课一定要 学完操作系统和计算机网络之后再学习,不然很多东西都听不懂。 -还有就是**一定要做笔记**,下面就是我做的 linux 系统编程、网络编程的笔记,可以在视频下方找到别人的笔记,进行增删改查,变成自己的笔记。 +还有就是**一定要做笔记**,下面就是我做的 Linux 系统编程、网络编程的笔记,可以在视频下方找到别人的笔记,进行增删改查,变成自己的笔记。 ![图片](https://img-blog.csdnimg.cn/img_convert/e269f181bb13b29c68ee21445011e713.png) @@ -244,7 +244,7 @@ ### 八股文 -如果说学习计网、OS、数据结构是让自己有理论基础,学习 linux 系统编程、网络编程是让自己有编码基础,那么**八股文就是让自己有面试基础**。 +如果说学习计网、OS、数据结构是让自己有理论基础,学习 Linux 系统编程、网络编程是让自己有编码基础,那么**八股文就是让自己有面试基础**。 有些知识点你可能掌握了,但问的时候却回答不上来,终究还是不熟悉套路。 @@ -285,13 +285,13 @@ 2. 三次握手? 3. 四次挥手? -#### linux 系统编程 +#### Linux 系统编程 1. 进程通信方式? 2. 线程通信方式? 3. 线程同步方式? -#### linux 网络编程 +#### Linux 网络编程 1. select、poll、epoll 区别? 2. 说说你理解的 IO 多路复用? @@ -312,7 +312,7 @@ ### 简历 -简历里一定要有项目,**我的项目就是 linux 网络编程课程后面的 web 服务器**。下面就是我写在简历上的项目: +简历里一定要有项目,**我的项目就是 Linux 网络编程课程后面的 web 服务器**。下面就是我写在简历上的项目: ![图片](https://img-blog.csdnimg.cn/img_convert/42c0e7834a8599e472c4bfa2d030ce8f.png) diff --git a/reader_nb/6_reader.md b/reader_nb/6_reader.md index eb5c9368..62fbaf13 100644 --- a/reader_nb/6_reader.md +++ b/reader_nb/6_reader.md @@ -56,7 +56,7 @@ OS 没有找过网课看,觉得看完 CSAPP 之后挺好理解的了。用的文字材料主要是小林的图解系统 + 操作系统概论。 -数据库网课看的是 15445,这是一个面向磁盘的数据库,后面发现大量的讲解的其实是 Mysql 的原理。然后文字材料参考的是 Mysql 技术内幕,Innodb 存储引擎和 Redis 设计与实现。 +数据库网课看的是 15445,这是一个面向磁盘的数据库,后面发现大量的讲解的其实是 MySQL 的原理。然后文字材料参考的是 MySQL 技术内幕,Innodb 存储引擎和 Redis 设计与实现。 进阶的内容大家可以参照自己想要发展的方向学习啦,我认为学到这里的人对计算机体系结构有了了解之后,应该很容易找自己的方向。像我对分布式和数据库感兴趣,就选了 6.824 和 15445 作为扩展学习内容。 @@ -68,7 +68,7 @@ OS 没有找过网课看,觉得看完 CSAPP 之后挺好理解的了。用的 6.824 和 15445 这两个一个是关于分布式系统的。 -他会讲一些关于 mapreduce 和 raft 的分布式算法,以及 15445,它一个基于磁盘的数据库(后来我看 innodb 引擎的时候发现其实就是对着 mysql 讲的)**选择这两个课程学习的好处在于他们的 lab 真的很好**,其实 CSAPP 出名的地方同样如此。 +他会讲一些关于 mapreduce 和 raft 的分布式算法,以及 15445,它一个基于磁盘的数据库(后来我看 innodb 引擎的时候发现其实就是对着 MySQL 讲的)**选择这两个课程学习的好处在于他们的 lab 真的很好**,其实 CSAPP 出名的地方同样如此。 差不多的内容,讲师的水平肯定不会有决定性差距,差距主要体现在这些公开课由很好的 lab,带你手把手对一个知识进行实现算法/数据结构,让你对知识的理解更加深刻。 diff --git a/reader_nb/7_reader.md b/reader_nb/7_reader.md index f0e5f7ce..4056457f 100644 --- a/reader_nb/7_reader.md +++ b/reader_nb/7_reader.md @@ -36,7 +36,7 @@ 首先推荐几个后端学习的仓库吧: -Github 上很出名的: +GitHub 上很出名的: - javaguide - cs-note @@ -93,11 +93,11 @@ emmm,这块的话只能说见仁见智了,个人认为开发里的虚拟机 ### 数据库 -以 Mysql 为例子。 +以 MySQL 为例子。 -科班基础有教材,非科班基础推荐小林大佬的《图解 mysql》。 +科班基础有教材,非科班基础推荐小林大佬的《图解 MySQL》。 -进阶的话,极客时间的《mysql45 讲》,书籍主要两本,分别是《高性能 mysql》和《mysql 技术内幕》,书籍建议有一定基础再看。 +进阶的话,极客时间的《MySQL 45 讲》,书籍主要两本,分别是《高性能 MySQL》和《MySQL 技术内幕》,书籍建议有一定基础再看。 面试重点:小林&帅地的文章里都有很多,不再赘述。 @@ -107,9 +107,9 @@ emmm,这块的话只能说见仁见智了,个人认为开发里的虚拟机 这个入门也是看一下教程会快一些,科班应该也没有专门的课程吧? -入门可以考虑尚硅谷之类的视频,进阶的话可以看看书和一些源码分析:《redis 的设计与实现》,老经典了吧。 +入门可以考虑尚硅谷之类的视频,进阶的话可以看看书和一些源码分析:《Redis 的设计与实现》,老经典了吧。 -不过 redis 个人认为难点在于运用,这点就不多说了,学完基础大家应该都会知道后续的学习路线了,可以期待一下小林大佬的《图解 redis》 +不过 Redis 个人认为难点在于运用,这点就不多说了,学完基础大家应该都会知道后续的学习路线了,可以期待一下小林大佬的《图解 Redis》 #### mq、kafka diff --git a/reader_nb/8_reader.md b/reader_nb/8_reader.md index 0f337fcb..87d55d30 100644 --- a/reader_nb/8_reader.md +++ b/reader_nb/8_reader.md @@ -16,7 +16,7 @@ 我的学习方式也比较笨,最开始就是抱着大厚书肯。 -《CSS 权威指南》(上下)、《Javascript 高级程序设计》(第四版)、《You don’t know JavaScript》、《Javascript 忍者秘籍》(第二版)这些就是我的入门书籍,这四本中前两本我都是看了两边,都在 1000 页左右,后面两本则是草草翻了一下。 +《CSS 权威指南》(上下)、《JavaScript 高级程序设计》(第四版)、《You don’t know JavaScript》、《JavaScript 忍者秘籍》(第二版)这些就是我的入门书籍,这四本中前两本我都是看了两边,都在 1000 页左右,后面两本则是草草翻了一下。 这个过程为我打下了比较扎实的 JS 功底,大概是用了 2 个月的时候,我大概就能摸清楚原型/原型链、Promise/异步、闭包、Event loop 等 JS 中的一些核心知识点了。我觉得一开始看视频会好一点,我自己学习的时候看书看不懂的地方也是去 B 站看相关知识点的讲解。 @@ -28,13 +28,13 @@ 计网方面在 B 站看了中科大的 mooc,讲的不错,看了自顶向下方法那本书,**但是这些都不如小林哥的笔记比较好!!!**不是我吹,我字节一面完全背的小林哥的笔记,面试官直接感叹:“我面了这么多人,从没有一个人像你一样说的这么细致的。”(得益于大学文科背书功底?) -算法方面是看了《算法(第四版)》,youtube 上看的普林斯顿的网课,跟着写了点代码,然后这个学期剩余时间几乎都在谈恋爱。 +算法方面是看了《算法(第四版)》,YouTube 上看的普林斯顿的网课,跟着写了点代码,然后这个学期剩余时间几乎都在谈恋爱。 接下来,就开始第一次面试。当时是陪对象去投春招,被 HR 拉着投了一个知名 K12 公司,当场被拉去面试,莫名其妙就过了。我看了一下名单上好像就一个人投了前端,好像那个人就在我前面,进去没多久就出来了,我自己却面了将近 3 个小时,写了 4 张 A4 纸正反向面。 暑期就去北京实习了,亲身感受了一层楼一夜之间被开除的感觉。我在北京实习的时候,每天上班地铁上背小林的笔记,周末去公司刷 leetcode,刷的方法就是按照题型刷一下。 -实习归来感觉自己太菜了,好多技术栈都没学过。回来之后补了 linux 的一些东西,看了 docker,跟着 webpack 官网撸了一边,看了 koa2、redux、react-redux 源码,看了《狼书》(一二册)、《前端开发核心知识进阶》看了半本,再次去学习 JS 的相关基础知识。 +实习归来感觉自己太菜了,好多技术栈都没学过。回来之后补了 Linux 的一些东西,看了 docker,跟着 Webpack 官网撸了一边,看了 koa2、redux、react-redux 源码,看了《狼书》(一二册)、《前端开发核心知识进阶》看了半本,再次去学习 JS 的相关基础知识。 11 月份的时候看了看,牛客上的面经,感觉自己好像也可以进字节了,就去面试了基本上每个厂都给了 offer,最后选择了去杭州阿里。 diff --git a/redis/data_struct/command.md b/redis/data_struct/command.md index f325925f..8bb2562d 100644 --- a/redis/data_struct/command.md +++ b/redis/data_struct/command.md @@ -50,11 +50,11 @@ SDS 和我们认识的 C 字符串不太一样,之所以没有使用 C 语言 ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/redis/数据类型/raw.png) -注意,embstr 编码和 raw 编码的边界在 redis 不同版本中是不一样的: +注意,embstr 编码和 raw 编码的边界在 Redis 不同版本中是不一样的: -- redis 2.+ 是 32 字节 -- redis 3.0-4.0 是 39 字节 -- redis 5.0 是 44 字节 +- Redis 2.+ 是 32 字节 +- Redis 3.0-4.0 是 39 字节 +- Redis 5.0 是 44 字节 可以看到`embstr`和`raw`编码都会使用`SDS`来保存值,但不同之处在于`embstr`会通过一次内存分配函数来分配一块连续的内存空间来保存`redisObject`和`SDS`,而`raw`编码会通过调用两次内存分配函数来分别分配两块空间来保存`redisObject`和`SDS`。Redis 这样做会有很多好处: @@ -64,7 +64,7 @@ SDS 和我们认识的 C 字符串不太一样,之所以没有使用 C 语言 但是 embstr 也有缺点的: -- 如果字符串的长度增加需要重新分配内存时,整个 redisObject 和 sds 都需要重新分配空间,所以**embstr 编码的字符串对象实际上是只读的**,redis 没有为 embstr 编码的字符串对象编写任何相应的修改程序。当我们对 embstr 编码的字符串对象执行任何修改命令(例如 append)时,程序会先将对象的编码从 embstr 转换成 raw,然后再执行修改命令。 +- 如果字符串的长度增加需要重新分配内存时,整个 redisObject 和 sds 都需要重新分配空间,所以**embstr 编码的字符串对象实际上是只读的**,Redis 没有为 embstr 编码的字符串对象编写任何相应的修改程序。当我们对 embstr 编码的字符串对象执行任何修改命令(例如 append)时,程序会先将对象的编码从 embstr 转换成 raw,然后再执行修改命令。 ### 常用指令 @@ -109,13 +109,13 @@ OK # 将 key 中储存的数字值增一 > INCR number (integer) 1 -# 将key中存储的数字值加 10 +# 将 key 中存储的数字值加 10 > INCRBY number 10 (integer) 11 # 将 key 中储存的数字值减一 > DECR number (integer) 10 -# 将key中存储的数字值键 10 +# 将 key 中存储的数字值键 10 > DECRBY number 10 (integer) 0 ``` @@ -164,13 +164,13 @@ OK # 初始化文章的阅读量 > SET aritcle:readcount:1001 0 OK -#阅读量+1 +#阅读量 +1 > INCR aritcle:readcount:1001 (integer) 1 -#阅读量+1 +#阅读量 +1 > INCR aritcle:readcount:1001 (integer) 2 -#阅读量+1 +#阅读量 +1 > INCR aritcle:readcount:1001 (integer) 3 # 获取对应文章的阅读量 @@ -249,21 +249,21 @@ List 类型的底层数据结构是由**双向链表或压缩列表**实现的 ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/redis/数据类型/list.png) ```shell -# 将一个或多个值value插入到key列表的表头(最左边),最后的值在最前面 +# 将一个或多个值 value 插入到 key 列表的表头 (最左边),最后的值在最前面 LPUSH key value [value ...] -# 将一个或多个值value插入到key列表的表尾(最右边) +# 将一个或多个值 value 插入到 key 列表的表尾 (最右边) RPUSH key value [value ...] -# 移除并返回key列表的头元素 +# 移除并返回 key 列表的头元素 LPOP key -# 移除并返回key列表的尾元素 +# 移除并返回 key 列表的尾元素 RPOP key -# 返回列表key中指定区间内的元素,区间以偏移量start和stop指定,从0开始 +# 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定,从 0 开始 LRANGE key start stop -# 从key列表表头弹出一个元素,没有就阻塞timeout秒,如果timeout=0则一直阻塞 +# 从 key 列表表头弹出一个元素,没有就阻塞 timeout 秒,如果 timeout=0 则一直阻塞 BLPOP key [key ...] timeout -# 从key列表表尾弹出一个元素,没有就阻塞timeout秒,如果timeout=0则一直阻塞 +# 从 key 列表表尾弹出一个元素,没有就阻塞 timeout 秒,如果 timeout=0 则一直阻塞 BRPOP key [key ...] timeout ``` @@ -358,24 +358,24 @@ Hash 类型的底层数据结构是由**压缩列表或哈希表**实现的: ### 常用命令 ```shell -# 存储一个哈希表key的键值 -HSET key field value -# 获取哈希表key对应的field键值 +# 存储一个哈希表 key 的键值 +HSET key field value +# 获取哈希表 key 对应的 field 键值 HGET key field -# 在一个哈希表key中存储多个键值对 +# 在一个哈希表 key 中存储多个键值对 HMSET key field value [field value...] -# 批量获取哈希表key中多个field键值 +# 批量获取哈希表 key 中多个 field 键值 HMGET key field [field ...] -# 删除哈希表key中的field键值 +# 删除哈希表 key 中的 field 键值 HDEL key field [field ...] -# 返回哈希表key中field的数量 +# 返回哈希表 key 中 field 的数量 HLEN key -# 返回哈希表key中所有的键值 +# 返回哈希表 key 中所有的键值 HGETALL key -# 为哈希表key中field键的值加上增量n +# 为哈希表 key 中 field 键的值加上增量 n HINCRBY key field n ``` @@ -392,13 +392,13 @@ Hash 类型的(key,field,value)的结构与对象的(对象 id,属 我们可以使用如下命令,将用户对象的信息存储到 Hash 类型: ```shell -# 存储一个哈希表uid:1的键值 +# 存储一个哈希表 uid:1 的键值 > HMSET uid:1 name Tom age 15 2 -# 存储一个哈希表uid:2的键值 +# 存储一个哈希表 uid:2 的键值 > HMSET uid:2 name Jerry age 13 2 -# 获取哈希表用户id为1中所有的键值 +# 获取哈希表用户 id 为 1 中所有的键值 > HGETALL uid:1 1) "name" 2) "Tom" @@ -410,9 +410,9 @@ Redis Hash 存储其结构如下图: ![](https://cdn.xiaolincoding.com/gh/xiaolincoder/redis/数据类型/hash存储结构.png) -在介绍 String 类型的应用场景时有所介绍,String + Json 也是存储对象的一种方式,那么存储对象时,到底用 String + json 还是用 Hash 呢? +在介绍 String 类型的应用场景时有所介绍,String + JSON 也是存储对象的一种方式,那么存储对象时,到底用 String + JSON 还是用 Hash 呢? -一般对象用 String + Json 存储,对象中某些频繁变化的属性可以考虑抽出来用 Hash 类型存储。 +一般对象用 String + JSON 存储,对象中某些频繁变化的属性可以考虑抽出来用 Hash 类型存储。 #### 购物车 @@ -457,21 +457,21 @@ Set 类型的底层数据结构是由**哈希表或整数集合**实现的: Set 常用操作: ```shell -# 往集合key中存入元素,元素存在则忽略,若key不存在则新建 +# 往集合 key 中存入元素,元素存在则忽略,若 key 不存在则新建 SADD key member [member ...] -# 从集合key中删除元素 +# 从集合 key 中删除元素 SREM key member [member ...] -# 获取集合key中所有元素 +# 获取集合 key 中所有元素 SMEMBERS key -# 获取集合key中的元素个数 +# 获取集合 key 中的元素个数 SCARD key -# 判断member元素是否存在于集合key中 +# 判断 member 元素是否存在于集合 key 中 SISMEMBER key member -# 从集合key中随机选出count个元素,元素不从key中删除 +# 从集合 key 中随机选出 count 个元素,元素不从 key 中删除 SRANDMEMBER key [count] -# 从集合key中随机选出count个元素,元素从key中删除 +# 从集合 key 中随机选出 count 个元素,元素从 key 中删除 SPOP key [count] ``` @@ -480,17 +480,17 @@ Set 运算操作: ```shell # 交集运算 SINTER key [key ...] -# 将交集结果存入新集合destination中 +# 将交集结果存入新集合 destination 中 SINTERSTORE destination key [key ...] # 并集运算 SUNION key [key ...] -# 将并集结果存入新集合destination中 +# 将并集结果存入新集合 destination 中 SUNIONSTORE destination key [key ...] # 差集运算 SDIFF key [key ...] -# 将差集结果存入新集合destination中 +# 将差集结果存入新集合 destination 中 SDIFFSTORE destination key [key ...] ``` @@ -548,7 +548,7 @@ Set 类型可以保证一个用户只能点一个赞,这里举例子一个场 ```shell > SISMEMBER article:1 uid:1 -(integer) 0 # 返回0说明没点赞,返回1则说明点赞了 +(integer) 0 # 返回 0 说明没点赞,返回 1 则说明点赞了 ``` #### 共同关注 @@ -590,9 +590,9 @@ key 可以是用户 id,value 则是已关注的公众号的 id。 ```shell > SISMEMBER uid:1 5 -(integer) 1 # 返回1,说明关注了 +(integer) 1 # 返回 1,说明关注了 > SISMEMBER uid:2 5 -(integer) 0 # 返回0,说明没关注 +(integer) 0 # 返回 0,说明没关注 ``` #### 抽奖活动 @@ -626,14 +626,14 @@ key 为抽奖活动名,value 为员工名称,把所有员工名称放入抽 如果不允许重复中奖,可以使用 SPOP 命令。 ```shell -# 抽取一等奖1个 +# 抽取一等奖 1 个 > SPOP lucky 1 1) "Sary" -# 抽取二等奖2个 +# 抽取二等奖 2 个 > SPOP lucky 2 1) "Jerry" 2) "Mark" -# 抽取三等奖3个 +# 抽取三等奖 3 个 > SPOP lucky 3 1) "John" 2) "Sean" @@ -664,38 +664,38 @@ Zset 类型的底层数据结构是由**压缩列表或跳表**实现的: Zset 常用操作: ```shell -# 往有序集合key中加入带分值元素 +# 往有序集合 key 中加入带分值元素 ZADD key score member [[score member]...] -# 往有序集合key中删除元素 +# 往有序集合 key 中删除元素 ZREM key member [member...] -# 返回有序集合key中元素member的分值 +# 返回有序集合 key 中元素 member 的分值 ZSCORE key member -# 返回有序集合key中元素个数 +# 返回有序集合 key 中元素个数 ZCARD key -# 为有序集合key中元素member的分值加上increment +# 为有序集合 key 中元素 member 的分值加上 increment ZINCRBY key increment member -# 正序获取有序集合key从start下标到stop下标的元素 +# 正序获取有序集合 key 从 start 下标到 stop 下标的元素 ZRANGE key start stop [WITHSCORES] -# 倒序获取有序集合key从start下标到stop下标的元素 +# 倒序获取有序集合 key 从 start 下标到 stop 下标的元素 ZREVRANGE key start stop [WITHSCORES] # 返回有序集合中指定分数区间内的成员,分数由低到高排序。 ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] -# 返回指定成员区间内的成员,按字典正序排列, 分数必须相同。 +# 返回指定成员区间内的成员,按字典正序排列,分数必须相同。 ZRANGEBYLEX key min max [LIMIT offset count] -# 返回指定成员区间内的成员,按字典倒序排列, 分数必须相同 +# 返回指定成员区间内的成员,按字典倒序排列,分数必须相同。 ZREVRANGEBYLEX key max min [LIMIT offset count] ``` Zset 运算操作(相比于 Set 类型,ZSet 类型没有支持差集运算): ```shell -# 并集计算(相同元素分值相加),numberkeys一共多少个key,WEIGHTS每个key对应的分值乘积 +# 并集计算 (相同元素分值相加),numberkeys 一共多少个 key,WEIGHTS 每个 key 对应的分值乘积 ZUNIONSTORE destkey numberkeys key [key...] -# 交集计算(相同元素分值相加),numberkeys一共多少个key,WEIGHTS每个key对应的分值乘积 +# 交集计算 (相同元素分值相加),numberkeys 一共多少个 key,WEIGHTS 每个 key 对应的分值乘积 ZINTERSTORE destkey numberkeys key [key...] ``` @@ -712,19 +712,19 @@ Zset 类型(Sorted Set,有序集合)可以根据元素的权重来排序 我们以博文点赞排名为例,小林发表了五篇博文,分别获得赞为 200、40、100、50、150。 ```shell -# arcticle:1 文章获得了200个赞 +# arcticle:1 文章获得了 200 个赞 > ZADD user:xiaolin:ranking 200 arcticle:1 (integer) 1 -# arcticle:2 文章获得了40个赞 +# arcticle:2 文章获得了 40 个赞 > ZADD user:xiaolin:ranking 40 arcticle:2 (integer) 1 -# arcticle:3 文章获得了100个赞 +# arcticle:3 文章获得了 100 个赞 > ZADD user:xiaolin:ranking 100 arcticle:3 (integer) 1 -# arcticle:4 文章获得了50个赞 +# arcticle:4 文章获得了 50 个赞 > ZADD user:xiaolin:ranking 50 arcticle:4 (integer) 1 -# arcticle:5 文章获得了150个赞 +# arcticle:5 文章获得了 150 个赞 > ZADD user:xiaolin:ranking 150 arcticle:5 (integer) 1 ``` @@ -880,7 +880,7 @@ String 类型是会保存为二进制的字节数组,所以,Redis 就把字 bitmap 基本操作: ```shell -# 设置值,其中value只能是 0 和 1 +# 设置值,其中 value 只能是 0 和 1 SETBIT key offset value # 获取值 @@ -894,18 +894,18 @@ BITCOUNT key start end bitmap 运算操作: ```shell -# BitMap间的运算 +# BitMap 间的运算 # operations 位移操作符,枚举值 AND 与运算 & OR 或运算 | XOR 异或 ^ NOT 取反 ~ -# result 计算的结果,会存储在该key中 -# key1 … keyn 参与运算的key,可以有多个,空格分割,not运算只能一个key -# 当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0。返回值是保存到 destkey 的字符串的长度(以字节byte为单位),和输入 key 中最长的字符串长度相等。 +# result 计算的结果,会存储在该 key 中 +# key1 … keyn 参与运算的 key,可以有多个,空格分割,not 运算只能一个 key +# 当 BITOP 处理不同长度的字符串时,较短的那个字符串所缺少的部分会被看作 0。返回值是保存到 destkey 的字符串的长度(以字节 byte 为单位),和输入 key 中最长的字符串长度相等。 BITOP [operations] [result] [key1] [keyn…] -# 返回指定key中第一次出现指定value(0/1)的位置 +# 返回指定 key 中第一次出现指定 value(0/1) 的位置 BITPOS [key] [value] ``` @@ -1082,10 +1082,10 @@ GEO 类型使用 GeoHash 编码方法实现了经纬度到 Sorted Set 中元素 ### 常用命令 ```shell -# 存储指定的地理空间位置,可以将一个或多个经度(longitude)、纬度(latitude)、位置名称(member)添加到指定的 key 中。 +# 存储指定的地理空间位置,可以将一个或多个经度 (longitude)、纬度 (latitude)、位置名称 (member) 添加到指定的 key 中。 GEOADD key longitude latitude member [longitude latitude member ...] -# 从给定的 key 里返回所有指定名称(member)的位置(经度和纬度),不存在的返回 nil。 +# 从给定的 key 里返回所有指定名称 (member) 的位置(经度和纬度),不存在的返回 nil。 GEOPOS key member [member ...] # 返回两个给定位置之间的距离。 @@ -1382,7 +1382,7 @@ Redis 常见的五种数据类型:**String(字符串),Hash(哈希) 这五种数据类型都由多种数据结构实现的,主要是出于时间和空间的考虑,当数据量小的时候使用更简单的数据结构,有利于节省内存,提高性能。 -这五种数据类型与底层数据结构对应关系图如下,左边是 Redis 3.0 版本的,也就是《Redis 设计与实现》这本书讲解的版本,现在看还是有点过时了,右边是现在 Github 最新的 Redis 代码的。 +这五种数据类型与底层数据结构对应关系图如下,左边是 Redis 3.0 版本的,也就是《Redis 设计与实现》这本书讲解的版本,现在看还是有点过时了,右边是现在 GitHub 最新的 Redis 代码的。 ![](https://img-blog.csdnimg.cn/img_convert/9fa26a74965efbf0f56b707a03bb9b7f.png) diff --git a/redis/data_struct/data_struct.md b/redis/data_struct/data_struct.md index 92ca2dcc..6fc1fb3b 100644 --- a/redis/data_struct/data_struct.md +++ b/redis/data_struct/data_struct.md @@ -10,7 +10,7 @@ 注意,**Redis 数据结构并不是指 String(字符串)对象、List(列表)对象、Hash(哈希)对象、Set(集合)对象和 Zset(有序集合)对象,因为这些是 Redis 键值对中值的数据类型,也就是数据的保存形式,这些对象的底层实现的方式就用到了数据结构**。 -我画了一张 Redis 数据类型(也叫 Redis 对象)和底层数据结构的对应关图,左边是 Redis 3.0 版本的,也就是《Redis 设计与实现》这本书讲解的版本,现在看还是有点过时了,右边是现在 Github 最新的 Redis 代码的(还未发布正式版本)。 +我画了一张 Redis 数据类型(也叫 Redis 对象)和底层数据结构的对应关图,左边是 Redis 3.0 版本的,也就是《Redis 设计与实现》这本书讲解的版本,现在看还是有点过时了,右边是现在 GitHub 最新的 Redis 代码的(还未发布正式版本)。 ![](https://img-blog.csdnimg.cn/img_convert/9fa26a74965efbf0f56b707a03bb9b7f.png) @@ -804,8 +804,8 @@ typedef struct zskiplist { 查找一个跳表节点的过程时,跳表会从头节点的最高层开始,逐一遍历每一层。在遍历某一层的跳表节点时,会用跳表节点中的 SDS 类型的元素和元素的权重来进行判断,共有两个判断条件: -- 如果当前节点指向的下一个节点(forward指向的节点)的权重「小于」要查找的权重时,跳表就会访问该层上的下一个节点。 -- 如果当前节点指向的下一个节点(forward指向的节点)的权重「等于」要查找的权重时,并且下一个节点的 SDS 类型数据「小于」要查找的数据时,跳表就会访问该层上的下一个节点。 +- 如果当前节点指向的下一个节点 (forward 指向的节点) 的权重「小于」要查找的权重时,跳表就会访问该层上的下一个节点。 +- 如果当前节点指向的下一个节点 (forward 指向的节点) 的权重「等于」要查找的权重时,并且下一个节点的 SDS 类型数据「小于」要查找的数据时,跳表就会访问该层上的下一个节点。 如果上面两个条件都不满足,或者下一个节点为空时,跳表就会使用目前遍历到的节点的 level 数组里的下一层指针,然后沿着下一层指针继续查找,这就相当于跳到了下一层接着查找。 @@ -962,7 +962,7 @@ quicklist 虽然通过控制 quicklistNode 结构里的压缩列表的大小或 于是,Redis 在 5.0 新设计一个数据结构叫 listpack,目的是替代压缩列表,它最大特点是 listpack 中每个节点不再包含前一个节点的长度了,压缩列表每个节点正因为需要保存前一个节点的长度字段,就会有连锁更新的隐患。 -**我看了 Redis 的 Github,在最新 6.2 发行版本中,Redis Hash 对象、ZSet 对象的底层数据结构的压缩列表还未被替换成 listpack,而 Redis 的最新代码(还未发布版本)已经将所有用到压缩列表底层数据结构的 Redis 对象替换成 listpack 数据结构来实现,估计不久将来,Redis 就会发布一个将压缩列表为 listpack 的发行版本**。 +**我看了 Redis 的 GitHub,在最新 6.2 发行版本中,Redis Hash 对象、ZSet 对象的底层数据结构的压缩列表还未被替换成 listpack,而 Redis 的最新代码(还未发布版本)已经将所有用到压缩列表底层数据结构的 Redis 对象替换成 listpack 数据结构来实现,估计不久将来,Redis 就会发布一个将压缩列表为 listpack 的发行版本**。 ### listpack 结构设计