不奢望岁月静好 只希望点滴积累

0%

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
1. 备份一般都会在备库上执行,在用–single-transaction 方法做逻辑备份的过程中
若主库的一个小表做了DDL、eg 添加一列
在从库会看到什么样的现象 ?

Q1:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Q2:START TRANSACTION WITH CONSISTENT SNAPSHOT
/* other tables */
Q3:SAVEPOINT sp;
/* 时刻 1 */
Q4:show create table `t1`;
/* 时刻 2 */
Q5:SELECT * FROM `t1`;
/* 时刻 3 */
Q6:ROLLBACK TO SAVEPOINT sp;
/* 时刻 4 */
/* other tables */

Q1、确保事务隔离级别为可重复读
Q2、确保得到一个一致性视图
Q3、设置一个保存点、Q6 回滚到这个保存点

若DDL语句在Q4之前到达、无影响、备份表结构为修改后的表结构
若在Q5之前到达、则insertcreate的结构不一致、会提示 Table definition has changed,
retry transaction、mysqldump终止
若在时刻23之间到达、会造成主从延时
若在Q6之前到达、不影响、备份结构是修改前的表结构


2. 删除表数据的比较好的方式?
1) delete from T limit 100000
2delete from T limit 5000;执行20
3)分20个线程去执行
1会造成大事务、单语句占用时间长、锁的时间比较长、可能造成主从延迟
3可能会人为造成锁冲突
2相对比较好一些

3. 唯一索引和普通索引如何选择?

4. change buffer 一开始是写内存的、然后写磁盘
假如刚写入内存就断电重启、会不会导致change buffer丢失 ?
不会丢失、虽然只是更新内存、但在事务提交的时候、把change buffer的操作也记录在了redo log里、崩溃恢复的时候可以找回


5. 扫描行数如何判断 ?
mysql在真正执行前并不能精确的知道满足条件的记录有多少条、只能根据统计信息来预估
这个统计信息就是索引的区分度、称为 索引基数(cardinality)、也就是索引基数越大、索引的区分度越好
show index from tt;
可以看到索引信息

6. mysql是如何得到索引基数的呢 ?
将整张表取出来一行行统计 - 可以得到精确值、单代价太高
所以采用 采样统计、当表数据持续变更的时候、变更的数据行超过 1/M 时、自动触发索引重新统计
show variables like 'innodb_stats_persistent'; 查看存储索引统计方式
on 代表统计信息会持久化存储、默认 N=20 M=10
off 的时候、代表统计信息只存储在内存中, 默认 N=8 M=16
由于是采样统计、不管N是多少、基数都很容易不准
索引统计值虽然不够精准、大体上还是差不多的、选错索引还有别的原因

7. 如何修正索引?
1 使用force index 强制选择一个索引
2 使用语句引导索引 order by b, a
3 新建更合适的索引或者删掉无用索引

8. 何时会出现mysql抖动、平时很快执行的sql在某些瞬间执行很慢?
1. redo log log大小超过配置值时、需要刷新到磁盘、此时系统会停止所有的更新操作、应尽可能的避免
2. 系统内存不足的时候、当需要新的内存页、内存不够用的时候、就淘汰一些数据页、给别的数据页使用、
若淘汰的是脏页、就需要将脏页写到磁盘
innodb使用缓冲池 buffer pool来管理内存、缓冲池中的内存页有3种状态
1)暂未使用的 2)使用了、并且是干净页(内存数据页和磁盘数据页内容一致) 3)使用了、并且是脏页
innodb的策略是尽量使用内存、对一个长时间运行的库来说、未被使用的页面很少
当要读入的数据页没有在内存的时候、要到缓冲池申请一个数据页、只能把最久不使用的数据页从内存淘汰
若淘汰掉的是一个干净页、就直接释放出来复用、若淘汰的是脏页、就先刷新到磁盘

所以:
1. 一个查询要淘汰的脏页个数过多的时候、会导致查询时间明显变长
2. 日志写满会将更新全部堵塞、写性能跌为0、应尽可能的避免

那么innodb是如何控制刷脏页的策略的呢 ?
一个是系统io能力、可以使用 fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest 来测试
一个是脏页比例上限:show variables like 'innodb_max_dirty_pages_pct'; 默认75%

另外:mysql在刷脏页的时候、若 innodb_flush_neighbors = 1 恰好连着的页也是脏页
会被一起刷掉、若 情况不妙、连着的连着页也是脏页。。。。会导致你的查询更慢
这个策略在机械硬盘时代是有意义的、可以减少io、但SSD设备的话、可以设置为0 减少sql执行时间
8.0 已默认为0

3. mysql认为系统空闲的时候、
4. mysql正常关闭的时候

9. 为什么数据删除、表空间确没有释放
数据删除流程:假如删除 210之间的6记录、它只被标记删除、并不会之间删除
之后需要insert 1~10之间的数据时、会复用这个位置
若删掉了一个数据页上的所有记录、整个数据页可以被直接复用
记录的复用 只限于符合范围条件的数据、
页的复用、可以用于任何数据
若相邻页利用率都很小、系统会把这两个页合并到其中一个数据页、另外一个标记为可复用
这些可以被复用、而未被复用的空间、称为空洞

不止delete会造成空洞、insert也会、随机插入会造成索引的数据页分裂
所以、大量增删改的表是可能存在空洞的、若能去掉空洞、就可以达到收缩表空间的目的
1. 重建表 可以新建一个与表A结构相同的表B、按照主键ID递增的顺序、把数据行一行一行的从表A读出、再插入到B
若将B作为临时表、数据从A导入B的操作完成后、用B替换A、就起到了收缩表A空间的作用
可以使用 alter table A engine=innodb 来重建、
执行流程类似、只是不需要自己创建临时表(是在server完成的、需要数据导入)、mysql自己完成转存数据、交换表名、删除旧表

显然整个过程最耗时的是、插入数据、在整个DDL过程、A表不能有更新、非online

mysql 5.6 版本引入 online ddl
流程:新建临时表、扫描A主键的所有数据页、
使用数据页中的A的记录生成B+树、存储到临时文件(存储引擎创建、整个DDL过程在innodb内部完成、无需数据挪到临时表、是原地操作)
生成临时文件的过程中、将所有对A的操作记录在一个日志文件中、row log
临时文件生成后、将日志文件中的操作应用到临时文件、得到一个逻辑数据上与表A相同的数据文件、
用临时文件替换表A的数据文件

iplace和online是同一概念 ??
no、只是ddl过程若是 online 的、一定是 inplace 的
若、inplace的DDL、可能不是online的、
eg 给一个innodb的表字段加fulltext索引、整个过程是inplace的、会阻塞 增删改查操作、不是online的


10. alter table t engine=innodb 其实是 recreate 重建表的过程、非online
analyze table t 不是重建表、不修改数据、只是对表的索引信息进行重新统计、会加MDL 读锁
optimize table t => recreate + analyze


