This repository has been archived by the owner on Aug 9, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
678 lines (648 loc) · 71.8 KB
/
atom.xml
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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://mengxiaoxu.github.io/</id>
<title>刘小绪同学的博客</title>
<updated>2020-09-12T14:16:46.546Z</updated>
<generator>https://github.com/jpmonette/feed</generator>
<link rel="alternate" href="https://mengxiaoxu.github.io/"/>
<link rel="self" href="https://mengxiaoxu.github.io/atom.xml"/>
<subtitle>正在学习写代码的码农</subtitle>
<logo>https://mengxiaoxu.github.io/images/avatar.png</logo>
<icon>https://mengxiaoxu.github.io/favicon.ico</icon>
<rights>All rights reserved 2020, 刘小绪同学的博客</rights>
<entry>
<title type="html"><![CDATA[拼多多(淘宝)空手套利案例分享]]></title>
<id>https://mengxiaoxu.github.io/post/dian-shang-ling-yu-tao-li-an-li/</id>
<link href="https://mengxiaoxu.github.io/post/dian-shang-ling-yu-tao-li-an-li/">
</link>
<updated>2020-08-07T15:45:50.000Z</updated>
<content type="html"><![CDATA[<p>虽然拼多多已经逐渐被一些人接受了,但还是有很多人都认为拼多多上面卖的都是假货,拼多多自己也搞了「百亿补贴」活动,最简单的套利方式就是赚差价,拼多多百亿补贴专场中的产品都是正品,比如 Airpods pro 苹果官网卖的是 1999 元,而拼多多百亿补贴专场卖的价格不到 1500,完全可以转手卖给朋友或者在闲鱼这些二手交易平台买卖套利。</p>
<p>疫情原因导致很多人都在家赋闲,每年 6 月高考过后的孩子都特别想去尝试挣钱的感觉,那么开网店是一个不错的选择,大多数人都会想我没有货源卖什么产品呢?淘宝看起来好复杂啊,我不会开店怎么办?其实这些知识只需要使用百度搜索一下就能找到,连一点点高级的搜索技巧都不需要,但是就是有太多想挣钱却又懒得去主动学习的人。</p>
<p>你只需要自己花几个小时去学习一下怎么开网店,再花一两天时间学习一下怎么去装修店铺,帮助上面说的那种人去开一个网店,或者告诉他注册一个淘宝店的方法,价钱由你自己决定收多少合适,简单的几步操作收几百块钱完全没有问题。一个很重要的点没有说到,这种流量从哪里去找?我这里以闲鱼平台举个例子。</p>
<figure data-type="image" tabindex="1"><img src="https://mengxiaoxu.github.io//post-images/1596728420739.jpeg" alt="" loading="lazy"></figure>
<p>图上我用了两种方式去引流,第一种是随便做一张海报突出代开网店、网店教学等关键词就行,这种方式相对来的直接一些,来咨询的人都是可以直接要联系方式进入下一个环节的;第二种是在淘宝搜了一些关于网店运营类的书籍图片放上去,以卖二手书的名义去获取流量,这种方式没有第一种方式精准,转化难度相对第一种方式也更难一些,配合上一些话术也是可以去引导的。其他平台类似就没不说了。</p>
<p>开了网店之后肯定是能卖出去东西才能获得收益, 一个新的店铺如何才能被平台曝光到更多买家的眼中呢?无非就是把店铺各项基础数据做好,增加店铺在平台的权重以获取更多的展示,商品的标题、主图、详情图、商品的销量、商品的评价、店铺的收藏数等等都是基础数据。当然最简单直接的方式就是烧钱开直通车,但是大多数店主都烧不起直通车。</p>
<p>抓住任何一个基础数据去做文章都是可以赚到钱的。就拿销量来说吧,大多数买家在各个平台购买产品时都会关注它,群众的眼睛是雪亮的,大家都在买的东西一般不会太差。比如我下面放的这张截图显示这双鞋已经卖出去 2207 件了,看起来销量不错值得信赖,大致看一下买家对商品的评价基本就会下单了,男生买东西大多数是这个流程。但是我告诉你这里面的所有数据都是可以作弊的。大家都知道刷单可以修改销量,但是刷单的成本很高,刷一单要 10 多块钱没多少商家扛得住。</p>
<figure data-type="image" tabindex="2"><img src="https://mengxiaoxu.github.io//post-images/1596724832415.jpeg" alt="" loading="lazy"></figure>
<p>在拼多多平台还有另外一种成本极低的修改销量模式,利用「拼多多商家后台」和「多多进宝」可以花几块钱就把销量改到上千,不到一百块钱销量就可以改到一万,而且这个修改销量的方法利用搜索引擎就能搜索到。改一个销量收商家一块钱他不会觉得贵,前文介绍帮助别人代开网店的那部分小白客户,他们就有改销量的需求。像店铺收藏数也是可以通过非常低的成本去实现数据的改动。</p>
<p>由于市场上还有很多公司使用这种方式在获利,具体改销量的操作步骤我就不详细描述了,原理就是通过设置优惠券的方式去购买产品,轻轻松松就能把销量改到几千上万,但是需要注意的一点是一天修改的数量别超过 3000,不然很容易被官方封店铺,别销量没有该成功反倒把人家店铺搞封了就尴尬了。</p>
<p>更多的流量应该从哪里来呢?拼多多平台买了东西之后就可以看到商家的电话,直接找那些销量低的商品打电话沟通就行了,拿到商家电话之后选择退款自己不花一分钱。不过前段时间拼多多改版,买拼多多商品不会再显示商家的电话了,因此这一条路暂时行不通了,只是作为一个介绍参考吧。</p>
<p>还有一种方式去获取这些流量,拼多多商家社区里面专门分出来了一个「新手社区」,一些新的卖家会在里面发帖子,帖子的发布者显示的是商家店铺,而且你可以通过商家发的帖子知道商家的大体需求,直接拼多多搜索店铺名与里面的客服沟通即可,当然其他像知乎、抖音一类的平台也能获取到精准流量,但是相比之下还是这里提供的两种方式来的直接一些。</p>
<p>前面提了一句店铺可能会被官方封禁,最近在网上又出现了一个零成本套利的方法,帮那些店铺已经被封了的人解封店铺,操作简单且不复杂,比如随便在百度搜素了一下就出现了一篇帖子,<a href="https://mh-studio.cn/share/113.html">强制开通第二个淘宝店铺教程(可解封)</a>里面大致给了个方向如何解封店铺,那些被官方永久封禁的店铺都是能解封的,据我所知解封一个店铺收的服务费是 2000RMB。</p>
<p>最后说一点像我前面提到的修改拼多多产品的销量,以及刚刚说完的帮助解封淘宝店铺,这些是阿里(拼多多)官方不知道的事情吗?这些很明显是官方故意留的后门,市面上有很多利用这种后门去赚钱的公司,咱们需要锻炼自己的眼睛,让它足够锐利的去发现这些后门获利的方向。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[查询性能优化]]></title>
<id>https://mengxiaoxu.github.io/post/cha-xun-xing-neng-you-hua/</id>
<link href="https://mengxiaoxu.github.io/post/cha-xun-xing-neng-you-hua/">
</link>
<updated>2020-03-15T11:19:34.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>参考内容:<br>
《高性能 MySQL(第三版))》</p>
</blockquote>
<p>在 <a href="https://mengxiaoxu.github.io/post/schema-yu-shu-ju-lei-xing-you-hua/">Schema 与数据类型优化</a>、<a href="https://mengxiaoxu.github.io/post/mysql-zhong-de-suo-yin">MySQL 中的索引</a>和<a href="https://mengxiaoxu.github.io/post/gao-xing-neng-suo-yin-ce-lue">高性能索引策略</a>中已经介绍了如何设计最优的库表结构、如何建立最好的索引,这些对于高性能来说还是必不可少的,但这些依旧还不够,如果查询写的糟糕,那么再好的库表结构、索引再合适,也无法实现高性能。</p>
<p>本文将从查询设计的一些基本原则开始介绍一些查询优化技巧,尽可能的去发挥 MySQL 真正的优势,并且避开它的弱点。</p>
<h2 id="为什么查询速度会慢">为什么查询速度会慢</h2>
<p>如果把一次查询看做一个任务,那么它则是由一系列更小的子任务构成,每个子任务都会消耗一定的时间,速度慢无非是其中一些子任务拖累了整个任务的进度,或者是执行了一些不必要的子任务。</p>
<p>MySQL 在执行查询时有哪些子任务?哪些子任务运行的时间很慢?这里很难给出完整的列表。完成这些子任务的时候,查询需要在不同的地方花费时间,比如网络、CPU 计算、生成执行计划、锁(互斥)等待等等操作。而这些操作又需要调用多少又会需要内存、CPU 操作、内存不足时导致的 I/O 操作、上下文切换和系统调用等等。</p>
<p>上面只列了一个查询生命周期的部分,我们要做的就是了解查询的生命周期,清楚查询的时间消耗情况,知道了这些才能帮助我们大大优化查询速度。</p>
<h2 id="优化数据访问">优化数据访问</h2>
<p>查询性能低下最基本的原因是访问的数据太多,除了一些不可避免的需要筛选大量数据的查询外,大部分性能低下的查询都可以通过减少访问的数据量进行优化。一般可以考虑下面两个因素:</p>
<ol>
<li>应用程序是否存在检索大量的超过需要的数据?一般意味着访问了太多的行或者太多的列;</li>
<li>MySQL 服务器层是否存在分析大量超过需要的数据行。</li>
</ol>
<p>有一些查询会请求超过实际需要的数据,然后这些多余的数据又会被应用程序丢弃,这样会给 MySQL 服务器带来额外的负担,同时增加网络、内存、CPU 的开销。典型案例比如:查询不需要的记录;多表关联时返回全部的列;总是取出全部列;重复查询相同的数据。</p>
<p>我们以<code>SELECT * FROM film_actor WHERE film_id = 1</code>这个查询来简要说明「MySQL 为了返回结果是否需要扫描额外的记录」。在有<code>film_id</code>列有索引的时候,通过<code>EXPLAIN</code>会看到使用的访问类型是<code>ref</code>,并且只需要扫描 10 行数据。而如果将索引去掉,则访问类型变成了<code>ALL</code>,需要扫描全表数据。</p>
<p>如果发现需要扫描大量数据但只返回少数的行,我们一般可以通过三个技巧来优化。第一是使用索引覆盖扫描,把所有需要用的列都放到索引中,这样存储引擎无须回表取对应行就可以返回结果了;第二是改变库表结构,比如使用单独的汇总表,这一点在 <a href="https://mengxiaoxu.github.io/post/schema-yu-shu-ju-lei-xing-you-hua/">Schema 与数据类型优化</a>中相关介绍;第三是重写这个复杂的查询,让 MySQL 优化器能够以更优化的方式执行这个查询。</p>
<h2 id="重构查询的方式">重构查询的方式</h2>
<p>上文已经提到了通过重写查询来提高性能,那么我们可以用哪些技巧来重构查询呢?这里我们需要明白的是,查询的目标找到一个更优的方法获得实际需要的结果,而不一定总是需要从 MySQL 中获得一模一样的结果集。</p>
<p>在设计查询的时候需要考虑的一个重要问题是:是否需要将一个复杂的查询分成多个简单的查询。因为网络通信、查询解析和优化是代价高昂的事情,所以在传统实现中总是强调让数据库完成尽可能多的工作。但是这个法则不适用于 MySQL,因为 MySQL 从设计上就让连接和断开连接很轻量级,而且现在的网络基础设施也更完善,带宽、时延等等都已经变得不再那么重要。</p>
<p>有时候将一个大的查询分解为多个小查询是很有必要的,当然如果一个查询能够胜任时写成多个独立的查询还是不明智的,是否需要拆分需要自己衡量各方面的因素。下面举一个切分查询的例子。</p>
<p>在一些应用中需要定期的删除旧数据,如果一次使用一个大的语句一次性完成的话,则可能需要一次锁住很多数据、沾满整个事务日志、耗尽系统资源、阻塞许多很小但重要的查询。比如<code>DELETE FROM messages WHERE created < DATE_SUB(NOW(), INTERVAL 3 MONTH)</code>,这个查询会消耗很多的资源,而如果我们将它拆分为很多个小的查询,这样就能将服务器上原本一次性的压力分散到一个很长的时间段中,这样就可以大大降低对服务器的影响,也可以大大减少删除时锁的持有时间。如下所示:</p>
<pre><code class="language-sql">rows_affected = 0
do {
rows_affected = do_query(
"DELETE FROM messages WHERE created < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 10000")
} while rows_affected > 0
</code></pre>
<p>当然我们还可以通过分解关联查询的方法来重构查询,比如下文代码块中的例子乍一看好像没什么好处,实际上它的优势可不少。</p>
<ol>
<li>让缓存的效率更高。因为许多应用程序可以方便的缓存单表查询对应的结果对象;</li>
<li>将查询分解后,执行单个查询可以减少锁的竞争;</li>
<li>在应用层做关联,可以更容易对数据库进行拆分,更容易做到高性能和可扩展;</li>
<li>可以减少冗余记录的查询。在应用程序做关联意味着对应某条记录应用只需要查询一次,而在数据库访问则可能需要重复访问;</li>
<li>查询本身效率也可能会提升。比如例子中使用 IN() 代替关联查询,可以让 MySQL 按照 ID 顺序进行查询;</li>
<li>更进一步,这样做相当于在应用中实现了哈希关联,而不是 MySQL 的嵌套循环关联。</li>
</ol>
<pre><code class="language-sql">SELECT * FROM tag
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag = 'mysql'
# 可以将上面的查询分解为下面的小查询
SELECT * FROM tag WHERE tag = 'mysql';
SELECT * FROM tag_post WHERE tag_id = 1234;
SELECT * FROM post WHERE id in (123, 456, 567, 9098, 8904)
</code></pre>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[高性能索引策略]]></title>
<id>https://mengxiaoxu.github.io/post/gao-xing-neng-suo-yin-ce-lue/</id>
<link href="https://mengxiaoxu.github.io/post/gao-xing-neng-suo-yin-ce-lue/">
</link>
<updated>2020-03-15T11:17:10.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>参考内容:<br>
《高性能 MySQL(第三版))》</p>
</blockquote>
<p>在 <a href="https://mengxiaoxu.github.io/post/mysql-zhong-de-suo-yin/">MySQL 中的索引</a>中已经介绍了常见的两种索引类型和索引的优缺点。高效的选择和使用索引有很多种方式,下面介绍几种常见的方法。</p>
<h2 id="独立的列">独立的列</h2>
<p>如果查询中的列不是独立的,则 MySQL 就不会使用索引,这里「独立的列」是指索引列不能是表达式的一部分,也不能是函数的参数。比如<code>SELECT actor_id FROM actor WHERE actor_id + 1 = 5</code>,凭肉眼都能看出来 WHERE 中的表达式其实等价于<code>actor_id = 4</code>,但是 MySQL 却无法自动解析这个表达式。因此我们应该养成简化 WHERE 条件的习惯,始终将索引列单独放在比较符号的一侧。</p>
<h2 id="多列索引">多列索引</h2>
<p>一些所谓的专家建议把 <s>WHERE 条件里面的列都建上索引</s>,实际上这个建议是非常错误的,因为这样一来最好的情况下也只能是「一星」索引,其性能比起真正的最优的索引可能差几个数量级。</p>
<blockquote>
<p>索引将相关的记录放到一起则获得「一星」;如果索引中的数据顺序和查找中的排列顺序一致则获得「二星」;如果索引中的列包含了查询中需要的全部列则获得「三星」。</p>
</blockquote>
<p>MySQL5.0 之前只能使用某一个单列索引,在这种情况下没有哪一个独立的单列索引是有效的。比如表<code>film_actor</code>在字段<code>film_id</code>和<code>actor_id</code>上各有一个单列索引,但是对于下面这个查询的 WHERE 条件单列索引却不会生效,MySQL 对这个查询会使用全表扫描。因此在 5.0 之后的版本引进了「索引合并」的策略。</p>
<pre><code class="language-sql">SELECT film_id, actor_id FROM film_actor WHERE actor_id = 1 OR film_id = 1
</code></pre>
<p>索引合并算法由三个变种:OR 条件的联合;AND 条件的相交;组合前两种情况的联合及相交,它的作用是同事使用单列索引进行扫描,并将结果进行合并。需要注意的是 MySQL 会使用这类技术优化复杂查询,更多的时候是说明了表上的索引建的很糟糕。比如当服务器对多个索引做相交操作时(通常有多个 AND 条件),通常意味着需要一个包含所有相关列的多列索引,而不是多个单列索引。</p>
<h2 id="选择合适的索引列顺序">选择合适的索引列顺序</h2>
<p>索引列的顺序是容易引起困惑的问题,这一点可以参考 <a href="https://mengxiaoxu.github.io/post/mysql-zhong-de-suo-yin/">MySQL 中的索引</a>中关于 B-Tree 的介绍,列顺序也决定了一个索引是否能够成为一个真正的三星索引。需要注意的是,本小节内容适用于 B-Tree 索引。</p>
<p>选择索引列的顺序有一个经验法则:将选择性最高的列放到索引最前列。但是这个法则在一些情况下没有避免随机 I/O 那么重要,这里想要说明的是这个法则不是放之四海皆准。</p>
<h2 id="聚簇索引">聚簇索引</h2>
<p>聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。当表有聚簇索引时,它的数据行实际上存放在索引的叶子页中,「聚簇」表示数据行和相邻的键值紧凑地存储在一起。当然因为无法把数据行放在两个不同的地方,所以一个表只能有一个聚簇索引。因为是存储引擎负责实现索引,因此并不是所有的存储引擎都支持聚簇索引,而且它的具体细节也和实现方式有关。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[MySQL 中的索引]]></title>
<id>https://mengxiaoxu.github.io/post/mysql-zhong-de-suo-yin/</id>
<link href="https://mengxiaoxu.github.io/post/mysql-zhong-de-suo-yin/">
</link>
<updated>2020-03-08T08:37:18.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>参考内容:<br>
《高性能 MySQL(第三版)》</p>
</blockquote>
<p>一说到「索引」这个词,不知道数据库的人可能首先联想到的就是一本书的索引,它的作用是方便读者快速查找书中的内容。数据库的索引也是这样的效果,是存储引擎用于快速找到记录的一种数据结构。</p>
<p>很明显索引会直接影响到查询的速度,从书籍中的索引可以联想到一些可能会存在的问题。不同的索引编排方式会有不同的查询速度,这对应于数据库使用不同数据结构来建立的索引;试想一下一本书的索引部分比内容部分还要厚,那索引就会起到反向的作用。</p>
<h2 id="索引类型">索引类型</h2>
<p>就像上面提到的一样,索引有很多种不同的类型,不同的索引类型可以针对不同的场景提供更好的性能。需要注意的是 MySQL 的索引不是在服务层实现的,而是在存储引擎实现的,所以并没有统一的索引标准,甚至有的存储引擎都不支持索引,下面介绍两个常用的索引。</p>
<h3 id="b-tree-索引">B-Tree 索引</h3>
<p>我们常说的索引就是 B-Tree 索引,要注意的是我们使用「B-Tree」这个术语是因为 MySQL 在<code>CREATE TABLE</code>和其它语句中使用的也使用该关键字,而且其底层结构使用的并不一定是 B-树。</p>
<p>以前整理过关于「B-树」和「B+树」的资料,详细的资料可以查看以前写的<a href="https://mp.weixin.qq.com/s/Re6c-LeEIsulpYRvOPZhLw">从二叉树到 B+ 树索引</a>。</p>
<p>值得注意的是 B-Tree 对索引列是顺序组织存储的,很适合查找范围数据,例如下面数据表的索引会按照图示的方式去存储。</p>
<pre><code class="language-sql">CREATE TABLE people (
last_name varchar(50) not null,
first_name varchar(50) not null,
dob date not null,
gender enum(‘m’, ‘f’) not null,
key(last_name, first_name, dob)
);
</code></pre>
<figure data-type="image" tabindex="1"><img src="https://mengxiaoxu.github.io//post-images/1583656907264.jpg" alt="" loading="lazy"></figure>
<p>因此这样的索引也就会有它自己的限制。1、如果不是按照索引的最左侧列开始查找,则无法使用索引;2、不能跳过索引中的列,也就是说上述索引无法用于查找 last_name 为 Smith 并且生日在某个特定日期的人;3、如果查询中有某个列的范围查询,则其右边所有列都无法使用索引优化查询。例如<code>WHERE last_name=‘Smith’ AND first_name LIKE ‘%J’ AND dob = ‘1976-12-23’</code>,这个查询就只能使用索引的前两列,因为这里的 LIKE 是一个范围条件。</p>
<h3 id="哈希索引">哈希索引</h3>
<p>如果你的数据结构没有忘的话,那么我想「哈希索引」对你来说应该是见名知意的,它只有精确匹配索引所有列的查询才有效。对每一行数据存储引擎都会对所有的索引列计算出一个哈希码。</p>
<p>既然是基于哈希表实现,那么就不可避免需要考虑哈希冲突的情况。一般避免哈希冲突的方式都是将槽变成一个链表,只要哈希函数选的好,能够让数据均匀的分到各个桶里面就不会出现很要命的性能问题。</p>
<p>因为哈希索引需要计算哈希值,所以不宜选择 MD5、SHA1 等较复杂的哈希函数;当然哈希值也不宜过长或者过短,过长索引值会占用较大的存储空间,过短太容易造成哈希冲突。哈希索引的存储非常的紧凑,所以查询速度会很快,但它也有自己的局限性:</p>
<p>1、哈希索引只包含哈希索值和行指针,而不存储字段值,所以不能使用哈希索引中的值来避免读取行;2、哈希索引数据并不是按照索引值顺序存储的,所以也不能用于排序;3、哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引列的全部内容来计算哈希值;4、哈希索引只支持等值比较查询,不支持任何范围查询;5、如果哈希冲突很多的话,一些索引维护操作的代价会很高。</p>
<p>以<code>SELECT id FROM url WHERE url_crc=CRC32("http://www.mysql.com")</code>为例,一旦出现了哈希冲突,即另一个字符串的哈希值和<code>http://www.mysql.com</code>是一样的,那么这个语句就无法正常的工作,需要在 WHERE 子句中包含常量值才能正常工作,比如加入<code>AND url="http://www.mysql.com"</code>。</p>
<h2 id="索引的优点">索引的优点</h2>
<p>「索引」这个主题深入讲完完全全值得写一本书了,要深入理解这部分知识可以查阅相关资料。需要注意的是,即便我们使用了对象关系映射(ORM)工具,仍然需要关心索引,即便是查询优化技术专家也很难兼顾到各种情况,更别说各种 ORM 了。总结下来索引有如下三个优点:</p>
<ol>
<li>索引大大减少了服务器需要扫描的数据量;</li>
<li>索引可以帮助服务器避免排序和临时表;</li>
<li>索引可以将随机 I/O 变为顺序 I/O。</li>
</ol>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Schema 与数据类型优化]]></title>
<id>https://mengxiaoxu.github.io/post/schema-yu-shu-ju-lei-xing-you-hua/</id>
<link href="https://mengxiaoxu.github.io/post/schema-yu-shu-ju-lei-xing-you-hua/">
</link>
<updated>2020-02-23T14:57:49.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>参考内容:<br>
《高性能 MySQL(第三版))》</p>
</blockquote>
<h2 id="选择优化的数据类型">选择优化的数据类型</h2>
<p>世面上常见的数据库大多支持了多种多样的数据类型,选择正确的数据类型对于获得高性能至关重要,一般都需要遵循如下的几个原则:</p>
<pre><code class="language-md">1、更小的通常更好:更小的通常更快,因为占用着更少的磁盘、内存和 CPU,并且处理时需要的 CPU 周期也更少;
2、简单就好:简单数据类型的操作通常需要更少的 CPU 周期;
3、尽量避免 NULL:如果查询中包含可为 NULL 的列,就会使得索引、索引统计和值比较变得复杂,因此在设计表是最好指定列为 NOT NULL。
</code></pre>
<h3 id="整数类型">整数类型</h3>
<p>在 MYSQL 中可以为整数类型指定宽度,例如<code>INT(11)</code>,但是这对大多数应用是没有意义的,它不会限制值的合法范围,只是规定了 MySQL 的一些交互工具(如 MySQL 命令行客户端)用来显示字符的个数。对于存储和计算来说<code>INT(1)</code>和<code>INT(20)</code>是相同的。</p>
<h3 id="字符串类型">字符串类型</h3>
<p>需要注意的是当 MySQL 存储 CHAR 值时,它会删掉所有的末尾空格,因为 CHAR 值会根据需要采用空格进行填充以方便比较,这导致的问题就是你使用 CHAR 存储的<code>'string '</code>会变成<code>'string'</code>。CHAR 的好处在于它是定长的,很适合存储像 MD5 值一样的定长值,定长值的 CHAR 类型不易产生碎片,而且对于非常短的列 CHAR 也会比 VERCHAR 好,比如<code>CHAR(1)</code>只需要一个字节,而<code>VERCHAR(1)</code>则需要两个字节,因为它还需要一个字节来存长度。</p>
<p>VERCHAR 类型在存储可变长字符串时,会比 CHAR 更节省空间,它需要使用 1 或者 2 个额外的字节记录字符串的长度。但由于行是变长的,当一个行占用的空间增长,并且在页内没有更多的可用空间可以存储,就容易产生碎片。</p>
<h3 id="使用枚举代替字符串">使用枚举代替字符串</h3>
<p>有时候可以使用枚举列代替常用的字符串类型,枚举列可以把一些不重复的字符串存储成一个预定义的集合,而且 MySQL 在存储枚举时非常紧凑,会根据列的数量压缩到一个或两个字节。比如下面的例子:</p>
<pre><code class="language-sql">CREATE TABLE enum_test(
e ENUM('fish', 'apple', 'dog') NOT NULL
);
INSERT INTO enum_test(e) VALUES('fish'), ('dog'), ('apple');
SELECT e+0 FROM enum_test;
# result
+-----+
| e+0 |
+-----+
| 1 |
| 2 |
| 3 |
+-----+
</code></pre>
<p>可以看到使用枚举类型后,上面三行数据实际上存储为了整数,而不是字符串,而且还有一个让人吃惊的地方:枚举字段是按照内部存储的整数而不是定义的字符串进行排序的,这一点需要特别注意,不然在写程序时容易中犯错。当然你也可以在查询时使用<code>FIELD()</code>函数显式地指定排序顺序。</p>
<p>可以看到上面</p>
<h2 id="范式和反范式">范式和反范式</h2>
<p>关系型数据库有设计范式的概念,这一点在大学的数据库课程中肯定都会提及。因为有比较高的范式,那么就只有很少或者没有重复的数据,因此在 UPDATE 时只需要修改更少的数据;高范式的表通常也更小,因此占用的内存也会更小,操作起来也会更快......</p>
<p>但是高范式也带来了另一个缺点,比较好的范式通常意味着需要关联,稍微复杂一点的查询就需要使用 JOIN,关联的代价是昂贵的,甚至让一些索引策略失效;而如果不需要关联,即使某个查询需要全表扫描,当数据比内存大时可能会比关联查询快的多。所以一般都会根据实际情况将范式与反范式混用,完全的范式化和完全的反范式化都是实验室才有的东西。</p>
<h2 id="缓存表和汇总表">缓存表和汇总表</h2>
<p>这里的「缓存表」和「汇总表」并没有什么标准的含义。我们用「缓存表」来存储那些可以从其他表获取,但是获取的速度很慢的数据;而「汇总表」则用来保存那些使用 GROUP BY 语句聚合数据的表。毫无疑问,我们存这些冗余数据也是为了性能。</p>
<p>比如近两年各种应用流行的年终报告,每次网易云音乐的年终报告都会把朋友圈撑满,其它类似于缓存一个用户的朋友数、一个文件的下载次数等等。这些数据实时计算的开销是很大的,而且多数情况下用户也等不起实时计算的时间,一般的解决方案都是通过增加计数器表(缓存表)来解决这个问题。</p>
<p>计算机科学中总是伴随着双面性,上面的计数器表带来性能提升的同时也带来了并发问题。网站的每一次点击都会导致对计数器的更新,对于任何想要更新这一行的事务来说,这条记录都有一个全局的互斥锁,这会使得这些事务只能串行的进行。每一次点击都会触发下面的语句,但大量的点击伴随着该行数据的互斥锁,想想性能也不会提升到哪里去吧。</p>
<pre><code class="language-sql">UPDATE hit_counter SET cnt = cnt + 1;
</code></pre>
<p>大多数应用都是读查询居多,为了提升读查询的速度,经常会需要增加一些额外的索引,增加冗余列、汇总表、缓存表等等。但是不要忘了这些技巧也会增加写查询的负担,还会增加开发难度,因此应该根据实际应用场景来做权衡。</p>
<h2 id="加快-alter-table-表的速度">加快 ALTER TABLE 表的速度</h2>
<p>MySQL 执行大部分修改表结构的方法都是用新的结构创建一个空表,然后从旧表中查出所有数据插入到新表,然后删除旧表。在内存不足、表很大、索引多的情况下会花费很长的时间。一个很严重的缺点是大部分 ALTER TABLE 操作将导致 MySQL 服务中断。</p>
<p>对于常见的场景我们有两种技巧避免服务中断。一种是先在一台不提供服务的机器上执行 ALTER TABLE 操作,然后和提供服务的主库进行切换;另一种技巧是「影子拷贝」,即用要求的表结构创建一张和源表无关的新表,然后通过重命名和删除表的操作交换两张表。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[刘小绪同学随笔(2020-02-18)]]></title>
<id>https://mengxiaoxu.github.io/post/liu-xiao-xu-tong-xue-sui-bi-2020-02-18/</id>
<link href="https://mengxiaoxu.github.io/post/liu-xiao-xu-tong-xue-sui-bi-2020-02-18/">
</link>
<updated>2020-02-19T13:35:29.000Z</updated>
<content type="html"><![CDATA[<p>以前在朋友圈提到过这样一个现象,重庆人和四川人说的都是四川话,但是大部分重庆人会说他们说的是重庆话,说「川渝是一家」的通常也都是四川人。</p>
<p>在深圳也有一个很怪的现象,两个客家人谈话会用客家话,两个潮汕人谈话会用潮汕话,两个广东人谈话会用粤语,反正就是尽可能用更小众的语言。</p>
<p>想了一下,故意用第三方听不懂的语言,实际上是很欠考虑的,如果是刚见面用方言寒暄几句我觉得还行,但是后面谈话就应该使用大家都能听懂的语言了。</p>
<p>疫情期间大家都没法出去玩,我和老叔倒是出去爬了爬山,村里的荔枝山别人进不去,整座山就我和叔两个人,单从疫情这个角度讲,荔枝山是比大部分地方都要安全的。</p>
<p>我自己可以在疫情期间爬爬山,结合我自己的感受,加上前段时间的「大奔进故宫」事件。我发现人们并不是痛恨特权,而是痛恨自己没有特权。大部人痛恨的不是腐败,痛恨的是自己没有腐败的机会。</p>
<p>上面四川和深圳两个例子也差不多是出于这样的优越感,鉴于四川除了成都外,其它地方投资的回报率太低,穷地方的人总会羡慕富有的地方,说川渝一家的人大概率不是成都人。</p>
<figure data-type="image" tabindex="1"><img src="https://mengxiaoxu.github.io//post-images/1582119428667.png" alt="" loading="lazy"></figure>
<p>春节期间看了一本《断舍离》,它讲究的是内部的自觉自省,虽然整本书挺啰嗦的,完完全全可以用一篇几千字的文章代替,但是它传达的人生整理理念很值得参考,感兴趣的读者大人可以在微信读书中阅读此书。下面是一段摘自书中的内容。</p>
<blockquote>
<p>我们习惯于思考「有效性」,却往往忽略了作为「有效性」前提的「必要性」,对物品也常常陷入这样的定式思维,导致家里各种杂物堆积,这些杂物给人的压迫感,以及狭窄空间给人的阻塞感,会让人的思维变得迟钝、行动变得迟缓!</p>
</blockquote>
<p>借助「断舍离」的理念,我删了 400 多个微信好友,处理了一些不会再使用的家具和书籍,才发现之前一直舍不得扔的那些东西扔了对我的积极作用更大,以前写过的一篇你如果只是一直囤干货,那永远也不可能进步,核心思想和断舍离基本一致,遗憾的是自己当时写下这篇文章后,竟然不懂得延伸到其它领域。</p>
<p>可能一部分人有读书摘抄语录的习惯,我个人在阅读技术书籍或是扫除我知识盲点的时候,我也会通过记笔记来加深自己的理解。想想自己强迫症式的记笔记面面俱到其实也是在浪费时间,大部分笔记自己都不会再去看第二遍的,舍弃一些不必要的形式会让自己的阅读更有收获。</p>
<p>还发现自己另外一个错误观点,我不管是写字还是看书都比大部分人慢,一直都认为是自己看书的方法不对,现在才发现问题的根本原因。是因为我对具体的领域不熟悉,所以看这个领域的书籍才会速度很慢,如果对这个领域熟悉了,那一目十行甚至几十行的速度应该都可以达到。结论就是书读的少了导致读书的速度慢。</p>
<figure data-type="image" tabindex="2"><img src="https://mengxiaoxu.github.io//post-images/1582119428667.png" alt="" loading="lazy"></figure>
<p>推荐一部美剧——《良医》,全剧的场景基本是在医院,但有深度的内容基本都和医院无关,除了最基本的医疗科普外,更多的是对家庭、爱、职场、心理等的探讨,下面是我摘的两句台词。</p>
<blockquote>
<p>Whenever people want you to do someting they think is wrong, they say it’s “reality”.<br>
当人们想让你做他们认为错误的事时,他们总会说这就是现实。</p>
</blockquote>
<blockquote>
<p>Very few things that are worthwhile in life come without a cost.<br>
人生中那些有意义的事大多是有代价的。</p>
</blockquote>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[MySQL 架构与历史]]></title>
<id>https://mengxiaoxu.github.io/post/mysql-jia-gou-yu-li-shi/</id>
<link href="https://mengxiaoxu.github.io/post/mysql-jia-gou-yu-li-shi/">
</link>
<updated>2020-02-16T13:26:48.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>参考内容:<br>
《高性能 MySQL(第三版))》<br>
<a href="https://t.zsxq.com/FuJaE23">码农翻身知识星球</a><br>
<a href="https://mp.weixin.qq.com/s/bM_g6Z0K93DNFycvfJIbwQ">数据库村的旺财和小强</a></p>
</blockquote>
<h2 id="存储引擎">存储引擎</h2>
<p><a href="https://dev.mysql.com/doc/internals/en/custom-engine-overview.html"> Writing a Custom Storage Engine / Overview</a> 中说存储引擎负责管理 MySQL 的数据存储和索引,MySQL 服务器通过已经定义好的 API 与存储引擎进行通信。</p>
<p>不同的存储引擎都有各自的优势和劣势,存储引擎相互之间不会通信,除了 InnoDB 外其它引擎都不会解析 SQL,API 屏蔽了存储引擎之间的差异,使得这些差异对上层查询过程透明。存储引擎 API 包含了几十个底层函数,下面列举了其中的一小部分函数,如果你要自己实现一个存储引擎,那所做的工作就是把这些接口函数实现。</p>
<pre><code class="language-c">//创建一个表
virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
//打开一个表
int open(const char *name, int mode, int test_if_locked);
//写入一行数据
int write_row(byte *buf);
//更新一行
int update_row(const byte *old_data, byte *new_data);
//删除一行
int delete_row(const byte *buf);
//开始一个事务
int start_stmt(THD *thd, thr_lock_type lock_type);
//提交一个事务
int (*commit)(THD *thd, bool all);
//回滚一个事务
int (*rollback)(THD *thd, bool all);
// ......
</code></pre>
<p>MySQL 内建了很多存储引擎,除此之外还有很多社区版的第三方引擎,对于这么多引擎我们应该怎么选择呢?这里可以归结为一句话「<strong>除非需要用到某些 InnoDB 不具备的特性,并且没有其它办法可以代替,否则都应该选择 InnoDB 引擎</strong>」。例如,如果要用到全文索引,建议优先考虑 InnoDB 加上 <a href="https://www.sphinx-doc.org/en/master/">Sphinx</a> 组合,而不是使用支持全文索引的 MyISAM。当然,如果不需要用到 InnoDB 的特性,同时其它引擎的特性能够更好的满足需求,也可以考虑一下其它引擎。</p>
<p>除非万不得已,否则不要混合使用多种存储引擎。如果应用需要不同的存储引擎,你至少应该考虑「事务」、「备份」、「崩溃恢复」、「特有的特性」几个因素。</p>
<h2 id="并发控制">并发控制</h2>
<p>数据库需要大量的磁盘读写操作,自然而然就会产生并发控制的问题。我们可以简单的使用读写锁来解决解决并发控制问题。读锁是共享的,多个客户可以在同一时刻读取同一个资源;写锁是排他的,一个写锁会阻塞其它写锁和读锁,只有这样才能在同一时间内只有一个用户能执行写入,并防止其它用户读取正在写入的同一资源。</p>
<p>上一段内容只说了锁,但是没有讨论锁的粒度,锁定资源时是直接锁定整个数据库还是仅锁定具体的表,亦或是只锁定要操作的那几行记录?首先我们需要明白的是任何时候,在给定的资源上,锁定的数据量越少则系统的并发程度越高,只要相互之间不冲突即可。</p>
<p>但是我们还忘了另一件事,加锁本身也是需要消耗资源的,获得锁、检查锁的解除、释放锁等等都会增加系统的开销,如果系统花费大量的时间来管理锁,而不是管理数据,那么系统的并发程度同样会受到影响。因此锁策略就是在锁的开销和数据的安全性之间寻求平衡。</p>
<p>大多数商业数据库在锁策略上并没有提供更多的选择,一般都是在表上施加行级锁。MySQL 则提供了多种选择,每种 MySQL 引擎都可以实现自己的锁策略和锁粒度,比较重要的两种锁策略是「表锁」和「行级锁」。</p>
<h2 id="多版本并发控制">多版本并发控制</h2>
<p>刘大在<a href="https://mp.weixin.qq.com/s/bM_g6Z0K93DNFycvfJIbwQ">数据库村的旺财和小强</a>中把「事务的隔离级别」和「多版本并发控制(MVCC)」讲的已经比较透彻了,此处我仅借助文字描述来加深我自己的理解。</p>
<p>MVCC 是行级锁的一个变种,但是它在很多情况下避免了加锁的操作,因为避免了加锁的这些开销,所以 MVCC 能够提升并发性能。包括 Oracle、PostgreSQL、MySQL 等等数据库都实现了 MVCC,只是各自的实现机制不一样,因为 MVCC 没有一个统一的实现标准,比较典型的实现方式有「乐观并发控制」和「悲观并发控制」。</p>
<p>MVCC 是通过保存数据在某个时间点的快照来实现的(这难道不是一个版本控制器吗)。以 InnoDB 为例,它是通过在每行记录后面保存两个隐藏的列来实现,一个保存了行的创建时间,另一个则保存了行的过期(删除)时间。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增,事务开始时刻的版本号会作为事务的版本号,用来和查询到的每行记录记录的版本号做比较。</p>
<p>因此根据事务的开始时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。因为同一条记录在存储器中拥有多个版本,每个事务也有自己的版本号,只要把事务和记录的版本号做对比即可知道需要取哪个版本的记录了。一言以蔽之,MVCC 是一个借助空间来换取时间的策略。</p>
<h2 id="小知识点">小知识点</h2>
<p>MySQL 使用文件系统的目录和文件来保存数据库和表的定义,所以大小写敏感就和具体的平台密切相关,在 Windows 中大小写不敏感,而在 Linux 等类 Unix 中则是敏感的。不同的存储引擎保存数据和索引的方式是不同的,但是表的定义则是在 MySQL 服务层统一处理的,<code>my_table</code>表的定义保存会被保存在<code>my_table.frm</code>中。</p>
<p>如果表在创建并导入数据后,不会再对该表进行修改操作,那么这样的表比较适合 MyISAM 压缩表,而且压缩表也支持索引。可以使用<code>myisampack</code>对 MyISAM 表进行压缩,压缩后的表是不能进行修改的,但是可以极大的减少磁盘的空间占用,因此也可以减少磁盘 I/O,这无疑是一种提升查询性能的方法。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[浅析正则表达式原理]]></title>
<id>https://mengxiaoxu.github.io/post/qian-xi-zheng-ze-biao-da-shi-yuan-li/</id>
<link href="https://mengxiaoxu.github.io/post/qian-xi-zheng-ze-biao-da-shi-yuan-li/">
</link>
<updated>2020-01-15T15:18:27.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>参考内容:<br>
《编译原理》<br>
<a href="https://juejin.im/post/5c738dd5e51d457fcb40aaaa">实现简单的正则表达式引擎</a><br>
<a href="https://zhuanlan.zhihu.com/p/27417442">正则表达式回溯原理</a><br>
<a href="http://www.alloyteam.com/2019/07/13574/">浅谈正则表达式原理</a></p>
</blockquote>
<p>最近在一个业务问题中遇到了一个正则表达式性能问题,于是查了点资料去回顾了下正则表达式的原理,简单整理了一下就发到这里吧;另外也是想试试 Apple Pencil 的手感如何,画的太丑不要嫌弃哈。</p>
<h2 id="有穷自动机">有穷自动机</h2>
<p>正则表达式的规则不是很多,这些规则也很容易就能理解,但是正则表达式并不能用来直接识别字符串,我们还需要引入一种适合转换为计算机程序的模型,我们引入的就是<a href="https://zh.wikipedia.org/wiki/%E6%9C%89%E9%99%90%E7%8A%B6%E6%80%81%E6%9C%BA">有穷自动机</a>。</p>
<p>在编译原理中通过构造有穷自动机把正则表达式编译成<strong>识别器</strong>,识别器以字符串<code>x</code>作为输入,当<code>x</code>是语言的句子时回答<code>是</code>,否则回答<code>不是</code>,这正是我们使用正则表达式时需要达到的效果。</p>
<p>有穷自动机分为<strong>确定性有穷自动机(DFA)<strong>和</strong>非确定性有穷自动机(NFA)</strong>,它们都能且仅能识别正则表达式所表示的语言。它们有着各自的优缺点,DFA 导出的识别器时间复杂度是多项式的,它比 NFA 导出的识别器要快的多,但是 DFA 导出的识别器要比与之对应的 NFA 导出的识别器大的多。</p>
<p>大部分正则表达式引擎都是使用 NFA 实现的,也有少部分使用 DFA 实现。从我们写正则表达式的角度来讲,DFA 实现的引擎要比 NFA 实现的引擎快的多,但是 DFA 支持的功能没有 NFA 那么强大,比如没有捕获组一类的特性等等。</p>
<p>我们可以用带标记的有向图来表示有穷自动机,称之为<strong>转换图</strong>,其节点是状态,有标记的边表示转换函数。同一个字符可以标记始于同一个状态的两个或多个转换,边可以由输入字符符号标记,其中 NFA 的边还可以用<code>ε</code>标记。</p>
<p>之所以一个叫有确定和非确定之分,是因为对于同一个状态与同一个输入符号,NFA 可以到达不同的状态。下面看两张图就能明白上面那一长串的文字了。</p>
<p>图中两个圈圈的状态表示接受状态,也就是说到达这个状态就表示匹配成功。细心的你应该发现了两张图所表示的正则表达式是一样的,这就是有穷自动机神奇的地方,每一个 NFA 我们都能通过算法将其转换为 DFA,所以我们先根据正则表达式构建 NFA,然后再转换成相应的 DFA,最后再进行识别。</p>
<figure data-type="image" tabindex="1"><img src="https://mengxiaoxu.github.io//post-images/1579101604765.jpg" alt="" loading="lazy"></figure>
<p>上图的画法在正则表达式很简单的时候还可以,如果遇到很复杂的正则表达式画起来还是挺费力的,如果想对自动机有更加深入的认识可以自行查阅相关资料。下面的图片是使用正则可视化工具生成的,对应的正则表达式是<code>^-?\d+(,\d{3})*(\.\d{1,2})?$</code>,它所匹配的字符串是<code>数字/货币金额(支持负数、千分位分隔符)</code>。</p>
<figure data-type="image" tabindex="2"><img src="https://mengxiaoxu.github.io//post-images/1579101622779.png" alt="" loading="lazy"></figure>
<h2 id="回溯">回溯</h2>
<p>NFA 引擎在遇到多个合法的状态时,它会选择其中一个并记住它,当匹配失败时引擎就会回溯到之前记录的位置继续尝试匹配。这种回溯机制正是造成正则表达式性能问题的主要原因。下面我们通过具体的例子来看看什么是回溯。</p>
<pre><code class="language-javascript">/ab{1,3}c/
</code></pre>
<table>
<thead>
<tr>
<th>正则</th>
<th>文本</th>
</tr>
</thead>
<tbody>
<tr>
<td>ab{1,3}c</td>
<td>abbbc</td>
</tr>
<tr>
<td><code>a</code>b{1,3}c</td>
<td><code>a</code>bbbc</td>
</tr>
<tr>
<td><code>ab{1,3}</code>c</td>
<td><code>ab</code>bbc</td>
</tr>
<tr>
<td><code>ab{1,3}</code>c</td>
<td><code>abb</code>bc</td>
</tr>
<tr>
<td><code>ab{1,3}</code>c</td>
<td><code>abbb</code>c</td>
</tr>
<tr>
<td><code>ab{1,3}c</code></td>
<td><code>abbbc</code></td>
</tr>
</tbody>
</table>
<p>上表中展示的是使用<code>ab{1,3}c</code>匹配<code>abbbc</code>的过程,如果把匹配字符串换成<code>abbc</code>,在第五步就会出现匹配失败的情况,第六步会回到上一次匹配正确的位置,进而继续匹配。这里的第六步就是「<strong>回溯</strong>」</p>
<table>
<thead>
<tr>
<th>正则</th>
<th>文本</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>ab{1,3}c</td>
<td>abbc</td>
<td></td>
</tr>
<tr>
<td><code>a</code>b{1,3}c</td>
<td><code>a</code>bbc</td>
<td></td>
</tr>
<tr>
<td><code>ab{1,3}</code>c</td>
<td><code>ab</code>bc</td>
<td></td>
</tr>
<tr>
<td><code>ab{1,3}</code>c</td>
<td><code>abb</code>c</td>
<td></td>
</tr>
<tr>
<td><code>ab{1,3}</code>c</td>
<td><code>abb</code><s>c</s></td>
<td>匹配失败</td>
</tr>
<tr>
<td><code>ab{1,3}c</code></td>
<td><code>abb</code>c</td>
<td>回溯</td>
</tr>
<tr>
<td><code>ab{1,3}c</code></td>
<td><code>abbc</code></td>
<td></td>
</tr>
</tbody>
</table>
<p>会出现上面这种情况的原因在于正则匹配采用了<a href="https://zh.wikipedia.org/wiki/%E5%9B%9E%E6%BA%AF%E6%B3%95">回溯法</a>。回溯法采用试错的思想,它尝试分步的去解决一个问题。在分步解决问题的过程中,当它通过尝试发现现有的分步答案不能得到有效的正确的解答的时候,它将取消上一步甚至是上几步的计算,再通过其它的可能的分步解答再次尝试寻找问题的答案。它通常采用最简单的递归来实现,在反复重复上述的步骤后可能找到一个正确的答案,也可能尝试所有的步骤后发现该问题没有答案,回溯法在最坏的情况下会导致一次复杂度为指数时间的计算。</p>
<p>上面一段的内容来源于<a href="https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5">维基百科</a>,精简一下就是深度优先搜索算法。贪婪量词、惰性量词、分支结构等等都是可能产生回溯的地方,在写正则表达式时要注意会引起回溯的地方,避免导致性能问题。</p>
<p><a href="https://blog.cloudflare.com/author/john-graham-cumming/">John Graham-Cumming</a> 在他的博文 <a href="https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/">Details of the Cloudflare outage on July 2, 2019</a> 中详细记录了因为一个正则表达式而导致线上事故的例子。该事故就是因为一个有性能问题的正则表达式,引起了灾难性的回溯,进而导致了 CPU 满载。</p>
<pre><code class="language-html">(?:(?:\"|'|\]|\}|\\|\d|(?:nan|infinity|true|false|null|undefined|symbol|math)|\`|\-|\+)+[)]*;?((?:\s|-|~|!|{}|\|\||\+)*.*(?:.*=.*)))
</code></pre>
<p>上面是引起事故的正则表达式,出问题的关键部分在<code>.*(?:.*=.*)</code>中,就是它引起的灾难性回溯导致 CPU 满载。那么我们应该怎么减少或避免回溯呢?无非是提高警惕性,好好写正则表达式;或者使用 DFA 引擎的正则表达式。</p>
<h2 id="0-9-与-d-的区别">[0-9] 与 \d 的区别</h2>
<p>此问题来源于<a href="https://stackoverflow.com/questions/16621738/d-is-less-efficient-than-0-9?newsletter=1&nlcode=55866%7cc739">Stackoverflow</a>,题主遇到的问题是<code>\d</code>比<code>[0-9]</code>的效率要低很多,并且给出了如下的测试结果,可以看到<code>\d</code>比<code>[0-9]</code>慢了差不多一倍。</p>
<pre><code class="language-javascript">Regular expression \d took 00:00:00.2141226 result: 5077/10000
Regular expression [0-9] took 00:00:00.1357972 result: 5077/10000 63.42 % of first
Regular expression [0123456789] took 00:00:00.1388997 result: 5077/10000 64.87 % of first
</code></pre>
<p>出现这个性能问题的原因在于<code>\d</code>匹配的不仅仅是<code>0123456789</code>,<code>\d</code>匹配的是所有的 Unicode 的数字,你可以从 <a href="http://www.fileformat.info/info/unicode/category/Nd/list.htm">Unicode Characters in the 'Number, Decimal Digit' Category</a> 中看到所有在 Unicode 中属于数字的字符。</p>
<p>此处多提一嘴,<code>[ -~]</code>可以匹配 ASCII 码中所有的可打印字符,你可以查看 <a href="http://ascii.911cha.com/">ASCII 码中的可显示字符</a>,就是从<code>" "</code>(32)至<code>"~"</code>(126)的字符。</p>
<h2 id="工具资源推荐">工具/资源推荐</h2>
<p>正则表达式确实很强大,但是它那晦涩的语法也容易让人头疼抓狂,不论是自己还是别人写的正则表达式都挺头大,好的是已经有人整理了<a href="https://github.com/any86/any-rule">常用正则大全</a>,也大神写了个叫做 <a href="http://verbalexpressions.github.io/">VerbalExpressions</a> 的小工具,主流开发语言的版本它都提供了,可以让你用类似于自然语言的方式来写正则表达式,下面是它给出的一个 JS 版示例。</p>
<pre><code class="language-javascript">// Create an example of how to test for correctly formed URLs
const tester = VerEx()
.startOfLine()
.then('http')
.maybe('s')
.then('://')
.maybe('www.')
.anythingBut(' ')
.endOfLine();
// Create an example URL
const testMe = 'https://www.google.com';
// Use RegExp object's native test() function
if (tester.test(testMe)) {
alert('We have a correct URL'); // This output will fire
} else {
alert('The URL is incorrect');
}
console.log(tester); // Outputs the actual expression used: /^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$/
</code></pre>
<p>文章大部分内容都是介绍的偏原理方面的知识,如果仅仅是想要学习如何使用正则表达式,可以看<a href="https://mengxiaoxu.github.io/post/CY4_BQLR4/">正则表达式语法</a>或者 <a href="https://github.com/ziishaned/learn-regex/blob/master/translations/README-cn.md">Learn-regex</a>,更为详细的内容推荐看由<a href="https://www.zhihu.com/people/qdlaoyao/activities">老姚</a>写的<a href="https://raw.githubusercontent.com/qdlaoyao/js-regex-mini-book/master/JavaScript%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%BF%B7%E4%BD%A0%E4%B9%A6%EF%BC%881.1%E7%89%88%EF%BC%89.pdf">JavaScript 正则表达式迷你书</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Vue + TypeScript 项目起手式]]></title>
<id>https://mengxiaoxu.github.io/post/vue-typescript-xiang-mu-qi-shou-shi/</id>
<link href="https://mengxiaoxu.github.io/post/vue-typescript-xiang-mu-qi-shou-shi/">
</link>
<updated>2020-01-09T13:51:57.000Z</updated>
<content type="html"><![CDATA[<p>在此前我使用的前端框架是 <a href="https://angular.io/">Angular</a>,使用过 <a href="https://www.typescriptlang.org/">TypeScript</a> 后你就会讨厌 JS 了,我学习 <a href="https://vuejs.org/">Vue</a> 时的最新版本是 2.5,相信大部分同学都不会认为 Vue 那样又细又长的代码很美观吧,简单看了一些网络博客后,我毅然决然引入了 TypeScript 进行开发,本文仅整理记录我自己遇到的一些坑。</p>
<h2 id="使用-cli">使用 Cli</h2>
<p><a href="https://cli.vuejs.org/zh/guide/">脚手架</a>是一个比较方便的工具,这里需要注意的是<code>@vue/cli</code>和<code>vue-cli</code>是不一样的,推荐使用<code>npm i -g @vue/cli</code>安装。</p>
<p>安装完成后,可以直接使用<code>vue create your-app</code>创建项目,你可以选择使用默认配置亦或是自己手动选择配置,按提示一步一步向下走即可,它会根据你的选择自己创建比如<code>tsconfig.json</code>等等配置文件。这里推荐使用<code>less</code>开发样式,<code>sass</code>老是在安装的过程中出问题。</p>
<p>当然你也可以使<code>vue ui</code>命令启动一个本地服务,它是一个 Vue 项目管理器,提供了一个可视化的页面供你管理自己的项目,它的样子如下图所示,还是比较清新的。</p>
<figure data-type="image" tabindex="1"><img src="https://mengxiaoxu.github.io//post-images/1578578071319.png" alt="" loading="lazy"></figure>
<h2 id="使用-vue-property-decorator">使用 vue-property-decorator</h2>
<p>Vue 官方维护了 <a href="https://github.com/vuejs/vue-class-component">vue-class-component</a> 装饰器,<a href="https://github.com/kaorun343/vue-property-decorator">vue-property-decorator</a> 则是在<code>vue-class-component</code>基础上增强了更多结合<code>Vue</code>特性的装饰器,它可以让 Vue 组件语法在结合了 TypeScript 语法后变得更加扁平化。</p>
<p>截止本文时间,<code>vue-property-decorator</code>共提供了 11 个装饰器和 1 个<code>Mixins</code>方法,下面用<code>@Prop</code>举个例子,是不是看起来引起极度舒适。</p>
<pre><code class="language-typescript">import { Vue, Component, Prop } from 'vue-property-decorator'
@Component
export default class YourComponent extends Vue {
@Prop(Number) readonly propA: number | undefined
@Prop({ default: 'default value' }) readonly propB!: string
@Prop([String, Boolean]) readonly propC: string | boolean | undefined
}
// 上面的内容将会被解析成如下格式
export default {
props: {
propA: {
type: Number
},
propB: {
default: 'default value'
},
propC: {
type: [String, Boolean]
}
}
}
</code></pre>
<h2 id="使用-vuex">使用 Vuex</h2>
<p>关于怎么使用<a href="https://vuex.vuejs.org/">Vuex</a>此处就不再做过多说明了,需要注意的一点是,如果你需要访问<code>$store</code>属性的话,那么你必须得继承<code>Vue</code>类,坑的地方是在某些情况下即使你没有继承<code>Vue</code>,它也能通过编译,只有在程序运行起来的时候才报错。</p>
<pre><code class="language-typescript">class ExampleApi extends Vue {
public async getExampleData() {
if (!this.$store.state.exampleData) {
const res = await http.get('url/exampleData');
if (res.result) {
this.$store.commit('setExampleData', res.data);
return res.data;
} else {
promptUtil.showMessage('get exampleData failed', 'warning');
}
} else {
return this.$store.state.exampleData;
}
}
}
</code></pre>
<h2 id="使用自己的配置含代理">使用自己的配置(含代理)</h2>
<p><code>vue.config.js</code>是一个可选的配置文件,如果项目的根目录中存在这个文件,那么它会被<code>@vue/cli-service</code>自动加载,它的配置项说明可以查看<a href="https://cli.vuejs.org/zh/config/">配置参考</a>。</p>
<p>我们再开发过程中都会使用代理来转发请求,代理的配置也是在这个文件中,它的官方说明在<a href="https://cli.vuejs.org/zh/config/#devserver-proxy">devserver-proxy</a>中,下面是一个简单的<code>vue.config.js</code>文件例子。</p>
<pre><code class="language-typescript">module.exports = {
filenameHashing: true,
outputDir: 'dist',
assetsDir: 'asserts',
indexPath: 'index.html',
productionSourceMap: false,
transpileDependencies: [
'vue-echarts',
'resize-detector'
],
devServer: {
hotOnly: true,
https: false,
proxy: {
"/statistics": {
target: "http://10.7.213.186:3889",
secure: false,
pathRewrite: {
"^/statistics": "",
},
changeOrigin: true
},
"/mail": {
target: "http://10.7.213.186:8888",
secure: false,
changeOrigin: true
}
}
}
}
</code></pre>
<h2 id="让-vue-识别全局方法和变量">让 Vue 识别全局方法和变量</h2>
<p>我们在项目中都会使用一些第三方 UI 组件,比如我自己就使用了 <a href="https://element.eleme.io/">Element</a>,但是在使用它的<code>$message</code>、<code>$notify</code>等方法时就直接报错了,究其原因就是<code>$message</code>等属性并没有在 Vue 实例中声明。</p>
<p>官方对此给出了很明确的解决方案,使用的是 TypeScript 的<a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation"> 模块补充特性</a>,可以查看<a href="https://cn.vuejs.org/v2/guide/typescript.html#%E5%A2%9E%E5%BC%BA%E7%B1%BB%E5%9E%8B%E4%BB%A5%E9%85%8D%E5%90%88%E6%8F%92%E4%BB%B6%E4%BD%BF%E7%94%A8">增强类型以配合插件使用</a>。既然知道是因为没有声明导致的错误,那我们就给它声明一下好了,在<code>src/shims-vue.d.ts</code>文件中添加如下代码即可,如果没有该文件请自行创建。</p>
<blockquote>
<p>看到网上也有一部分人说的是<code>src/vue-shim.d.ts</code>,反正不管是怎么命名这个文件的,它们的作用是一样的。</p>
</blockquote>
<pre><code class="language-typescript">declare module 'vue/types/vue' {
interface Vue {
$message: any,
$confirm: any,
$prompt: any,
$notify: any
}
}
</code></pre>
<p>这里顺道提一下,<code>src/shims-vue.d.ts</code>文件中的如下代码是为了让你的 IDE 明白以<code>.vue</code>结尾的文件是什么玩意儿。</p>
<pre><code class="language-typescript">declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}
</code></pre>
<h2 id="路由懒加载">路由懒加载</h2>
<p>Vue Router 官方有关于<a href="https://router.vuejs.org/zh/guide/advanced/lazy-loading.html">路由懒加载</a>的说明,但不知道为什么官方给的这个说明在我的项目里面都没有生效,但使用<code>require.ensure()</code>按需加载组件可以生效。</p>
<pre><code class="language-typescript">// base-view 是模块名,写了相同的模块名则代码会被组织到同一个文件中
const Home = (r: any) => require.ensure([], () => r(require('@/views/home.vue')), layzImportError, 'base-view');
// 路由加载错误时的提示函数
function layzImportError() {
alert('路由懒加载错误');
}
</code></pre>
<p>上面的方式会在编译的时候把文件自动分成多个小文件,编译后的文件会以你自己命名的模块名来命名,如果代码之间有相互依赖,依赖部分代码编译后的文件会以两个模块名相连后进行命名。</p>
<p>但是需要注意的是,这样拆分小文件之后引入了另外一个新的问题,因为客户端会缓存这些编译后的 js 文件,如果功能 A 同时依赖了<code>a.js</code>和<code>b.js</code>两个文件,但用户在使用其它功能时已经把<code>a.js</code>缓存到本地了,使用功能 A 时需要请求<code>b.js</code>文件,这时程序就很容易报错,因为此时在客户端这两个文件不是同一个版本,所以可能导致<code>a.js</code>调用<code>b.js</code>中的方法已经被删了,进而导致客户端页面异常。</p>
<h2 id="关于引入第三方包">关于引入第三方包</h2>
<p>项目在引入第三方包的时候经常会报出各种奇奇怪怪的错误,这里仅提供我目前找到的一些解决办法。</p>
<pre><code class="language-typescript">/*
引入 jquery 等库可以尝试下面这种方式
只需要把相应的 js 文件放到指定文件夹即可
**/
const $ = require('@/common/js/jquery.min.js');
const md5 = require('@/common/js/md5.js');
</code></pre>
<p>引入一些第三方样式文件、UI 组件等,如果引入不成功可以尝试建一个 js 文件,将导入语句都写在 js 文件中,然后再在<code>main.ts</code>文件中导入这个 js 文件,这个方法能解决大部分的问题。例如我先建了一个<code>lib.js</code>,然后在<code>main.ts</code>中引入<code>lib.js</code>就没有报错。</p>
<pre><code class="language-typescript">// src/plugins/lib.js
import Vue from 'vue';
// 树形组件
import 'vue-tree-halower/dist/halower-tree.min.css';
import {VTree} from 'vue-tree-halower';
// 饿了么组件
import Element from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
// font-awesome 图标
import '../../node_modules/font-awesome/css/font-awesome.css';
import VueCookies from 'vue-cookies';
import VueJWT from 'vuejs-jwt';
Vue.use(VueJWT);
Vue.use(VueCookies);
Vue.use(VTree);
Vue.use(Element);
// src/main.ts
import App from '@/app.vue';
import Vue from 'vue';
import router from './router';
import store from './store';
import './registerServiceWorker';
import './plugins/lib';
Vue.config.productionTip = false;
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app');
</code></pre>
<p>因为第三方包写的各有特点,在引入不成功的时候基本也只能是见招拆招,当然如果你的功底比较深厚,你也可以自己写一个<code>index.d.ts</code>文件,实在不行的话,那个特殊的组件不使用 TypeScript 来写也能解决,我目前还没有找一个可以完全解决第三方包引入错误的方法,如果您已经有相关的方法了,希望能与你一起探讨交流。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[2019 年个人总结]]></title>
<id>https://mengxiaoxu.github.io/post/2019-nian-zong-jie/</id>
<link href="https://mengxiaoxu.github.io/post/2019-nian-zong-jie/">
</link>
<updated>2020-01-05T16:00:47.000Z</updated>
<content type="html"><![CDATA[<p>写总结的习惯是从 2015 年开始的,我的大学学费是县政协资助的,叔叔阿姨们唯一的要求就是每年给他们写个总结汇报一下学习情况,毕业后敦促我写总结的人则从外力转为内心。</p>
<h2 id="一点感动">一点感动</h2>
<p>上半年我还很年轻,那时候还会经常使用 QQ、Soul、同桌、一罐 等等社交产品,无意结识了一个还在读高中的同性恋女孩子,我没学过心理学不知道用什么专业名词描述她的情况,反正就是心理上有严重的问题,玻璃心、想自杀等等。几次拼了我老命陪她聊到半夜两三点,现在完完全全能正视自己的情况了。</p>
<p>让我感动的是有次她问我在干啥,我随便拍了一张自己的被汗水打湿衣服发给她,告诉她自己正在打羽毛球。小姑娘给我说我穿的衣服不好看,说我才多大穿的衣服太老了,我也就随口一说叫她给我这个老年人推荐推荐衣服,因为她要上课后面就一直没有回我消息。</p>
<p>第二天早上睡醒了看到了小姑娘的几十条消息,是半夜两点多发的,给我挑了好几件衣服裤子,还给我画了几张示意图(如下),瞬间收获一份小感动。我也遵从小姑娘的意见买了两件上班穿穿,结果一到部门就是众目睽睽之下给我说穿的好酷,穿几次了都还是会引来大家不一样的目光,个性低调的我还是选择走大众程序员路线,老就老吧。</p>
<figure data-type="image" tabindex="1"><img src="https://mengxiaoxu.github.io//post-images/1578240118299.jpg" alt="" loading="lazy"></figure>
<figure data-type="image" tabindex="2"><img src="https://mengxiaoxu.github.io//post-images/1578240130505.jpg" alt="" loading="lazy"></figure>
<p>前几天小姑娘给我发了她暗恋的小姐姐的照片,虽然极少时候还是会上课偷偷玩手机,但也在努力的备战高考。我做的不好的就是她多次给我讲自己在龙岗,我每次都把她当成龙华的,希望写了这篇总结之后不再记错吧。</p>
<h2 id="赚钱理财">赚钱理财</h2>
<p>这个小标题起的有点大,仅说说我自己的实际情况吧。凭着运气,2019 年的银行理财收益在 4.5% 左右,基金收益在 7% 左右。我没有去玩股票,网上各种理财课程可能都会给你讲股票的收益多么多么高,但是他们从来不会给你说玩股票的风险有多高,更不可能给你讲玩股票会严重影响自己的心情,可能连自己的本职工作都会搞砸,所以我不建议职场新人进入股市。</p>
<p>房东忙的时候我会帮他带房客看房,他也给了我小几千块钱的介绍费,加上每个月没交网费直接用他的,还时不时蹭蹭房东的饭局,也给自己省下来周末出去散步的费用了。上半年也给别人分享过两三个课程,在群里分享过一点小技能,大家给发了点红包,交个朋友、图个开心。</p>
<p>总的来讲,理财这方面做得很差,没有花什么时间去学习,我们的大学也没有教给学生一点金融知识,这一年只读了几本写给小白的理财书,今年在这个领域要多花一点功夫,希望能入得了门吧。</p>
<h2 id="写书失败">写书失败</h2>
<p>快要毕业的时候和电子工业出版社签了一份合同,合同内容就是我要写一本叫做《知识图谱:自顶向下方法》,这本书的计划内容是我的毕业设计,已经写了一百多页的内容了,但现在确定以失败告终。</p>
<p>一者我手里现有的数据属于机密数据,没办法拿来直接使用;二来书中有很大一部分内容涉及到网络爬虫,上半年网上曝了很多因为抓数据而入狱的案例,出版社和我都害怕;三者知识图谱所需要的数据量很大,而且我写的那个领域又是中国特有的经济责任审计领域,大量数据都得从政府网站来,更害怕了;最重要的原因是自己懒,写书的那几个月确实非常的累,想想自己都还是个菜鸟呐,有啥资本教给别人知识,心里给了自己后退的理由。</p>
<p>小时候曾夸下海口说要给父亲写个传记,也不知道有没有那么一丢丢可能性实现,写<a href="https://mp.weixin.qq.com/s/3dpKtLW44PXcPfWO8v4SiA">家里的狗</a>时,发现写这样的内容会增加我的多巴胺分泌,以后不开心了就写这样的小故事。</p>
<h2 id="运动健身">运动健身</h2>
<p>在深圳校友会骑行社师兄师姐们的带领下,同时也得益于一起入职的小伙伴送了我一辆 MERIDA,我喜欢上了骑行这项运动,基本上每周五都会出去骑几十公里,中间还参加了环漓江骑行和 TREK100 骑行,锻炼的同时也见到了美丽的风景。深圳对自行车是不太友好的,基本没有什么自行车道,所以我们大部分时间都是等到晚上车少,交警下班了之后才开始骑行。</p>
<p>除了骑行每周一也会打两小时羽毛球,谈不上专业,但至少打的不再是广场球了。偶尔也会出去爬爬山啥的,身体确实比上学时候要好很多,而且多锻炼能让自己的精神面貌更好,精气神好也能稍稍掩盖长得丑的缺点。以前每年再怎么也会因为感冒一类的问题进几次医院,19 年仅一次因为智齿发炎去过医院。</p>
<h2 id="削减迷茫">削减迷茫</h2>
<p>大概在四五月份的时候吧,几乎天天失眠,经常夜里只睡了三四个小时,有时甚至通宵未眠,心里很清楚是因为迷茫了,大概就是「晚上想了千条路,早上醒来走原路」的状态。好在自己的调节能力还不算差,同时也有楼下的叔叔、自己的好朋友能唠唠嗑,差不多两个月就回归正常状态了。</p>
<p>从几个比我晚入职半年的小伙伴那里了解到,他们现在的情况和我四五月份的情况差不多,我想绝大部分普通人都会经历这个迷茫期吧,大部分人也都能通过时间调节过来,调节不过来的那部分人就成为了媒体比较喜欢的人。</p>
<p>现在迷茫的雾气已经没有那么浓了,初入社会肯定有很多的不成熟,但谁不是这样过来的呢?更何况我并不像多数程序员那样交友严重同质化,周末也不会死宅在家里不出去,猜测我应该比大多数人更潇洒自在的,嘿嘿。</p>
<h2 id="新的思想">新的思想</h2>
<p>大家基本都是看着金庸武侠小说(相关影视作品)长大的,没有人写武侠小说能超过金庸。偶然一天在推特上刷到一条评论,大意是:没有人写武侠小说能超过金庸不正代表着社会的进步吗?金庸的成就如此巨大,一个很重要的历史背景是那时候大家没有那么多小说可看呀,哪里像今天遍地的网络小说。咱们没必要去争论这个观点的对错,重要的是它告诉了我们一个不一样的角度去看待问题。</p>
<p>上面只是一个特例,思维方式是一点一点改变的,认知水平是一点一点提升的,一年时间修正了不少我此前狭隘的观点,这样的修正还在继续,我也会让这样的修正持续下去。</p>
<h2 id="写在最后">写在最后</h2>
<p>巴黎圣母院被烧、凉山火灾、女排十连冠、NBA 事件、无锡高架桥倒塌......等等发生在 2019 年的大事,不知道还有多少朋友会记起来。时间从来不会等谁,网友也都是不长记性的,成熟的一部分无非是经历的多了,失望的多了,然后变得更耐操一点,总之生活依旧得继续,人总会亦悲亦喜,那为啥不把悲缩小喜放大呢?</p>
<p>成功没有银弹、没有捷径,<a href="https://mp.weixin.qq.com/s/j1MtPn_2YwPhiLCJHZ4GYQ">少讲大道理,多解决小问题</a>。</p>
]]></content>
</entry>
</feed>