-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
522 lines (301 loc) · 209 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Lucky4</title>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2020-08-29T10:06:11.754Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>Lucky4</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>极客时间《MySQL实战45讲》——06全局锁和表锁</title>
<link href="http://yoursite.com/2020/08/29/%E6%9E%81%E5%AE%A2%E6%97%B6%E9%97%B4%E3%80%8AMySQL%E5%AE%9E%E6%88%9845%E8%AE%B2%E3%80%8B%E2%80%94%E2%80%9406%E5%85%A8%E5%B1%80%E9%94%81%E5%92%8C%E8%A1%A8%E9%94%81/"/>
<id>http://yoursite.com/2020/08/29/极客时间《MySQL实战45讲》——06全局锁和表锁/</id>
<published>2020-08-29T05:23:59.000Z</published>
<updated>2020-08-29T10:06:11.754Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="#前言">前言</a></li><li><a href="#全局锁">全局锁</a><ul><li><a href="#表锁">表锁</a></li><li><a href="#元数据锁">元数据锁</a></li></ul></li><li><a href="#表级锁">表级锁</a></li></ul><p><br></p><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在最近一个需求的上线过程中出现了较大的麻烦,原因是当时 rd 没有对一个表的新字段加索引,导致上线后接口响应变慢,之后在找 DBA 加索引的时候,DBA 对该表进行了 kill 链接操作,之后才将索引加上。当时还不懂原理,最近正好学习到了这门课程,了解了当时为什么这么操作。</p><p>接下来就来介绍下涉及到的锁的相关知识点。</p><p>数据库锁设计的初衷是处理并发问题。作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则。而锁就是用来实现这些访问规则的重要数据结构。根据加锁的范围,MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类。</p><p><br></p><h2 id="全局锁"><a href="#全局锁" class="headerlink" title="全局锁"></a>全局锁</h2><p>全局锁就是对整个数据库实例加锁。</p><p>MySQL 提供了一个加全局读锁的方法,命令是:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">Flush</span> <span class="keyword">tables</span> <span class="keyword">with</span> <span class="keyword">read</span> <span class="keyword">lock</span>;</div></pre></td></tr></table></figure></p><p>释放全局锁:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">unlock</span> <span class="keyword">tables</span>;</div></pre></td></tr></table></figure></p><p>当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。</p><p>全局锁的典型使用场景是,做全库逻辑备份。也就是把整库每个表都 select 出来存成文本。</p><p>对于不支持事务的 MyISAM 等存储引擎,可以使用该方法来进行备份,而对于支持事务的 InnoDB 存储引擎,可以直接使用 MySQL 官方自带的逻辑备份工具 mysqldump ,当 mysqldump 使用参数–single-transaction 的时候,导数据之前就会启动一个事务,来确保拿到一致性视图。而由于 MVCC 的支持,这个过程中数据是可以正常更新的。</p><p><br></p><h2 id="表级锁"><a href="#表级锁" class="headerlink" title="表级锁"></a>表级锁</h2><p>MySQL 里面表级别的锁有两种:一种是<code> 表锁 </code>,一种是<code> 元数据锁(meta data lock,MDL) </code>。</p><p><br></p><h4 id="表锁"><a href="#表锁" class="headerlink" title="表锁"></a>表锁</h4><p>表锁的语法:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">lock</span> <span class="keyword">tables</span> t <span class="keyword">read</span>;</div><div class="line"><span class="keyword">lock</span> <span class="keyword">tables</span> t write;</div><div class="line"><span class="keyword">unlock</span> <span class="keyword">tables</span>;</div></pre></td></tr></table></figure></p><p>作用:</p><ul><li>加读锁:本线程可读,不可写;其他线程可读,不可写。</li><li>加写锁:本线程可读,可写;其他线程不可读,不可写。</li></ul><h4 id="元数据锁"><a href="#元数据锁" class="headerlink" title="元数据锁"></a>元数据锁</h4><p>另一类表级的锁是 <code> MDL(metadata lock) </code>。MDL 不需要显式使用,在访问一个表的时候会被自动加上。MDL 的作用是,保证读写的正确性。你可以想象一下,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的。</p><p>因此,在 MySQL 5.5 版本中引入了 MDL,当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。</p><ul><li>读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。</li><li>读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。</li></ul><p>事务中的 MDL 锁,在语句执行开始时申请,但是语句结束后并不会马上释放,而会等到整个事务提交后再释放。</p><img src="/2020/08/29/极客时间《MySQL实战45讲》——06全局锁和表锁/示例.jpg" alt="示例" title="示例"><p>我们可以看到 session A 先启动,这时候会对表 t 加一个 MDL 读锁。由于 session B 需要的也是 MDL 读锁,因此可以正常执行。</p><p>之后 session C 会被 blocked,是因为 session A 的 MDL 读锁还没有释放,而 session C 需要 MDL 写锁,因此只能被阻塞。</p><p>如果只有 session C 自己被阻塞还没什么关系,但是之后所有要在表 t 上新申请 MDL 读锁的请求也会被 session C 阻塞。前面我们说了,所有对表的增删改查操作都需要先申请 MDL 读锁,就都被锁住,等于这个表现在完全不可读写了。</p><p>如果某个表上的查询语句频繁,而且客户端有重试机制,也就是说超时后会再起一个新 session 再请求的话,这个库的线程很快就会爆满。</p><p>你现在应该知道了,事务中的 MDL 锁,在语句执行开始时申请,但是语句结束后并不会马上释放,而会等到整个事务提交后再释放。</p><p>那么如何安全地给小表加字段?</p><p>alter table 语句里面设定等待时间,如果在这个指定的等待时间里面能够拿到 MDL 写锁最好,拿不到也不要阻塞后面的业务语句,先放弃。之后开发人员或者 DBA 再通过重试命令重复这个过程。</p><p><br></p><h2 id="行锁"><a href="#行锁" class="headerlink" title="行锁"></a>行锁</h2>]]></content>
<summary type="html">
<p>目录:</p>
<ul>
<li><a href="#前言">前言</a></li>
<li><a href="#全局锁">全局锁</a><ul>
<li><a href="#表锁">表锁</a></li>
<li><a href="#元数据锁">元数据锁</a></li>
</summary>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
</entry>
<entry>
<title>《高性能的MySQL》阅读笔记——创建高性能的索引</title>
<link href="http://yoursite.com/2020/08/08/%E3%80%8A%E9%AB%98%E6%80%A7%E8%83%BD%E7%9A%84MySQL%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0%E2%80%94%E2%80%94%E5%88%9B%E5%BB%BA%E9%AB%98%E6%80%A7%E8%83%BD%E7%9A%84%E7%B4%A2%E5%BC%95/"/>
<id>http://yoursite.com/2020/08/08/《高性能的MySQL》阅读笔记——创建高性能的索引/</id>
<published>2020-08-08T09:23:57.000Z</published>
<updated>2020-09-19T15:45:27.991Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="#前言">前言</a></li><li><a href="#维护索引和表">维护索引和表</a><ul><li><a href="#优化排序">优化排序</a></li></ul></li></ul><br><h2 id="维护索引和表"><a href="#维护索引和表" class="headerlink" title="维护索引和表"></a>维护索引和表</h2><h4 id="优化排序"><a href="#优化排序" class="headerlink" title="优化排序"></a>优化排序</h4><p>对于索引的选择性非常低的列,如 sex 列,该如何排序?</p><p>可以增加一些特殊的索引来做排序,如创建 (sex, rating) 索引用于下面的查询:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> cols <span class="keyword">FROM</span> <span class="keyword">profiles</span> <span class="keyword">WHERE</span> sex=<span class="string">'M'</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> rating <span class="keyword">LIMIT</span> <span class="number">10</span>;</div></pre></td></tr></table></figure></p><p>即使有索引,但是用户需要翻页,并且页面比较靠后的时候可能也会很慢。如下面的 sql :<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> cols <span class="keyword">FROM</span> <span class="keyword">profiles</span> <span class="keyword">WHERE</span> sex=<span class="string">'M'</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> rating <span class="keyword">LIMIT</span> <span class="number">100000</span>, <span class="number">10</span>;</div></pre></td></tr></table></figure></p><p>随着偏移量的增加 MySQL 需要花费大量的时间来扫描需要丢弃的数据。</p><p>一个解决方法就是延迟关联,通过覆盖索引返回需要的主键,在根据这些主键关联表,获得需要的行。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> cols <span class="keyword">FROM</span> <span class="keyword">profiles</span> <span class="keyword">INNER</span> <span class="keyword">JOIN</span> (</div><div class="line"> <span class="keyword">SELECT</span> cols <span class="keyword">FROM</span> <span class="keyword">profiles</span> <span class="keyword">WHERE</span> x.sex=<span class="string">'M'</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> rating <span class="keyword">LIMIT</span> <span class="number">100000</span>, <span class="number">10</span></div><div class="line">) <span class="keyword">AS</span> x <span class="keyword">USING</span>(cols);</div></pre></td></tr></table></figure></p><p>延迟关联其实涉及到 LIMIT 语句的一些细节用法,接下来参考 MySQL 官方文档,介绍下 ORDER BY 和 LIMIT 。</p>]]></content>
<summary type="html">
<p>目录:</p>
<ul>
<li><a href="#前言">前言</a></li>
<li><a href="#维护索引和表">维护索引和表</a><ul>
<li><a href="#优化排序">优化排序</a></li>
</ul>
</li>
</ul>
<br>
</summary>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
</entry>
<entry>
<title>《Python参考手册》阅读笔记——协程</title>
<link href="http://yoursite.com/2020/07/19/%E3%80%8APython%E5%8F%82%E8%80%83%E6%89%8B%E5%86%8C%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0%E2%80%94%E2%80%94%E5%8D%8F%E7%A8%8B/"/>
<id>http://yoursite.com/2020/07/19/《Python参考手册》阅读笔记——协程/</id>
<published>2020-07-19T06:52:42.000Z</published>
<updated>2020-08-15T13:33:53.583Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="#1-协程">1 协程</a><ul><li><a href="#yield-语句">yield 语句</a></li><li><a href="#生成器">生成器</a></li><li><a href="#协程">协程</a></li><li><a href="#基于任务调度程序">基于任务调度程序</a></li></ul></li></ul><p><br></p><h2 id="1-协程"><a href="#1-协程" class="headerlink" title="1 协程"></a>1 协程</h2><p><code>协程</code> 是 Python 的语言特性中一个很重要的部分,学习要抓住重点,因此,很有必要了解一下。</p><p>了解协程之前,先熟悉一下<code>生成器</code>的概念。</p><p><br></p><h4 id="yield-语句"><a href="#yield-语句" class="headerlink" title="yield 语句"></a>yield 语句</h4><p>使用 <code>yield</code> 语句,可以定义生成器对象,让函数生成一个结果序列,以便在迭代中使用。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">countdown</span><span class="params">(n)</span>:</span></div><div class="line"> <span class="keyword">while</span> n > <span class="number">0</span>:</div><div class="line"> <span class="keyword">yield</span> n</div><div class="line"> n -= <span class="number">1</span></div></pre></td></tr></table></figure><h4 id="生成器"><a href="#生成器" class="headerlink" title="生成器"></a>生成器</h4><p>任何使用 yield 的关键字函数都称为 <code>生成器</code> ,调用生成器将创建一个对象,并通过调用 <code>next()</code> 方法生成序列。</p><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>c = countdoen(<span class="number">5</span>)</div><div class="line"><span class="meta">>>> </span>c.next()</div><div class="line"><span class="meta">>>> </span><span class="number">5</span></div><div class="line"><span class="meta">>>> </span>c.next</div><div class="line"><span class="meta">>>> </span><span class="number">4</span></div></pre></td></tr></table></figure><p>next() 调用生成器函数一直运行到下一条 yield 语句为止,此时 next() 将返回值传递给 yield ,函数终止执行。</p><p>通常使用 for 循环的方式生成值:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span><span class="keyword">for</span> i <span class="keyword">in</span> countdown(<span class="number">5</span>):</div><div class="line"> <span class="keyword">print</span> i</div><div class="line"></div><div class="line"><span class="number">5</span> <span class="number">4</span> <span class="number">3</span> <span class="number">2</span> <span class="number">1</span></div><div class="line">>>></div></pre></td></tr></table></figure></p><p>异常:</p><ul><li>StopIteration 异常</li><li>GeneratorExit 异常</li></ul><p>决不能通过单独的线程或信号处理程序在该生成器上异步地调用 close() 方法。</p><p>通过调用 <code>close()</code> 方法关闭生成器对象。<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>c.close()</div></pre></td></tr></table></figure></p><h4 id="协程"><a href="#协程" class="headerlink" title="协程"></a>协程</h4><p>通常,函数运行时要输入一组参数,但是也可以把函数编写为一个任务,从而能处理发送给它的一系列输入,这类函数称为 <code>协程</code> 。可使用 <code>(yield)</code> 的形式创建协程。如下所示:</p><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_matches</span><span class="params">(matchtext)</span>:</span></div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> line = (<span class="keyword">yield</span>)</div><div class="line"> <span class="keyword">if</span> matchtext <span class="keyword">in</span> line:</div><div class="line"> <span class="keyword">print</span> line</div></pre></td></tr></table></figure><p>调用:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>matcher = print_matches(<span class="string">"python"</span>)</div><div class="line"><span class="meta">>>> </span>matcher.next()</div><div class="line"><span class="meta">>>> </span>matcher.send(<span class="string">"Hello World"</span>)</div><div class="line"><span class="meta">>>> </span>matcher.send(<span class="string">"Python is cool"</span>)</div><div class="line">python <span class="keyword">is</span> cool</div><div class="line"><span class="meta">>>> </span>matcher.close()</div></pre></td></tr></table></figure></p><p>其中 matcher.next() 用来执行到 (yield) 之前,协程终止。使用 matcher.send() 为协程发送值,(yield) 表达式返回这个值,并执行下面的语句,直至再次遇到 (yield) 语句。</p><p>协程的运行一般是无期限的,除非它被显式的关闭或自己退出。使用 close() 方法可以关闭输入值的流。</p><p>协程可用于实现某种形式的并发,例如,安排一个集中式的任务管理器或时间循环,将数据发送到成百上千个用于执行任务的协程中。协程经常可以和使用消息队列的程序一起使用。</p><p><br></p><h4 id="基于任务调度程序"><a href="#基于任务调度程序" class="headerlink" title="基于任务调度程序"></a>基于任务调度程序</h4><p>select 模块可以用来实现基于小任务(greenlet)或协程的服务器,下面是一个使用协程实现的基于 I/O 的任务调度程序,其中借助了 select 模块进行循环获取 I/O。<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> collections</div><div class="line"><span class="keyword">import</span> select</div><div class="line"><span class="keyword">import</span> types</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Task</span><span class="params">(object)</span>:</span></div><div class="line"> <span class="string">"""</span></div><div class="line"> 表示运行任务的对象</div><div class="line"> """</div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init</span><span class="params">(self, target)</span>:</span></div><div class="line"> self.target = target</div><div class="line"> self.sendval = <span class="keyword">None</span></div><div class="line"> self.stack = []</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">(self)</span>:</span></div><div class="line"> <span class="keyword">try</span>:</div><div class="line"> result = self.target.send(self.sendval)</div><div class="line"> <span class="keyword">if</span> isinstance(result, SystemCall):</div><div class="line"> <span class="keyword">return</span> result</div><div class="line"> <span class="keyword">if</span> isinstance(result, types.GeneratorType):</div><div class="line"> self.stack.append(self.target)</div><div class="line"> self.sendval = <span class="keyword">None</span></div><div class="line"> self.target = result</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> self.stack:</div><div class="line"> <span class="keyword">return</span></div><div class="line"> self.sendval = result</div><div class="line"> self.target = self.stack.pop()</div><div class="line"> <span class="keyword">except</span> StopIteration:</div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> self.stack:</div><div class="line"> <span class="keyword">raise</span></div><div class="line"> self.sendval = <span class="keyword">None</span></div><div class="line"> self.target = self.stack.pop()</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">SystemCall</span><span class="params">(object)</span>:</span></div><div class="line"> <span class="string">"""</span></div><div class="line"> 表示“系统调用”的对象</div><div class="line"> """</div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">handle</span><span class="params">(self, sched, task)</span>:</span></div><div class="line"> <span class="keyword">pass</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">ReadWait</span><span class="params">(SystemCall)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, f)</span>:</span></div><div class="line"> self.f = f</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">handle</span><span class="params">(self, sched, task)</span>:</span></div><div class="line"> fileno = self.f.fileno()</div><div class="line"> sched.readwait(task, fileno)</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">WriteWait</span><span class="params">(SystemCall)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, f)</span>:</span></div><div class="line"> self.f = f</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">handle</span><span class="params">(self, sched, task)</span>:</span></div><div class="line"> fileno = self.f.fileno()</div><div class="line"> sched.writewait(task, fileno)</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">NewTask</span><span class="params">(SystemCall)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, target)</span>:</span></div><div class="line"> self.target = target</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">handle</span><span class="params">(self, sched, target)</span>:</span></div><div class="line"> sched.new(self.target)</div><div class="line"> sched.schedule(task)</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Scheduler</span><span class="params">(object)</span>:</span></div><div class="line"> <span class="string">"""</span></div><div class="line"> 调度程序对象</div><div class="line"> """</div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></div><div class="line"> self.task_queue = collections.deque()</div><div class="line"> self.read_waiting = {}</div><div class="line"> self.write_waiting = {}</div><div class="line"> self.numtasks = <span class="number">0</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">schedule</span><span class="params">(self, task)</span>:</span></div><div class="line"> <span class="string">"""</span></div><div class="line"> 将任务放入任务调度队列</div><div class="line"> """</div><div class="line"> self.task_queue.append(task)</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">new</span><span class="params">(self, target)</span>:</span></div><div class="line"> <span class="string">"""</span></div><div class="line"> 通过协程新建任务</div><div class="line"> """</div><div class="line"> newtask = Task(target)</div><div class="line"> self.schedule(newtask)</div><div class="line"> self.numtasks += <span class="number">1</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">readwait</span><span class="params">(self, task, fd)</span>:</span></div><div class="line"> <span class="string">"""</span></div><div class="line"> 让任务等待文件描述符上的数据</div><div class="line"> """</div><div class="line"> self.read_waiting[fd] = task</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">writewait</span><span class="params">(self, task, fd)</span>:</span></div><div class="line"> <span class="string">"""</span></div><div class="line"> 让任务等待写入文件描述符</div><div class="line"> """</div><div class="line"> self.write_waiting[fd] = task</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">mainloop</span><span class="params">(self, count=<span class="number">-1</span>, timeout=None)</span>:</span></div><div class="line"> <span class="string">"""</span></div><div class="line"> 调度程序主循环</div><div class="line"> """</div><div class="line"> <span class="keyword">while</span> self.numtasks:</div><div class="line"> <span class="keyword">if</span> self.read_waiting <span class="keyword">or</span> self.write_waiting:</div><div class="line"> wait = <span class="number">0</span> <span class="keyword">if</span> self.task_queue <span class="keyword">else</span> timeout</div><div class="line"> r, w, e = select.select(self.read_waiting, self.write_waiting, [],</div><div class="line"> wait)</div><div class="line"> <span class="keyword">for</span> fileno <span class="keyword">in</span> r:</div><div class="line"> self.schedule(self.read_waiting.pop(fileno))</div><div class="line"> <span class="keyword">for</span> fileno <span class="keyword">in</span> w:</div><div class="line"> self.schedule(self.write_waiting.pop(fileno))</div><div class="line"></div><div class="line"> <span class="keyword">while</span> self.task_queue:</div><div class="line"> task = self.task_queue.popleft()</div><div class="line"> <span class="keyword">try</span>:</div><div class="line"> result = task.run()</div><div class="line"> <span class="keyword">if</span> isinstance(result, SystemCall):</div><div class="line"> result.handle(self, task)</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> self.schedule(task)</div><div class="line"> <span class="keyword">except</span> StopIteration:</div><div class="line"> self.numtasks -= <span class="number">1</span></div><div class="line"> </div><div class="line"> <span class="keyword">if</span> count > <span class="number">0</span>:</div><div class="line"> count -= <span class="number">1</span></div><div class="line"> <span class="keyword">if</span> count == <span class="number">0</span>:</div><div class="line"> <span class="keyword">return</span></div></pre></td></tr></table></figure></p><p>使用这种 I/O 任务调度程序实现的网络时间服务器示例:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">from</span> socket <span class="keyword">import</span> socket, AF_INET, SOCK_STREAM</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">time_server</span><span class="params">(address)</span>:</span></div><div class="line"> <span class="keyword">import</span> time</div><div class="line"> </div><div class="line"> s = socket(AF_INET, SOCK_STREAM)</div><div class="line"> s.bind(address)</div><div class="line"> s.listen(<span class="number">5</span>)</div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> <span class="keyword">yield</span> ReadWait(s)</div><div class="line"> conn, addr = s.accept()</div><div class="line"> <span class="keyword">print</span> <span class="string">"Connection from %s"</span> % str(addr)</div><div class="line"> <span class="keyword">yield</span> WriteWait(conn)</div><div class="line"> resp = time.ctime() + <span class="string">"\r\n"</span></div><div class="line"> conn.send(resp.encode(<span class="string">'latin-1'</span>))</div><div class="line"> conn.close()</div><div class="line"></div><div class="line"></div><div class="line">sched = Scheduler()</div><div class="line">sched.new(time_server((<span class="string">''</span>, <span class="number">10000</span>))) <span class="comment"># 10000端口上的服务器</span></div><div class="line">sched.new(time_server((<span class="string">''</span>, <span class="number">11000</span>))) <span class="comment"># 11000端口上的服务器</span></div><div class="line">sched.run()</div></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<p>目录:</p>
<ul>
<li><a href="#1-协程">1 协程</a><ul>
<li><a href="#yield-语句">yield 语句</a></li>
<li><a href="#生成器">生成器</a></li>
<li><a href="#协程"
</summary>
<category term="Python" scheme="http://yoursite.com/tags/Python/"/>
</entry>
<entry>
<title>《大型网站技术架构——核心原理与案例分析》阅读笔记</title>
<link href="http://yoursite.com/2020/07/19/%E3%80%8A%E5%A4%A7%E5%9E%8B%E7%BD%91%E7%AB%99%E6%8A%80%E6%9C%AF%E6%9E%B6%E6%9E%84%E2%80%94%E2%80%94%E6%A0%B8%E5%BF%83%E5%8E%9F%E7%90%86%E4%B8%8E%E6%A1%88%E4%BE%8B%E5%88%86%E6%9E%90%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
<id>http://yoursite.com/2020/07/19/《大型网站技术架构——核心原理与案例分析》阅读笔记/</id>
<published>2020-07-19T02:03:42.000Z</published>
<updated>2020-08-15T13:26:34.559Z</updated>
<content type="html"><![CDATA[<p>最近在繁忙的测试工作中,抽出了一点点时间,粗略的看了这本关于架构的书。像我这种小菜鸡看这种书也就是普及一下知识,但是书中应用性能优化部分讲到的关于程序编写的几点内容,我觉得对于以后的编程学习还是很重要的,由于没有时间写博客,就先留个坑,以后补上。</p><p>着急找知识可以先看这个链接上的内容:<a href="https://blog.csdn.net/xiaoxufox/article/details/53176455" target="_blank" rel="external">https://blog.csdn.net/xiaoxufox/article/details/53176455</a></p>]]></content>
<summary type="html">
<p>最近在繁忙的测试工作中,抽出了一点点时间,粗略的看了这本关于架构的书。像我这种小菜鸡看这种书也就是普及一下知识,但是书中应用性能优化部分讲到的关于程序编写的几点内容,我觉得对于以后的编程学习还是很重要的,由于没有时间写博客,就先留个坑,以后补上。</p>
<p>着急找知识可
</summary>
<category term="架构" scheme="http://yoursite.com/tags/%E6%9E%B6%E6%9E%84/"/>
</entry>
<entry>
<title>读写锁</title>
<link href="http://yoursite.com/2020/06/27/%E8%AF%BB%E5%86%99%E9%94%81/"/>
<id>http://yoursite.com/2020/06/27/读写锁/</id>
<published>2020-06-27T10:20:32.000Z</published>
<updated>2020-08-16T02:42:00.810Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="#1-读写锁">1 读写锁</a><ul><li><a href="#前言">前言</a></li><li><a href="#信号量机制解决读者写者问题">信号量机制解决读者写者问题</a></li><li><a href="#读写锁模拟">读写锁模拟</a></li><li><a href="#延伸">延伸</a></li><li><a href="#参考">参考</a></li></ul></li></ul><h2 id="1-读写锁"><a href="#1-读写锁" class="headerlink" title="1 读写锁"></a>1 读写锁</h2><h4 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h4><p>在阅读《高性能的MySQL》第一章时,提及到了 MySQL 的并发控制,其最基本的实现原理就是读写锁,那么如何实现一个读写锁呢?下面是一个教科书版本的读写问题描述:</p><blockquote><p>一个数据文件或记录可被多个进程共享,允许多个进程同时读一个共享对象,因为读操作不会使数据文件混乱,不允许写进程与其他读进程或写进程同时访问共享对象,因为这种访问会引起混乱。</p></blockquote><p><br></p><h4 id="信号量机制解决读者写者问题"><a href="#信号量机制解决读者写者问题" class="headerlink" title="信号量机制解决读者写者问题"></a>信号量机制解决读者写者问题</h4><p>设置一个读写进程互斥信号量 <code>Wmutex</code> ,设置一个整形变量 <code>Readcount</code> 表示正在读的进程数目。只要有一个 Reader 进程在读,便不允许 Writer 进程去写。仅当,<code>Readercount=0</code> ,表示尚无 Reader 进程在读时,Reader 进程执行 <code> Wait(Wmutex) </code> 操作,防止写进程操作,为释放资源做准备。若 <code> wait(Wmutex) </code> 操作成功,Reader 进程便进行读操作,<code> Readercount+1 </code>。同理,当 Reader 进程在执行了<code> Readercount-1 </code>后其值为 0 时,才需执行<code> signal(Wmutex) </code>操作,以便让 Write 进程写操作。又因为<code> Readercount </code>是一个可被多个 Reader 进程访问的临界资源,因此设置一个互斥信号量<code> rmutex </code>。</p><p><br></p><h4 id="读写锁模拟"><a href="#读写锁模拟" class="headerlink" title="读写锁模拟"></a>读写锁模拟</h4><p>这里利用了 Python 的线程来进行了算法实现:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># coding: utf-8</span></div><div class="line"><span class="keyword">import</span> threading</div><div class="line"><span class="keyword">import</span> time</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">ReadWriteLock</span><span class="params">(object)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></div><div class="line"> self.w_lock = threading.Lock()</div><div class="line"> self.r_lock = threading.Lock()</div><div class="line"> self.read_count = <span class="number">0</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">acquire_read</span><span class="params">(self)</span>:</span></div><div class="line"> self.r_lock.acquire(blocking=<span class="keyword">True</span>)</div><div class="line"> <span class="keyword">if</span> self.read_count == <span class="number">0</span>:</div><div class="line"> self.w_lock.acquire()</div><div class="line"> self.read_count += <span class="number">1</span></div><div class="line"> self.r_lock.release()</div><div class="line"> print(self.read_count)</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">release_read</span><span class="params">(self)</span>:</span></div><div class="line"> self.r_lock.acquire(blocking=<span class="keyword">True</span>)</div><div class="line"> self.read_count -= <span class="number">1</span></div><div class="line"> <span class="keyword">if</span> self.read_count == <span class="number">0</span>:</div><div class="line"> self.w_lock.release()</div><div class="line"> self.r_lock.release()</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">acquire_write</span><span class="params">(self)</span>:</span></div><div class="line"> self.w_lock.acquire(blocking=<span class="keyword">True</span>)</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">release_write</span><span class="params">(self)</span>:</span></div><div class="line"> self.w_lock.release()</div><div class="line"></div><div class="line"></div><div class="line">rw_lock = ReadWriteLock()</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">ReadThread</span><span class="params">(threading.Thread)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></div><div class="line"> threading.Thread.__init__(self)</div><div class="line"> self.rw_lock = rw_lock</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">(self)</span>:</span></div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> <span class="keyword">try</span>:</div><div class="line"> rw_lock.acquire_read()</div><div class="line"> print(<span class="string">u"Thread %s is reading..."</span> % self.name)</div><div class="line"> time.sleep(<span class="number">2</span>)</div><div class="line"> <span class="keyword">finally</span>:</div><div class="line"> rw_lock.release_read()</div><div class="line"></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">WriteThread</span><span class="params">(threading.Thread)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self)</span>:</span></div><div class="line"> threading.Thread.__init__(self)</div><div class="line"> self.rw_lock = rw_lock</div><div class="line"> </div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">run</span><span class="params">(self)</span>:</span></div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> <span class="keyword">try</span>:</div><div class="line"> rw_lock.acquire_write()</div><div class="line"> print(<span class="string">u"Thread %s is writing..."</span> % self.name)</div><div class="line"> time.sleep(<span class="number">2</span>)</div><div class="line"> <span class="keyword">finally</span>:</div><div class="line"> rw_lock.release_write()</div><div class="line"></div><div class="line"></div><div class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</div><div class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">5</span>):</div><div class="line"> read_thread = ReadThread()</div><div class="line"> read_thread.start()</div><div class="line"></div><div class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> range(<span class="number">5</span>):</div><div class="line"> write_thread = WriteThread()</div><div class="line"> write_thread.start()</div></pre></td></tr></table></figure></p><p><br></p><h4 id="延伸"><a href="#延伸" class="headerlink" title="延伸"></a>延伸</h4><p>读优先,写优先</p><p><br></p><h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><p>《计算机操作系统(第4版)汤小丹、汤子瀛》</p>]]></content>
<summary type="html">
<p>目录:</p>
<ul>
<li><a href="#1-读写锁">1 读写锁</a><ul>
<li><a href="#前言">前言</a></li>
<li><a href="#信号量机制解决读者写者问题">信号量机制解决读者写者问题</a></li>
<li><a
</summary>
<category term="Python" scheme="http://yoursite.com/tags/Python/"/>
</entry>
<entry>
<title>MySQL常见面试问题</title>
<link href="http://yoursite.com/2020/06/26/MySQL%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98/"/>
<id>http://yoursite.com/2020/06/26/MySQL常见面试问题/</id>
<published>2020-06-26T02:58:49.000Z</published>
<updated>2020-07-31T15:21:06.918Z</updated>
<content type="html"><![CDATA[<h4 id="对于性别字段是否应该加索引?"><a href="#对于性别字段是否应该加索引?" class="headerlink" title="对于性别字段是否应该加索引?"></a>对于性别字段是否应该加索引?</h4><p>否,索引选择性低,导致过多随机 IO。</p><h4 id="主键为什么要使用自增主键-AUTO-INCREMENT?"><a href="#主键为什么要使用自增主键-AUTO-INCREMENT?" class="headerlink" title="主键为什么要使用自增主键 AUTO_INCREMENT?"></a>主键为什么要使用自增主键 AUTO_INCREMENT?</h4><ul><li>插入数据顺序插入,追加操作,避免分页操作,减少对性能的影响。</li><li>从性能考虑,如果使用其他如身份证号做主键,那么会导致辅助索引的叶节点存储空间的浪费。</li></ul><h4 id="为什么要设置主键,主键的选择条件?"><a href="#为什么要设置主键,主键的选择条件?" class="headerlink" title="为什么要设置主键,主键的选择条件?"></a>为什么要设置主键,主键的选择条件?</h4><ul><li>构建索引,提高查询效率,减少 IO 操作,避免额外的排序工作。</li><li>占用的空间不能过大,否则会导致辅助索引存储空间的浪费。</li><li>唯一,非空?</li></ul>]]></content>
<summary type="html">
<h4 id="对于性别字段是否应该加索引?"><a href="#对于性别字段是否应该加索引?" class="headerlink" title="对于性别字段是否应该加索引?"></a>对于性别字段是否应该加索引?</h4><p>否,索引选择性低,导致过多随机 IO。</p
</summary>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
</entry>
<entry>
<title>《MySQL技术内幕》阅读笔记——索引与算法</title>
<link href="http://yoursite.com/2020/06/22/%E3%80%8AMySQL%E6%8A%80%E6%9C%AF%E5%86%85%E5%B9%95%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0%E2%80%94%E2%80%94%E7%B4%A2%E5%BC%95%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
<id>http://yoursite.com/2020/06/22/《MySQL技术内幕》阅读笔记——索引与算法/</id>
<published>2020-06-22T15:55:20.000Z</published>
<updated>2020-08-08T09:46:37.221Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="#5-索引与算法">5 索引与算法</a><ul><li><a href="#二分查找法">二分查找法</a></li><li><a href="#B+-树">B+ 树</a></li><li><a href="#B+-树索引">B+ 树索引</a></li><li><a href="#B+-树索引的使用">B+ 树索引的使用</a></li><li><a href="#几种常见索引">几种常见索引</a></li></ul></li></ul><h2 id="5-索引与算法"><a href="#5-索引与算法" class="headerlink" title="5 索引与算法"></a>5 索引与算法</h2><p>InnoDB 存储引擎支持两种常见的索引,一种是 B+ 树索引,另一种是哈希索引。哈希索引是自适应的,根据标情况自动生成。B+ 树中的 B 不是代表二叉树(binary),而是代表平衡(balance)。</p><p>B+ 树索引并不能找到一个给定键值的具体行,B+ 树能找到的是具体被查找数据行所在的页。然后数据库把页读入内存,再在内存中进行查找,得到查找的数据。</p><p><br></p><h4 id="二分查找法"><a href="#二分查找法" class="headerlink" title="二分查找法"></a>二分查找法</h4><p>MySQL 中的页是 InnoDB 磁盘管理的最小单位。每页的 Page Directory 的槽是按照主键的顺序存放的,对于某一条具体记录的查询是通过 Page Directory 进行二分查找进行二分得到的。</p><p><br></p><h4 id="B-树"><a href="#B-树" class="headerlink" title="B+ 树"></a>B+ 树</h4><p>B+ 树是多路平衡二叉树,在 B+ 树中,所有记录节点都是按照键值大小顺序存放在同一层页节点中,各个叶节点指针进行连接。</p><p>一颗 B+ 树示意图:<br><img src="/2020/06/22/《MySQL技术内幕》阅读笔记——索引与算法/B+树.jpg" alt="B+树" title="B+树"></p><p>B+ 树的插入数据后,为了保持平衡,不可避免的会进行拆页操作,拆页同时意味着对磁盘的操作。</p><p>B+ 树的删除操作会进行页的合并操作。</p><p><br></p><h4 id="B-树索引"><a href="#B-树索引" class="headerlink" title="B+ 树索引"></a>B+ 树索引</h4><p>B+ 树索引本质是 B+ 树在数据库中的实现,在数据库中 B+ 树的高度一般在 2~3层,即查找某一键值的行记录,最多需要 2 到 3 次 I/O。</p><p>B+ 树索引可分为聚集索引和非聚集索引,区别是索引的叶节点存放的是整行的数据还是单纯的主键。</p><p><br></p><p><strong>聚集索引</strong><br>InnoDB 存储引擎是索引组织表,即表中的数据按主键顺序存放,而聚集索引就是按照每张表的主键构造一颗 B+ 树,叶节点存放着整张表的行数据,聚集索引的叶节点就是数据页。数据是索引的一部分,每一个数据页通过一个双向链表进行链接。</p><p>聚集索引的存储并不是物理上连续,而是逻辑上连续。其中有两点:一是页通过双向链表链接,页按照主键顺序排列。另一点是每个页中的记录也是通过双向链表进行维护,物理存储上同样可以不按照主键存储。</p><p>聚集索引对于主键的排序查找和范围查找速度很快:</p><ul><li>主键排序查找:如查询最后注册的 10 为用户,由于 B+ 树索引是双向链表,可以快速找到最后一个数据页,并取出 10 条记录。</li><li>范围查找:如果要查找主键范围内的数据,通过页节点的上层中间节点就可以得到页的范围,之后直接读取数据页。</li></ul><p><strong>辅助索引</strong><br>也叫非聚集索引,索引节点包含索引键值和主键值,用来找到与索引对应的行数据,该过程又称为“回表”操作,会进行多余的 I/O 操作。</p><p>非聚集索引示意图:<br><img src="/2020/06/22/《MySQL技术内幕》阅读笔记——索引与算法/非聚集索引.jpg" alt="非聚集索引" title="非聚集索引"></p><p><strong>B+树索引管理</strong><br>创建索引:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> tbl_name</div><div class="line"><span class="keyword">ADD</span> <span class="keyword">KEY</span> index_name (<span class="keyword">column</span>);</div></pre></td></tr></table></figure></p><p>删除索引:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> tbl_name</div><div class="line"><span class="keyword">DROP</span> <span class="keyword">KEY</span> index_name;</div></pre></td></tr></table></figure></p><p>对于索引的添加或者删除操作,MySQL 会先创建一张新的临时表,然后把数据导入临时表,删除原表,在把临时表重命名为原来的表。</p><p>重建索引可以优化由于分页导致的磁盘空间浪费,可以使用如下 sql 语句,避免删除原表而导致的多余操作:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> T <span class="keyword">engine</span>=<span class="keyword">InnoDB</span></div></pre></td></tr></table></figure></p><p>参考极客时间 MySQL 公开课,索引上集。</p><p><br></p><h4 id="B-树索引的使用"><a href="#B-树索引的使用" class="headerlink" title="B+ 树索引的使用"></a>B+ 树索引的使用</h4><p><strong>法则:访问高选择性字段并从表中取出很少一部分行时使用 B+ 树索引。</strong></p><p>案例1:对于性别字段是否要加索引?<br>答:不行,因为索引的选择性低,会导致过多的回表操作,进而导致过多的随机 I/O ,MySQL 优化器会认为不如直接使用全表扫描。</p><p>可以使用 force index 强制使用索引,或者改变写法,如:order b, a ,引导优化器使用索引。</p><p>更多内容参考,极客时间 MySQL 课程第 10 讲。</p><p><strong>顺序读、随机读</strong><br>顺序读:是指顺序的读取磁盘上的块。<br>随机读:指访问的块不是连续的,需要磁头不断移动,读取速度较低。</p><p>在数据库中,顺序读指根据索引的叶节点数据就能顺序地读取所需的行数据。</p><p>随机读,一般指访问叶节点不能完全得到结果,需要辅助索引叶节点中的主键去找实际的行数据。一般来说,辅助索引和主键所在的数据段不同,因此访问是随机的。</p><p><strong>辅助索引的优化使用</strong><br>InnoDB 会先从辅助索引的页节点判断是否能得到所需的数据,如果能,优化器就会优先选择辅助索引。</p><p><br></p><h4 id="几种常见索引"><a href="#几种常见索引" class="headerlink" title="几种常见索引"></a>几种常见索引</h4><p><strong>联合索引</strong><br>联合索引指对表上的多个列做索引,可以避免多余的回表操作,联合索引还是一颗 B+ 树,不同的是索引的键值的数量大于 2。</p><p>联合索引示意图:<br><img src="/2020/06/22/《MySQL技术内幕》阅读笔记——索引与算法/联合索引.jpg" alt="联合索引" title="联合索引"></p><p>对于一个联合索引 (a, b),执行以下查询:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">TABLE</span> <span class="keyword">WHERE</span> a=xxx;</div><div class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">TABLE</span> <span class="keyword">WHERE</span> a=xx, b=xxx;</div><div class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> <span class="keyword">TABLE</span> <span class="keyword">WHERE</span> b=xxx;</div></pre></td></tr></table></figure></p><p>前两个 SQL 语句会使用索引,第三个索引会失效,因为页节点上 b 的值不是排序的,查询使用不到索引。</p><p>如果需要查询排序的结果,可以联合所以索引,避免多余的排序操作。如下 SQL ,优化器会优先选择联合索引,相比单列索引,可以避免不必要的排序操作:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> bug_log <span class="keyword">WHERE</span> userid = <span class="number">1</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span> buy_date <span class="keyword">DESC</span> <span class="keyword">LIMIT</span> <span class="number">3</span>;</div></pre></td></tr></table></figure></p><p><strong>唯一索引</strong><br>创建唯一索引:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> tbl_name</div><div class="line"><span class="keyword">ADD</span> <span class="keyword">UNIQUE</span>(<span class="keyword">column</span>)</div></pre></td></tr></table></figure></p><p>唯一索引与普通索引的查询过程:</p><ul><li>普通索引:查找到满足条件的第一个记录 (5, 500) 后,需查找下一个记录,直到不满足 k=5 的条件为止。</li><li>唯一索引:找到第一个满足条件的记录后,就会停止检索。</li></ul><p>在查询性能上没有多大差别。</p><p>唯一索引与普通索引的更新过程:</p><ul><li>要更新的目标页在内存中<ul><li>普通索引:找到 3 到 5 之间的位置,插入数值,语句执行结束。</li><li>唯一索引:找到 3 到 5 之间的位置,判断是否有冲突,插入数值,语句执行结束。</li></ul></li><li>目标页不在内存中<ul><li>普通索引:将记录更新在 change buffer 中,语句执行结束。</li><li>唯一索引:需要将数据页读入内存,判断是否有冲突,插入值,语句执行结束。</li></ul></li></ul><p>使用 chagge buffer 减少了随机磁盘访问,对于写多读少的场景适合使用,如账单、日志类系统。</p><p>更多内容查看,极客时间 MySQL 教程第 9 讲。</p>]]></content>
<summary type="html">
<p>目录:</p>
<ul>
<li><a href="#5-索引与算法">5 索引与算法</a><ul>
<li><a href="#二分查找法">二分查找法</a></li>
<li><a href="#B+-树">B+ 树</a></li>
<li><a href="#B
</summary>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
</entry>
<entry>
<title>《高性能的MySQL》阅读笔记——MySQL架构与历史</title>
<link href="http://yoursite.com/2020/06/06/%E3%80%8A%E9%AB%98%E6%80%A7%E8%83%BD%E7%9A%84MySQL%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0%E2%80%94%E2%80%94MySQL%E6%9E%B6%E6%9E%84%E4%B8%8E%E5%8E%86%E5%8F%B2/"/>
<id>http://yoursite.com/2020/06/06/《高性能的MySQL》阅读笔记——MySQL架构与历史/</id>
<published>2020-06-06T03:53:10.000Z</published>
<updated>2021-01-12T22:22:04.212Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="#1-MySQL-架构与历史">1 MySQL 架构与历史</a><ul><li><a href="#MySQL-逻辑架构">MySQL 逻辑架构</a></li><li><a href="#并发控制">并发控制</a></li><li><a href="#事务">事务</a></li><li><a href="#死锁">死锁</a></li><li><a href="#多版本并发控制">多版本并发控制</a></li><li><a href="#MySQL-的存储引擎">MySQL 的存储引擎</a></li></ul></li></ul><h2 id="1-MySQL-架构与历史"><a href="#1-MySQL-架构与历史" class="headerlink" title="1 MySQL 架构与历史"></a>1 MySQL 架构与历史</h2><h4 id="MySQL-逻辑架构"><a href="#MySQL-逻辑架构" class="headerlink" title="MySQL 逻辑架构"></a>MySQL 逻辑架构</h4><p>MySQL 逻辑架构图:<br><img src="/2020/06/06/《高性能的MySQL》阅读笔记——MySQL架构与历史/MySQL逻辑架构.jpg" alt="MySQL逻辑架构" title="MySQL逻辑架构"></p><ul><li><p>最上层的服务负责连接管理,授权认证,安全等。</p></li><li><p>第二层包含了 MySQL 的核心功能。包括:解析、分析、优化、缓存及所有内置函数(日期,时间等),跨存存储引擎功能:存储过程、触发器、视图等。</p></li><li><p>第三层包含了存储引擎。存储引擎类似 UNIX 的文件系统,服务器通过 API 与存储引擎通信。存储引擎 API 包含了几十个底层函数,用于执行如:“开始一个事务”,“根据主键提取一行记录”等操作。存储引擎不解析 SQL 。</p></li></ul><p><strong>连接管理与安全</strong><br>每个客户端连接都会在服务器进程拥有一个线程,这个连接的查询只会在这个单独的线程中执行。服务器会缓存线程或使用线程池,无需手动销毁每个连接的线程。</p><p><strong>优化与执行</strong><br>MySQL 会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询,表读取顺序,选择合适的索引等。</p><p><br></p><h4 id="并发控制"><a href="#并发控制" class="headerlink" title="并发控制"></a>并发控制</h4><p>无论何时,只要有多个查询需要在同一时刻修改数据,都会产生并发控制问题。MySQL 在两个层面对并发控制进行了处理:服务器层和存储引擎层,接下来主要讨论 MySQL 如何控制并发读写。</p><p><br></p><p><strong>读写锁</strong><br>在处理并发读写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。即共享锁和排他锁,又称读锁和写锁。</p><p>读锁是共享的,写锁是排他的,会阻塞其他写锁和读锁。</p><p><strong>锁粒度</strong><br>一种提高共享资源并发性的方式就是让锁定的对象更有选择性。尽量只锁定需要修改的部分,而不是锁住所有资源。锁定的数据量越少,系统的并发程度越高。</p><p>但加锁也需要消耗资源。锁的各种操作如:获得锁、检查锁是否已经解除、释放锁等,都会增加开销。</p><p>所谓锁策略,就是在锁的开销和数据的安全性之间寻求平衡。大多数商业数据库一般都会在表上施加行级锁。MySQL 的多种存储引擎都可以实现自己的锁策略和锁粒度。</p><p><strong>表锁</strong><br>MySQL 中最基本的,开销最小的锁策略。用户在进行写操作是需获得写锁,阻塞其他用户对该表的读写操作。没有写锁才能获得读锁,读锁之间不相互阻塞。</p><p>MySQL 服务器在执行 ALTER TABLE 之类的语句时会使用表锁。</p><p><strong>行级锁</strong><br>行级锁可以最大程度地支持并发处理,同时开销最大。行级锁只在存储引擎层实现,MySQL 服务器没有实现。</p><p><br></p><h4 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h4><p>事务就是一组原子性的 SQL 查询。事务的 4 个特性包括:</p><ol><li><code>原子性(atomicity)</code></li><li><code>一致性(consistency)</code></li><li><code>隔离性(isolation)</code></li><li><code>持久性(durability)</code></li></ol><p><strong>隔离级别</strong><br>在 SQL 标准定义了四种隔离级别,每一种隔离级别都规定了一个事务中所做的修改,哪些事务是可见的,哪些是不可见的。较低的隔离级别通常可以执行更高的并发,系统的开销更低。标准的 4 种隔离级别如下:</p><ol><li><code>READ UNCOMMITTED(未提交读)</code>:在该隔离级别下,事务中的修改,即使没有提交,对其他事务也是可见的。会产生脏读问题。</li><li><code>READ COMMITTED(提交读)</code>:一个事务从开始到提交之前,所做的任何修改对其他事务都是不可见的。会产生不可重复读问题。</li><li><code>REPEATABLE READ(可重复读)</code>:该级别保证了同一个事务多次读取同样记录的结果是一样的。会产生幻读问题。</li><li><code>SERIALIZABLE(可串行化)</code>:在读取数据的每一行都加锁。<img src="/2020/06/06/《高性能的MySQL》阅读笔记——MySQL架构与历史/隔离级别导致问题.jpg" alt="MySQL逻辑架构" title="MySQL逻辑架构"></li></ol><ul><li><code>脏读</code>:事务可以读取未提交的数据,如:T1 修改一个数据但未提交,T2 随后读取这个数据。如果 T1 撤销了这次修改,那么 T2 读取的数据是脏数据。</li><li><code>不可重复读</code>:执行两次相同的查询,可能会得到不同的结果。</li><li><code>幻读</code>:事务1读取了某个范围的数据,事务2在该范围又插入了数据,事务1再次读取该范围数据时会产生幻行。InnDB 等存储引擎通过 MVCC 解决了该问题。</li></ul><p><br></p><h4 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h4><p>死锁指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致的恶性循环现象。多个事务以不同的顺序锁定资源时,就可能产生死锁,多个事务同时锁定一个资源时也会产生死锁。</p><p>死锁的例子:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">事务1</div><div class="line"><span class="keyword">START</span> <span class="keyword">TRANSACTION</span>;</div><div class="line"><span class="keyword">UPDATE</span> StockPrice <span class="keyword">SET</span> <span class="keyword">close</span> = <span class="number">45.50</span> <span class="keyword">WHERE</span> stock_id = <span class="number">4</span>;</div><div class="line"><span class="keyword">UPDATE</span> StockPrice <span class="keyword">SET</span> <span class="keyword">close</span> = <span class="number">19.80</span> <span class="keyword">WHERE</span> stock_id = <span class="number">3</span>;</div><div class="line"></div><div class="line">事务2</div><div class="line"><span class="keyword">START</span> <span class="keyword">TRANSACTION</span>;</div><div class="line"><span class="keyword">UPDATE</span> StockPrice <span class="keyword">SET</span> <span class="keyword">close</span> = <span class="number">20.12</span> <span class="keyword">WHERE</span> stock_id = <span class="number">3</span>;</div><div class="line"><span class="keyword">UPDATE</span> StockPrice <span class="keyword">SET</span> <span class="keyword">close</span> = <span class="number">47.20</span> <span class="keyword">WHERE</span> stock_id = <span class="number">4</span>;</div></pre></td></tr></table></figure></p><p>此时,陷入了死循环,只有外部因素接触才可能解除死锁。</p><p>为了解决这个问题,数据库系统实现了死锁检测和死锁超时机制。InnoDB 检测到死锁后能立即返回一个错误,并将持有最少行排他锁的事务进行回滚。</p><p>事务型的系统,死锁时不可避免的,程序的设计时应考虑如何处理死锁。</p><p><strong>事务日志</strong><br><br>事务日志可以帮忙提高事务的效率。特点:</p><ul><li>修改行为记录到日志。</li><li>写日志采用顺序 I/O,而非随机 I/O。</li><li>事务日志持久化后,后台慢慢刷回磁盘</li><li>称为预写日志(Write-Ahead Logging),修改数据写两次磁盘。</li></ul><p><strong>MySQL 中的事务</strong><br><br>自动提交(<code>AUTOINCREMENT</code>):MySQL 默认采用,每个查询都被当做一个事务执行提交。</p><p>查看并设置自动提交模式:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SHOW</span> <span class="keyword">VARIABLES</span> <span class="keyword">LIKE</span> <span class="string">'AUTOCONMMIT'</span></div><div class="line"><span class="keyword">SET</span> AUTOCOMMIT = <span class="number">1</span>;</div></pre></td></tr></table></figure></p><p>设置事务的隔离级别:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SET</span> <span class="keyword">SESSION</span> <span class="keyword">TRANSACTION</span> <span class="keyword">ISOLATION</span> <span class="keyword">LEVEL</span> <span class="keyword">READ</span> COMMITABLE;</div></pre></td></tr></table></figure></p><p><strong>隐式和显示锁定</strong><br><br>InooDB 采用两阶段锁定协议。事务在执行过程中随时可能会锁定,只有在 COMMIT 或 ROLLBACK 的时候才会释放,所有锁都会释放。<br>InnoDB 的显式锁定:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> ... <span class="keyword">LOCK</span> <span class="keyword">IN</span> <span class="keyword">SHARE</span> <span class="keyword">MODE</span></div><div class="line"><span class="keyword">SELECT</span> ... <span class="keyword">FOR</span> <span class="keyword">UPDATE</span></div></pre></td></tr></table></figure></p><p><br></p><h4 id="多版本并发控制"><a href="#多版本并发控制" class="headerlink" title="多版本并发控制"></a>多版本并发控制</h4><p>MySQL 的大多数事务型存储引擎实现的都不是简单的行级锁,它们一般都实现了多版本并发控制(MVCC)。可以认为 MVCC 是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。</p><p><strong>实现原理</strong><br><br>通过保存数据在某个时间点的快照(InnoDB 的 undo 段)来实现。不管事务执行多长时间,每个事务看到的数据都是一致的。根据事务的开始时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。</p><p><strong>InnoDB 的 MVCC</strong><br><br>InnoDB <code>MVCC</code> 是通过在每行记录后面保存两个隐藏的列实现的。一个保存的是开始时间,一个保存的是过期时间,时间指的是版本号。每新开始一个事务,系统版本号会递增,做为该事务的事务版本号,用来和查询到的每行记录做比较。</p><p>MVCC 的具体操作:</p><ul><li><code>SELECT</code><ul><li>InnoDB 会根据两个条件检查每行记录:<ul><li>InnoDB 只查找版本早于当前事务版本的数据行,确保事务读取的行,要么是事务开始前已经纯在的,要么是事务本身操作的。</li><li>行的删除版本要么未定义,要么大于当前事务的版本号。确保事务读取到的行,在事务开始之前未被删除。</li></ul></li></ul></li><li><code>INSERT,DELETE</code><ul><li>InnoDB 为新插入的每一行,保存当前系统版本号作为行版本号。</li><li>InnoDB 为删除的每一行保存当前系统的版本号作为行版本号。</li></ul></li><li><code>UPDATE</code><ul><li>InnoDB 为插入的新记录,使用当前系统版本号作为<code>新行</code>行版本号,同时保存当前系统版本号到<code>原来的行</code>作为行删除标识。</li></ul></li></ul><p>使用 MVCC 使得大多数读操作不需要加锁,提高读取数据性能,但是同时需要额外的存储空间,用来检查维护工作。有时需要在并发(行锁比表锁并发程度更高)和系统开销(获取锁,释放锁,锁检测耗费CPU)上做权衡。</p><p>MVCC 只在 REPEATABLE READ 和 READ COMMITED 两个隔离级别下工作。</p><p><br></p><h4 id="MySQL-的存储引擎"><a href="#MySQL-的存储引擎" class="headerlink" title="MySQL 的存储引擎"></a>MySQL 的存储引擎</h4><p>在文件系统中,MySQL 将每个数据库(也可称为 schema)保存为数据目录下的一个子目录。创建表时,MySQL会在数据库子目录下创建一个和表同名的 <code>.frm</code> 文件保存表的定义。</p><p>可以使用 <code> SHOW TABLE STATUS </code> 显示表的相关信息。</p><p><br></p><p><strong>InnDB 存储引擎</strong><br>InoDB 是 MySQL 的默认 <code>事务型存储引擎</code> 。它被设计用来处理大量的短期事务。</p><p>InnoDB 的数据存储在 <code>表空间</code> 中,表空间是由 InnoDB 管理的一个黑盒子,由一系列的数据文件组成。在高版本的 MySQL 中,InnoDB 可以将每个表的数据和索引存放在单独的文件中。</p><p>InnoDB 采用 <code>MVCC</code> 来支持高并发,并且通过 <code>间隙锁</code> 策略防止幻读的出现。</p><p>InnoDB 做了很多内部优化,包括从磁盘读取数据时采用的<code> 可预测性预读 </code>,能够自动在内存中创建<code> 自适应哈希索引 </code>,以及能够加速插入操作的<code> 插入缓冲区 </code>等。</p><p>支持热备份,有相应的工具。</p><p><strong>MyISAM 存储引擎</strong><br>MyISAM 提供了大量特性,包括全文索引,压缩,空间函数等(GIS)。但 MyISAM 不支持事务和行级锁,而且崩溃后无法恢复。</p>]]></content>
<summary type="html">
<p>目录:</p>
<ul>
<li><a href="#1-MySQL-架构与历史">1 MySQL 架构与历史</a><ul>
<li><a href="#MySQL-逻辑架构">MySQL 逻辑架构</a></li>
<li><a href="#并发控制">并发控制</a
</summary>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
</entry>
<entry>
<title>小demo记录</title>
<link href="http://yoursite.com/2020/05/30/%E5%B0%8Fdemo%E8%AE%B0%E5%BD%95/"/>
<id>http://yoursite.com/2020/05/30/小demo记录/</id>
<published>2020-05-30T13:22:48.000Z</published>
<updated>2020-06-14T14:05:24.740Z</updated>
<content type="html"><![CDATA[<h2 id="Python3-8-安装"><a href="#Python3-8-安装" class="headerlink" title="Python3.8 安装"></a>Python3.8 安装</h2><p>参考:</p><ul><li><a href="https://www.cnblogs.com/shyw/p/12238229.html" target="_blank" rel="external">https://www.cnblogs.com/shyw/p/12238229.html</a></li></ul><h2 id="Virtualenv"><a href="#Virtualenv" class="headerlink" title="Virtualenv"></a>Virtualenv</h2><p>参考:</p><ul><li><a href="https://virtualenv.pypa.io/en/latest/installation.html" target="_blank" rel="external">https://virtualenv.pypa.io/en/latest/installation.html</a></li><li><a href="https://www.cnblogs.com/linga/p/11588223.html" target="_blank" rel="external">https://www.cnblogs.com/linga/p/11588223.html</a></li></ul><h2 id="运行应用"><a href="#运行应用" class="headerlink" title="运行应用"></a>运行应用</h2><p>注意点:</p><ul><li>应用的名字不要和pyton的模块重名,比如 email 这种都会报错。</li></ul><h2 id="解决安装-MySQLdb-问题"><a href="#解决安装-MySQLdb-问题" class="headerlink" title="解决安装 MySQLdb 问题"></a>解决安装 MySQLdb 问题</h2><ol><li>pip3 install PyMySQL</li><li>修改 pymsql/<strong>init</strong>.py 中的内容,调用 install_as_MySQLdb() 函数</li><li>在 python 的 site-package 包中创建 MySQLdb.py</li><li>在 MySQLdb.py 中 import pymysql</li></ol><p>参考:</p><ul><li><a href="https://www.zhihu.com/question/58175718" target="_blank" rel="external">https://www.zhihu.com/question/58175718</a></li><li><a href="https://stackoverflow.com/questions/4960048/how-can-i-connect-to-mysql-in-python-3-on-windows?answertab=votes#tab-top" target="_blank" rel="external">https://stackoverflow.com/questions/4960048/how-can-i-connect-to-mysql-in-python-3-on-windows?answertab=votes#tab-top</a></li></ul><h2 id="MySQL-connetion-refused"><a href="#MySQL-connetion-refused" class="headerlink" title="MySQL connetion refused"></a>MySQL connetion refused</h2><p>修改 /etc/mysql/<em>*</em>/mysqld.cnf</p><h2 id="调试刚写好的-Model"><a href="#调试刚写好的-Model" class="headerlink" title="调试刚写好的 Model"></a>调试刚写好的 Model</h2><ol><li>先 export APP_CONFIG_FILE=<code>pwd</code>/config.py</li><li>然后输入 python 进入 shell</li><li>调用相关 model</li></ol><h2 id="发送邮件时总是无法登陆"><a href="#发送邮件时总是无法登陆" class="headerlink" title="发送邮件时总是无法登陆"></a>发送邮件时总是无法登陆</h2><ul><li>改成SMTP_SSL发送</li><li>端口改成465</li></ul>]]></content>
<summary type="html">
<h2 id="Python3-8-安装"><a href="#Python3-8-安装" class="headerlink" title="Python3.8 安装"></a>Python3.8 安装</h2><p>参考:</p>
<ul>
<li><a href="http
</summary>
<category term="项目" scheme="http://yoursite.com/tags/%E9%A1%B9%E7%9B%AE/"/>
</entry>
<entry>
<title>《MySQL必知必会》阅读笔记</title>
<link href="http://yoursite.com/2020/05/22/%E3%80%8AMySQL%E5%BF%85%E7%9F%A5%E5%BF%85%E4%BC%9A%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
<id>http://yoursite.com/2020/05/22/《MySQL必知必会》阅读笔记/</id>
<published>2020-05-22T15:22:36.000Z</published>
<updated>2020-12-03T19:35:07.589Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="#3-使用MySQL">3 使用MySQL</a><ul><li><a href="#选择数据库">选择数据库</a></li></ul></li><li><a href="#4-检索数据">4 检索数据</a><ul><li><a href="#检索不同的行">检索不同的行</a></li><li><a href="#限制结果">限制结果</a></li><li><a href="#完全限定表名">完全限定表名</a></li></ul></li><li><a href="#5-排序检索数据">5 排序检索数据</a></li><li><a href="#6-过滤数据">6 过滤数据</a><ul><li><a href="#WHERE-子句操作符">WHERE 子句操作符</a></li></ul></li><li><a href="#7-数据过滤">7 数据过滤</a><ul><li><a href="#组合-WHERE-子句">组合 WHERE 子句</a></li><li><a href="#IN-操作符">IN 操作符</a></li><li><a href="#NOT-操作符">NOT 操作符</a></li></ul></li><li><a href="#8-用通配符进行过滤">8 用通配符进行过滤</a><ul><li><a href="#LIKE-操作符">LIKE 操作符</a></li><li><a href="#使用通配符的技巧">使用通配符的技巧</a></li></ul></li><li><a href="#9-用正则表达式进行搜索">9 用正则表达式进行搜索</a></li><li><a href="#12-汇总数据">12 汇总数据</a><ul><li><a href="#聚集函数">聚集函数</a></li><li><a href="#聚集不同值">聚集不同值</a></li><li><a href="#组合聚集函数">组合聚集函数</a></li></ul></li><li><a href="#13-分组数据">13 分组数据</a><ul><li><a href="#创建分组">创建分组</a></li><li><a href="#过滤分组">过滤分组</a></li><li><a href="#分组和排序">分组和排序</a></li><li><a href="#SELECT-子句顺序">SELECT 子句顺序</a></li></ul></li><li><a href="#14-使用子查询">14 使用子查询</a><ul><li><a href="#利用子查询进行过滤">利用子查询进行过滤</a></li><li><a href="#作为计算字段使用子查询">作为计算字段使用子查询</a></li></ul></li><li><a href="#16-联结">15 联结</a><ul><li><a href="#创建联结">创建联结</a></li></ul></li><li><a href="#16-创建高级联结">16 创建高级联结</a><ul><li><a href="#自联结">自联结</a></li><li><a href="#自然联结">自然联结</a></li><li><a href="#外部联结">外部联结</a></li></ul></li><li><a href="#17-组合查询">17 组合查询</a><ul><li><a href="#创建组合查询">创建组合查询</a></li></ul></li><li><a href="#21-创建和操纵表">21 创建和操纵表</a><ul><li><a href="#创建表">创建表</a></li><li><a href="#更新表">更新表</a></li><li><a href="#删除表">删除表</a></li><li><a href="#重命名表">重命名表</a></li></ul></li><li><a href="#22-使用视图">22 使用视图</a><ul><li><a href="#视图">视图</a></li><li><a href="#使用视图">使用视图</a></li></ul></li><li><a href="#26-管理事务处理">26 管理事务处理</a><ul><li><a href="#事务处理">事务处理</a></li><li><a href="#控制事务处理">控制事务处理</a></li></ul></li><li><a href="#30-改善性能">30 改善性能</a><br><br></li></ul><h2 id="3-使用MySQL"><a href="#3-使用MySQL" class="headerlink" title="3 使用MySQL"></a>3 使用MySQL</h2><p>使用数据库:<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> USE crashcourse;</div></pre></td></tr></table></figure></p><p>返回可用的数据库:<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> SHOW DATABASES;</div></pre></td></tr></table></figure></p><p>返回数据库内的表:<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> SHOW TABLES;</div></pre></td></tr></table></figure></p><p>显示表中的列:<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> SHOW COLUMNS FROM customers</div></pre></td></tr></table></figure></p><p>交接 <code>SHOW</code> 命令,可以使用:<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> HELP SHOW;</div></pre></td></tr></table></figure></p><h2 id="4-检索数据"><a href="#4-检索数据" class="headerlink" title="4 检索数据"></a>4 检索数据</h2><h4 id="检索不同的行"><a href="#检索不同的行" class="headerlink" title="检索不同的行"></a>检索不同的行</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> <span class="keyword">DISTINCT</span> vend_id</div><div class="line"><span class="keyword">FROM</span> products;</div></pre></td></tr></table></figure><p><code>DISTINCT</code> 应用所有的列,不能不分指定。</p><h4 id="限制结果"><a href="#限制结果" class="headerlink" title="限制结果"></a>限制结果</h4><p>使用 <code>LIMIT</code> 限定输出的结果数量:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> prod_name</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">LIMIT</span> <span class="number">5</span>, <span class="number">5</span>;</div></pre></td></tr></table></figure></p><p>第一个数为开始的位置,第二个数为检索的行数。</p><h4 id="完全限定表名"><a href="#完全限定表名" class="headerlink" title="完全限定表名"></a>完全限定表名</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> products.prod_name</div><div class="line"><span class="keyword">FROM</span> products;</div></pre></td></tr></table></figure><p><br></p><h2 id="5-排序检索数据"><a href="#5-排序检索数据" class="headerlink" title="5 排序检索数据"></a>5 排序检索数据</h2><p>子句:SQL 语句由子句构成,包括一个关键字和所提供的的数据。子句如 SELECT 子句,FROM 子句。</p><p>可以使用 <code>ORDER BY</code> 子句取一个或多个列名字,对输出进行排序。</p><p>SQL 的输出结果默认是升序 <code>ASC</code>,可以使用 <code>DESC</code>进行降序输出。</p><p>一个例子:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> prod_id, prod_price, prod_name</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> prod_price <span class="keyword">DESC</span>, prod_name;</div></pre></td></tr></table></figure></p><p>对 prod_price 降序,对 prod_name 升序。</p><p>ORDER BY 位于 FROM 之后,LIMIT 之前。</p><p><br></p><h2 id="6-过滤数据"><a href="#6-过滤数据" class="headerlink" title="6 过滤数据"></a>6 过滤数据</h2><p>数据库一般包含大量数据,使用时很少需要检索表中所有行,通常检索所需数据需要指定过滤条件。</p><h4 id="WHERE子句操作符"><a href="#WHERE子句操作符" class="headerlink" title="WHERE子句操作符"></a>WHERE子句操作符</h4><p>WHERE 子句的操作符包括:<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">- =</div><div class="line">- <></div><div class="line">- != </div><div class="line">- <</div><div class="line">- <=</div><div class="line">- BETWEEN</div></pre></td></tr></table></figure></p><p>空值检查,在建表时,表设计人员可以指定其中的列是否可以不包含。在一个列不包含值时,称其为包含空值 <code>NULL</code>。</p><p>可以使用 IS NULL 子句来判断:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> prod_name</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">WHERE</span> prod_price <span class="keyword">IS</span> <span class="literal">NULL</span>;</div></pre></td></tr></table></figure></p><p>在过滤不具有特定值的行时,NULL 不会返回,必须指定。</p><p><br></p><h2 id="7-数据过滤"><a href="#7-数据过滤" class="headerlink" title="7 数据过滤"></a>7 数据过滤</h2><p>操作符:用来联结或改变 WHERE 子句中的子句的关键字,也称逻辑操作符。</p><h4 id="组合-WHERE-子句"><a href="#组合-WHERE-子句" class="headerlink" title="组合 WHERE 子句"></a>组合 WHERE 子句</h4><p>使用的操作符包括 <code>AND</code> ,<code>OR</code>,它们的计算次序是 AND 高于 OR ,可以使用括号来明确执行顺序。</p><h4 id="IN-操作符"><a href="#IN-操作符" class="headerlink" title="IN 操作符"></a>IN 操作符</h4><p><code>IN</code> 操作符与 OR 有相似的功能,但是有如下优点:</p><ul><li>比 OR 执行的更快。</li><li>可以包含其他 SELECT 子句,能够动态的建立 WHERE 子句。</li></ul><h4 id="NOT-操作符"><a href="#NOT-操作符" class="headerlink" title="NOT 操作符"></a>NOT 操作符</h4><p><code>NOT</code>支持对 IN、BETWEEN、EXISTS 取反。</p><p><br></p><h2 id="8-用通配符进行过滤"><a href="#8-用通配符进行过滤" class="headerlink" title="8 用通配符进行过滤"></a>8 用通配符进行过滤</h2><p>通配符:用来匹配值的一部分的特殊字符。<br>搜索模式:由字面值、通配符组合而成。</p><p>在子句中使用通配符,需使用 <code>LIKE</code> 操作符,指示 MySQL 根据后端的通配符进行匹配而不是等值匹配。</p><h4 id="LIKE-操作符"><a href="#LIKE-操作符" class="headerlink" title="LIKE 操作符"></a>LIKE 操作符</h4><p><code>%</code> 表示任意字符出现的次数。<br><code>_</code> 表示只匹配单个字符。</p><p>例子:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> prod_id, prod_name</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">WHERE</span> prod_name <span class="keyword">LIKE</span> <span class="string">'_ ton anvil'</span>;</div></pre></td></tr></table></figure></p><h4 id="使用通配符的技巧"><a href="#使用通配符的技巧" class="headerlink" title="使用通配符的技巧"></a>使用通配符的技巧</h4><p>通配符效率较低,有以下技巧:</p><ul><li>不要放在搜索的开始位置,因为会令后面的索引失效。</li></ul><p><br></p><h2 id="9-用正则表达式进行搜索"><a href="#9-用正则表达式进行搜索" class="headerlink" title="9 用正则表达式进行搜索"></a>9 用正则表达式进行搜索</h2><p>随着过滤条件的复杂性增加,WHERE 子句的复杂性也会增加,可以使用正则表达式。</p><p>使用 <code>REGEXP</code> 操作符进行基本的字符匹配:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> prod_name</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">WHERE</span> prod_name REGEXP <span class="string">'1000'</span></div><div class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> prod_name;</div></pre></td></tr></table></figure></p><p>一个区分:LIKE 是匹配整个列,REGEXP 是在列值内进行匹配,详细请看书解释。</p><p><br></p><h2 id="12-汇总数据"><a href="#12-汇总数据" class="headerlink" title="12 汇总数据"></a>12 汇总数据</h2><p>聚集函数:运行在行组上,计算和返回单个值的函数。</p><h4 id="聚集函数"><a href="#聚集函数" class="headerlink" title="聚集函数"></a>聚集函数</h4><ul><li><code>AVG()</code></li><li><code>COUNT()</code></li><li><code>MAX()</code></li><li><code>MIN()</code></li><li><code>SUM()</code></li></ul><h4 id="聚集不同值"><a href="#聚集不同值" class="headerlink" title="聚集不同值"></a>聚集不同值</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> <span class="keyword">AVG</span>(<span class="keyword">DISTINCT</span> prod_price) <span class="keyword">AS</span> avg_price</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">WHERE</span> vend_id = <span class="number">1003</span>;</div></pre></td></tr></table></figure><h4 id="组合聚集函数"><a href="#组合聚集函数" class="headerlink" title="组合聚集函数"></a>组合聚集函数</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> <span class="keyword">COUNT</span>(*) <span class="keyword">AS</span> num_items,</div><div class="line"> <span class="keyword">MIN</span>(prod_price) <span class="keyword">AS</span> price_min,</div><div class="line"> <span class="keyword">MAX</span>(prod_price) <span class="keyword">AS</span> price_max,</div><div class="line"> <span class="keyword">AVG</span>(prod_price) <span class="keyword">AS</span> price_avg</div><div class="line"><span class="keyword">FROM</span> products;</div></pre></td></tr></table></figure><p><br></p><h2 id="13-分组数据"><a href="#13-分组数据" class="headerlink" title="13 分组数据"></a>13 分组数据</h2><p>如果要查询每个产品的供应商供应数目,WHERE 子句不能满足要求,可以使用分组,把数据分成多个逻辑组,对每个组进行计算。</p><h4 id="创建分组"><a href="#创建分组" class="headerlink" title="创建分组"></a>创建分组</h4><p>分组使用 <code>GROUP BY</code> 子句。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> vend_id, <span class="keyword">COUNT</span>(*) <span class="keyword">AS</span> num_prods</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> vend_id;</div></pre></td></tr></table></figure></p><p>使用 GROUP BY 子句的一些重要规定:</p><ul><li>GROUP BY 子句中列出的每个列必须是检索列或有效表达式。如果在 SELECT 中使用表达式,则必须在 GROUP BY 子句中指定相同的表达式,不能使用别名。</li><li>除聚集计算外,SELECT 语句中的每个列都必须在 GROUP BY 子句给出。</li><li>如果分组中具有 NULL 值,则 NULL 将作为一个分组返回,如果列中有多行 NULL 值,它们将分为一组。</li><li>GROUP BY 子句必须出现在 WHERE 子句之后,ORDER BY 子句之前。</li></ul><h4 id="过滤分组"><a href="#过滤分组" class="headerlink" title="过滤分组"></a>过滤分组</h4><p>使用 <code>HAVING</code> 过滤分组。WHERE 过滤行,HAVING 过滤分组。</p><p>HAVING 和 WHERE 的区别,WHERE 在分组前进行过滤,HAVING 在分组后进行过滤。</p><p>一个例子,过滤 12 个月内两个以上的订单的顾客:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> vend_id, <span class="keyword">COUNT</span>(*) <span class="keyword">AS</span> num_prods</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">WHERE</span> prod_price >= <span class="number">10</span></div><div class="line"><span class="keyword">GROUP</span> <span class="keyword">BY</span> vend_id</div><div class="line"><span class="keyword">HAVING</span> <span class="keyword">COUNT</span>(*) >=<span class="number">2</span>;</div></pre></td></tr></table></figure></p><h4 id="分组和排序"><a href="#分组和排序" class="headerlink" title="分组和排序"></a>分组和排序</h4><p>GROUP BY 具有排序功能,但如果是有排序需求的话,必须加上 ORDER BY。</p><h4 id="SELECT-子句顺序"><a href="#SELECT-子句顺序" class="headerlink" title="SELECT 子句顺序"></a>SELECT 子句顺序</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">1. <span class="keyword">SELECT</span></div><div class="line"><span class="number">2.</span> <span class="keyword">FROM</span></div><div class="line"><span class="number">3.</span> <span class="keyword">WHERE</span></div><div class="line"><span class="number">4.</span> <span class="keyword">GROUP</span> <span class="keyword">BY</span></div><div class="line"><span class="number">5.</span> <span class="keyword">HAVING</span></div><div class="line"><span class="number">6.</span> <span class="keyword">ORDER</span> <span class="keyword">BY</span></div><div class="line"><span class="number">7.</span> <span class="keyword">LIMIT</span></div></pre></td></tr></table></figure><p><br></p><h2 id="子查询"><a href="#子查询" class="headerlink" title="子查询"></a>子查询</h2><p>子查询即嵌套在其他查询中的查询。</p><h4 id="利用子查询进行过滤"><a href="#利用子查询进行过滤" class="headerlink" title="利用子查询进行过滤"></a>利用子查询进行过滤</h4><p>一个例子,列出订购物品 TNT2 的所有客户:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> cust_id</div><div class="line"><span class="keyword">FROM</span> orders</div><div class="line"><span class="keyword">WHERE</span> order_num <span class="keyword">IN</span> (<span class="keyword">SELECT</span> order_num</div><div class="line"> <span class="keyword">FROM</span> orderitems</div><div class="line"> <span class="keyword">WHERE</span> prod_id = <span class="string">'TNT2'</span>);</div></pre></td></tr></table></figure></p><p>查询由内向外进行处理。</p><h4 id="作为计算字段使用子查询"><a href="#作为计算字段使用子查询" class="headerlink" title="作为计算字段使用子查询"></a>作为计算字段使用子查询</h4><p>一个例子,求 customers 表中每个客户的订单总数。订单与客户id 存储在 order 表中。</p><p>首先对客户 10001 的订单进行计数:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> <span class="keyword">COUNT</span>(*)</div><div class="line"><span class="keyword">FROM</span> orders</div><div class="line"><span class="keyword">WHERE</span> cust_id = <span class="number">10001</span>;</div></pre></td></tr></table></figure></p><p>为了对每个客户执行 COUNT(<em>) 计算,应该将 COUNT(</em>) 作为一个字查询:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> cust_name,</div><div class="line"> cust_state,</div><div class="line"> (<span class="keyword">SELECT</span> <span class="keyword">COUNT</span>(*)</div><div class="line"> <span class="keyword">FROM</span> orders</div><div class="line"> <span class="keyword">WHERE</span> orders.cust_id = customers.cust_id) <span class="keyword">AS</span> orders</div><div class="line"><span class="keyword">FROM</span> customers</div><div class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> cust_name;</div></pre></td></tr></table></figure></p><p><br></p><h2 id="联结表"><a href="#联结表" class="headerlink" title="联结表"></a>联结表</h2><p>联结是一种机制,用来在一条 SELECT 语句中关联表,可以联结多个表返回一组输出。</p><p>联结在实际的数据库表中不存在,存在于查询的执行当中。</p><h4 id="创建联结"><a href="#创建联结" class="headerlink" title="创建联结"></a>创建联结</h4><p>创建一个简单联结:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> vend_name, prod_name, prod_price</div><div class="line"><span class="keyword">FROM</span> vendors, products</div><div class="line"><span class="keyword">WHERE</span> vendors.vend_id = products.vend_id</div><div class="line"><span class="keyword">ORDER</span> <span class="keyword">BY</span> vend_name, prod_name;</div></pre></td></tr></table></figure></p><p>注意 SQL 语句中 WHERE 子句的重要性,如果没有 WHERE 子句,那么将会导致笛卡尔积数量的结果。</p><p>内联结:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> vend_name, prod_name, prod_price</div><div class="line"><span class="keyword">FROM</span> vendors <span class="keyword">INNER</span> <span class="keyword">JOIN</span> products</div><div class="line"><span class="keyword">ON</span> vendors.vend_id = products.vend_id;</div></pre></td></tr></table></figure></p><p>相比上面的查询,具有明确的语法,联结条件使用 ON 子句而不是使用 WHERE。</p><p><em>表联结和子查询可以实现相同的功能。</em></p><p><br></p><h2 id="16-创建高级联结"><a href="#16-创建高级联结" class="headerlink" title="16 创建高级联结"></a>16 创建高级联结</h2><h4 id="自联结"><a href="#自联结" class="headerlink" title="自联结"></a>自联结</h4><p>使用表别名的原因是能在单条 SELECT 语句中不止一次引用相同的表。</p><p>如果物品(ID 为 DTNTR)存在问题,想知道该供应商的其它物品是否有问题。可以使用如下子查询:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> prod_id, prod_name</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">WHERE</span> vend_id = (<span class="keyword">SELECT</span> vend_id</div><div class="line"> <span class="keyword">FROM</span> products</div><div class="line"> <span class="keyword">WHERE</span> prod_id = <span class="string">'DTNTR'</span>);</div></pre></td></tr></table></figure></p><p>使用自联结同样可以实现该查询:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> p1.prod_id, p1.prod_name</div><div class="line"><span class="keyword">FROM</span> products <span class="keyword">AS</span> p1, products <span class="keyword">AS</span> p2</div><div class="line"><span class="keyword">WHERE</span> p1.vend_id = p2.vend_id</div><div class="line"> <span class="keyword">AND</span> p2.prod_id = <span class="string">'DTNTR'</span>;</div></pre></td></tr></table></figure></p><h4 id="自然联结"><a href="#自然联结" class="headerlink" title="自然联结"></a>自然联结</h4><p>标准的联结返回所有的数据,甚至相同的列多次出现。自然联结使每个列只返回一次。</p><p>一个例子:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> c.* o.order_num, o.order_date,</div><div class="line"> oi.prod_id, oi.quantity, OI.item_price</div><div class="line"><span class="keyword">FROM</span> customer <span class="keyword">AS</span> c, orders <span class="keyword">AS</span> o, orderitems <span class="keyword">AS</span> oi</div><div class="line"><span class="keyword">WHERE</span> c.cust_id = o.cust_id</div><div class="line"> <span class="keyword">AND</span> oi.order_num = o.order_num</div><div class="line"> <span class="keyword">AND</span> prod_id = <span class="string">'FB'</span>;</div></pre></td></tr></table></figure></p><h4 id="外部联结"><a href="#外部联结" class="headerlink" title="外部联结"></a>外部联结</h4><p>选择左边表选择所有行。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> customer.cust_id, orders.order_num</div><div class="line"><span class="keyword">FROM</span> customers <span class="keyword">LEFT</span> <span class="keyword">OUTER</span> <span class="keyword">JOIN</span> orders</div><div class="line"> <span class="keyword">ON</span> orders.cust_id = customers.cust_id;</div></pre></td></tr></table></figure></p><p><br></p><h2 id="17-组合查询"><a href="#17-组合查询" class="headerlink" title="17 组合查询"></a>17 组合查询</h2><p>复合查询:MySQL 允许执行多个查询,并将结果作为单个查询返回。</p><p>使用复合查询的两种基本情况:</p><ol><li>不同表的单个查询。</li><li>相同表的多个查询按单个返回。</li></ol><p>组合查询可以和多个 WHERE 条件的语句由相同的功能。</p><h4 id="创建组合查询"><a href="#创建组合查询" class="headerlink" title="创建组合查询"></a>创建组合查询</h4><p>使用 <code>UNION</code> 完成组合工作。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> vend_id, prod_id, prod_price</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">UNION</span></div><div class="line"><span class="keyword">SELECT</span> vend_id, prod_id, prod_price</div><div class="line"><span class="keyword">FROM</span> products</div><div class="line"><span class="keyword">WHERE</span> vend_id <span class="keyword">IN</span> (<span class="number">1001</span>, <span class="number">1002</span>);</div></pre></td></tr></table></figure></p><p>使用 UNION 的规则:</p><ul><li>两条以上 SELECT 语句。</li><li>每个查询必须包含相同的列,表达式,聚集函数。</li><li>类型必须兼容,可以进行转换。</li></ul><p><code>UNION</code> 默认去重,如果不想去重,可以使用 <code>UNION ALL</code>。</p><p>如果想对结果排序,可以在最后加 <code>ORDER BY</code>。</p><p><br></p><h2 id="创建和操纵表"><a href="#创建和操纵表" class="headerlink" title="创建和操纵表"></a>创建和操纵表</h2><h4 id="创建表"><a href="#创建表" class="headerlink" title="创建表"></a>创建表</h4><p><strong>使用 NULL 值</strong><br><br>每列后添加 <code>NOT NULL</code> 关键字。</p><p><strong>主键</strong><br><br>多个列也可以做为主键,但必须唯一。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="keyword">order</span></div><div class="line">(</div><div class="line"> order_num <span class="built_in">int</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>,</div><div class="line"> order_item <span class="built_in">int</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>,</div><div class="line"> PRIMARY <span class="keyword">KEY</span>(order_num, order_item)</div><div class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span>;</div></pre></td></tr></table></figure></p><p><strong>使用 AUTO_INCREMENT</strong><br><br>每执行 INSERT 操作后,会对该列自动增量,给出下一个可用的值。这样给每行唯一的一个 id ,可用作主键。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">cust_id int NOT NULL AUTO_INCREMENT,</div></pre></td></tr></table></figure><p>每个表只允许一个 <code>AUTO INCREMENT</code> 列,并且必须被索引(设置主键)。</p><p>获得下一个可用的 id:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> <span class="keyword">last_insert_id</span>()</div></pre></td></tr></table></figure></p><p><strong>指定默认值</strong><br>在 CREATE TABLE 语句的列定义中使用 <code>DEFAULT</code> 关键字。</p><p><strong>引擎类型</strong><br>在建表语句 CREATE_TABLE 后添加,ENGINE=InnDB 。 </p><p><br></p><h2 id="22-使用视图"><a href="#22-使用视图" class="headerlink" title="22 使用视图"></a>22 使用视图</h2><p>视图是虚拟的表。只包含使用时动态检索数据的查询。</p><h4 id="视图"><a href="#视图" class="headerlink" title="视图"></a>视图</h4><p>一个例子,可以把如下查询:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> cust_name, cust_contact</div><div class="line"><span class="keyword">FROM</span> customers, orders, orderitems</div><div class="line"><span class="keyword">WHERE</span> customers.cust_id = orders.cust_id</div><div class="line"> <span class="keyword">AND</span> orderitems.order_num = order.order_num</div><div class="line"> <span class="keyword">AND</span> prod_id = <span class="string">'abc'</span>;</div></pre></td></tr></table></figure></p><p>包装为一个名为 productcustomers 的虚拟表:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> cust_name, cust_contact</div><div class="line"><span class="keyword">FROM</span> productcustomers</div><div class="line"><span class="keyword">WHERE</span> prod_id = <span class="string">'abc'</span>;</div></pre></td></tr></table></figure></p><p>productcustomers 是一个视图,它不包含表中应该有的任何列或数据,它包含的是一个 SQL 查询(与上面用以正确联结表的相同的查询)。</p><p>视图的常见应用:</p><ul><li>重用 SQL 语句。</li><li>使用表的组成部分而不是整个表。</li><li>保护数据。只给予表的特定部分的访问权限。</li><li>更改数据格式和表示。</li><li>极客时间里的,事务的隔离级别的实现。</li></ul><p>可以用与表的相同的方式利用它们,SELECT、INSERT、UPDATE。</p><p>重要的是知道,视图仅仅是查看存储在别处的数据的一种设施,它们的数据都是从别的表检索出来的。</p><p>视图的规则和限制:</p><ul><li>唯一命名。</li><li>可以嵌套。</li><li>视图的检索 SELECT 中也有 ORDER BY ,那么视图的 ORDER BY 将被覆盖。</li><li>视图不能索引。</li><li>可以和表一起使用。</li></ul><h4 id="使用视图"><a href="#使用视图" class="headerlink" title="使用视图"></a>使用视图</h4><p>使用视图与计算字段:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">CREATE</span> <span class="keyword">VIEW</span> orderitemsexpanded <span class="keyword">AS</span></div><div class="line"><span class="keyword">SELECT</span> order_num,</div><div class="line"> prod_id,</div><div class="line"> quantity,</div><div class="line"> item_price,</div><div class="line"> quantity * item_price <span class="keyword">AS</span> expanded_price</div><div class="line"><span class="keyword">FROM</span> orderitems;</div></pre></td></tr></table></figure></p><p>使用:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> *</div><div class="line"><span class="keyword">FROM</span> orderitemsexpanded</div><div class="line"><span class="keyword">WHERE</span> order_num = <span class="number">25</span>;</div></pre></td></tr></table></figure></p><p>更新视图,视图可更新,会对基表进行操作。</p><p><br></p><h2 id="26-管理事务处理"><a href="#26-管理事务处理" class="headerlink" title="26 管理事务处理"></a>26 管理事务处理</h2><p>事务是一种机制,事务处理可以用来维护数据库的完整性,它保证成批处理的 MySQL 操作要么完全执行,要么完全不执行。</p><h4 id="事务处理"><a href="#事务处理" class="headerlink" title="事务处理"></a>事务处理</h4><p>事务涉及的几个术语:</p><ul><li>事务(transaction):指一组 SQL 语句。</li><li>回退(rollback):指撤销指定 SQL 语句的过程。</li><li>提交(commit):指将未存储的 SQL 语句结果写入数据库表。</li><li>保留点(savepoint):指事务处理中设置的临时占位符,你可以对它发布回退。</li></ul><h4 id="控制事务处理"><a href="#控制事务处理" class="headerlink" title="控制事务处理"></a>控制事务处理</h4><p>事务开始语句:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">START</span> <span class="keyword">TRANSACTION</span></div></pre></td></tr></table></figure></p><p>事务回退语句:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">ROLLBACK</span></div></pre></td></tr></table></figure></p><p>例子:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> ordertotals;</div><div class="line"><span class="keyword">START</span> <span class="keyword">TRANSACTION</span>;</div><div class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> ordertotals;</div><div class="line"><span class="keyword">ROLLBACK</span>;</div><div class="line"><span class="keyword">SELECT</span> * <span class="keyword">FROM</span> ordertotals;</div></pre></td></tr></table></figure></p><p>哪些语句可以回退?<br>可以回退 INSERT、DELETE、UPDATE 语句。但是不可以回退 SELECT、CREATE、DROP 语句。</p><p>一般的 MySQL 语句都是直接针对数据库表执行,会进行隐含提交。而事务必须明确使用 <code>COMMIT</code> 语句进行提交。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">START</span> <span class="keyword">TRANSACTION</span>;</div><div class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> orderitems <span class="keyword">WHERE</span> order_num = <span class="number">20010</span>;</div><div class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> orders <span class="keyword">WHERE</span> order_num =<span class="number">20010</span>;</div><div class="line"><span class="keyword">COMMIT</span>;</div></pre></td></tr></table></figure></p><p>如果其中一条 DELETE 执行失败,则另一条会被自动撤销。</p><p>隐含事务关闭:当 <code>COMMIT</code> 或 <code>ROLLBACK</code> 语句执行后,事务会自动关闭。</p><p>使用保留点:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SAVEPOINT</span> <span class="keyword">delete</span>;</div><div class="line"><span class="keyword">ROLLBACK</span> <span class="keyword">TO</span> delete1;</div></pre></td></tr></table></figure></p><p>为指示 MySQL 不自动提交更改,可以使用以下语句:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SET</span> autocommit =<span class="number">0</span>;</div></pre></td></tr></table></figure></p><p><code>autocommit</code> 针对的是每个连接而不是服务器。</p><p><br></p><h2 id="30-改善性能"><a href="#30-改善性能" class="headerlink" title="30 改善性能"></a>30 改善性能</h2><p>MySQL 是用一系列的默认设置预先配置的,但使用过程中需要经常调整内存、缓冲区大小等。为查看当前设置,可使用:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SHOW</span> <span class="keyword">VARIABLES</span>;</div></pre></td></tr></table></figure></p><p>或<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SHOW</span> <span class="keyword">STATUS</span>;</div></pre></td></tr></table></figure></p><p>MySQL 是一个多用户多线程的 DBMS,经常同时执行多个任务,如果某一个执行缓慢,则所有都会执行缓慢,可使用:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">SHOW</span> <span class="keyword">PROCESSLIST</span>;</div></pre></td></tr></table></figure></p><p>显示所有活动线程(数据库实例是一个进程,每个进程又有许多线程连接),还可使用 KILL 命令终结特定贤线程。<br>具体语法可以参考 MySQL 官网。</p><p>使用 <code>EXPLAIN</code> 语句让 MySQL 解释它将如何执行一条 SELECT 语句。</p><p>决不要检索比需求还多的数据,如 SELECT *。</p><p>使用正确的数据类型。</p><p>导入数据时应该关闭自动提交。</p><p>可以使用 UNION 代替 OR。</p><p>索引改善查询的性能,伤害插入、删除的性能,不经常被搜索不需加索引。</p><p>最重要,每条规则都在某些条件下会打破。</p>]]></content>
<summary type="html">
<p>目录:</p>
<ul>
<li><a href="#3-使用MySQL">3 使用MySQL</a><ul>
<li><a href="#选择数据库">选择数据库</a></li>
</ul>
</li>
<li><a href="#4-检索数据">4 检索数据</a><
</summary>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
</entry>
<entry>
<title>《Linux命令行大全》阅读笔记2</title>
<link href="http://yoursite.com/2020/05/04/%E3%80%8ALinux%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%A4%A7%E5%85%A8%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B02/"/>
<id>http://yoursite.com/2020/05/04/《Linux命令行大全》阅读笔记2/</id>
<published>2020-05-04T03:30:13.000Z</published>
<updated>2020-05-04T03:30:13.591Z</updated>
<summary type="html">
</summary>
</entry>
<entry>
<title>《Linux命令行大全》阅读笔记3</title>
<link href="http://yoursite.com/2020/05/03/%E3%80%8ALinux%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%A4%A7%E5%85%A8%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B03/"/>
<id>http://yoursite.com/2020/05/03/《Linux命令行大全》阅读笔记3/</id>
<published>2020-05-03T14:07:52.000Z</published>
<updated>2020-08-16T02:39:04.473Z</updated>
<content type="html"><![CDATA[<p>目录:</p><ul><li><a href="#1-软件包管理">1 软件包管理</a><ul><li><a href="#软件包系统">软件包系统</a></li><li><a href="#软件包的工作方式">软件包的工作方式</a></li><li><a href="#常见软件包管理任务">常见软件包管理任务</a></li></ul></li><li><a href="#2-编译程序">2 编译程序</a><ul><li><a href="#什么是编译">什么是编译</a></li><li><a href="#编译一个C程序">编译一个 C 程序</a></li></ul></li><li><a href="#3-文件搜索和grep">3 文件搜索和grep</a><ul><li><a href="#文件搜索">文件搜索</a></li><li><a href="#grep文本搜索">grep文本搜索</a></li></ul></li><li><a href="#4-网络">4 网络</a><ul><li><a href="#检查网络">检查网络</a></li></ul></li></ul><h2 id="1-软件包管理"><a href="#1-软件包管理" class="headerlink" title="1 软件包管理"></a>1 软件包管理</h2><p>软件包管理是一种在系统上安装、维护软件的方法。可以通过 Lunux 经销商发布的软件包来安装,也可通过自行下载源代码,进行编译安装。</p><p><br></p><h5 id="软件包系统"><a href="#软件包系统" class="headerlink" title="软件包系统"></a>软件包系统</h5><p>不同的 Linux 发行版用的是不同的软件包系统,不同的软件包系统使用了不同的技术,即 Debian 的 .deb 技术和 Red Hat 的 .rpm 技术。下面列出了两个基本软件包系统:</p><ul><li>Debian 类(.deb 技术):发行版本包括 ubuntu 等。</li><li>Red Hat 类(.rpm 技术):发行版本包括 CentOS、Fedora、Red Hat 等。</li></ul><h4 id="软件包的工作方式"><a href="#软件包的工作方式" class="headerlink" title="软件包的工作方式"></a>软件包的工作方式</h4><p><strong>软件包文件</strong></p><p>一个压缩文件,包括安装文件、文本说明以及安装脚本等文件。</p><p><strong>库</strong></p><p>一个中心库或者第三方库包含了成千上万个软件包。</p><p><strong>高级和低级软件工具</strong></p><p>软件包管理系统通常包含两类工具————执行安装、删除软件包文件等任务的高级工具和进行元数据搜索及提供依赖性解决的低级工具。低级如 dpkg,rpm;高级如 apt-get,yum。</p><h4 id="常见软件包管理任务"><a href="#常见软件包管理任务" class="headerlink" title="常见软件包管理任务"></a>常见软件包管理任务</h4><p>在库里面查找软件包(下面的代码例子都以 Debian 系统为例)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">> apt-get update</div><div class="line">> apt-cache search search_string</div></pre></td></tr></table></figure><p>安装库中的软件包<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">> apt-get update</div><div class="line">> apt-get install package_name</div></pre></td></tr></table></figure></p><p>安装软件包文件中的软件包,如果软件包文件并不是从库源中下载的,那么我们就可用低级工具直接安装。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> dpkg --install package_file</div></pre></td></tr></table></figure><p>例如,先下载第三方 MySQL 的 .deb 文件,然后在安装。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> sudo dpkg -i /路径/mysql-apt-config_w.x.y-z_all.deb</div></pre></td></tr></table></figure></p><p>删除软件包,删除软件包可利用高级工具,也可利用低级工具,高级命令如下:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> apt-get remove package_name</div></pre></td></tr></table></figure></p><p>更新库中的软件包<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">> apt-get update</div><div class="line">> apt-get upgrade</div></pre></td></tr></table></figure></p><p>更新软件包文件的软件包<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> dpkg --install package_name</div></pre></td></tr></table></figure></p><p>列出已安装的软件包列表<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> dpkg --list</div></pre></td></tr></table></figure></p><p>判断软件包是否已安装<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> dpkg --status package_name</div></pre></td></tr></table></figure></p><p>显示已安装软件包的相关信息<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> apt-cache show package_name</div></pre></td></tr></table></figure></p><p><br></p><h2 id="2-编译程序"><a href="#2-编译程序" class="headerlink" title="2 编译程序"></a>2 编译程序</h2><p>介绍如何通过源代码生成可执行程序。</p><p>为什么要编译软件?有如下两个原因:</p><ul><li>库中的程序不能够满足用户的所有需求。</li><li>库中的版本不一定存在最新的版本。<br><br></li></ul><h4 id="什么是编译"><a href="#什么是编译" class="headerlink" title="什么是编译"></a>什么是编译</h4><p>编译是一个将源代码翻译成计算机处理器能够识别的语言的过程。</p><p>计算机的发展过程中经历了一下几个变化:</p><ol><li>机器语言(数值代码)</li><li>汇编语言</li><li>高级编程语言</li></ol><p>高级语言编写的程序通过编译器转换为机器语言,有些编译器将高级语言先转换为汇编语言,然后在把汇编语言转换为机器语言。</p><p>链接器程序可以实现编译器的输出与编译程序所需要的库之间的链接。该操作可以生成一个可执行文件。<br></p><h4 id="编译一个-C-程序"><a href="#编译一个-C-程序" class="headerlink" title="编译一个 C 程序"></a>编译一个 C 程序</h4><p>1.获取程序,如:diction-1.11.tar.gz<br>2.解压<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> tar -xzf diction-1.11.tar.gz</div></pre></td></tr></table></figure></p><p>3.生成程序<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">> ./configure</div><div class="line">> make</div><div class="line">> sudo make install</div></pre></td></tr></table></figure></p><p>4.查看程序是否可执行<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> which diction</div></pre></td></tr></table></figure></p><p>在上面的步骤中,每个步骤都做了什么呢,我们具体分析一下:</p><ul><li>configure 程序其实是源代码树下的一个 shell 脚本,它的任务就是分析生成环境,适应不同的环境来生成可执行程序,同时还会校验当前系统是否安装了必要的外部工具和组件。</li><li>Makefile 是 configure 在源目录生成的文件,描述了生成最后可执行程序时的各部件之间的联系及依赖关系,它的作用是指导 make 命令如何生成可执行成的配置文件, make 是 Linux 提供的一个程序,该程序的作用其实就是输入 Makefile 编译最终的程序。</li><li>打包好的源代码一般包含一个特殊的 make 目标程序,它便是 install(install 是在当前 make 文件夹下生成的目标文件),该目标程序将会在系统目录下安装最后生成的可执行程序,通常会主动安装在 /usr/local/bin 目录下,为了获得执行权限,需要使用 sudo 命令来运行安装可执行程序。</li></ul><p>补充:make 会生成需要生成的文件,如果没有改动,就不会执行任何操作。可用于维护目标文件的更新。<br><br></p><h2 id="3-文件搜索和grep"><a href="#3-文件搜索和grep" class="headerlink" title="3 文件搜索和grep"></a>3 文件搜索和grep</h2><p>在日常的 Linux 操作中,常常需要搜索文件或者匹配一些文件或内容,这就需要几个常用的搜索命令,主要包括 find、grep 等,接下来详细介绍下使用。<br><br></p><h4 id="文件搜索"><a href="#文件搜索" class="headerlink" title="文件搜索"></a>文件搜索</h4><p><code>locate</code> 命令,通过快速搜索数据库,寻找路径名与给定字符串相匹配的文件。</p><p>查找 bin 目录下,以 zip 开头的程序。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> locate bin/zip</div></pre></td></tr></table></figure></p><p>补充:locate 的数据库的内容是通过每天的定时任务获得,不保证实时性,为了实时更新,可以手动执行 updatedb 程序。</p><p>locate 程序仅仅是依据文件名进行查找,而 <code>find</code> 程序则是依据文件的各种数属性在既定的目录里查找。find 程序有两个比较重要的选项,<code>test</code> 选项和 <code>action</code> 选项。首先介绍 test 选项。</p><p>限制文件类型搜索,在当前目录下搜索文类型文件,并统计数量:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> find ~ -type f | wc -l</div></pre></td></tr></table></figure></p><p>find 支持的常用文件类型:</p><ul><li>f:普通文件。</li><li>l:符号链接。</li><li>d:目录。</li><li>b:块设备。</li><li>c:字符设备文件。</li></ul><p>find 命令支持多种 test 参数:</p><ul><li>-name pattern:匹配特定通配符模式的文件或目录。</li><li>-newer file:匹配内容的修改时间比 file 更近的文件或目录。</li><li>-pern mode:寻找权限与模式匹配的目录或文件。</li><li>-size n:匹配 n 大小的文件。</li><li>type c:匹配 c 类型的文件。</li></ul><p>更多的选项还是通过 find 的 man 命令查看。</p><p>test 选项之间的可以使用操作符,可以建立复杂的匹配条件。</p><p>查找访问权限不是 0600 的文件和访问权限不是0700 的目录:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)</div></pre></td></tr></table></figure></p><p>上述命令中使用了 -or ,-not 和 () 操作符,还存在一种另外一种未使用过的 -and 操作符。命令中由于括号在 shell 环境下有特殊含义,所有要对它进行转义,使用反斜杠。</p><p>action 选项可以对搜索的结果执行动作。以下是预定义动作:</p><ul><li>-delete:删除匹配文件。</li><li>-print:输入。</li></ul><p>一个例子:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> find ~ type f -name "*.bak" -delete</div></pre></td></tr></table></figure></p><p>用户也可以自定义动作,有两种方式,第一种方式是使用 <code>exec</code> 操作,另一种是使用 <code>xargs</code> 处理。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> find ~ -type f -name "foo*" -exec ls -l '{}' ';'</div></pre></td></tr></table></figure><p>其中,{} 花括号代表的是当前路径,分号为必须得分隔符表示命令结束。该例子中每次找到匹配的文件后执行 ls -l 命令,-ok 会在执行前询问。</p><p>该命令的缺点就是匹配到文件后就会执行一次命令,用户更希望只调用一次就完成所有操作。可以使用 xargs 来提高效率。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">> find ~ -type f -name "foo*" -print | xargs ls -l</div></pre></td></tr></table></figure></p><p>find 命令执行结果直接作为 xargs 输入,xargs 将其转化为 ls 命令的输入参数列表,执行 ls 操作。在 xargs 命令后添加 –show-limits 可查看最大输入参数数量。</p><h4 id="grep文本搜索"><a href="#grep文本搜索" class="headerlink" title="grep文本搜索"></a>grep文本搜索</h4><p><code>grep</code>名字源于“global regular expression print”,grep 搜索文本文件中与指定正则表达式匹配的行,并将结果送至标准输出。</p><p>语法:<br>grep [optins] regex [file…]</p><p>grep 常用选:</p><ul><li>-c:输出匹配项数目。</li><li>-n:匹配行前加上行号。</li><li>-r:递归搜索。</li><li>-l:只输出匹配文件名。</li></ul><p>例子:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">> grep bzip dirlist*.txt</div><div class="line">dirlist-bin.txt:bzip2</div><div class="line">dirlist-bin.txt:bzip2recover</div></pre></td></tr></table></figure></p><p>命令在文件中搜索到了两个匹配项。</p><p>只展示匹配的文件:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">> grep -l bzip dirlist*.txt</div><div class="line">dirlist-bin.txt</div></pre></td></tr></table></figure></p><p><br></p><h2 id="4-网络"><a href="#4-网络" class="headerlink" title="4 网络"></a>4 网络</h2><h4 id="检查网络"><a href="#检查网络" class="headerlink" title="检查网络"></a>检查网络</h4><p><code>ping</code>——向网络主机发送特殊数据包,<code>ping</code> 命令会向指定的网络主机发送特殊的网络数据包 IMCP ECHO_REQUEST 。多数网络设备收到数据包后会做出回应,通过此法验证网络连接是否正常。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="meta">$</span> ping baidu.com</div></pre></td></tr></table></figure><p><code>traceroute</code>——跟踪网络数据包的传输路径,显示文件通过网络从本地系统传输到指定主机过程中所有停靠点的列表。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="meta">$</span> traceroute www.baidu.com</div></pre></td></tr></table></figure><p><code>netstate</code>——检查网络设置及相关统计数据,<code>netstat</code> 可用于查看不同网络及数据。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="meta">$</span> netstat -ie</div></pre></td></tr></table></figure><p>该命令用于查看网络接口信息。该命令的输出中,第一个称为 eth0 ,是以太网端口;第二个称为 l0 ,是系统用来自己访问自己的回环虚拟接口。</p>]]></content>
<summary type="html">
<p>目录:</p>
<ul>
<li><a href="#1-软件包管理">1 软件包管理</a><ul>
<li><a href="#软件包系统">软件包系统</a></li>
<li><a href="#软件包的工作方式">软件包的工作方式</a></li>
<li><a
</summary>
<category term="Linux" scheme="http://yoursite.com/tags/Linux/"/>
</entry>
<entry>
<title>《Linux命令行大全》阅读笔记</title>
<link href="http://yoursite.com/2020/04/25/%E3%80%8ALinux%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%A4%A7%E5%85%A8%E3%80%8B%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
<id>http://yoursite.com/2020/04/25/《Linux命令行大全》阅读笔记/</id>
<published>2020-04-24T20:31:36.000Z</published>
<updated>2020-04-28T15:52:11.425Z</updated>
<content type="html"><![CDATA[<h1 id="第一部分"><a href="#第一部分" class="headerlink" title="第一部分"></a>第一部分</h1><h2 id="学习shell"><a href="#学习shell" class="headerlink" title="学习shell"></a>学习shell</h2><h3 id="shell是什么"><a href="#shell是什么" class="headerlink" title="shell是什么"></a>shell是什么</h3><p>shell 是一个接收由键盘输入的命令,并将其传递给操作系统来执行的程序。sh是最初的 UNIX shell 程序,bash 是 sh 的增强版本,在 Linux 系统中使用 terminal 与 shell 进行交互。</p><p>几个简单的命令:</p><ul><li>date:显示系统当前日志。</li><li>cal:显示当月的日志。</li><li>df:查看磁盘驱动器的当前可用空间。</li><li>free:显示可用内存。</li></ul><h2 id="导航"><a href="#导航" class="headerlink" title="导航"></a>导航</h2><p>Linux 系统中无论多少驱动器或存储设备与计算机相连,只有一个目录树,存储设备挂载到文件系统树的不同位置。</p><p>几个常用的目录命令:</p><ul><li>pwd:显示当前工作目录。</li><li>ls:列出工作目录的文件和目录。</li><li>cd:切换目录。</li><li>cd - :切换到当前的工作目录。</li><li>cd ~username:切换到其他用户的目录。</li></ul><h2 id="Linux-系统"><a href="#Linux-系统" class="headerlink" title="Linux 系统"></a>Linux 系统</h2><h3 id="ls-命令的乐趣"><a href="#ls-命令的乐趣" class="headerlink" title="ls 命令的乐趣"></a>ls 命令的乐趣</h3><p>ls 可以指定多个问题的目录<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ ls ~ /usr</div></pre></td></tr></table></figure></p><p>选项和参数:</p><ul><li>-a:列出所有文件,包括隐藏的文件。</li><li>-l:使用长格式显示结果。</li><li>-r:字母降序。</li><li>-t:按修改时间排序。</li><li>-S:按照文件大小对结果排序。</li></ul><p>格式内容:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">-rw-r--r-- 1 root root 47584 2012-04-03 11:05 xxx.png</div><div class="line">drwxrwxr-x 1 root root 47584 2012-04-03 11:05 xxx.png</div></pre></td></tr></table></figure></p><p>第一列,”-“ 表示是不同文件,”d” 表示目录。第二列,”1” 文件硬链接数目。47584 表示字节数的文件大小。</p><h3 id="file-确定文件类型"><a href="#file-确定文件类型" class="headerlink" title="file 确定文件类型"></a>file 确定文件类型</h3><p>file 命令可以用来确定文件类型;<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ file picture.jpg</div></pre></td></tr></table></figure></p><p>类 UNIX 系统中,所有的东西都是一个文件。</p><h3 id="less-查看文本文件内容"><a href="#less-查看文本文件内容" class="headerlink" title="less 查看文本文件内容"></a>less 查看文本文件内容</h3><p>使用 less 命令查看文本文件内容,文件内容按页查看。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ less /etc/passwd</div></pre></td></tr></table></figure></p><p>less 程序的常用的键盘命令:</p><ul><li>b:后翻一页。</li><li>space:前翻一页。</li><li>G:文件末尾。</li><li>g:文件开头。</li><li>/char:查找文件内容。</li><li>n:下一个出现的字符。</li><li>q:退出。</li></ul><h3 id="Linux-系统中的一些目录"><a href="#Linux-系统中的一些目录" class="headerlink" title="Linux 系统中的一些目录"></a>Linux 系统中的一些目录</h3><ul><li>/:根目录。</li><li>/bin:系统启动和运行所必须的二进制文件(程序)。</li><li>/boot:内核启动相关文件。</li><li>/dev:包含设备节点的目录。</li><li>/etc:</li></ul>]]></content>
<summary type="html">
<h1 id="第一部分"><a href="#第一部分" class="headerlink" title="第一部分"></a>第一部分</h1><h2 id="学习shell"><a href="#学习shell" class="headerlink" title="学习s
</summary>
<category term="Linux" scheme="http://yoursite.com/tags/Linux/"/>
</entry>
<entry>
<title>绩效反思</title>
<link href="http://yoursite.com/2020/04/13/%E7%BB%A9%E6%95%88%E5%8F%8D%E6%80%9D/"/>
<id>http://yoursite.com/2020/04/13/绩效反思/</id>
<published>2020-04-13T15:05:44.000Z</published>
<updated>2020-04-13T15:20:22.290Z</updated>
<content type="html"><![CDATA[<h2 id="惨不忍睹"><a href="#惨不忍睹" class="headerlink" title="惨不忍睹"></a>惨不忍睹</h2><h4 id="反映问题"><a href="#反映问题" class="headerlink" title="反映问题"></a>反映问题</h4><ul><li>定位问题要精确到前端还是后端</li><li>沟通专业一点</li><li>测试效率和质量有待提升</li><li>责任意识,有敷衍的情况发生</li><li>加强提取有效信息的能力</li><li>业务和技术加强主动</li><li>需求评审要主动</li><li>对于复杂的功能需求没法独立完成</li><li>希望多问问题,不了解的问题积极咨询</li></ul>]]></content>
<summary type="html">
<h2 id="惨不忍睹"><a href="#惨不忍睹" class="headerlink" title="惨不忍睹"></a>惨不忍睹</h2><h4 id="反映问题"><a href="#反映问题" class="headerlink" title="反映问题"></a
</summary>
</entry>
<entry>
<title>networkx-初探</title>
<link href="http://yoursite.com/2018/12/17/networkx-%E5%88%9D%E6%8E%A2/"/>
<id>http://yoursite.com/2018/12/17/networkx-初探/</id>
<published>2018-12-17T08:35:46.000Z</published>
<updated>2018-12-18T02:44:05.407Z</updated>
<content type="html"><![CDATA[<h2 id="networkx-初探"><a href="#networkx-初探" class="headerlink" title="networkx 初探"></a>networkx 初探</h2><ul><li><a href="#创建一个图">创建一个图</a></li><li><a href="#节点">节点</a></li><li><a href="#边">边</a></li><li><a href="#访问边">访问边</a></li><li><a href="#添加属性">添加属性</a></li><li><a href="#有向图">有向图</a><br><br></li></ul><p>在撰写论文的时候经常需要画类似网络连接图,networkx 是一个常用的网络分析包,我们使用它结合 matplotlib 来完成画图任务,下面介绍 networkx 的一些基本概念。</p><h3 id="创建一个图"><a href="#创建一个图" class="headerlink" title="创建一个图"></a>创建一个图</h3><p>创建一个没有节点和边的图:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span><span class="keyword">import</span> networkx <span class="keyword">as</span> nx</div><div class="line"><span class="meta">>>> </span>G=nx.Graph()</div></pre></td></tr></table></figure></p><p>在 networkx 中 nodes 可以是任何可哈希的对象,包括 string/image/xml/graph等。</p><h3 id="节点"><a href="#节点" class="headerlink" title="节点"></a>节点</h3><p>向图中添加,读取节点有多种方式。</p><p>常用的方式添加节点:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.add_node(<span class="number">1</span>)</div><div class="line"><span class="meta">>>> </span>G.add_nodes_from([<span class="number">2</span>,<span class="number">3</span>])</div></pre></td></tr></table></figure></p><p>通过载入 nbunchd 可迭代对象方式添加节点:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>H=nx.path_graph(<span class="number">10</span>)</div><div class="line"><span class="meta">>>> </span>G.add_nodes_from(H)</div></pre></td></tr></table></figure></p><p>将图作为节点添加:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.add_node(H)</div></pre></td></tr></table></figure></p><p>删除节点:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.remove_node(H)</div><div class="line"><span class="meta">>>> </span>G.remove_nodes_from([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>])</div></pre></td></tr></table></figure></p><h3 id="边"><a href="#边" class="headerlink" title="边"></a>边</h3><p>向图里添加边:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.add_edge(<span class="number">1</span>,<span class="number">2</span>)</div><div class="line"><span class="meta">>>> </span>e=(<span class="number">2</span>,<span class="number">3</span>)</div><div class="line"><span class="meta">>>> </span>G.add_edge(*e) <span class="comment"># unpack edge tuple*</span></div><div class="line"><span class="meta">>>> </span>G.add_edges_from([(<span class="number">1</span>,<span class="number">2</span>),(<span class="number">1</span>,<span class="number">3</span>)])</div></pre></td></tr></table></figure></p><p>通过载入 nbunchd 可迭代对象方式添加边:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.add_edges_from(H.edges())</div></pre></td></tr></table></figure></p><p>删除边:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>Graph.remove_edge(<span class="number">1</span>, <span class="number">2</span>)</div><div class="line"><span class="meta">>>> </span>Graph.remove_edges_from([(<span class="number">2</span>, <span class="number">3</span>), (<span class="number">4</span>, <span class="number">5</span>)])</div><div class="line"><span class="meta">>>> </span>G.clear() <span class="comment"># 删除所有边</span></div></pre></td></tr></table></figure></p><p>查看边和节点:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.nodes()</div><div class="line">[<span class="string">'a'</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="string">'spam'</span>, <span class="string">'m'</span>, <span class="string">'p'</span>, <span class="string">'s'</span>]</div><div class="line"><span class="meta">>>> </span>G.edges()</div><div class="line">[(<span class="number">1</span>, <span class="number">2</span>), (<span class="number">1</span>, <span class="number">3</span>)]</div><div class="line"><span class="meta">>>> </span>G.neighbors(<span class="number">1</span>)</div><div class="line">[<span class="number">2</span>, <span class="number">3</span>]</div></pre></td></tr></table></figure></p><p>查看节点和边的个数:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.number_of_nodes()</div><div class="line"><span class="number">8</span></div><div class="line"><span class="meta">>>> </span>G.number_of_edges()</div><div class="line"><span class="number">2</span></div></pre></td></tr></table></figure></p><p>通过其他图的边创建图:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>H=nx.DiGraph(G)</div><div class="line"><span class="meta">>>> </span>H.edges()</div><div class="line">[(<span class="number">1</span>, <span class="number">2</span>), (<span class="number">2</span>, <span class="number">1</span>)]</div><div class="line"><span class="meta">>>> </span>edgelist=[(<span class="number">0</span>,<span class="number">1</span>),(<span class="number">1</span>,<span class="number">2</span>),(<span class="number">2</span>,<span class="number">3</span>)]</div><div class="line"><span class="meta">>>> </span>H=nx.Graph(edgelist)</div></pre></td></tr></table></figure></p><h3 id="访问边"><a href="#访问边" class="headerlink" title="访问边"></a>访问边</h3><p>访问邻边:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G[<span class="number">1</span>]</div><div class="line">{<span class="number">2</span>: {}}</div><div class="line"><span class="meta">>>> </span>G[<span class="number">1</span>][<span class="number">2</span>]</div><div class="line">{}</div></pre></td></tr></table></figure></p><p>设置边属性:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.add_edge(<span class="number">1</span>,<span class="number">3</span>)</div><div class="line"><span class="meta">>>> </span>G[<span class="number">1</span>][<span class="number">3</span>][<span class="string">'color'</span>]=<span class="string">'blue'</span></div></pre></td></tr></table></figure></p><h3 id="添加属性"><a href="#添加属性" class="headerlink" title="添加属性"></a>添加属性</h3><p>为图添加属性:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G = nx.Graph(day=<span class="string">"Friday"</span>)</div><div class="line"><span class="meta">>>> </span>G.graph</div><div class="line">{<span class="string">'day'</span>: <span class="string">'Friday'</span>}</div></pre></td></tr></table></figure></p><p>为节点添加属性:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.add_node(<span class="number">1</span>, time=<span class="string">'5pm'</span>)</div><div class="line"><span class="meta">>>> </span>G.add_nodes_from([<span class="number">3</span>], time=<span class="string">'2pm'</span>)</div><div class="line"><span class="meta">>>> </span>G.node[<span class="number">1</span>]</div><div class="line">{<span class="string">'time'</span>: <span class="string">'5pm'</span>}</div><div class="line"><span class="meta">>>> </span>G.node[<span class="number">1</span>][<span class="string">'room'</span>] = <span class="number">714</span></div><div class="line"><span class="meta">>>> </span>G.nodes(data=<span class="keyword">True</span>)</div><div class="line">[(<span class="number">1</span>, {<span class="string">'room'</span>: <span class="number">714</span>, <span class="string">'time'</span>: <span class="string">'5pm'</span>}), (<span class="number">3</span>, {<span class="string">'time'</span>: <span class="string">'2pm'</span>})]</div></pre></td></tr></table></figure></p><p>为边添加属性:<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>G.add_edge(<span class="number">1</span>, <span class="number">2</span>, weight=<span class="number">4.7</span> )</div><div class="line"><span class="meta">>>> </span>G.add_edges_from([(<span class="number">3</span>,<span class="number">4</span>),(<span class="number">4</span>,<span class="number">5</span>)], color=<span class="string">'red'</span>)</div><div class="line"><span class="meta">>>> </span>G.add_edges_from([(<span class="number">1</span>,<span class="number">2</span>,{<span class="string">'color'</span>:<span class="string">'blue'</span>}), (<span class="number">2</span>,<span class="number">3</span>,{<span class="string">'weight'</span>:<span class="number">8</span>})])</div><div class="line"><span class="meta">>>> </span>G[<span class="number">1</span>][<span class="number">2</span>][<span class="string">'weight'</span>] = <span class="number">4.7</span></div><div class="line"><span class="meta">>>> </span>G.edge[<span class="number">1</span>][<span class="number">2</span>][<span class="string">'weight'</span>] = <span class="number">4</span></div></pre></td></tr></table></figure></p><h3 id="有向图"><a href="#有向图" class="headerlink" title="有向图"></a>有向图</h3><p>networkx 提供了查看出度、入度、邻接节点等方法处理有向图。<br><figure class="highlight py"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span>DG=nx.DiGraph()</div><div class="line"><span class="meta">>>> </span>DG.add_weighted_edges_from([(<span class="number">1</span>,<span class="number">2</span>,<span class="number">0.5</span>), (<span class="number">3</span>,<span class="number">1</span>,<span class="number">0.75</span>)]) <span class="comment">#构建有向图</span></div><div class="line"><span class="meta">>>> </span>DG.out_degree(<span class="number">1</span>,weight=<span class="string">'weight'</span>) <span class="comment"># 查看出度</span></div><div class="line"><span class="number">0.5</span></div><div class="line"><span class="meta">>>> </span>DG.degree(<span class="number">1</span>,weight=<span class="string">'weight'</span>) <span class="comment"># 查看出度入度之和</span></div><div class="line"><span class="number">1.25</span></div><div class="line"><span class="meta">>>> </span>DG.successors(<span class="number">1</span>) <span class="comment"># 查看临界节点</span></div><div class="line">[<span class="number">2</span>]</div><div class="line"><span class="meta">>>> </span>DG.neighbors(<span class="number">1</span>)</div><div class="line">[<span class="number">2</span>]</div></pre></td></tr></table></figure></p><p>参考:<a href="https://networkx.github.io/documentation/stable/tutorial.html#" target="_blank" rel="external">networkx 官方文档</a></p>]]></content>
<summary type="html">
<h2 id="networkx-初探"><a href="#networkx-初探" class="headerlink" title="networkx 初探"></a>networkx 初探</h2><ul>
<li><a href="#创建一个图">创建一个图</a></
</summary>
<category term="networkx Python" scheme="http://yoursite.com/tags/networkx-Python/"/>
</entry>
<entry>
<title>Pro Git 阅读笔记</title>
<link href="http://yoursite.com/2018/11/28/Pro-Git-%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/"/>
<id>http://yoursite.com/2018/11/28/Pro-Git-阅读笔记/</id>
<published>2018-11-28T11:03:29.000Z</published>
<updated>2018-11-28T11:14:26.868Z</updated>
<content type="html"><![CDATA[<h2 id="ProGit-阅读笔记"><a href="#ProGit-阅读笔记" class="headerlink" title="ProGit 阅读笔记"></a>ProGit 阅读笔记</h2><ul><li><a href="#git-由来">Git 由来</a><ul><li><a href="#版本控制">版本控制</a></li><li><a href="#集中化的版本控制系统">集中化的版本控制系统</a></li><li><a href="#分布式版本控制系统">分布式版本控制系统</a></li><li><a href="#git-相比于-svn-等的特点">Git 相比于 svn 等的特点</a></li><li><a href="#三种状态">三种状态</a></li></ul></li><li><a href="#git-基础">Git 基础</a><ul><li><a href="#取得项目的-git-仓库">取得项目的 Git 仓库</a></li><li><a href="#记录每次更新到仓库">记录每次更新到仓库</a></li><li><a href="#查看历史提交">查看历史提交</a></li><li><a href="#撤销操作">撤销操作</a></li><li><a href="#远程仓库的使用">远程仓库的使用</a></li></ul></li><li><a href="#git-分支">Git 分支</a><ul><li><a href="#何谓分支">何谓分支</a></li><li><a href="#基本的分支与合并">基本的分支与合并</a></li><li><a href="#分支管理">分支管理</a></li><li><a href="#远程分支">远程分支</a></li><li><a href="#衍合">衍合</a></li></ul></li><li><a href="#服务器上的-git">服务器上的 Git</a><ul><li><a href="#协议">协议</a><ul><li><a href="#ssh-协议">SSH 协议</a></li><li><a href="#git-协议">Git 协议</a></li><li><a href="#https-协议">HTTP/S 协议</a></li></ul></li></ul></li><li><a href="#分布式-git">分布式 Git</a><ul><li><a href="#储藏">储藏</a></li><li><a href="#重写历史">重写历史</a></li><li><a href="#衍合与挑拣">衍合与挑拣</a><br><br></li></ul></li></ul><h1 id="Git-由来"><a href="#Git-由来" class="headerlink" title="Git 由来"></a>Git 由来</h1><h2 id="版本控制"><a href="#版本控制" class="headerlink" title="版本控制"></a>版本控制</h2><p>版本控制是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统。</p><h2 id="集中化的版本控制系统"><a href="#集中化的版本控制系统" class="headerlink" title="集中化的版本控制系统"></a>集中化的版本控制系统</h2><p>如 CVS, Subversion 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,客户端通过客户端连接到服务器,取出最新文件或者提交更新。</p><img src="/2018/11/28/Pro-Git-阅读笔记/1.jpg" alt="1.jpg" title=""><h2 id="分布式版本控制系统"><a href="#分布式版本控制系统" class="headerlink" title="分布式版本控制系统"></a>分布式版本控制系统</h2><p>如 Git, Bazaar 等,客户端并不只是提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的<br>本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份)。</p><img src="/2018/11/28/Pro-Git-阅读笔记/2.jpg" alt="2.jpg" title=""><h2 id="Git-相比于-svn-等的特点"><a href="#Git-相比于-svn-等的特点" class="headerlink" title="Git 相比于 svn 等的特点"></a>Git 相比于 svn 等的特点</h2><ul><li>直接快照,而非比较差异,为提高性能。</li></ul><img src="/2018/11/28/Pro-Git-阅读笔记/3.jpg" alt="3.jpg" title=""><img src="/2018/11/28/Pro-Git-阅读笔记/4.jpg" alt="4.jpg" title=""><ul><li><p>近乎所有操作都可本地执行。</p><p> 由于其分布式的特点,每个客户端都保存着仓库的备份,在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网,如提交更新。</p></li><li><p>时刻保持数据完整性。</p><p> 在保存到Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。Git 使用SHA-1 算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个SHA-1 哈希值,作为指纹字符串。</p></li></ul><h2 id="三种状态"><a href="#三种状态" class="headerlink" title="三种状态"></a>三种状态</h2><p>对于任何一个文件,在 Git 内都只有三种状态:已提交<br>(committed),已修改(modified)和已暂存(staged)。</p><p>Git 管理项目时,文件流转的三个工作区域:Git 的本地数据目录,工作目录以暂存区<br>域。</p><img src="/2018/11/28/Pro-Git-阅读笔记/5.jpg" alt="5.jpg" title=""><h1 id="Git-基础"><a href="#Git-基础" class="headerlink" title="Git 基础"></a>Git 基础</h1><h2 id="取得项目的-Git-仓库"><a href="#取得项目的-Git-仓库" class="headerlink" title="取得项目的 Git 仓库"></a>取得项目的 Git 仓库</h2><p>从当前目录初始化:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git init</div></pre></td></tr></table></figure></p><p>从现有仓库克隆:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git clone git://github.com/schacon/grit.git</div></pre></td></tr></table></figure></p><p>克隆某个分支:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git clone -b breach-name git://github.com/schacon/grit.git</div></pre></td></tr></table></figure></p><h2 id="记录每次更新到仓库"><a href="#记录每次更新到仓库" class="headerlink" title="记录每次更新到仓库"></a>记录每次更新到仓库</h2><p>工作目录下面的所有文件都不外乎这两种状态:已跟踪或未跟踪。</p><p>已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新,已修改或者已放入暂存区。而所有其他文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存区域。</p><img src="/2018/11/28/Pro-Git-阅读笔记/6.jpg" alt="6.jpg" title=""><p>检查当前文件状态:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git status</div></pre></td></tr></table></figure></p><p>跟踪新文件以及将跟踪后更新后暂存:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git add README</div></pre></td></tr></table></figure></p><p>要暂存这次更新,需要运行git add 命令(这是个多功能命令,根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时<br>把有冲突的文件标记为已解决状态等)</p><p>忽略某些文件,编辑 .gitignore 文件:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ cat .gitignore</div><div class="line">*.[oa]</div><div class="line">*~</div></pre></td></tr></table></figure></p><p>查看已暂存和未暂存的更新:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">$ git diff</div><div class="line"></div><div class="line">修改之后还没有暂存起来的变化内</div><div class="line">容</div><div class="line"></div><div class="line">$ git diff --cached</div><div class="line"></div><div class="line">看已经暂存起来的文件和上次提交时的快照之间的差异</div></pre></td></tr></table></figure></p><p>提交更新:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git commit</div></pre></td></tr></table></figure></p><p>移除文件,即从已跟踪文件清除:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git rm grit.gemspec</div></pre></td></tr></table></figure></p><p>如果删除之前修改过并且已经放到暂存区域的话,则必须<br>要用强制删除选项-f(译注:即force 的首字母),以防误删除文件后丢失修改的内容。</p><p>把文件从Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。用–cached 选项即可。</p><h2 id="查看历史提交"><a href="#查看历史提交" class="headerlink" title="查看历史提交"></a>查看历史提交</h2><p>回顾提交历史:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git log</div></pre></td></tr></table></figure></p><p>-2 仅显示最近的两次更新:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git log -2</div></pre></td></tr></table></figure></p><p>git log 还有其他一些选项,方便查看内容。</p><h2 id="撤销操作"><a href="#撤销操作" class="headerlink" title="撤销操作"></a>撤销操作</h2><p>撤消刚才的提交操作,重新提交:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git commit --amend</div></pre></td></tr></table></figure></p><p>取消已经暂存的文件,不小心用 git add * 全加到了暂存区域。该如何撤消暂存其中的一个文件呢?取消暂存benchmarks.rb 文件:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">git add *</div><div class="line"></div><div class="line">git reset HEAD benchmarks.rb</div></pre></td></tr></table></figure></p><p>取消对文件的修改,回到之前的状态,与上面的相比,该命令取消了未暂存的文件修改:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git checkout -- benchmarks.rb</div></pre></td></tr></table></figure></p><h2 id="远程仓库的使用"><a href="#远程仓库的使用" class="headerlink" title="远程仓库的使用"></a>远程仓库的使用</h2><p>查看当前的远程库:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git remote -v</div></pre></td></tr></table></figure></p><p>添加远程仓库:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git remote add pb git://github.com/paulboone/ticgit.git</div></pre></td></tr></table></figure></p><p>从远程仓库抓取数据:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git fetch [remote-name]</div></pre></td></tr></table></figure></p><p>有一点很重要,<br>需要记住,fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好<br>了,才能手工合并,git pull 相当于 git fetch 和 git merge。</p><p>推送数据到远程仓库:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git push [remote-name] [branch-name]</div></pre></td></tr></table></figure></p><p>重命名远端仓库:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git remote rename [remote-name] [new-name]</div></pre></td></tr></table></figure></p><p>删除远端仓库:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git remote rm [remote-name]</div></pre></td></tr></table></figure></p><h1 id="Git-分支"><a href="#Git-分支" class="headerlink" title="Git 分支"></a>Git 分支</h1><h2 id="何谓分支"><a href="#何谓分支" class="headerlink" title="何谓分支"></a>何谓分支</h2><p>在Git 中提交时,会保存一个提交(commit)对象,它包含一个指向暂存内容快照的指针,作者和相关附属信息,以一定数量(也可能没有)指向该提交对象直接祖先的指针:第一次提交是没有直接祖先的,普通提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先。</p><img src="/2018/11/28/Pro-Git-阅读笔记/7.jpg" alt="7.jpg" title=""><img src="/2018/11/28/Pro-Git-阅读笔记/8.jpg" alt="8.jpg" title=""><blockquote><p>Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。</p></blockquote><p><br></p><p>创建 test 分支:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git branch testing</div></pre></td></tr></table></figure></p><p>会在当前 commit 对象上新建一个分支指针。</p><img src="/2018/11/28/Pro-Git-阅读笔记/9.jpg" alt="9.jpg" title=""><p>切换到新建的 testing 分支:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git checkout testing</div></pre></td></tr></table></figure></p><h2 id="基本的分支与合并"><a href="#基本的分支与合并" class="headerlink" title="基本的分支与合并"></a>基本的分支与合并</h2><p>在 master 上与 hotfix 分支合并:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git checkout master</div><div class="line">$ git merge hotfix</div></pre></td></tr></table></figure></p><img src="/2018/11/28/Pro-Git-阅读笔记/12.jpg" alt="12.jpg" title=""><p>上述方式的合并叫快进。</p><p>master 和 iss53 的合并叫合并提交:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git checkout master</div><div class="line">$ git merge iss53</div></pre></td></tr></table></figure></p><img src="/2018/11/28/Pro-Git-阅读笔记/13.jpg" alt="13.jpg" title=""><p>有时合并的时候可能会遇到冲突,任何包含未解决冲突的文件都会以未合并(unmerged)状态列出。</p><img src="/2018/11/28/Pro-Git-阅读笔记/14.jpg" alt="14.jpg" title=""><p>可以看到======= 隔开的上半部分,是 HEAD (即 master 分支,在运行 merge 命令时检出的分支)中的内容,下半部分是在 iss53 分支中的内容。解决冲突的办法无非是二者选其一或者由你亲自整合到一起。</p><p>解决了所有文件里的所有冲突后,运行 git add 将把它们标记为已解决(resolved)。因为一旦暂存,就表示冲突已经解决。</p><h2 id="分支管理"><a href="#分支管理" class="headerlink" title="分支管理"></a>分支管理</h2><p>给出当前所有分支的清单:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git branch</div><div class="line"> iss53</div><div class="line">* master</div><div class="line"> testing</div></pre></td></tr></table></figure></p><p>要从该清单中筛选出你已经(或尚未)与当前分支合并的分支,可以用–merge 和–no-merged 选项,比如git branch -merge 查看哪些分支已被并入当前分支:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git branch --merged</div><div class="line"> iss53</div><div class="line">* master</div></pre></td></tr></table></figure></p><h2 id="远程分支"><a href="#远程分支" class="headerlink" title="远程分支"></a>远程分支</h2><p>远程分支(remote branch)是对远程仓库状态的索引。它们是一些无法移动的本地分支;只有在进行 Git 的网络活动时才会更新。</p><p>(远程仓库名)/(分支名) 这样的形式表示远程分支。</p><img src="/2018/11/28/Pro-Git-阅读笔记/15.jpg" alt="15.jpg" title=""><p>可以运行 git fetch origin 来进行同步。该命令首先找到origin 是哪个服务器,从上面获取你尚未拥有的数据,更新你本地的数据库,然后把 origin/master 的指针移到它最新的位置。</p><img src="/2018/11/28/Pro-Git-阅读笔记/16.jpg" alt="16.jpg" title=""><p>如果你有个叫 serverfix 的分支需要和他人一起开发,可以运行git push (远程仓库名) (分支名):</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git push origin serverfix:serferfix</div></pre></td></tr></table></figure><p>它的意思是“提取我的 serverfix 并更新到远程仓库的serverfix”。通过此语法,你可以把本地分支推送到某个命名不同的远程分支:若想把远程分支叫作 awesomebranch,可以用git push origin serverfix:awesomebranch 来推送数据。</p><p>删除远程分支,git push [远程名] :[分支名]。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git push origin :serverfix</div></pre></td></tr></table></figure></p><h2 id="衍合"><a href="#衍合" class="headerlink" title="衍合"></a>衍合</h2><p>把一个分支整合到另一个分支的办法有两种:merge(合并) 和 rebase(衍合)。</p><p>衍合(rebase),有了rebase 命令,就可以把在一个分支里提交的改变在另一个分支里重放一遍。</p><p>它的原理是回到两个分支(你所在的分支和你想要衍合进去的分支)的共同祖先,提取你所在分支每次提交时产生的差异(diff),把这些差异分别保存到临时文件里,然后从当前分支转换到你需要衍合入的分支,依序施用每一个差异补丁文件。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git checkout experiment</div><div class="line">$ git rebase master</div><div class="line">$ git checkout master</div><div class="line">$ git merge experiment</div></pre></td></tr></table></figure><img src="/2018/11/28/Pro-Git-阅读笔记/17.jpg" alt="17.jpg" title=""><p>衍合的风险:永远不要衍合那些已经推送到公共仓库的更新。</p><h1 id="服务器上的-Git"><a href="#服务器上的-Git" class="headerlink" title="服务器上的 Git"></a>服务器上的 Git</h1><h2 id="协议"><a href="#协议" class="headerlink" title="协议"></a>协议</h2><p>Git 可以使用四种主要的协议来传输数据:本地传输,SSH 协议,Git 协议和HTTP 协议。</p><h3 id="SSH-协议"><a href="#SSH-协议" class="headerlink" title="SSH 协议"></a>SSH 协议</h3><p>SSH 是唯一一个同时便于读和写操作的网络协议。SSH 同<br>时也是一个验证授权的网络协议。</p><p>通过SSH 克隆一个Git 仓库:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git clone ssh://user@server:project.git</div></pre></td></tr></table></figure></p><p>Git 默认使用 SSH。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git clone user@server:project.git</div></pre></td></tr></table></figure></p><p>优点:</p><pre><code>* 首先,如果你想拥有对网络仓库的写权限,基本上不可能不使用 SSH 。* 再次,通过 SSH 进行访问是安全的——所有数据传输都是加密和授权的。</code></pre><p>缺点:</p><pre><code>* SSH 的限制在于你不能通过它实现仓库的匿名访问。即使仅为读取数据,人们也必须在能通过 SSH 访问主机的前提下才能访问仓库,这使得 SSH 不利于开源的项目。</code></pre><h3 id="Git-协议"><a href="#Git-协议" class="headerlink" title="Git 协议"></a>Git 协议</h3><p>这是一个包含在 Git 软件包中的特殊守护进程; 它会监听一个提供类似于 SSH 服务的特定端口(9418)</p><p>优点:</p><pre><code>* Git 协议是现存最快的传输协议,因为省去了加密和授权的开销。</code></pre><p>缺点:</p><pre><code>* Git 协议消极的一面是缺少授权机制。用 Git 协议作为访问项目的唯一方法通常是不可取的。一般做法是,同时提供SSH 接口,让几个开发者拥有推送(写)权限,其他人通过git:// 拥有只读权限。</code></pre><h3 id="HTTP-S-协议"><a href="#HTTP-S-协议" class="headerlink" title="HTTP/S 协议"></a>HTTP/S 协议</h3><p>HTTP 或 HTTPS 协议的优美之处在于架设的简便性。基本上, 只需要把 Git 的纯仓库文件放在 HTTP 的文件根目录下,配置一个特定的 post-update 挂钩(hook),就搞定了。</p><p>克隆仓库:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git clone http://example.com/gitproject.git</div></pre></td></tr></table></figure></p><p>优点:</p><pre><code>* 使用 HTTP 协议的好处是易于架设。几条必要的命令就可以让全世界读取到仓库的内容。</code></pre><p>缺点:</p><pre><code>* 相对来说客户端效率更低。克隆或者下载仓库内容可能会花费更多时间,而且 HTTP 传输的体积和网络开销比其他任何一个协议都大。因为它没有按需供应的能力——传输过程中没有服务端的动态计算——因而 HTTP 协议经常会被称为傻瓜(dumb) 协议。</code></pre><h1 id="分布式-Git"><a href="#分布式-Git" class="headerlink" title="分布式 Git"></a>分布式 Git</h1><h2 id="储藏"><a href="#储藏" class="headerlink" title="储藏"></a>储藏</h2><p>经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。解决这个问题的办法就是git stash命令。</p><p>现在你想切换分支,但是你还不想提交你正在进行中的工作;所以你储藏这些变更。为了往堆栈推送一个新的储藏,只要运行git stash:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git stash</div></pre></td></tr></table></figure><p>要查看现有的储藏,你可以使用git stash list:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git stash list</div><div class="line">stash@{0}: WIP on master: 049d078 added the index file</div><div class="line">stash@{1}: WIP on master: c264051... Revert "added file_size"</div><div class="line">stash@{2}: WIP on master: 21d80a5... added number to log</div></pre></td></tr></table></figure></p><p>你可以重新应用你刚刚实施的储藏:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ git stash apply</div><div class="line"></div><div class="line">or</div><div class="line"></div><div class="line">$ git stash apply stash@2</div></pre></td></tr></table></figure></p><p>apply 选项只尝试应用储藏的工作——储藏的内容仍然在栈上。要移除它,你可以运行git stash drop:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git stash drop stash@{0}</div></pre></td></tr></table></figure></p><p>你也可以运行git stash pop 来重新应用储藏,同时立刻将其从堆栈中移走。</p><h2 id="重写历史"><a href="#重写历史" class="headerlink" title="重写历史"></a>重写历史</h2><p>改变最近一次提交:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git commit --amend</div></pre></td></tr></table></figure></p><p>修改多个提交说明:</p><p>如果你想修改或改变说明、增加文件、删除文件或任何其他事情。你可以通过给 git rebase 增加 -i 选项来以交互方式地运行 rebase。你必须通过告诉命令衍合到哪次提交,来指明你需要重写的提交的回溯深度。</p><p>修改最近三次提交,范围内的每一次提交都会被重写,无论你是否修改说明:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">$ git rebase -i HEAD~3</div><div class="line"></div><div class="line">pick f7f3f6d changed my name a bit</div><div class="line">pick 310154e updated README formatting and added blame</div><div class="line">pick a5f4a0d added cat-file</div><div class="line"></div><div class="line"># Rebase 710f0f8..a5f4a0d onto 710f0f8</div><div class="line">#</div><div class="line"># Commands:</div><div class="line"># p, pick = use commit</div><div class="line"># e, edit = use commit, but stop for amending</div><div class="line"># s, squash = use commit, but meld into previous commit</div><div class="line">#</div><div class="line"># If you remove a line here THAT COMMIT WILL BE LOST.</div><div class="line"># However, if you remove everything, the rebase will be aborted.</div><div class="line">#</div></pre></td></tr></table></figure></p><p>可以使用交互式的衍合来彻底重排或删除提交。</p><p>重排提交:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">pick f7f3f6d changed my name a bit</div><div class="line">pick 310154e updated README formatting and added blame</div><div class="line">pick a5f4a0d added cat-file</div></pre></td></tr></table></figure></p><p>改为:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">pick 310154e updated README formatting and added blame</div><div class="line">pick f7f3f6d changed my name a bit</div></pre></td></tr></table></figure></p><p>压制(Squashing)提交:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">pick f7f3f6d changed my name a bit</div><div class="line">squash 310154e updated README formatting and added blame</div><div class="line">squash a5f4a0d added cat-file</div></pre></td></tr></table></figure></p><p>当你保存并退出编辑器,Git 会应用全部三次变更然后将你送回编辑器来归并三次提交说明。</p><p>拆分提交就是撤销一次提交,然后多次部分地暂存或提交直到结束。</p><p>将“updated README formatting and added blame”拆分成两次提交:第一次为“updated<br>README formatting”,第二次为“added blame”。你可以在rebase -i脚本中修改你想拆分的提交前的指令<br>为“edit”。</p><p>拆分提交:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">pick f7f3f6d changed my name a bit</div><div class="line">edit 310154e updated README formatting and added blame</div><div class="line">pick a5f4a0d added cat-file</div></pre></td></tr></table></figure></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ git reset HEAD^</div><div class="line">$ git add README</div><div class="line">$ git commit -m 'updated README formatting'</div><div class="line">$ git add lib/simplegit.rb</div><div class="line">$ git commit -m 'added blame'</div><div class="line">$ git rebase --continue</div></pre></td></tr></table></figure><h2 id="衍合与挑拣"><a href="#衍合与挑拣" class="headerlink" title="衍合与挑拣"></a>衍合与挑拣</h2><p>另一个引入代码的方法是挑拣。挑拣类似于针对某次特定提交的衍合。它首先提取某次提交的补丁,然后试着应用在当前分支上。如果某个特性分支上有多个 commits,但你只想引入其中之一就可以使用这种方法。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf</div><div class="line">Finished one cherry-pick.</div><div class="line">[master]: created a0a41a9: "More friendly message when locking the index fails."</div><div class="line">3 files changed, 17 insertions(+), 3 deletions(-)</div></pre></td></tr></table></figure><img src="/2018/11/28/Pro-Git-阅读笔记/18.jpg" alt="18.jpg" title="">]]></content>
<summary type="html">
<h2 id="ProGit-阅读笔记"><a href="#ProGit-阅读笔记" class="headerlink" title="ProGit 阅读笔记"></a>ProGit 阅读笔记</h2><ul>
<li><a href="#git-由来">Git 由来</a>
</summary>
<category term="Git" scheme="http://yoursite.com/tags/Git/"/>
</entry>
<entry>
<title>二叉平衡树——B-树,B+树与MySQL数据库B-Tree索引</title>
<link href="http://yoursite.com/2018/09/02/%E4%BA%8C%E5%8F%89%E5%B9%B3%E8%A1%A1%E6%A0%91%E2%80%94%E2%80%94B-%E6%A0%91%EF%BC%8CB-%E6%A0%91%E4%B8%8EMySQL%E6%95%B0%E6%8D%AE%E5%BA%93%E7%B4%A2%E5%BC%95/"/>
<id>http://yoursite.com/2018/09/02/二叉平衡树——B-树,B-树与MySQL数据库索引/</id>
<published>2018-09-01T22:44:45.000Z</published>
<updated>2018-09-07T17:58:16.723Z</updated>
<content type="html"><![CDATA[<h2 id="二叉平衡树"><a href="#二叉平衡树" class="headerlink" title="二叉平衡树"></a>二叉平衡树</h2><blockquote><p>AVL树是带有平衡条件的二叉查找树,该平衡条件是每个节点的左子树和右子树的高度最多差1的二叉查找树。(空树的高度定义为-1。)</p></blockquote><p>在该树上的所有操作的时间复杂度为O(logN)。插入过程中涉及到了单旋转和双旋转,具体实现<a href="https://github.com/Lucky4/Data-Structures-and-Algorithm-Analysis-in-C/blob/master/%E4%BA%8C%E5%8F%89%E6%A0%91/%E4%BA%8C%E5%8F%89%E5%B9%B3%E8%A1%A1%E6%A0%91/%E4%BA%8C%E5%8F%89%E5%B9%B3%E8%A1%A1%E6%A0%91%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C.cpp" target="_blank" rel="external">请看</a>。<br><br></p><h2 id="B-树"><a href="#B-树" class="headerlink" title="B-树"></a>B-树</h2><p>阶为M的B-树是一棵有下列结构特性的树:</p><ul><li>树的根的儿子数在2和M之间。</li><li>除根外,所有非树叶节点的儿子树在M/2(向上取整)和M之间。</li><li>所有的树叶都在相同的深度上。</li><li>每个节点存放至少2个,至多M个关键字。<br><br></li></ul><p>B-树深度最多是logM/2(N)向上取整,Insert和Delete操作复杂度为O(MlogM(N)),Find操作复杂度为O(logN)。<br><br></p><h2 id="B-树-1"><a href="#B-树-1" class="headerlink" title="B+树"></a>B+树</h2><p>B+树与B-树向比,增加了以下特性:</p><ul><li>非叶子结点的子树指针与关键字个数相同。每个关键字不保存数据,只用来索引,所有数据都保存在叶子节点。</li><li>为所有叶子结点增加一个链指针。</li><li>所有关键字都在叶子结点出现。<br><br></li></ul><h2 id="MySQL数据库B-Tree索引"><a href="#MySQL数据库B-Tree索引" class="headerlink" title="MySQL数据库B-Tree索引"></a>MySQL数据库B-Tree索引</h2><p><a href="https://blog.csdn.net/no_endless/article/details/77073549" target="_blank" rel="external">MySQL 聚集索引/非聚集索引简述</a><br><a href="https://www.cnblogs.com/zlcxbb/p/5757245.html" target="_blank" rel="external">MyISAM和InnoDB的索引实现</a><br><br></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://blog.csdn.net/aqzwss/article/details/53074186" target="_blank" rel="external">参考1</a><br><a href="https://blog.csdn.net/no_endless/article/details/77073549" target="_blank" rel="external">参考2</a><br><a href="https://www.cnblogs.com/zlcxbb/p/5757245.html" target="_blank" rel="external">参考3</a><br>参考4《数据结构与算法分析:C语言描述》</p>]]></content>
<summary type="html">
<h2 id="二叉平衡树"><a href="#二叉平衡树" class="headerlink" title="二叉平衡树"></a>二叉平衡树</h2><blockquote>
<p>AVL树是带有平衡条件的二叉查找树,该平衡条件是每个节点的左子树和右子树的高度最多差1的二
</summary>
<category term="二叉平衡树" scheme="http://yoursite.com/tags/%E4%BA%8C%E5%8F%89%E5%B9%B3%E8%A1%A1%E6%A0%91/"/>
</entry>
<entry>
<title>Python源码剖析——Dict对象</title>
<link href="http://yoursite.com/2018/08/28/Python%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90%E2%80%94%E2%80%94Dict%E5%AF%B9%E8%B1%A1/"/>
<id>http://yoursite.com/2018/08/28/Python源码剖析——Dict对象/</id>
<published>2018-08-28T02:02:32.000Z</published>
<updated>2018-08-28T22:04:07.175Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>现代的编程语言都在语言级或标准库中提供某种<code>关联式的容器</code>,容器中的元素通常是以(key,value)的形式存在的。</p><p>关联式容器总会极大地关注键的搜索效率,其实现都会基于良好的数据结构,如C++中的map的实现基于红黑树,搜索时间复杂度为O(logN)。</p><p>Python中的关联容器<code>PyDictObject</code>即dict是基于<code>散列表</code>实现的。Dict对搜索的效率要求很高,因为Python本身的实现中dict被大量的使用,如Python字节码的运行环境,其中存放变量名和变量值,通过查找变量名获得变量值。因此dict的实现没有采用平衡二叉树,而是采用了散列表,最优情况下散列表的搜索复杂度为O(1)。<br><br></p><h2 id="散列表"><a href="#散列表" class="headerlink" title="散列表"></a>散列表</h2><p>Python在处理哈希冲突的时候采用<code>开放地址法</code>,在删除散列表中的元素的时候,不能进行真正的删除,因为会导致冲突探测链的探测过早的结束,所以Python采用一种<code>伪删除</code>的方式。<br><br></p><h2 id="PyDictObject"><a href="#PyDictObject" class="headerlink" title="PyDictObject"></a>PyDictObject</h2><h4 id="关联容器的entry"><a href="#关联容器的entry" class="headerlink" title="关联容器的entry"></a>关联容器的entry</h4><p>关联容器中的一个(key,value)元素称为一个<code>entry</code>或slot。Python中,一个entry定义如下:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// dictobject.h</span></div><div class="line"></div><div class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> {</span></div><div class="line"> <span class="comment">/* Cached hash code of me_key. Note that hash codes are C longs.</span></div><div class="line"> * We have to use Py_ssize_t instead because dict_popitem() abuses</div><div class="line"> * me_hash to hold a search finger.</div><div class="line"> */</div><div class="line"> Py_ssize_t me_hash;</div><div class="line"> PyObject *me_key;</div><div class="line"> PyObject *me_value;</div><div class="line">} PyDictEntry;</div></pre></td></tr></table></figure><p>PyDictObject存放的是PyObject*,可以存放任何Python对象。<code>me_hash</code>域存储的是<code>me_key</code>的散列值。</p><p>PyDictObject中entry可以在3种状态间转换:</p><ul><li><code>Unused态</code>:该entry从现在到之前都没有存储过(key,value)对。</li><li><code>Active态</code>:存储了一个(key,value)对entry便传化为Active态。</li><li><code>Dummy态</code> : entry中的(key,value)对被“伪删除”后,进入Dummy态。</li></ul><img src="/2018/08/28/Python源码剖析——Dict对象/pic1.jpg" alt="entry三种状态" title="entry三种状态"><p><br></p><h4 id="关联容器的实现"><a href="#关联容器的实现" class="headerlink" title="关联容器的实现"></a>关联容器的实现</h4><p>关联容器PyDictObject是一大堆entry的集合,集合结构如下:<br><figure class="highlight c"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// dictobject.j</span></div><div class="line"></div><div class="line"><span class="class"><span class="keyword">struct</span> _<span class="title">dictobject</span> {</span></div><div class="line"> PyObject_HEAD</div><div class="line"> Py_ssize_t ma_fill; <span class="comment">/* # Active + # Dummy */</span></div><div class="line"> Py_ssize_t ma_used; <span class="comment">/* # Active */</span></div><div class="line"></div><div class="line"> <span class="comment">/* The table contains ma_mask + 1 slots, and that's a power of 2.</span></div><div class="line"> * We store the mask instead of the size because the mask is more</div><div class="line"> * frequently needed.</div><div class="line"> */</div><div class="line"> Py_ssize_t ma_mask;</div><div class="line"></div><div class="line"> <span class="comment">/* ma_table points to ma_smalltable for small tables, else to</span></div><div class="line"> * additional malloc'ed memory. ma_table is never NULL! This rule</div><div class="line"> * saves repeated runtime null-tests in the workhorse getitem and</div><div class="line"> * setitem calls.</div><div class="line"> */</div><div class="line"> PyDictEntry *ma_table;</div><div class="line"> PyDictEntry *(*ma_lookup)(PyDictObject *mp, PyObject *key, <span class="keyword">long</span> hash);</div><div class="line"> PyDictEntry ma_smalltable[PyDict_MINSIZE];</div><div class="line">};</div></pre></td></tr></table></figure></p><p>当PyDictObject中的entry数量超过<code>8</code>个时,Python会认为这是一个大dict了,将会申请额外内存空间,并将<code>ma_table</code>指向这块空间。</p><img src="/2018/08/28/Python源码剖析——Dict对象/pic2.jpg" alt="entry三种状态" title="entry三种状态"><p><br></p><h4 id="PyDictObject中元素搜索"><a href="#PyDictObject中元素搜索" class="headerlink" title="PyDictObject中元素搜索"></a>PyDictObject中元素搜索</h4><p>PyDictObject提供两种搜索策略,<code>lookdict</code>和<code>lookdict_string</code>,Python默认搜索方式为<code>lookdict_string<code>。</code></code></p><p>Python获取冲突链上的第一个entry的过程采用,将hash值(我认为是元素序号)与entry的数量做一个与操作,结果自然会落在entry的数量之下。</p><p><code>freeslot</code>正是用来指向探测序列中第一个处于Dummy态的entry,如果搜索失败,freeslot会提供一个处于Dummy态的entry。<br><br></p><p>根据hash值获得的冲突探测链上<code>第一个entry</code>与待查找的元素的比较:</p><ol><li>根据hash值获得entry的索引,是冲突探测链上的第一个entry的索引。</li><li>在两种情况下搜索结束:<ul><li>entry处于Unused态,表明冲突探测链搜索完成,搜索失败;</li><li>ep->me_key == key,表明entry的key与待搜索的key匹配,搜索成功。</li></ul></li><li>若当前entry处于Dummy态,这是freeslot。</li><li>检查Active态entry中的key与待查找的key是否“值相同”,若成立,搜索成功。</li></ol><p><code>遍历探测链</code>时发生的lookdict所进行的操作:</p><ol><li>根据Python所采用的探测函数,获得探测链中的下一个待检查的entry。</li><li>检查到一个Unused态entry,表明搜索失败,这时有两种结果:<ul><li>如果freeslot不为空,则返回freeslot所指entry。</li><li>如果freeslot为空,则返回该Unused态entry。</li></ul></li><li>检查entry中的key与带查找的key是否符合“引用相同”规则。</li><li>检查entry中的key与待查找的key是否符合“值相同”规则。</li><li>遍历过程中,如果发现Dummy态entry,且freeslot未设置,则设置freeslot。<br><br></li></ol><h4 id="PyDictObject插入"><a href="#PyDictObject插入" class="headerlink" title="PyDictObject插入"></a>PyDictObject插入</h4><p>搜索成功,返回处于Active的entry,直接替换me_value。<br>搜索失败,返回Unused或Dummy的entry,完整设置me_key、me_hash和me_value。</p><p>如果table的装载率大于<code>2/3</code>时,后续的插入动作遭遇到冲突的可能性会非常大,在装载率大于或等于<code>2/3</code>时需要改变table的大小。<br><br></p><h4 id="PyDictObject的删除"><a href="#PyDictObject的删除" class="headerlink" title="PyDictObject的删除"></a>PyDictObject的删除</h4><p>先计算hash值,搜索相应的entry,删除entry维护的元素,将entry从Active态变换为Dummy态,调整PyDictObject对象中维护table使用情况的变量。</p>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>现代的编程语言都在语言级或标准库中提供某种<code>关联式的容器</code>,容器中的元素通常是以(key,value)的形式存在的。<
</summary>
<category term="Python" scheme="http://yoursite.com/tags/Python/"/>
</entry>
<entry>
<title>堆和栈的区别</title>
<link href="http://yoursite.com/2018/08/21/%E5%A0%86%E5%92%8C%E6%A0%88%E7%9A%84%E5%8C%BA%E5%88%AB/"/>
<id>http://yoursite.com/2018/08/21/堆和栈的区别/</id>
<published>2018-08-21T01:44:57.000Z</published>
<updated>2018-08-26T19:55:24.200Z</updated>
<content type="html"><![CDATA[<h2 id="内存分配中堆和栈的区别"><a href="#内存分配中堆和栈的区别" class="headerlink" title="内存分配中堆和栈的区别"></a>内存分配中堆和栈的区别</h2><p>管理方式:</p><ul><li>栈由编译器自动管理。</li><li>堆由程序员手工管理。</li></ul><p>空间大小:</p><ul><li>栈一般有1~2M。</li><li>32位操作系统堆可以达到4G。</li></ul><p>碎片问题:</p><ul><li>栈的先进后出的方式导致它不会产生碎片。</li><li>堆频繁的malloc/free会产生不连续的空间,造成碎片。</li></ul><p>生长方向不同:</p><ul><li>栈向下生长。</li><li>堆向上生长。</li></ul><p>分配效率:</p><ul><li>栈由于有就是那几底层支持,效率较高。</li><li>堆需要一定算法分配可用内存空间,效率较低。</li></ul>]]></content>
<summary type="html">
<h2 id="内存分配中堆和栈的区别"><a href="#内存分配中堆和栈的区别" class="headerlink" title="内存分配中堆和栈的区别"></a>内存分配中堆和栈的区别</h2><p>管理方式:</p>
<ul>
<li>栈由编译器自动管理。</li>
</summary>
<category term="内存" scheme="http://yoursite.com/tags/%E5%86%85%E5%AD%98/"/>
</entry>
<entry>
<title>Python源码剖析——Python内建对象</title>
<link href="http://yoursite.com/2018/08/15/Python%E6%BA%90%E7%A0%81%E5%89%96%E6%9E%90%E2%80%94%E2%80%94Python%E5%86%85%E5%BB%BA%E5%AF%B9%E8%B1%A1/"/>
<id>http://yoursite.com/2018/08/15/Python源码剖析——Python内建对象/</id>
<published>2018-08-14T23:18:29.000Z</published>
<updated>2018-08-15T16:05:29.726Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote><p>在Python的世界中,一切都是对象,包括面向对象中的“类”也是通过Python内的对象来实现的。</p></blockquote><h2 id="Python内的对象"><a href="#Python内的对象" class="headerlink" title="Python内的对象"></a>Python内的对象</h2><p>在Python中,对象就是C的结构体在堆上申请的一块内存。</p><ul><li>Python中所有的内建的类型对象(int, str等对象)都是被静态初始化的。</li><li>对象一旦创建, 内存大小就是不变的(变长的对象内部维护一个指向可变内存的指针)</li></ul><h3 id="对象的基石——PyObject"><a href="#对象的基石——PyObject" class="headerlink" title="对象的基石——PyObject"></a>对象的基石——PyObject</h3>]]></content>
<summary type="html">
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><blockquote>
<p>在Python的世界中,一切都是对象,包括面向对象中的“类”也是通过Python内的对象来实现的。</p>
</b
</summary>
<category term="Python" scheme="http://yoursite.com/tags/Python/"/>
</entry>
</feed>