11. 若一个高配的机器、redo log设置的太小、会发生什么 ?
每次事务提交都有些redo log、若设置太小、会被很快写满、系统不得不停止所有更新、去刷新redo log
同时、change buffer的优化作用也会失效、因为checkpoint一直往前推进、会触发merge操作、然后又进一步的触发脏页刷新
看到的现象会是 磁盘压力小、但是数据库出现间歇性的性能下降

12. count(*) 是怎么工作的 ?
mysiam将数据总行记录在了磁盘上、所以查询 count(*) 会直接返回
innodb 因为考虑事务的原因、不能这么记录、是从记录一条条遍历出来的、但是会遍历最小的那颗树

so:
mysiam count(*) 快、但是不支持事务、
show table status 返回快、但不准确
count(*) 会遍历全表、导致性能问题

那么如何自己实现计数?
1)使用缓存系统 - 数据易丢失、统计不精准(先写redis、记录未插入、则多1、先写db redis未写入 则少1)
2)使用统计表 使用事务保存

count(*)、count(id)、count(1) 都标识返回满足条件的结果集的总行数
count(field)表示 filed不为null的总个数

count(id) innodb会遍历整张表、取出每行的id值、返回给server层、server拿到id后、判断不为空、累计行
count(1) inndb会遍历整张表、但不取值、server对于返回的每一行、放数字1进去、判断不为空、按行累加
所以、count(1)比count(id)快、因为从引擎返回id会涉及到数据行解析、及copy字段值得操作

count(field) not null字段、独处记录、判断不能为null、按行累加
允许为null的字段、执行的时候判断到有可能为null、会先取出值、判断、不为null才累加

count(*) 不会取出全部字段、专门做了优化、不取值
所以 效率: count(field) < count(主键) < count(1) ~ count(*)

13. mysql 怎么知道binlog是完整的 ?
一个事务的binlog是有完整格式的、statement格式的binlog最后会有commit
raw格式的binlog、最后会有一个 xid
5.6之后的版本、还引入了 binlog-checksum 来保证内容的正确性

14. redo log 和 binlog 如何关联:
有一个共同的数据段、xid、崩溃修复的时候会按顺序扫描 redo log
若既有prepare又有commit、则直接提交
若只有prepare 则拿 xid去找binlog 对应的事务

处于prepare阶段的redo log + 完整binlog 重启就能恢复、why ?
binlog写完之后、mysql崩溃、此时binlog已经写入、就会被从库使用
所以在主库上也要提交这个事务、保证主从数据的一致性

为何不是先写 redo log再写binlog、然后直接两个log完整时才恢复数据 ?
对于innodb来说、redo log提交完成、事务就不能回滚(会覆盖到别的事务的更新)、
而如果redo log 直接提交、然后binlog失败的时候、inndo回滚不了、数据和binlog就又不一致了
两阶段提交就是为了给所有人一个机会、啊哈~

15. 对于正常运行的实例、数据写入后的最终落盘、是从redo log更新的还是buffer pool ?
redo log 并未记录数据页的完整数据、所以、没有能力自己去更新磁盘数据页、不存在redo log更新进去的情况
a.若数据页被修改之后、跟磁盘的数据页不一致、称为脏页、最终数据落盘就是把内存中的数据页写盘
b.若在崩溃恢复的场景中、innodb判断到一个数据页可能在崩溃中丢失了更新、就将其读到内存、
然后让redo log更新内存内容、回到 a 的情况


fio -filename=$filename -direct=1 -iodepth 1 -thread -rw=randrw -ioengine=psync -bs=16k -size=500M -numjobs=10 -runtime=10 -group_reporting -name=mytest

16. order by 是如何工作的 ?
explain 中的 using filesort 代表需要排序
mysql 会给每个线程分配一块内存用于排序、称为 sort_buffer

1) 初始化sort_buffer 确定放入 name city age 三个字段、
2) 从索引 city 找到 第一个满足 city=xx 条件的主键id
3) 从主键id索引取出整行、取name city age 三个字段的值、写入 sort_buffer 中
4) 从索引city取下一条满足条件的记录id
5) 重复34 直到无满足条件的记录
6) 对sort buffer中的数据按照name进行快排
7) 取结果集前1000行返回给client

若单行数据超过 max_length_for_sort_data 的值、则会先排序、再回表取数据
1) 初始化sort_buffer 确定放入 name city age 三个字段、
2) 从索引 city 找到 第一个满足 city=xx 条件的主键id
3) 从主键id索引取出整行、取name & id 两个个字段的值、写入 sort_buffer 中
4) 从索引city取下一条满足条件的记录id
5) 重复34 直到无满足条件的记录
6) 对sort buffer中的数据按照name进行快排
7) 取结果集前1000行、回原表扫描city、name和age三个字段值 返回给client

此时假如 name本身有索引、天然有序、则不需要额外排序、会变成
1) 从索引 city name 找到满足 city=杭州的 条件的主键
2) 回主键id索引查出正行、取name、city、age的值 作为结果集的一部分直接返回
3) 从索引 name city取出下一个满足记录的主键id
4) 重复23、直到足够1000条记录、或者无满足条件的记录



使用 optimizer_trace 进行sql追踪、一般情况下、一个新的跟踪会覆盖掉以前的跟踪


/* 打开 optimizer_trace,只对本线程有效 */
SET optimizer_trace='enabled=on';

/* @a 保存 Innodb_rows_read 的初始值 */
select VARIABLE_VALUE into @a from performance_schema.session_status where variable_name = 'Innodb_rows_read';

/* 执行语句 */
select city, name,age from t where city='杭州' order by name limit 1000;

/* 查看 OPTIMIZER_TRACE 输出 */
SELECT * FROM `information_schema`.`OPTIMIZER_TRACE`\G

/* @b 保存 Innodb_rows_read 的当前值 */
select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read';

/* 计算 Innodb_rows_read 差值 */
select @b-@a;





CREATE TABLE `tt` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`),
KEY `b` (`b`)
) ENGINE=InnoDB;

delimiter ;;
create procedure idata()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into tt values(i, i, i);
set i=i+1;
end while;
end;;
delimiter;
call idata();

select * from t where a between 10000 and 20000;


set long_query_time=0;
select * from t where a between 10000 and 20000; /*Q1*/
select * from t force index(a) where a between 10000 and 20000;/*Q2*/


SELECT IFNULL(driver_id, 0) driverId, count(*) serviceCnt, MAX(booking_start_time) lastBookingDate FROM car_order_finished_collect_2018 WHERE driver_id IS NOT NULL AND order_status > 43 AND order_status < 60 AND booking_user_id = 1148735 AND driver_id in (25311) GROUP BY driver_id ORDER BY COUNT(*) DESC, MAX(booking_start_time) DESC\G;
SELECT IFNULL(driver_id, 0) driverId, count(*) serviceCnt, MAX(booking_start_time) lastBookingDate FROM car_order_finished_collect_2018 WHERE driver_id IS NOT NULL AND order_status > 43 AND order_status < 60 AND booking_user_id = 80004692 AND driver_id in (10101406) GROUP BY driver_id ORDER BY COUNT(*) DESC, MAX(booking_start_time) DESC\G;

CPU流水线设计

指令流水线: 若把指令执行拆分成取指令 -> 指令译码 -> 指令执行 三个部分、这就是一个三级的流水线、若把指令执行进一步拆分成 ALU 计算(指令执行) -> 内存访问 -> 数据写回 就变成了5级流水线、

1
2
五级流水线:
表示在同一个时钟周期里、同时运行5条指令的不同阶段, 虽然执行一条指令的时钟周期变成5、但可以提高CPU的主频, 只需要保证最复杂的一个流水线级的操作在一个时钟周期内完成、无需确保最复杂的那条指令在时钟周期里执行完成

超长流水线性能瓶颈

1
2
3
4
5
6
增加流水线的深度是有性能成本的
用来同步时钟周期的、不再是指令级别, 而是流水线级别、每一个流水线对应的输出、都要放到流水线寄存器(Pipeline Register) 然后在下一个时钟周期交给下一个流水线级处理、so. 增加流水线级、就要多出一级写入流水线寄存器的操作、虽然很快、假设20ps、但不断增加流水线深度、这些操作占整个指令的执行时间的比例就会不断增加

eg. 指令执行3ns(3000ps)、设计20级的流水线、流水线的操作就需要 20ps * 20 = 400ps 占比 400/3000超过10% 、也就是但从的增加流水线级数、会带来更多的额外开销、

所以要合理的设计流水线级数

流水线设计的冒险

流水线设计需要解决的三大冒险: 结构冒险, 数据冒险控制冒险
结构冒险: 本质上是硬件层面的资源竞争问题、CPU在同一个时钟周期、同时在运行两条计算机指令的不同阶段、但可能会用到同样的硬件了

同一时钟周期、两个指令访问同一资源.png

1
2
3
4
如上所示: 在第一条指令执行到访存(MEM)阶段的时候、流水线里的第4条指令、在执行指令Fetch的操作、访存和取指令、都要进行内存数据的读取, 内存只有一个地址译码器的作为地址输入、就只能在一个时钟周期里读取一条数据、无法同时执行第一条指令的读取内存数据和第4条指令的读取指令的操作

类似的资源冲突:
常用的键盘、不是每个键的背后都有一根独立的线路、而是多个键公用一个线路、如果在同一时间、按下两个共用一个线路的按键、这两个按键的信号都没办法传输出去、就出现了按下键却不生效的情况

在CPU的结构冒险里、对于内存访问和取指令的冲突、直观的解决方案是把内存拆成2部分、让他们有各自的地址译码器、分别是存放指令的程序内存和存放数据的数据内存(对应体系结构叫 哈佛架构(Harvard Architecture)), 但这样拆对于指令和数据需要的内存空间、就无法根据实际的应用动态分配了、解决了资源冲突问题、也失去了灵活性
现代CPU架构借鉴了哈佛架构的思路、采用了普林斯顿架构、在高速缓存方面拆分成指令缓存和数据缓存
内存的访问速度远比CPU慢、所以现代的CPU不会直接读取主内存、会从主内存把指令和数据加载到高速缓存中、这样后续访问都是访问高速缓存、而指令缓存和数据缓存的拆分使得CPU在进行数据访问和取指令的时候、不会发生资源冲突了

数据冒险: 三种不同的依赖关系

1
数据冒险就是多个指令间有数据依赖的情况、可以分为 先读后写、先写后读和写后再写、如果满足不了依赖关系、最终结果就会出错

通过流水线停顿(Pipeline Stall):插入nop操作 来解决数据冒险

操作数前推

1
2
3
4
5
6
7
8
add $t0, $s2,$s1
add $s2, $s1,$t0
1. 第一条指令 将s1s2 寄存器里的数据相加、写入 t0 寄存器
2. 第二条指令 将 s1t0 的数据相加、写入 s2 寄存器
指令2 的执行、依赖t0的值、而t0的值来自于前一条指令的计算结果、所以指令2需要等前一指令的数据写回之后才能执行、就遇到了数据依赖冒险、
使用插入nop来解决、可以解决、但是会浪费两个时钟周期

其实、如果第一条指令的执行结果可以直接传给第二条指令的执行输入、在第一条指令的执行阶段完成之后、直接将数据传输给下一条指令的ALU、下一指令就不需要插入两个nop阶段、就可以继续执行阶段, 如下图所示、这种方案就叫操作数前推

image.png

乱序执行

1
即使综合运用流水线停顿、操作数前推、增加资源等解决结构冒险和数据冒险的问题、仍然会遇到不得不停下整个流水线等待前一指令完成的情况、但是: 如果后边有指令不需要依赖前边指令的执行结果就可以不必等待前边指令完成、直接占用nop即可、这样的解决方案、在计算机里叫 乱序执行

不得不nop填充.png

nop占用.png

乱序执行的CPU.png

乱序执行的步骤

1
2
3
4
5
6
7
8
1. 取指令和指令译码阶段、同其它CPU、会一级一级顺序进行Fetch和Decode
2. 指令译码完成后、CPU不会直接进行指令执行、而是进行指令分发、把指令发到保留站(Reservation Stations)、类似火车站、指令类似列车
3. 指令不会立即执行、而是等待依赖数据完成才执行. 类似火车要等乘客到齐才出发
4. 依赖数据到齐之后、指令可以交给后边的功能单元(Function Unit, FU)、就是ALU来执行、很多功能单元可以并行运行、但不同功能单元可以执行的指令不同、类似铁轨可以从上海北上、到北京或者哈尔滨;有些是南下、到广州或者深圳
5. 指令执行的阶段完成后、不能立即把结果写回寄存器、而是把结果写入指令重排区(Re-Order buffer, ROB).
6. 在重排缓冲区、CPU会按照取指令的顺序、对指令的计算结果重新排序、 只有排在前边的指令都完成才会提交指令、完成整改指令的运算结果
7. 实际的指令计算结果不直接写入内存或者高速缓存、而是先写入存储缓冲区(Store Buffer),最终才写入高速缓存和内存
so. 在乱序执行的情况下、只有CPU内部指令的执行层面、可能乱序、只要在指令的译码阶段正确的分析出指令之间的数据依赖关系、乱序就只会在无相互影响的指令间发生

分支预测

缩短分支延迟: 将调解判断、地址调整 提前到指令译码阶段进行、无需放到指令执行阶段. 这种方式本质上和数据冒险的操作数前推方案类似、就是在硬件电路层面讲一些计算结果更早的反馈到流水线中、反馈更快、后边指令的等待时间就变短了

分支预测: 1) 静态预测、假装分支不发生. 分支预测失败的代价是: 丢弃已取出的指令&清空已使用的寄存器的操作
2) 动态分支预测:
a. 一级分支预测: 用1比特、记录当前分支的比较清空、来预测下一次分支时候的比较情况
b. 双模态预测器: 从2byte记录对应状态、提高预测的准确度

1
CPU的流水线设计里、会遇到eg. 指令依赖等的情况、使得下一条指令不能正确执行. 但是通过抢跑的方式、可以得到提升指令 吞吐率 的机会、流水线架构的CPU、是主动的冒险选择

流水线设计总结

1
2
3
4
5
6
7
8
9
为了不浪费CPU的性能、把指令执行的过程切分成一个个的流水线、来提升CPU的吞吐率, 而每增加一级的流水线、也会增加overhead、同样因为指令不是顺序执行

数据冒险和分支冒险: 通过插入nop来解决

nop空转: 通过乱序执行来解决

乱序执行: 是在指令执行阶段通过一个类似线程池的保留站、让系统之家动态调度先执行哪些指令、前提是不破坏数据依赖性. CPU只要等到在指令结果的最终提交阶段、再通过重排序的方式、确保指令是顺序执行的

超标量: (Superscalar) 和 多发射(Multi issue) 在同一时间把多条指令发射到不同的译码器

指令周期

1
2
3
4
5
计算机每执行一条指令的过程、可以分解为:
1. Fetch(取得指令), 也就是从PC寄存器找到对应的指令地址、根据指令地址从内存找到具体指令、加载到指令寄存器、然后将PC寄存器自增、好在未来执行下一条指令
2. Decode(指令译码): 根据指令寄存器里的指令、解析成将要进行什么样的操作、是R、I、J中的哪一种指令、具体要操作哪些寄存器、数据或者内存地址
3. Execute(执行指令), 也就是实际运行对应的R、I、J这些特定的指令、进行算术逻辑操作、数据传输或者直接的地址跳转
4. 重复1~3的步骤

Machine Cycle: 又称为机器周期或者CPU周期, CPU的内部操作速度很快、但是访问内存的速度却慢很多、每一条指令都从内存中加载而来、所以一般把从内存中读取一条指令的最短时间称为CPU周期

Clock Cycle: 也就是时钟周期及机器的主频、一个CPU周期通常是几个时钟周期的积累、一个CPU周期的时间、就是这几个时钟周期的总和

对于一个指令周期来说、取出一条指令、然后执行、至少需要两个CPU周期、取出指令至少需要一个CPU周期、执行也至少需要一个CPU周期、复杂的指令需要更多的CPU周期

so. 一个指令周期包含多个CPU周期、而一个CPU周期又包含多个时钟周期

建立数据通路

1
2
3
4
5
6
一般来说、数据通路就是处理器单元、通常有两类元件组成

1. 操作元件: 也叫组合逻辑单元、就是ALU、功能就是在特定的输入下、根据下面的组合电路的逻辑、生成特定的输出
2. 存储元件: 也叫状态原件, eg. 计算过程中需要用到的寄存器、无论是通用寄存器还是状态寄存器、其实都是存储原件

通过数据总线的方式、将操作元件和存储元件连接起来、就可以完成数据的存储、处理和传输了、这就是所谓的建立数据通路

控制器: 可以看成只是机械的重复 Fetch -> Decode -> Execute 循环中的前两个步骤、然后将最后一个步骤通过控制器产生的信号、交给ALU去处理

1
2
3
控制器电路听起来很简单、其实特别复杂,
1. 所有CPU支持的指令、都会在控制器里边被解析成不同的输出信号、eg. Intel CPU支持2000个以上的指令、意味着: 控制器输出的控制信号、至少有2000种不同的组合
控制器里的ALU和各种组合逻辑电路、可以认为是一个固定功能的电路、控制器翻译出来的、就是不同的控制信号、这些控制信号、告诉ALU去做不同的计算

CPU所需要的硬件电路

1
2
3
4
5
6
7
8
要想搭建CPU、需要再数字电路层面、实现一些功能.
一、ALU: 实际就是一个没有状态的、根据输入计算输出结果的一个电路
二、需要一个寄存器(可进行状态读写的电路元件)
需要一个电路(可存储上次计算的结果)、这个计算结果不一定立刻能拿到电路的下游使用、但可在需要的时候拿出来使用、常见能够进行状态读写的电路有: 锁存器(Latch) 和 D触发器(Data / Delay Flip-flop)
三、需要有一个`自动`的电路: 可按照固定的周期、不停地实现呢PC寄存器自增、自动的执行 Fetch-Decode-Execute的步骤
四、需要一个译码电路: 无论是decode还是对于拿到的内存地址去获对应的数据或者指令、都需要通过一个电路找到对应的数据、这个就是译码器电路

虽然CPU已经是有几十亿个晶体管组成的及其复杂的电路、但 仍然是由一个个的基本功能的电路组成的

时序逻辑电路

1
2
3
4
解决了以下问题:
1. 自动运行, 时序电路接通后可以不停的开启和关闭开关、进入下一个自动运行的状态、使得控制器不停的让PC寄存器自增读取下一条指令成为可能
2. 存储问题, 通过时序电路实现的触发器、可以把计算结果存储在特定的电路里、而不是像组合逻辑电路、一旦输入变化、对应的输出也会变化
3. 时序协调问题, 无论是程序实现的软件指令还是硬件层面、各种指令的操作都有先后的顺序要求、时序电路使得不同的事件按照时间顺序发生

时钟信号的硬件实现

1
2
时钟: CPU的祝您是又一个晶体振荡器实现的、而这个晶体振荡器生成的电路信号、就是时钟信号
反馈电路: 将电路的输出信号作为输入信号的电路构造方式、叫做反馈电路

通过D触发器实现存储功能

1
2
3
4
5
6
7
将R和S两个信号通过一个反相器合并、可以通过一个数据信号D进行Q的写入操作

只要CLK信号是1、R和S就可以设置输出Q、而当CLK信号是0的时候、无论R和S怎么设置、输出信号Q是不变的、这样、整个电路就成了我们最常用的D型触发器、用来控制R和S这两个开关的信号、可以视为一个输入的数据信号D、也就是Data、这就是D型触发器的由来

一个D型触发器、只能控制1bit的读写、若同时拿出多个D型触发器并列在一起、并且把用同一个CLK信号控制作为所有D型触发器的开关、就变成了一个N位的D型触发器、可以同时控制N位的读写

CPU中的寄存器可以直接通过D型触发器来构造、可以在D触发器的基础上、加上更多的开关、来实现清0或者全置位这样的操作

PC寄存器所需要的计数器

1
2
3
4
5
6
7
8
9
PC寄存器还有个名字叫程序计数器、为什么呢 ?
有了时钟信号、可以提供定时的输入、有了D型触发器、可以在时钟信号控制的时间点写入数据、将这两个功能组合起来、就可以实现一个自动的计数器了

加法器的两个输入、一个始终设置为1、另外一个个来自于一个D型触发器A、将加法器的输出结果、写到这个D触发器的A里边、于是 D型触发器里边的数据就会在固定的时钟信号为1的时候更新一次

加法计数、内存取值、乃至后边的命令执行、最终其实都是由时钟信号来控制执行时间点和先后顺序的、这也是需要时序电路最核心的原因

在最简单的情况下、需要让每一条指令、从程序计数器、到获取指令、执行指令、都在一个时钟周期内完成、若PC计数器增长太快、程序就会出错, 因为此时前一次运算的结果还没写回对应的寄存器
在这种情况下、需要在一个时钟周期里、确保执行完成一条最复杂的CPU指令、即: 耗时最长的一条CPU指令、这样的CPU设计、称为 单指令周期处理器

读写数据所需要的译码器

1
2
3
4
5
6
7
8
现在数据可以存储在D触发器里了、若将多个D触发器放在一起、就可以形成很大的一块存储空间、甚至可以当成一块内存来使用、
eg. 现在的电脑内存可能有8G、16G、那么怎么才能知道写入和读取的数据是这块大的内存里、哪几个bit呢 ?
于是: 我们需要一个电路来完成寻址的工作、就是 译码器
将寻址退化到最简单的模型、就是两个地址中选择一个、称为 2-1选择器、
若输入的信号有三个不同的开关、就可以从 2³=8中选择一个地址了、即3-8译码器
现代计算机是64位的、寻址空间是264次方、需要一个有64个开关的译码器

所以: 译码器的本质、就是从输出的多个位的信号中、根据一定的开关和电路组合、选择自己想要的信号、除了可以寻址、还可以将对应的需要运行的指令码同样通过译码器、找出我们期望执行的指令、也就是opcode及后边对应的操作数或者寄存器地址

因为从内存取指令的时间很长、若使用单指令周期处理器、意味着我们的指令都要去等待一些慢速操作、这些不同指令执行速度的差异、也正是计算机指令有指令周期、CPU周期和时钟周期之分的原因、因此, 优化现代CPU性能时、用的CPU都不是单指令周期处理器、而是通过流水线、分支预测等技术、来实现在一个周期里同时执行多个指令

年关、慵懒极了、想看看电视剧、读读小说、结束一年的忙忙碌碌.
看了庆余年、看评论提到了 陈情令、初播便得到很多好评的一部电视剧, 彼时未得闲暇、未曾顾及, 此时看到、不免心生欢喜.

回忆开头、未懂, 不明所以, 剧情渐入回忆, 我渐渐沉迷...
想以人物为主线、理理自己的思绪、乱七八糟、若不慎入眼、看看且过即可...

绵绵: 善良正直真性情的小女孩、也算是剧中最幸福的一个, 地位不高、
      首次出现在与金子轩一起去云深不知处听学时、那时结识魏无羡. 
      后在被温氏要挟、去不夜天听训时、被魏婴救过(挡下烙铁). 
      射日之征后, 魏婴被诬陷、绵绵挺身而出、直言 魏婴不是这样的人, 遂脱离金家氏族. 
      最后一场戏是魏婴、蓝忘机再次去乱葬岗时路过山野小屋、竟是绵绵与丈夫、还有一个乖巧的女儿、
      也叫绵绵, 只怕是绵绵无尽的思念也未可知~
      结局也算美好、在告别金家氏族、告别蓝忘机之后、远离了`正义`, 活成了自己喜欢的样子.
      戏份不多、确是活的最洒脱的女子.

薛洋: 作恶多端、却无法相恨, 他坏、但坏的明明白白, 恨不起来大概是因为他受过的那许多的苦.
      从小遭受了太多世界的冰冷、从未感受过暖、世界不曾教过他、又缘何怪他不懂 ?晓星尘给他点滴的爱、
      他就把晓星尘放在心头, 若他成长的道路上有阳光、他何尝不会是一个好孩子 ? 
      或许有很多朋友愿意说、魏婴也遭受了很多打击与诬陷, 区别就在于所有的遭遇处于人生哪个阶段罢了.
      三观构建完成、就有了判断力、忍耐力、有了思考. 偏偏薛洋在人生最关键的时候遭受的都是冰冷, 
      从未有一米阳光, 对这个很坏的孩子更多的是同情吧、愿下一世、有阳光普照、有温暖与美好、
      带着你的聪慧、做个阳光明媚的孩子、一如你喜欢的晓星尘...

晓星尘、宋岚: 明月清风晓星尘,傲雪凌霜宋子琛, 开场被他们深深吸引、可惜戏份不多, 再出现已难回首...
     愿悲情的你们、下一世真正的活成明月清风. 也愿彼此间多一些信任, 就如 蓝湛和魏婴 ~

魏婴: 魏无羡 (夷陵老祖)、年少时经过几年流离生活、后被江枫眠带回江家、收为弟子、初始、
      江澄不喜欢他、但孩童总是天真、很快就成为好友、又有阿姐的悉心照料, 生活也算幸福、自身天赋极高、
      很快成为同辈里的佼佼者、排名仅在姑苏双壁之下, 修为极高、性格跳脱、善良随性、师姐的话就是
      `阿羡大事从不胡闹`, 佩剑取名`随意`、可见一般、活脱脱已调皮任性的小公子、但为人极善良、
      很多细节、他应该是将师姐看作母亲一般、在她面前任性、一副小孩子的模样、惹得江澄嫉妒, 
      后在云深不知处结识蓝二公子、调侃他`披麻戴孝`、偏生又爱挑逗他、此后接下不解之缘, 后在玄武洞
      救下绵绵、还惹得蓝二公子一身醋意, 身受重伤的他、挑逗忘玑、他要听歌、忘玑创作 `忘羡`, 
      随后温氏不满、欲灭江氏一族、为救江澄、舍弃自身金丹, 后被丢入乱葬岗、创造阴虎符. 
      射日后、力保温情族人、与众人为敌; 后与温氏族人一起在乱葬岗生活了一段时间, 也就是在此时、
      温宁开始对他不离不弃. 后金子轩惨死、温宁温情姐弟不忍拖累他、然后去自首、被挫骨扬灰(后证实宁未死)、
      接近崩溃的魏婴孤身来到不夜天与众人对峙, 师姐又为挡剑而死、当场崩溃、心如死灰
      十六年后、魏婴重生、亦是蓝湛的重生, 那个问灵十三载的少年郎...
      后师姐问及笛子叫何名字、遂取`陈情`, 且不乱猜、各人心想吧; 从`随意`到`陈情`, 承载了多少苦难?
      还好本心善良的魏婴和蓝湛一起重生、蓝湛将内心疮痍的`夷陵老祖`又养成了一个`小可爱`, 
      会撒娇、会调侃、会躲在身后~~~. 他知道蓝湛哥哥一定会护他周全...
      愿来生、还是那个偏偏白衣少年郎~~~

江澄: 江晚吟, 背负太多家族使命, 性格里有很多怯懦、也有许多坚强、从小被魏婴的光环笼罩、
       内心好强、想要胜过魏婴、却处处比不过、但也是从心底里心疼魏婴, 在乱葬岗为了家族、
      一剑刺在乱石上、这一剑、又何尝不是救了魏婴 ? 若不是他、必是别人、那时候怕刺的不再是石头...
      莲花坞被侵占时、身心俱碎、发现外出为姐姐买药的魏婴将要被温氏发现时、挺身而出、
      让自己成为目标, 后被化丹手化去金丹, 一心好强的他、痛不欲生 ...
      这些何尝不是坚强?何尝不是关爱?有时对魏婴的挑剔怕是来源于亲昵的任性、心底的依赖...
      在穷奇道、魏婴就走温清族人、在乱葬岗下、魏婴带着温情族人一起生活、他气愤, 又怎么不是
      来自于想要被魏婴保护的渴望 ? 姐姐虽对他们万般宠爱、终究帮不了太多、一个十几岁的孩子
      突然成为偌大的江氏家主、心中不免惶恐, 重建莲花坞、保护家族的重任就落在了他的肩上、
      注定不能成为魏婴一般潇潇洒洒的人....
      十六年后、与魏婴重聚、他生气、为何不先来找他、也是源自心底那份情吧? 有些话说得不合适、
      但难掩心中的善、不是如同魏婴那般孤注一掷的善、彻彻底底的善、因为背负了太多的使命吧.....
      愿你来生、只做个任性的小公子、哎、你又这么好强、该如何是好?

江厌离: 暖情小姐姐、若得此姐、此生足矣. 在虞紫鸢强势的性格下居然能长出如此温婉的性格、着实不易、
       从小就保护阿羡、阿澄, 给他们煲汤、调节他们的不愉快. 无论是师姐的身份还是姐姐的身份, 
       他都是合格的、江澄和魏婴对她也都是依赖的, 在去往云深不知处时、偶遇未来夫婿金子轩, 
       开始喜欢他, 但此时子轩并不喜欢厌离、一番波折、总算是相互欢喜, 不料、在孩子满月酒宴时, 
       由于金光瑶捣乱、苏涉乱吹笛、导致温宁失控、误杀金子轩. 暖心的姐姐并没有恨魏婴、她相信他
       是发自心底的信任, 一刻也不曾动摇. 在她心里、羡羡和阿澄是最好的弟弟. 有次夜猎、
       魏婴被金家人侮辱`家仆之子`, 厌离挺身而出、一改柔弱、说`他是我弟弟、不是外人`, 要求对方道歉. 
       愿来生, 所爱之人携手白头、疼爱的弟弟膝下承欢, 愿小小心愿都能称心如意...

蓝湛: 蓝忘机, 含光君, 也是剧中称呼最多的一个(都是羡羡给的~~~), 有很多坎坷、也有很多甜美、
      雅正端方、严于律己、逢乱必出...
      遇到魏婴之前、清冷如冰, 遇见之后慢慢的因为知己、后深不可离...
      从最初的高傲冰冷变得调皮、玄武洞内调侃魏婴的情节、大概暖到了很多人. 魏婴帮他包扎伤口、
      上凝血草时、他突然抓住魏婴的手、将凝血草敷在了救绵绵时留下的烫伤上、顺便来了一句: 不用谢、哈哈~
      羡羡救绵绵、恐怕含光君还吃了一潭子醋、正经答道 `你要是没那个意思、不要随便撩拨别人`
      没忍住笑喷了, 含光君居然会这么可爱~, 一本正经的可爱这么好玩儿~~~
      他们也经历了很多的磨难、玄武洞、不夜城、穷奇道、乱葬岗...
      心一点点的被揪着, 不敢放松. 
      魏婴出事后、第一次违反家规、被罚3年禁闭、出来之后、无时不在寻找. 问灵十三载、等一不归人
      大部分剧友都觉得刻骨难忘吧 ?待到被问时、只轻轻一句、我有悔, 不夜天没有和你站在一起. 
      十六年里、受过魏无羡受的伤, 喝过魏无羡喝过的酒, 种魏无羡种过的思追, 气魏无羡气过的蓝启仁, 
      违反过魏无羡违反的家规, 将阿苑带回云深不知处、取字思追(思君不可追), 将思追养成了魏无羡、
      一样善良肆意洒脱、敢于做自己, 一样的善良热血、将破魔咒珍藏了16年.
      十六年后、魏婴复生、成为魏婴最真切的守护神、凭借`忘羡` 立即认出魏婴、此后对魏婴可谓无微不至的
      关怀、不允许别人对他丝毫的伤害、魏婴怕狗、躲在忘玑身后的情节也是温暖了很多剧友~~~, 
      醉酒抓鸡的情节也是深入人心. 魏婴被欺负时、他对哥哥说、我想带一人回云深不知处、藏起来..., 
     被人戏称`护妻狂魔`,^.^~
      愿来生, 温润如玉、有母爱陪伴、多些笑容、早遇知己, 愿给你所有的美好


蓝涣: 蓝曦臣, 泽芜君. 与弟弟含光君一起并称`姑苏双壁`, 清煦温雅、永远都是那么善良、那么温柔、理智
      修为极高、从不欺负弱小、若他称谦谦公子第二、怕是没人敢居第一吧?整部剧除了两个情节失态、
      一直是温润如玉、长者之风、十分疼爱弟弟忘玑、初见魏婴、觉得适合做弟弟的朋友、便各种暗示
      可以猜透忘玑心中所想, 魏婴身死魂消后、一直陪伴在侧. 道出忘玑身上戒鞭痕来历、
      又在云梦观音庙吐露忘玑对魏婴的13年苦守、促使二人心意相通
      一直拿金光瑶当成最好的朋友、聂明玦和金光瑶关系不和、他居中调节、魏婴回归后、发现金光瑶
      所做的事情、他不愿相信、对忘玑说: 你相信魏婴、我相信阿瑶、他是你的知己、阿瑶也是我的知己(非原话). 
      又将随意出入云深不知处的令牌给金光瑶, 可见信任非同一般. 可惜、金光瑶却真实的死在了他手上、
      恐怕心中的痛无人能晓吧 ?愿有人将你温暖、替你扛下一些使命...
      之前温氏毁掉云深不知处、打伤他最疼爱的弟弟、害他狼狈出逃、在清缴温氏时、
      他依然冷静的将温情与温家分开、仗义执言、不迁怒于人、难能可贵.
      借言百度百科:
      1. 蓝曦臣沉吟道:"这位温情的大名我知晓几分,似乎没听说她参与过射日之征中任何一场凶案的。"
      聂明玦道:"可她也没有阻拦过. "
      蓝曦臣道:"温情是温若寒的亲信之一,如何能阻拦?"
      2. 聂明玦道:"英魂长存" 蓝曦臣道:"愿安息. "
      他的心底相信魏婴做的不算冤枉吧?只是敌人、仇人死了、他依然愿意安静的道一声"愿安息".
      来生、愿你仍为翩翩君子、温润如玉、愿你有属于自己的幸福、有人疼、有人爱、不用一人担尽天下心.

蓝思追: 阿苑, 从小漂泊、经历不夜城被毁、同族人一起经历不幸、后被魏婴救出、带领他们生活在乱葬岗下.
    还好、余生皆是暖情. 小小年纪、还不知凄苦悲凉、有族人陪伴、魏婴逗玩、在乱葬岗依然生活的快乐、
    被魏婴当成萝卜种在地里的情节好暖心, 后被蓝二哥哥效仿羡羡当成小兔子种、哈哈哈、好温暖~~~
    小时候称蓝二哥哥有钱哥哥、喜欢抱大腿儿(~.~), 魏婴出事后、被蓝二哥哥悄悄收养、改名 蓝苑、字思追. 
    在蓝湛的培养下、变成一个谦谦君子、又糅合了魏婴的灵动、善良、恣意洒脱、挺招人喜欢的, 初见鬼将军很怕、
    但不让人伤害他, 初见十六年后的魏婴、不让别人欺负他、善良的孩子~~~
    愿来生、满满的岁月静好 ....

温晁: 温若寒小公子, 嚣张跋扈、高傲自大、行径恶劣. 喜欢显摆、因为不满江家不服从温家的指令,
      所以找了个借口把江家灭门。魏无羡和江澄虽然有幸逃过一命,但是江澄的金丹被温逐流炼化,
      彻底丢失了灵力。而魏无羡在把金丹给了江澄后,被温晁丢进乱葬岗里
      剧中、我真心找不到他的一点儿善良...
      就算在最后、都还在斥责温逐流、彻彻底底很可恶的一个人, 愿来生、学会善良、读懂温暖...

温情: 岐山温氏人、一个旁支、高傲能干的妙手神医, 内心善良. 倾尽全力的保护族人、保护弟弟, 
     不夜天听训时、被温若寒恐吓、为了弟弟安危、告诉弟弟不要乱跑、不要和魏婴混在一起,  自己却逮着机会就帮助魏婴,
     后莲花坞被温氏侵占、江枫眠和虞紫鸢被害、收留江家姐弟并为他们疗伤, 又帮助魏婴实现换丹. 
     最悲情的角色、天性善良、不作恶、却是剧中唯一被挫骨扬灰的一个...
     来生、愿安好、与自己的族人过着静美的小日子 ~~~

温宁: 暖心的小天使、从小被抽去一部分灵识、显得呆呆傻傻, 因魏婴在云深不知处赏识、
      内心孤僻的温宁从此感恩、在不夜城救过魏婴、在莲花坞被毁时帮过魏婴, 射日后、魏婴在穷奇道救下
      温宁及其族人、后成为`鬼将军`(傀儡)但保留了意识、功力大增、听魏婴吹笛杀人、后被金光瑶控制
      十六年后重逢与魏婴重逢、拔出他头部的铁钉、更加只听魏婴一个人的、称他为`公子`. 
      整部剧一直感觉他是一个温暖的小天使, 呆萌可爱 ...
      愿来生, 他可以生的美好、做一个暖情的小公子, 弥补此生的孤独.
      愿来生只做白衣少年温琼林...

金子轩: 金家公子、为人高傲、但心底善良、敢于追求内心的爱、人物着墨不多. 来生, 愿幸福~~~

金光瑶: 人物性格比较复杂、表面温和、内心阴暗、但真心帮助蓝曦臣. 剧里做了很多坏事、最终也只是
        为他人做嫁衣、母亲虽为艺伎、不多的着笔、却让人难忘、是一个温和、知书识礼的好母亲
       后来利用蓝曦臣(清心音)设计杀害聂明玦、并做成凶尸. 为了得到秦仓业支持、娶同父异母
       妹妹秦愫、又为免受非议、杀死亲生子金如松. 为了夺取金家地位、杀死父亲金光善. 与薛洋一般、
       是一个缺少温暖的孩子、内心有太多的恨与恶、千般不该、设计杀死聂明玦、那个曾经给他支持、
       拿他当亲兄弟对待的人. 就算知道他放走薛洋、也只是把他赶出去、并未伤害他、这样的人都伤害、
       心中有个无法原谅的坎儿....
       来生、愿其母觅得良人、给他一个温暖的家, 愿多些温暖、少些心中阴霾. 

聂怀桑: 初时、同魏婴一般、算是志趣相投. 修为不高、成为家主之后被称为: 三不知、然而看似柔弱无害
       的他, 确是藏的最深的一个、剧中有很多伏笔, 比如: 十六年后说书人说到夷陵老祖时的回头一看
       大梵山假的舞天女重生
       温家墓前的扫墓老丈其实是聂怀桑...
       从魏婴十六年后重生到后续的情节、由他一人策划、只为复兄仇, 算不
       得坏、不是大奸大恶之人、亦无泽芜君的容人之量、终究是被内心的情感左右、算不得对、算不得错、
       好在、此后任逍遥~~~, 也没什么野心、亦真心喜欢和魏婴做朋友.
       来生、愿有哥哥一生庇佑、只做恣意山水的快活少年.

好多情节、令人禁不住落泪、或是真情流露、或是温暖荡漾、或者是悲情感慨, 人物是复杂的、性格是综合的、确也更符合现实的人性吧. 世上人心终是横看成火、侧看成冰. 本着不讨论的原则写文、可能还是注入了不少自己的感情、魏婴和蓝湛的爱情也实在太感人了耶、^*^ 、闲来无事可以看看~

附图册:
绵绵.png
温情.png
江厌离..png

羡羡和忘玑.png

羡羡和忘玑.png

羡羡和忘玑.png

蓝曦臣.png

魏婴和聂怀桑.png

晓星尘.png

基本标准

  1. 开源: 遇到有问题的bug可以通过修改源码的方式来修复或者规避、而不是只能等待下一个官方版本发版来修复
  2. 产品社区有一定活跃度: 遇到问题的时候、可以快速找到类似场景、快速修复, 相比于每一个bug都需要自己通过阅读源码来解决、毕竟线上问题挂着的的成本很高
    社区活跃度较高的产品、与周边应用的兼容性也会比较好、省去很多造轮子的时间
  3. 性能要求:
    1) 确保消息可靠传递、不丢失消息
    2) 支持集群、确保某个节点宕掉不会影响整个服务
    3) 性能、能满足大部分实际需求场景的需要

目前可选方案

RabbitMQ
1
2
3
4
5
6
7
8
9
源码实现 : Erlang
特点: 轻量级、性能好、方便部署和使用、维护简单
支持灵活的路由配置、相对其它MQ、在Producer和Queue之间增加Exchange模块、可根据配置规则将生产者发出的Msg发送到不同队列
Client支持多种语言

短处:
1. 消息堆积处理支持不好、消息大量堆积时会导致MQ的性能急速下降
2. 处理消息的性能比较一般、根据硬件配置不同、每秒大概可以处理的消息数量为: 几w ~ 十几w
3. 源码语言为Erlang、比较小众、扩展开发成本增加
RocketMQ
1
2
3
4
5
2012年阿里开源出来的产品、17年交给apache维护
源码: Java
性能: 稳定性、可靠性和性能都不错、对响应延迟也做了优化、大多数情况可以做到ms级的响应、每秒可处理消息 几十万(性能比RabbitMQ高一个数量级)

短处: 相比国外同类产品、与周边生态系统的集成度和兼容性稍低
Kafka
1
2
3
4
5
6
7
8
9
10
11
12
13
最初设计是为了处理海量日志、早期版本不保证消息可靠性、也不支持集群、但对于处理海量日志这个诉求是比较好的选择

近期版本、在数据可靠性、稳定性和功能特性上都可以满足大多数的场景需求

源码实现: Java + Scala
优势:
1. 与周边生态系统的兼容性最好、尤其是大数据和流计算领域、几乎所有相关开源软件都优先支持Kafka
2. 异步收发性能最好、在服务器配置高时、极限情况每秒可处理2000w消息

短处:
异步批量处理带来的问题是: 同步收发响应延时较高、它是攒一批然后批量发送、每秒消息数没那么高的时候、kafka的延时反而会高、

不适合在线业务场景

第二梯队mq

ZeroMQ

严格来说不能算是消息队列、而是一个基于消息队列的多线程网络库
如果需求是将消息队列的功能集成到系统进程中、可以考虑ZeroMQ

ActiveMQ

社区已经不活跃、进入了老年期

使用 hexo 框架搭建网站

先搭建基础环境
1
2
3
brew install node // 安装node
node -v // 检查npm是否安装成功
npm -v // 查看npm版本
安装hexo
1
2
3
4
5
6
npm install hexo-cli-g // 安装hexo
hexo init xxx // 初始化hexo站点目录
cd xxx // 进入目录
hexo clean
hexo g // 生成站点, g -> generate
hexo s // 运行网站, s -> server

此时、本地网站已经搭建完成, 访问 localhost:4000 即可.

配置文件 _config.yml
1
2
3
4
5
6
7
8
title: 牛牛的Blog
subtitle: 不奢望岁月静好 只希望点滴积累
description: '多数是学习笔记、偶尔发发感慨、给生活添加些许乐趣'
keywords: Java, Mysql, Linux
author: niuniu
language: zh
timezone: ''
theme: next // 定义主题
更换主题
1
2
3
4
下载: https://hexo.io/themes/ 
theme: next // 定义主题 _config.yml
hexo g // 重新生成站点
hexo s // 重新运行站点
分类和标签
1
2
hexo new file-name (file-name是你的文章名, 也可以直接在 spurce/_post 文件夹下新建md文件)
hexo new page tags

打开 source/tags/index.md、修改为:

1
2
type: "tags"
comments: flase

1
hexo new page categories // 打开分类功能

打开 source/categories/index.md、修改为:

1
2
type: categories
comments: false

使用:

1
2
3
4
5
6
7
8
 在文章头部添加
tags:
- tag1
- tag2
- tag3
categories:
- cat1
- cat2

修改hexo侧边栏标签
1
2
3
4
5
6
7
8
9
10
11
menu:
home: / || home
about: /about/ || user
tags: /tags/ || tags
categories: /categories/ || th
archives: /archives/ || archive
MySQL: /categories/MySQL || database
Redis: /categories/Redis || database
#schedule: /schedule/ || calendar
#sitemap: /sitemap.xml || sitemap
#commonweal: /404/ || heartbeat

注意: :前边展示的是侧边栏显示名称
/xxx/ 展示的是分类名称
|| 后边是图标
图标从 fontawesome.com 获取

将链接修改为蓝色

打开themes/next/source/css/_common/components/post/post.styl 文件, 将下面的代码复制到文件最后

1
2
3
4
5
6
7
8
.post-body p a{
color: #0593d3;
border-bottom: none;
&:hover {
color: #0477ab;
text-decoration: underline;
}
}
修改代码块样式

打开 themes/next/_config.yml, 搜索 custom_file_path, 去掉 style 前边的注释, 然后新建或者打开 {blog_root}/source/_date/styles.styl 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
code {
color: #D1082D;
background: #E8E5E6;
margin: 2px;
// font-size: 14px; // 设置你的字体大小
// font-family: Source Corde Pro; // 设置你喜欢的字体
}
// 大代码块的自定义样式
.highlight, pre {
margin: 5px 0;
padding: 5px;
border-radius: 3px;
}
.highlight, code, pre {
border: 1px solid #d6d6d6;
}

使用valine评论系统

新版本的 https://valine.js.org/ 支持npm安装

1
2
3
4
# Install leancloud's js-sdk
npm install leancloud-storage --save
# Install valine
npm install valine --save

新的版本next主题已经默认支持, 需要注册leanCloud账号 https://leancloud.cn/, 获取appIdappKey, 然后修改配置文件 **themes/hexo-theme-next/_config.yml**, 搜索 valine, 修改配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Valine.
# You can get your appid and appkey from https://leancloud.cn
# more info please open https://valine.js.org
valine:
enable: false
appid: # your leancloud application appid
appkey: # your leancloud application appkey
notify: false # mail notifier , https://github.com/xCss/Valine/wiki
verify: false # Verification code
placeholder: Just go go # comment box placeholder
avatar: mm # gravatar style
guest_info: nick,mail,link # custom comment header
pageSize: 10 # pagination size

enable 改为 true, 将刚刚申请的 appIdappKey 填入,

leancloud appId申请请参考: https://valine.js.org/quickstart.html

评论头像显示
  1. 注册 gravatar, valine 默认使用 gravatar的头像管理

  2. 进行评论, 注意要在评论时填写自己注册后绑定的邮箱、才会显示头像

    如下图所示

image-20200322112148688

生成一个新的ssh key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
nj:~ nj$ cd ~/.ssh
nj:.ssh nj$ ls
id_rsa id_rsa.pub known_hosts
nj:.ssh nj$ ssh-keygen -t rsa -C "iMac_personnal_publicKey"
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/nj/.ssh/id_rsa):
/Users/nj/.ssh/id_rsa_personal
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/nj/.ssh/id_rsa_personal.
Your public key has been saved in /Users/nj/.ssh/id_rsa_personal.pub.
The key fingerprint is:
SHA256:1gepuxDHwJRnFbKvc0Zq/NGrFGE9kEXS06jxatPPrSQ iMac_personnal_publicKey
The key's randomart image is:
+---[RSA 2048]----+
| ....=*oo |
| o. ooo=+ . |
| oo. =+o. |
| o =.o.. |
| . S =o. |
| = =++. |
| . B.=.Eo.. |
| o B . +o .|
| . o.. .. |
+----[SHA256]-----+
njdeiMac:.ssh nj$ ls
id_rsa id_rsa_personal known_hosts
id_rsa.pub id_rsa_personal.pub`
  1. 打开新生村的 ~/.ssh/id_rsa_personal.pub 文件、将内容添加到 github 后台
  2. 打开 ~/.ssh/config 文件
    1
    2
    3
    4
    Host xxx //你的host别名
    HostName github.com
    User yyy //github的注册用户名
    IdentityFile ~/.ssh/id_rsa_personal
  3. 将github ssh仓库中的 git@github.com 替换成新建的Host别名
    1
    2
    eg. git@github.com:hbxn740150254/BestoneGitHub.git
    -> git@xxx:hbxn740150254/BestoneGitHub.git