-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
558 lines (306 loc) · 103 KB
/
Copy pathatom.xml
File metadata and controls
558 lines (306 loc) · 103 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>李庭嘉的博客</title>
<subtitle>工具谁都能用,掌握原理才是核心</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://yoursite.com/"/>
<updated>2019-03-17T15:09:33.364Z</updated>
<id>http://yoursite.com/</id>
<author>
<name>Do Lee</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title></title>
<link href="http://yoursite.com/2019/03/17/python%E3%80%81redis%E5%B8%B8%E7%94%A8%E5%8F%98%E9%87%8F%E4%B8%8B%E7%9A%84%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
<id>http://yoursite.com/2019/03/17/python、redis常用变量下的数据结构/</id>
<published>2019-03-17T15:09:33.364Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<p>list</p><ul><li>python 数组</li><li>redis 双向链表</li></ul>]]></content>
<summary type="html">
<p>list</p>
<ul>
<li>python 数组</li>
<li>redis 双向链表</li>
</ul>
</summary>
</entry>
<entry>
<title>读《Redis深度历险记》</title>
<link href="http://yoursite.com/2019/03/17/%E8%AF%BB%E3%80%8ARedis%E6%B7%B1%E5%BA%A6%E5%8E%86%E9%99%A9%E8%AE%B0%E3%80%8B/"/>
<id>http://yoursite.com/2019/03/17/读《Redis深度历险记》/</id>
<published>2019-03-17T13:55:00.000Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<h1 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h1><h2 id="string"><a href="#string" class="headerlink" title="string"></a>string</h2><h2 id="list"><a href="#list" class="headerlink" title="list"></a>list</h2><h2 id="set"><a href="#set" class="headerlink" title="set"></a>set</h2><h2 id="有序-set"><a href="#有序-set" class="headerlink" title="有序 set"></a>有序 set</h2><h2 id="hash"><a href="#hash" class="headerlink" title="hash"></a>hash</h2><h1 id="设计"><a href="#设计" class="headerlink" title="设计"></a>设计</h1><h2 id="内存回收机制"><a href="#内存回收机制" class="headerlink" title="内存回收机制"></a>内存回收机制</h2><p>redis不会实时回收内存;<br>它的key是分页存放的,当一个页的所有key都被删除后,才会统一回收该页的内存;<br>虽然没有立即回收内存,但是不用的内存还是可以分配给新的key使用;<br>手动执行<code>flushdb</code>,可以立即回收内存。</p><h2 id="过期机制"><a href="#过期机制" class="headerlink" title="过期机制"></a>过期机制</h2><h3 id="主节点"><a href="#主节点" class="headerlink" title="主节点"></a>主节点</h3><ul><li>懒汉式删除</li><li>定时删除<ul><li>每10s一次</li><li>从过期字典中随机抽取20个,删除当中已过期的key</li><li>如果上一步中,已过期的key占20个的1/4以上,重复上一步</li><li>为避免阻塞时间过长,整个循环不超过25ms<h3 id="从节点"><a href="#从节点" class="headerlink" title="从节点"></a>从节点</h3>仅依靠主节点发送del指令</li></ul></li></ul><h3 id="可能出现的问题"><a href="#可能出现的问题" class="headerlink" title="可能出现的问题"></a>可能出现的问题</h3><p>如果有大量key同时过期,此时客户端请求,服务端正好处于过期扫描中,那么至少要等待25ms(另外可能存在多个内存页的回收耗时)</p><h3 id="建议"><a href="#建议" class="headerlink" title="建议"></a>建议</h3><p>避免大量key同时过期,timeout += random(second)</p><h1 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h1><h2 id="管道"><a href="#管道" class="headerlink" title="管道"></a>管道</h2><p><code>pipeline</code>的本质和单次发送命令差不多,只是变成了连续发送命令到服务端,省去多次等待IO的时间</p><h2 id="避免大key"><a href="#避免大key" class="headerlink" title="避免大key"></a>避免大key</h2><p>指的是大的value,以下情况容易造成卡顿:</p><ul><li>迁移</li><li>扩容时申请内存</li></ul><p>用scan扫描出大key,-i 每 scan 100条 sleep 0.1s<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">redis-cli --bigkeys -i 0.1</span><br></pre></td></tr></table></figure></p><h1 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h1><h2 id="分布式锁"><a href="#分布式锁" class="headerlink" title="分布式锁"></a>分布式锁</h2><h2 id="限流"><a href="#限流" class="headerlink" title="限流"></a>限流</h2><h2 id="附近的人(坐标,两点间距离)"><a href="#附近的人(坐标,两点间距离)" class="headerlink" title="附近的人(坐标,两点间距离)"></a>附近的人(坐标,两点间距离)</h2><h1 id="运维"><a href="#运维" class="headerlink" title="运维"></a>运维</h1><h2 id="客户端连接数"><a href="#客户端连接数" class="headerlink" title="客户端连接数"></a>客户端连接数</h2><h2 id="复制积压缓存区"><a href="#复制积压缓存区" class="headerlink" title="复制积压缓存区"></a>复制积压缓存区</h2><h2 id="安全"><a href="#安全" class="headerlink" title="安全"></a>安全</h2><h3 id="改写-禁用危险指令"><a href="#改写-禁用危险指令" class="headerlink" title="改写/禁用危险指令"></a>改写/禁用危险指令</h3><p>改写 keys 为 rename_keys<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rename-command keys rename_keys</span><br></pre></td></tr></table></figure></p><p>禁用<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rename-command keys ''</span><br></pre></td></tr></table></figure></p><h3 id="不用6379,加密码,走内网,非root启动red-is"><a href="#不用6379,加密码,走内网,非root启动red-is" class="headerlink" title="不用6379,加密码,走内网,非root启动red is"></a>不用6379,加密码,走内网,非root启动red is</h3><h3 id="SSL代理-spiped"><a href="#SSL代理-spiped" class="headerlink" title="SSL代理-spiped"></a>SSL代理-spiped</h3><p>实在要走外网时</p>]]></content>
<summary type="html">
<h1 id="数据结构"><a href="#数据结构" class="headerlink" title="数据结构"></a>数据结构</h1><h2 id="string"><a href="#string" class="headerlink" title="strin
</summary>
<category term="数据存储" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/"/>
<category term="redis" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/redis/"/>
<category term="redis" scheme="http://yoursite.com/tags/redis/"/>
<category term="读书笔记" scheme="http://yoursite.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>MySQL-什么是分区</title>
<link href="http://yoursite.com/2019/02/26/MySQL-%E4%BB%80%E4%B9%88%E6%98%AF%E5%88%86%E5%8C%BA/"/>
<id>http://yoursite.com/2019/02/26/MySQL-什么是分区/</id>
<published>2019-02-26T09:42:41.000Z</published>
<updated>2019-03-17T15:09:33.363Z</updated>
<content type="html"><![CDATA[<h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><p>分区类似于水平分表。<br>水平分表需要用户预先手动显式创建出多张分表(如tbl_user0, tbl_user1, tbl_user2),在物理上实实在在的创建多张表,通过客户端代理(Sharding-JDBC等)或者中间件代理(Mycat等)来实现分表逻辑。<br>分区是MySQL的一个插件Plugin功能,将一张大表的数据在数据库底层分成多个分区文件(如tbl_user#P#p0.ibd, tbl_user#P#p1.ibd, tbl_user#P#p2.ibd),和水平分表不同的是分区不需要显式的创建“分表”,数据库会自动创建分区文件的,用户看到的只是一张普通的表,其实是对应的是多个分区,这个是对用户是屏蔽的、透明的,在使用上和使用一张表完全一样,不需要借助任何功能来实现。<br>分区是一种逻辑上的水平分表,在物理层面还是一张表。</p><h1 id="分区类型"><a href="#分区类型" class="headerlink" title="分区类型"></a>分区类型</h1><p>range分区:基于一个给定的连续区间范围(区间要求连续并且不能重叠),把数据分配到不同的分区</p><ul><li>注意最后一个区要设置在最大值以内,否则有部分数据没有专属分区,会导致bug</li></ul><p>list分区:类似于range分区,区别在于list分区是居于枚举出的值列表分区,range是基于给定的连续区间范围分区<br>hash分区:基于给定的分区个数,把数据分配到不同的分区<br>key分区:类似于hash分区</p><h1 id="弊端"><a href="#弊端" class="headerlink" title="弊端"></a>弊端</h1><p>分区表很帅气,为什么大部分互联网还是更多的选择自己分库分表来水平扩展咧?</p><p>回答:</p><p>1)分区表,分区键设计不太灵活,如果不走分区键,很容易出现全表锁</p><p>2)一旦数据量并发量上来,如果在分区表实施关联,就是一个灾难</p><p>3)自己分库分表,自己掌控业务场景与访问模式,可控。分区表,研发写了一个sql,都不确定mysql是怎么玩的,不太可控</p><p>4)运维的坑</p><p><strong>参考</strong></p><ul><li><a href="https://blog.csdn.net/vbirdbest/article/details/82461109" target="_blank" rel="noopener">https://blog.csdn.net/vbirdbest/article/details/82461109</a></li><li><a href="https://www.cnblogs.com/xiaoboluo768/p/5191964.html" target="_blank" rel="noopener">https://www.cnblogs.com/xiaoboluo768/p/5191964.html</a></li><li><a href="https://blog.csdn.net/laoyang360/article/details/52886987" target="_blank" rel="noopener">https://blog.csdn.net/laoyang360/article/details/52886987</a></li><li>range分区的bug <a href="https://blog.csdn.net/cug_jiang126com/article/details/51984628" target="_blank" rel="noopener">https://blog.csdn.net/cug_jiang126com/article/details/51984628</a></li><li>MySQL 5.7.18 分区有bug <a href="https://www.cnblogs.com/liujiacai/p/7611071.html" target="_blank" rel="noopener">https://www.cnblogs.com/liujiacai/p/7611071.html</a></li></ul>]]></content>
<summary type="html">
<h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><p>分区类似于水平分表。<br>水平分表需要用户预先手动显式创建出多张分表(如tbl_user0, tbl_user1, tbl_user2),
</summary>
<category term="数据存储" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/"/>
<category term="MySQL" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/MySQL/"/>
<category term="性能优化" scheme="http://yoursite.com/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/"/>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
</entry>
<entry>
<title>MySQL-分区及中间件简介</title>
<link href="http://yoursite.com/2019/02/26/%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
<id>http://yoursite.com/2019/02/26/中间件基础知识/</id>
<published>2019-02-26T09:42:41.000Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<h1 id="分区"><a href="#分区" class="headerlink" title="分区"></a>分区</h1><p>分区类似于水平分表。<br>水平分表需要用户预先手动显式创建出多张分表(如tbl_user0, tbl_user1, tbl_user2),在物理上实实在在的创建多张表,通过客户端代理(Sharding-JDBC等)或者中间件代理(Mycat等)来实现分表逻辑。<br>分区是MySQL的一个插件Plugin功能,将一张大表的数据在数据库底层分成多个分区文件(如tbl_user#P#p0.ibd, tbl_user#P#p1.ibd, tbl_user#P#p2.ibd),和水平分表不同的是分区不需要显式的创建“分表”,数据库会自动创建分区文件的,用户看到的只是一张普通的表,其实是对应的是多个分区,这个是对用户是屏蔽的、透明的,在使用上和使用一张表完全一样,不需要借助任何功能来实现。<br>分区是一种逻辑上的水平分表,在物理层面还是一张表。</p><h2 id="分片规则配置"><a href="#分片规则配置" class="headerlink" title="分片规则配置"></a>分片规则配置</h2><h2 id="JDBC规范重写"><a href="#JDBC规范重写" class="headerlink" title="JDBC规范重写"></a>JDBC规范重写</h2><h2 id="SQL解析"><a href="#SQL解析" class="headerlink" title="SQL解析"></a>SQL解析</h2><h3 id="fdb"><a href="#fdb" class="headerlink" title="fdb"></a>fdb</h3><h3 id="jsqlparser"><a href="#jsqlparser" class="headerlink" title="jsqlparser"></a>jsqlparser</h3><h3 id="Druid"><a href="#Druid" class="headerlink" title="Druid"></a>Druid</h3><h2 id="SQL改写"><a href="#SQL改写" class="headerlink" title="SQL改写"></a>SQL改写</h2><h2 id="SQL路由"><a href="#SQL路由" class="headerlink" title="SQL路由"></a>SQL路由</h2><h3 id="单表路由"><a href="#单表路由" class="headerlink" title="单表路由"></a>单表路由</h3><p>单表路由最为简单,但路由结果不一定落入唯一库(表),因为支持根据between和in这样的操作符进行分片,所以最终结果仍然可能落入多个库(表)。</p><h3 id="Binding表路由"><a href="#Binding表路由" class="headerlink" title="Binding表路由"></a>Binding表路由</h3><p>Binding表可理解为分库分表规则完全一致的主从表。举例说明:订单表和订单详情表都根据订单ID作为分片键,任意时刻分片逻辑均相同。这样的关联查询和单表查询难度和性能相当。</p><h3 id="笛卡尔积路由"><a href="#笛卡尔积路由" class="headerlink" title="笛卡尔积路由"></a>笛卡尔积路由</h3><p>笛卡尔积查询最为复杂,因为无法根据Binding关系定位分片规则的一致性,所以非Binding表的关联查询需要拆解为笛卡尔积组合执行。查询性能较低,而且数据库连接数较高,需谨慎使用。</p><p><strong>参考</strong></p><ul><li><a href="https://blog.csdn.net/qq_22075041/article/details/79490230" target="_blank" rel="noopener">https://blog.csdn.net/qq_22075041/article/details/79490230</a></li></ul>]]></content>
<summary type="html">
<h1 id="分区"><a href="#分区" class="headerlink" title="分区"></a>分区</h1><p>分区类似于水平分表。<br>水平分表需要用户预先手动显式创建出多张分表(如tbl_user0, tbl_user1, tbl_user2),
</summary>
<category term="数据存储" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/"/>
<category term="MySQL" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/MySQL/"/>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
<category term="大数据" scheme="http://yoursite.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
</entry>
<entry>
<title>从SQL的执行原理优化多表查询</title>
<link href="http://yoursite.com/2019/02/26/%E4%BB%8ESQL%E7%9A%84%E6%89%A7%E8%A1%8C%E5%8E%9F%E7%90%86%E4%BC%98%E5%8C%96%E5%A4%9A%E8%A1%A8%E6%9F%A5%E8%AF%A2/"/>
<id>http://yoursite.com/2019/02/26/从SQL的执行原理优化多表查询/</id>
<published>2019-02-26T09:42:41.000Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<h3 id="查询sql语句的执行流程"><a href="#查询sql语句的执行流程" class="headerlink" title="查询sql语句的执行流程"></a>查询sql语句的执行流程</h3><p>根据这个顺序,将可优化的点往前放,尽早降低后面的处理次数</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">From - 将涉及的表加载到内存</span><br><span class="line"></span><br><span class="line">ON - 作为产生笛卡尔积表的挑选条件</span><br><span class="line"></span><br><span class="line">JOIN - 此处产生笛卡尔积表(A表*B表),需要大量内存</span><br><span class="line"></span><br><span class="line">WHERE - 数据进行过滤,取出符合条件的记录行,生成一张临时表</span><br><span class="line"></span><br><span class="line">GROUP BY - 将上一步的临时表切分成若干临时表</span><br><span class="line"></span><br><span class="line">SELECT - 如果有GROUP BY,会对N个临时表进行N次查询,最终形成一个新的临时表</span><br><span class="line"></span><br><span class="line">HAVING - 对SELECT 语句执行之后的临时表中的数据过滤</span><br><span class="line"></span><br><span class="line">ORDER BY</span><br><span class="line"></span><br><span class="line">LIMIT</span><br></pre></td></tr></table></figure><blockquote><p>当两张表的数据量比较大,又需要连接查询时,应该使用 <code>FROM table1 JOIN table2 ON xxx</code> 的语法,避免使用 <code>FROM table1,table2 WHERE xxx</code> 的语法,因为后者会在内存中先生成一张数据量比较大的笛卡尔积表,增加了内存的开销。</p></blockquote><h3 id="计算SQL平均耗时"><a href="#计算SQL平均耗时" class="headerlink" title="计算SQL平均耗时"></a>计算SQL平均耗时</h3><p>计算 sql查询所耗费的平均时间时,要关掉mysql的查询缓存,防止多次执行从缓存中读取数据<br>可以用 <code>SQL_NO_CACHE</code><br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">SELECT</span><br><span class="line"> SQL_NO_CACHE *</span><br><span class="line">FROM</span><br><span class="line"> mm_member</span><br></pre></td></tr></table></figure></p><h3 id="慎用子查询"><a href="#慎用子查询" class="headerlink" title="慎用子查询"></a>慎用子查询</h3><p>mm_member 为用户表(370 行),mm_log 为登陆记录(15W 行)<br>现在要求我们根据这两张表查出2017-02-06那天有那些用户登录过系统。</p><h4 id="join"><a href="#join" class="headerlink" title="join"></a>join</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">SELECT</span><br><span class="line"> SQL_NO_CACHE mm.*</span><br><span class="line">FROM</span><br><span class="line"> mm_member mm</span><br><span class="line">JOIN</span><br><span class="line"> mm_log ml</span><br><span class="line">ON</span><br><span class="line"> mm.id = ml.member_id</span><br><span class="line">WHERE</span><br><span class="line"> ml.access_time LIKE '%2017-02-06%'</span><br><span class="line">GROUP BY</span><br><span class="line"> ml.member_id;</span><br></pre></td></tr></table></figure><p>运行次数 mm_member * mm_log, 但是在on这一步就把后面的运行次数降到了 mm_member 的级别。</p><p>耗时:0.120s</p><h4 id="子查询-in"><a href="#子查询-in" class="headerlink" title="子查询 in"></a>子查询 in</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">SELECT</span><br><span class="line"> SQL_NO_CACHE mm.username</span><br><span class="line">FROM</span><br><span class="line"> mm_member mm</span><br><span class="line">WHERE</span><br><span class="line"> mm.id IN (SELECT ml.member_id FROM mm_log ml WHERE ml.access_time LIKE '%2017-02-06%' GROUP BY ml.member_id);</span><br></pre></td></tr></table></figure><p>运行次数 mm_member * mm_log, 和我们以为的不同,这里 IN 后面的子查询,是每一行mm_member都会去重新执行一遍,并且没有用到缓存。</p><p>耗时:40.834s > 0.120s*370=44.4</p><p>其中子查询语句:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT SQL_NO_CACHE member_id FROM mm_log ml WHERE ml.access_time LIKE '%2017-02-06%' GROUP BY ml.member_id;</span><br></pre></td></tr></table></figure></p><p>仅耗时 0.11s</p><h4 id="子查询-exist"><a href="#子查询-exist" class="headerlink" title="子查询 exist"></a>子查询 exist</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">SELECT</span><br><span class="line"> SQL_NO_CACHE mm.*</span><br><span class="line">FROM</span><br><span class="line"> mm_member mm</span><br><span class="line">WHERE</span><br><span class="line"> EXISTS (SELECT * FROM mm_log ml WHERE mm.id = ml.member_id AND ml.access_time LIKE '%2017-02-06%');</span><br></pre></td></tr></table></figure><p>相比于IN,这里的优化在于子查询,多了<code>mm.id = ml.member_id</code>条件,在where处就把不符合条件的记录过滤掉。</p><p>耗时:34s</p><p>对于在子查询中使用IN的性能高还是是用EXITS的性能高,有一种普遍的说法是:</p><p>1,在外表大,内表小,外表中有索引的情况下,使用IN。</p><p>2,在外表小,内表大,内表中有索引的情况下,使用EXITS。</p><p><strong>参考</strong></p><p>感谢大神分享,本文更多是总结和自己的理解补充,内容和例子都来自于</p><ul><li>sql语句的执行流程 <a href="https://blog.csdn.net/csdn_hklm/article/details/78394412" target="_blank" rel="noopener">https://blog.csdn.net/csdn_hklm/article/details/78394412</a></li><li>笛卡尔积表 <a href="http://www.cnblogs.com/cdf-opensource-007/p/6502556.html" target="_blank" rel="noopener">http://www.cnblogs.com/cdf-opensource-007/p/6502556.html</a></li><li>慎用子查询 <a href="https://www.cnblogs.com/cdf-opensource-007/p/6540521.html" target="_blank" rel="noopener">https://www.cnblogs.com/cdf-opensource-007/p/6540521.html</a></li></ul>]]></content>
<summary type="html">
<h3 id="查询sql语句的执行流程"><a href="#查询sql语句的执行流程" class="headerlink" title="查询sql语句的执行流程"></a>查询sql语句的执行流程</h3><p>根据这个顺序,将可优化的点往前放,尽早降低后面的处理次数</
</summary>
<category term="数据存储" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/"/>
<category term="MySQL" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/MySQL/"/>
<category term="性能优化" scheme="http://yoursite.com/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/"/>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
</entry>
<entry>
<title>存儲引擎對比</title>
<link href="http://yoursite.com/2019/02/16/%E5%AD%98%E5%84%B2%E5%BC%95%E6%93%8E%E5%B0%8D%E6%AF%94/"/>
<id>http://yoursite.com/2019/02/16/存儲引擎對比/</id>
<published>2019-02-16T09:42:41.000Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<h2 id="LevelDb"><a href="#LevelDb" class="headerlink" title="LevelDb"></a>LevelDb</h2><p>适用场景:多些少读<br>| | |<br>| —————— | —————— |<br>| | |<br>| | |</p><h2 id="RocksDB"><a href="#RocksDB" class="headerlink" title="RocksDB"></a>RocksDB</h2><h2 id="LMDB"><a href="#LMDB" class="headerlink" title="LMDB"></a>LMDB</h2>]]></content>
<summary type="html">
<h2 id="LevelDb"><a href="#LevelDb" class="headerlink" title="LevelDb"></a>LevelDb</h2><p>适用场景:多些少读<br>| | |<br>| —————— | —————— |<br>|
</summary>
<category term="数据存储" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/"/>
<category term="MySQL" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/MySQL/"/>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
<category term="大数据" scheme="http://yoursite.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
</entry>
<entry>
<title>MySQL-分区及中间件简介</title>
<link href="http://yoursite.com/2019/02/06/mysql%E4%B8%AD%E9%97%B4%E4%BB%B6%E5%AF%B9%E6%AF%94/"/>
<id>http://yoursite.com/2019/02/06/mysql中间件对比/</id>
<published>2019-02-06T09:42:41.000Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<h2 id="中间件"><a href="#中间件" class="headerlink" title="中间件"></a>中间件</h2><p>分类</p><ul><li><p>lib库</p><ul><li>业务直接到数据库,少一层proxy效率更高</li><li>没有proxy的lvs的单点问题</li></ul></li><li><p>proxy</p><ul><li>统一管理所有到数据库的连接,连接复用</li><li>基础查询功能抽象,减少代码耦合</li><li>易于实现监控、数据迁移、连接管理等功能</li></ul></li></ul><h3 id="sharding-jdbc(开源,lib)"><a href="#sharding-jdbc(开源,lib)" class="headerlink" title="sharding-jdbc(开源,lib)"></a>sharding-jdbc(开源,lib)</h3><blockquote><p>当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架<br>可惜是Java写的</p><h4 id="功能"><a href="#功能" class="headerlink" title="功能"></a>功能</h4><p>1)以jar包形式提供服务</p></blockquote><p>2)分片灵活,支持等号、between、in等多维度分片</p><p>3)sql解析,支持聚合、分组、排序、limit、or等</p><h4 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h4><p>1)分片规则:策略自定义、复数分片数</p><p>2)JDBC规范重写:针对DataSource、Connection、Statement、PreparedStatement和ResultSet接口封装</p><p>3)sql解析:druid</p><p>4)sql改写:修改逻辑表名->真实表名;替换不支持的功能,如:avg->sum和count</p><p>5)sql路由:单表路由、binding表路由、笛卡尔积路由</p><p>6)sql执行:多线程并发执行sql</p><p>7)结果归并:遍历类、排序类(归并排序)、聚合类(比较型、累加型、平均型)、分组类</p><h3 id="NewSQL"><a href="#NewSQL" class="headerlink" title="NewSQL"></a>NewSQL</h3><p><a href="https://baijiahao.baidu.com/s?id=1592030931074047993&wfr=spider&for=pc" target="_blank" rel="noopener">https://baijiahao.baidu.com/s?id=1592030931074047993&wfr=spider&for=pc</a></p><h3 id="mycat(开源,proxy)"><a href="#mycat(开源,proxy)" class="headerlink" title="mycat(开源,proxy)"></a>mycat(开源,proxy)</h3><blockquote><p>社区爱好者在阿里cobar基础上进行二次开发,解决了cobar的一些问题,且加入了新的功能。<br>不适用场景<br><a href="https://www.sohu.com/a/211953442_470018" target="_blank" rel="noopener">https://www.sohu.com/a/211953442_470018</a></p></blockquote><h3 id="MaxScale"><a href="#MaxScale" class="headerlink" title="MaxScale"></a>MaxScale</h3><blockquote><p>mariadb (MySQL原作者维护的一个版本)研发,目前版本不支持分库分表。</p></blockquote><h3 id="MySQL-Route"><a href="#MySQL-Route" class="headerlink" title="MySQL Route"></a>MySQL Route</h3><blockquote><p>MySQL 官方Oracle公司发布出来的一个中间件</p></blockquote><h3 id="Atlas"><a href="#Atlas" class="headerlink" title="Atlas"></a>Atlas</h3><blockquote><p>360团队基于 mysql proxy 把lua用c改写,在高并发下经常会挂掉。</p></blockquote><h3 id="DBproxy(开源,proxy)"><a href="#DBproxy(开源,proxy)" class="headerlink" title="DBproxy(开源,proxy)"></a>DBproxy(开源,proxy)</h3><blockquote><p>针对atlas进行改进,形成了新的高可靠、高可用企业级数据库中间件DBProxy</p></blockquote><h3 id="oneproxy(不开源,proxy)"><a href="#oneproxy(不开源,proxy)" class="headerlink" title="oneproxy(不开源,proxy)"></a>oneproxy(不开源,proxy)</h3><blockquote><p>基于mysql协议的数据库中间件。利用c进行开发的,专注于性能和稳定性。</p></blockquote><h3 id="vitess"><a href="#vitess" class="headerlink" title="vitess"></a>vitess</h3><blockquote><p>Youtube上产使用的,架构复杂,不支持MySQL原生协议</p></blockquote><h3 id="TDDL"><a href="#TDDL" class="headerlink" title="TDDL"></a>TDDL</h3><blockquote><p>Tabao开发,主要解决了分库分表对应用的透明化以及异构数据库之间的数据复制。<br>复杂度相对较高。当前公布的文档较少,只开源动态数据源,分表分库部分还未开源,还需要依赖diamond,不推荐使用。</p></blockquote><h3 id="DBProxy"><a href="#DBProxy" class="headerlink" title="DBProxy"></a>DBProxy</h3><blockquote><p>美团点评基于 Atlas 的改进。<br>其特性主要有:读写分离、负载均衡、支持分表、IP过滤、sql语句黑名单、DBA平滑下线DB、从库流量配置、动态加载配置项</p></blockquote><p><strong>参考</strong></p><ul><li><a href="https://www.cnblogs.com/mliudong/p/3625522.html" target="_blank" rel="noopener">https://www.cnblogs.com/mliudong/p/3625522.html</a></li><li><a href="https://www.cnblogs.com/taoxinrui/p/6782791.html" target="_blank" rel="noopener">https://www.cnblogs.com/taoxinrui/p/6782791.html</a></li></ul>]]></content>
<summary type="html">
<h2 id="中间件"><a href="#中间件" class="headerlink" title="中间件"></a>中间件</h2><p>分类</p>
<ul>
<li><p>lib库</p>
<ul>
<li>业务直接到数据库,少一层proxy效率更高</li>
<l
</summary>
<category term="数据存储" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/"/>
<category term="MySQL" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/MySQL/"/>
<category term="MySQL" scheme="http://yoursite.com/tags/MySQL/"/>
<category term="大数据" scheme="http://yoursite.com/tags/%E5%A4%A7%E6%95%B0%E6%8D%AE/"/>
</entry>
<entry>
<title>关于拖拽排序的后端存储设计</title>
<link href="http://yoursite.com/2019/02/04/%E5%85%B3%E4%BA%8E%E6%8B%96%E6%8B%BD%E6%8E%92%E5%BA%8F%E7%9A%84%E5%90%8E%E7%AB%AF%E5%AD%98%E5%82%A8%E8%AE%BE%E8%AE%A1/"/>
<id>http://yoursite.com/2019/02/04/关于拖拽排序的后端存储设计/</id>
<published>2019-02-04T05:37:41.000Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2><div class="table-container"><table><thead><tr><th>课程</th><th>讲师</th><th>排序</th></tr></thead><tbody><tr><td>课程1</td><td>1</td><td>1</td></tr><tr><td>课程2</td><td>1</td><td>2</td></tr><tr><td>课程3</td><td>1</td><td>3</td></tr><tr><td>课程4</td><td>1</td><td>4</td></tr><tr><td>课程5</td><td>1</td><td>5</td></tr><tr><td>课程6</td><td>1</td><td>6</td></tr></tbody></table></div><p>可以随意调整课程顺序,如:将<code>课程1</code>调到<code>课程3</code>和<code>课程4</code>之间</p><div class="table-container"><table><thead><tr><th>课程</th><th>排序</th></tr></thead><tbody><tr><td>课程2</td><td>1</td></tr><tr><td>课程3</td><td>2</td></tr><tr><td>课程1</td><td>3</td></tr><tr><td>课程4</td><td>4</td></tr><tr><td>课程5</td><td>5</td></tr><tr><td>课程6</td><td>6</td></tr></tbody></table></div><p>单次涉及变更的课程数量没有限制,大多为100以内,也有200+的,极少数超过300;<br>总课程数量为1千万左右</p><h2 id="存储方案"><a href="#存储方案" class="headerlink" title="存储方案"></a>存储方案</h2><h3 id="1-mysql-直接存排序"><a href="#1-mysql-直接存排序" class="headerlink" title="1. mysql 直接存排序"></a>1. mysql 直接存排序</h3><p>一次变更需要多个update<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">update table set orderField = orderField + 1 where orderField > (select orderField from table where id = currentID)</span><br><span class="line">update table set orderField = orderField + 1 where id = currentID</span><br></pre></td></tr></table></figure></p><p>第一个update语句,用到了子查询,或许会很慢,可以伪造数据试一下</p><h3 id="2-mysql-存优先级"><a href="#2-mysql-存优先级" class="headerlink" title="2. mysql 存优先级"></a>2. mysql 存优先级</h3><p>优先级越大排序越靠前</p><div class="table-container"><table><thead><tr><th>ID</th><th>课程</th><th>优先级</th></tr></thead><tbody><tr><td>1</td><td>课程1</td><td>6</td></tr><tr><td>2</td><td>课程2</td><td>5</td></tr><tr><td>3</td><td>课程3</td><td>4</td></tr><tr><td>4</td><td>课程4</td><td>3</td></tr><tr><td>5</td><td>课程5</td><td>2</td></tr><tr><td>6</td><td>课程6</td><td>1</td></tr></tbody></table></div><p>将<code>课程1</code>调到<code>课程3</code>和<code>课程4</code>之间,为方便处理,让优先级为float(否则和排序方案1并无区别)</p><div class="table-container"><table><thead><tr><th>ID</th><th>课程</th><th>优先级</th></tr></thead><tbody><tr><td>2</td><td>课程2</td><td>5</td></tr><tr><td>3</td><td>课程3</td><td>4</td></tr><tr><td>1</td><td>课程1</td><td>3.5</td></tr><tr><td>4</td><td>课程4</td><td>3</td></tr><tr><td>5</td><td>课程5</td><td>2</td></tr><tr><td>6</td><td>课程6</td><td>1</td></tr></tbody></table></div><p>但总有可能,多个id的优先级一样,不是很稳妥</p><h3 id="3-redis-list"><a href="#3-redis-list" class="headerlink" title="3. redis list"></a>3. redis list</h3><p>利用reids的列表<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"># 将`课程1`的ID加到`课程3`和`课程4`之间</span><br><span class="line">LINSERT lecture:list BEFORE "课程4-ID" "课程1-ID"</span><br><span class="line"># 删除`课程1`的ID</span><br><span class="line">LREM lecture:list 1 value "课程1-ID"</span><br></pre></td></tr></table></figure></p><p>先去查redis,再根据列表顺序排序</p><h3 id="4-mysql-json-list"><a href="#4-mysql-json-list" class="headerlink" title="4. mysql json:list"></a>4. mysql json:list</h3><p>思路和方案3差不多,只是列表改为mysql的json实现,不知道具体效率如何</p><h3 id="5-mysql-最暴力,逐条update"><a href="#5-mysql-最暴力,逐条update" class="headerlink" title="5. mysql 最暴力,逐条update"></a>5. mysql 最暴力,逐条update</h3><p>实际查看原来的项目后,发现用的是for循环多次调用update…非常不建议…</p><h2 id="对比"><a href="#对比" class="headerlink" title="对比"></a>对比</h2><h3 id="讲师课程,数量-100"><a href="#讲师课程,数量-100" class="headerlink" title="讲师课程,数量 100"></a>讲师课程,数量 100</h3><div class="table-container"><table><thead><tr><th>方案</th><th>查看课程列表</th><th>倒序</th><th>末位->中间</th><th>中间->首位</th><th>末位->首位</th></tr></thead><tbody><tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>2</td><td>2</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>3</td><td>3</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>4</td><td>4</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>5</td><td>5</td><td>1</td><td>1</td><td>1</td><td>1</td></tr></tbody></table></div><h3 id="讲师课程,数量-200"><a href="#讲师课程,数量-200" class="headerlink" title="讲师课程,数量 200"></a>讲师课程,数量 200</h3><div class="table-container"><table><thead><tr><th>方案</th><th>查看课程列表</th><th>倒序</th><th>末位->中间</th><th>中间->首位</th><th>末位->首位</th></tr></thead><tbody><tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>2</td><td>2</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>3</td><td>3</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>4</td><td>4</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>5</td><td>5</td><td>1</td><td>1</td><td>1</td><td>1</td></tr></tbody></table></div><h3 id="讲师课程,数量-300"><a href="#讲师课程,数量-300" class="headerlink" title="讲师课程,数量 300"></a>讲师课程,数量 300</h3><div class="table-container"><table><thead><tr><th>方案</th><th>查看课程列表</th><th>倒序</th><th>末位->中间</th><th>中间->首位</th><th>末位->首位</th></tr></thead><tbody><tr><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>2</td><td>2</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>3</td><td>3</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>4</td><td>4</td><td>1</td><td>1</td><td>1</td><td>1</td></tr><tr><td>5</td><td>5</td><td>1</td><td>1</td><td>1</td><td>1</td></tr></tbody></table></div>]]></content>
<summary type="html">
<h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2><div class="table-container">
<table>
<thead>
<tr>
<th>课程</th>
<th>讲师</th
</summary>
<category term="项目案例" scheme="http://yoursite.com/categories/%E9%A1%B9%E7%9B%AE%E6%A1%88%E4%BE%8B/"/>
<category term="项目案例" scheme="http://yoursite.com/tags/%E9%A1%B9%E7%9B%AE%E6%A1%88%E4%BE%8B/"/>
</entry>
<entry>
<title>读《人月神话》</title>
<link href="http://yoursite.com/2019/01/20/%E8%AF%BB%E3%80%8A%E4%BA%BA%E6%9C%88%E7%A5%9E%E8%AF%9D%E3%80%8B/"/>
<id>http://yoursite.com/2019/01/20/读《人月神话》/</id>
<published>2019-01-20T04:02:41.000Z</published>
<updated>2019-01-20T05:39:33.407Z</updated>
<content type="html"><![CDATA[<p>列几个我觉得重要的部分:</p><ol><li><p>沟通成本,N人团队的两两组合沟通时间,我的理解是排列组合的 C(N) </p><blockquote><p>团队精简化,5-10人为一组,太多也管不过来。<br>如果要添加人手,在项目最初加入比中期加入效率高。<br>在发现项目进度延迟较大后,为了赶进度而加人手,往往没什么效果,反而会拖慢进度。</p></blockquote></li><li><p>权力下放</p><blockquote><p>不要过多插手下属的任务,每个人都需要自己的舞台和成就感,才能发挥最大的团队力量。</p></blockquote></li><li><p>信息隔离</p><blockquote><p>鉴于沟通成本,并非所有人都需要知道所有的事情。</p></blockquote></li><li><p>项目的进度 != 人*月</p><blockquote><p>由于沟通成本和软件的复杂度,团队进度排期 >> 个人小项目排期</p></blockquote></li></ol>]]></content>
<summary type="html">
<p>列几个我觉得重要的部分:</p>
<ol>
<li><p>沟通成本,N人团队的两两组合沟通时间,我的理解是排列组合的 C(N) </p>
<blockquote>
<p>团队精简化,5-10人为一组,太多也管不过来。<br>如果要添加人手,在项目最初加入比中期加入效率高。<
</summary>
<category term="读书笔记" scheme="http://yoursite.com/categories/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
<category term="软件设计" scheme="http://yoursite.com/tags/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1/"/>
<category term="读书笔记" scheme="http://yoursite.com/tags/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>所谓设计模式</title>
<link href="http://yoursite.com/2019/01/20/%E6%89%80%E8%B0%93%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/"/>
<id>http://yoursite.com/2019/01/20/所谓设计模式/</id>
<published>2019-01-20T02:02:41.000Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<blockquote><p>个人观点:对于 python 来说,它本来就是一个很灵活的语言,要想实现相应的功能,并不一定非得用这些设计模式的概念。<br>而且每个设计模式都有各自的使用场景和优缺点,正确理解,领悟各自的适用场景才是最重要的。</p></blockquote><h1 id="创建模式"><a href="#创建模式" class="headerlink" title="创建模式"></a>创建模式</h1><h2 id="简单工厂模式"><a href="#简单工厂模式" class="headerlink" title="简单工厂模式"></a>简单工厂模式</h2><p>对于一个方法,需要在多种情景下调用。如发送消息:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">class SendModule:</span><br><span class="line"> """</span><br><span class="line"> 发生消息的原型</span><br><span class="line"> """</span><br><span class="line"> def send(self):</span><br><span class="line"> pass</span><br><span class="line"></span><br><span class="line">class EMail(SendModule):</span><br><span class="line"> """</span><br><span class="line"> 具体的应用场景1</span><br><span class="line"> """</span><br><span class="line"> def send(self):</span><br><span class="line"> print("通过邮件发送")</span><br><span class="line"> </span><br><span class="line">class SMS(SendModule):</span><br><span class="line"> """</span><br><span class="line"> 具体的应用场景2</span><br><span class="line"> """</span><br><span class="line"> def send(self):</span><br><span class="line"> print("通过短信发送")</span><br><span class="line"></span><br><span class="line">class SendFactory:</span><br><span class="line"> """</span><br><span class="line"> 这里就是简单工厂的部分</span><br><span class="line"> 在这里判断用哪个情景来发送消息</span><br><span class="line"> """</span><br><span class="line"> def send(self, type):</span><br><span class="line"> if type == 1:</span><br><span class="line"> EMail().send()</span><br><span class="line"> else:</span><br><span class="line"> SMS().send()</span><br></pre></td></tr></table></figure></p><p>更实际的,直接将SendFactory用方法来封装。<br>这里只返回哪个类对象,send方法让具体的业务调用。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">def sender_factory(type):</span><br><span class="line"> """</span><br><span class="line"> 这里就是简单工厂的部分</span><br><span class="line"> 在这里判断用哪个情景来发送消息</span><br><span class="line"> """</span><br><span class="line"> def send(self):</span><br><span class="line"> if type == 1:</span><br><span class="line"> return EMail()</span><br><span class="line"> else:</span><br><span class="line"> return SMS()</span><br></pre></td></tr></table></figure></p><h2 id="工厂模式"><a href="#工厂模式" class="headerlink" title="工厂模式"></a>工厂模式</h2><p>不需要用 sender_factory 来选择用哪个情景,直接根据具体的业务场景来调用 EMail().send() / SMS().send()。<br>免去了改 sender_factory 逻辑的烦恼。<br>其实简单工厂和工厂,主要的核心还是在于有一个相同的方法send,分别用于多种类。</p><h2 id="抽象工厂模式"><a href="#抽象工厂模式" class="headerlink" title="抽象工厂模式"></a>抽象工厂模式</h2><p>将组装的部分(产品)放在一个工厂类里面,组成一个实体。<br>也可以认为前面的工厂模式是这里的产品。<br>注意,各产品的统一方法,各工厂的统一方法。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line">class 鸭子头:</span><br><span class="line"> def talk(self):</span><br><span class="line"> print("鸭子叫")</span><br><span class="line">class 鸭子身体:</span><br><span class="line"> def move(self):</span><br><span class="line"> print("鸭子摆尾巴")</span><br><span class="line">class 鸭子肢体:</span><br><span class="line"> def walk(self):</span><br><span class="line"> print("鸭子走路")</span><br><span class="line"></span><br><span class="line">class 鸡头:</span><br><span class="line"> def talk(self):</span><br><span class="line"> print("鸡鸣")</span><br><span class="line">class 鸡身体:</span><br><span class="line"> def move(self):</span><br><span class="line"> print("鸡摆尾巴")</span><br><span class="line">class 鸡肢体:</span><br><span class="line"> def walk(self):</span><br><span class="line"> print("鸡走路")</span><br><span class="line"></span><br><span class="line">class 鸭子:</span><br><span class="line"> """</span><br><span class="line"> 本工厂包含了多个产品</span><br><span class="line"> """</span><br><span class="line"> def show():</span><br><span class="line"> return 鸭子头() + 鸭子身体() + 鸭子肢体()</span><br><span class="line"></span><br><span class="line">class 鸡:</span><br><span class="line"> """</span><br><span class="line"> 本工厂包含了多个产品</span><br><span class="line"> """</span><br><span class="line"> def show():</span><br><span class="line"> return 鸡头() + 鸡身体() + 鸡肢体()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">class Test(factory):</span><br><span class="line"> """</span><br><span class="line"> 具体的使用</span><br><span class="line"> """</span><br><span class="line"> factory.show()</span><br></pre></td></tr></table></figure><h2 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h2><p>对 python ,class 默认就是单例</p><p>优点:</p><ul><li>优化内存占用</li></ul><h2 id="建造者模式"><a href="#建造者模式" class="headerlink" title="建造者模式"></a>建造者模式</h2><h2 id="原型模式"><a href="#原型模式" class="headerlink" title="原型模式"></a>原型模式</h2><h1 id="结构型模式"><a href="#结构型模式" class="headerlink" title="结构型模式"></a>结构型模式</h1><h2 id="建造者模式-1"><a href="#建造者模式-1" class="headerlink" title="建造者模式"></a>建造者模式</h2><h2 id="建造者模式-2"><a href="#建造者模式-2" class="headerlink" title="建造者模式"></a>建造者模式</h2><h2 id="建造者模式-3"><a href="#建造者模式-3" class="headerlink" title="建造者模式"></a>建造者模式</h2><h2 id="建造者模式-4"><a href="#建造者模式-4" class="headerlink" title="建造者模式"></a>建造者模式</h2><h2 id="建造者模式-5"><a href="#建造者模式-5" class="headerlink" title="建造者模式"></a>建造者模式</h2><h2 id="建造者模式-6"><a href="#建造者模式-6" class="headerlink" title="建造者模式"></a>建造者模式</h2><ul><li>参考 *</li></ul><ul><li><a href="https://www.cnblogs.com/geek6/p/3951677.html" target="_blank" rel="noopener">https://www.cnblogs.com/geek6/p/3951677.html</a></li><li>《精通python设计模式》</li></ul>]]></content>
<summary type="html">
<blockquote>
<p>个人观点:对于 python 来说,它本来就是一个很灵活的语言,要想实现相应的功能,并不一定非得用这些设计模式的概念。<br>而且每个设计模式都有各自的使用场景和优缺点,正确理解,领悟各自的适用场景才是最重要的。</p>
</blockquote>
</summary>
<category term="软件设计" scheme="http://yoursite.com/categories/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1/"/>
<category term="软件设计" scheme="http://yoursite.com/tags/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1/"/>
</entry>
<entry>
<title>所谓设计模式-六大原则</title>
<link href="http://yoursite.com/2019/01/20/%E6%89%80%E8%B0%93%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%85%AD%E5%A4%A7%E5%8E%9F%E5%88%99/"/>
<id>http://yoursite.com/2019/01/20/所谓设计模式-六大原则/</id>
<published>2019-01-20T02:02:41.000Z</published>
<updated>2019-01-20T05:39:33.407Z</updated>
<content type="html"><![CDATA[<p>总原则:开闭原则(Open Close Principle)<br>开闭原则就是说对扩展开放,对修改关闭。<br>在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。<br>所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。<br>想要达到这样的效果,我们需要使用接口和抽象类等,后面的具体设计中我们会提到这点。</p><p>1、单一职责原则<br>不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不然,就应该把类拆分。</p><p>2、里氏替换原则(Liskov Substitution Principle)<br>里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科</p><p>历史替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。</p><p>3、依赖倒转原则(Dependence Inversion Principle)<br>这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。</p><p>4、接口隔离原则(Interface Segregation Principle)<br>这个原则的意思是:每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。</p><p>5、迪米特法则(最少知道原则)(Demeter Principle)<br>就是说:一个类对自己依赖的类知道的越少越好。也就是说无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。</p><p>最少知道原则的另一个表达方式是:只与直接的朋友通信。类之间只要有耦合关系,就叫朋友关系。耦合分为依赖、关联、聚合、组合等。我们称出现为成员变量、方法参数、方法返回值中的类为直接朋友。局部变量、临时变量则不是直接的朋友。我们要求陌生的类不要作为局部变量出现在类中。</p><p>6、合成复用原则(Composite Reuse Principle)<br>原则是尽量首先使用合成/聚合的方式,而不是使用继承。</p><h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><ul><li><a href="https://www.cnblogs.com/geek6/p/3951677.html" target="_blank" rel="noopener">https://www.cnblogs.com/geek6/p/3951677.html</a></li></ul>]]></content>
<summary type="html">
<p>总原则:开闭原则(Open Close Principle)<br>开闭原则就是说对扩展开放,对修改关闭。<br>在程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,实现一个热插拔的效果。<br>所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。<b
</summary>
<category term="软件设计" scheme="http://yoursite.com/categories/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1/"/>
<category term="软件设计" scheme="http://yoursite.com/tags/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1/"/>
</entry>
<entry>
<title>关于python的代码规范</title>
<link href="http://yoursite.com/2018/12/25/%E5%85%B3%E4%BA%8Epython%E7%9A%84%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
<id>http://yoursite.com/2018/12/25/关于python的代码规范/</id>
<published>2018-12-25T14:02:41.000Z</published>
<updated>2019-01-20T05:39:33.407Z</updated>
<content type="html"><![CDATA[<p>为了代码的易读性,方便团队多人协作,请大家统一按此标准去规范自己的代码。</p><h2 id="PEP8"><a href="#PEP8" class="headerlink" title="PEP8"></a>PEP8</h2><p>PEP8是 python 的代码规范标准,具体细节参考下面链接,在此不多叙述。</p><ul><li>原文 <a href="https://www.python.org/dev/peps/pep-0008/" target="_blank" rel="noopener">https://www.python.org/dev/peps/pep-0008/</a></li><li>中文翻译 <a href="https://blog.csdn.net/ratsniper/article/details/78954852" target="_blank" rel="noopener">https://blog.csdn.net/ratsniper/article/details/78954852</a></li></ul><p>可以用一些工具,如<code>atuopep8</code>去做辅助校验。</p><h2 id="保持代码简洁"><a href="#保持代码简洁" class="headerlink" title="保持代码简洁"></a>保持代码简洁</h2><p>不用的代码,不管是废弃的功能,还是未来有用,但目前用不上的,都删除。<br>如果需要,再从git版本记录中找回。<br>lib中的统一工具除外。</p><h2 id="内部命名规范"><a href="#内部命名规范" class="headerlink" title="内部命名规范"></a>内部命名规范</h2><ul><li>数量 count</li><li>开始时间 begin_time</li><li>结束时间 end_time</li><li>分页,起始值 offset,数量 limit</li><li>关键字查找,参数为q,代码中的变量为key_word</li></ul><h2 id="sql语句"><a href="#sql语句" class="headerlink" title="sql语句"></a>sql语句</h2><p>sql语句被包装成字符串,一般工具无法对此进行格式化校验,但仍需遵循以下规则:</p><h3 id="关键字大写"><a href="#关键字大写" class="headerlink" title="关键字大写"></a>关键字大写</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select id,name from test_barrage where id = 3;</span><br></pre></td></tr></table></figure><p>修正为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT id,name FROM test_barrage WHERE id = 3;</span><br></pre></td></tr></table></figure><h3 id="缩进"><a href="#缩进" class="headerlink" title="缩进"></a>缩进</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT id,name FROM test_barrage WHERE id = 3 and type=2 ORDER BY id;</span><br></pre></td></tr></table></figure><p>错误点:</p><ol><li><code>type=2</code>空格前后应有空格<code>type = 2</code></li><li>sql语句过长,<code>SELECT</code>、<code>WHERE</code>、<code>ORDER BY</code> 需要一行一个</li><li><code>WHERE</code>条件一行一个</li></ol><p>修正为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">SELECT id,name FROM test_barrage </span><br><span class="line">WHERE id = 3 </span><br><span class="line"> and type=2 </span><br><span class="line">ORDER BY id;</span><br></pre></td></tr></table></figure><h3 id="慎用"><a href="#慎用" class="headerlink" title="慎用*"></a>慎用*</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * from test_barrage where id = 3;</span><br></pre></td></tr></table></figure><p>如果不是必要,修正为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELECT id,name FROM test_barrage WHERE id = 3;</span><br></pre></td></tr></table></figure><h3 id="参数化"><a href="#参数化" class="headerlink" title="参数化"></a>参数化</h3><p>为了防止sql注入,pymysql提供了两种参数校验方式</p><ol><li><p>列表传参</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sql = "SELECT `id`, `password` </span><br><span class="line"> FROM `users` </span><br><span class="line"> WHERE `email`=%s"</span><br><span class="line">cursor.execute(sql, ['webmaster@python.org'])</span><br></pre></td></tr></table></figure></li><li><p>字典传参</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sql = "SELECT `id`, `password` </span><br><span class="line"> FROM `users` </span><br><span class="line"> WHERE `email`=%(email)s"</span><br><span class="line">cursor.execute(sql, {"email":'webmaster@python.org'})</span><br></pre></td></tr></table></figure></li></ol><p>第一种比较容易出错,统一采用第二种</p><h2 id="pylint"><a href="#pylint" class="headerlink" title="pylint"></a>pylint</h2><p>代码提交前用 pylint 进行对代码规范进行打分,9.5以上方为合格。具体使用方法请参考:</p><ul><li><a href="https://pypi.org/project/pylint/" target="_blank" rel="noopener">https://pypi.org/project/pylint/</a></li><li><a href="https://www.cnblogs.com/zhangningyang/p/8652941.html" target="_blank" rel="noopener">https://www.cnblogs.com/zhangningyang/p/8652941.html</a></li><li><a href="https://www.ibm.com/developerworks/cn/linux/l-cn-pylint/index.html" target="_blank" rel="noopener">https://www.ibm.com/developerworks/cn/linux/l-cn-pylint/index.html</a></li></ul>]]></content>
<summary type="html">
<p>为了代码的易读性,方便团队多人协作,请大家统一按此标准去规范自己的代码。</p>
<h2 id="PEP8"><a href="#PEP8" class="headerlink" title="PEP8"></a>PEP8</h2><p>PEP8是 python 的代码规范
</summary>
<category term="代码规范" scheme="http://yoursite.com/categories/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
<category term="代码规范" scheme="http://yoursite.com/tags/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
</entry>
<entry>
<title>URL命名规范</title>
<link href="http://yoursite.com/2018/12/25/%E5%85%B3%E4%BA%8E%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E7%9A%84%E7%96%91%E9%97%AE/"/>
<id>http://yoursite.com/2018/12/25/关于服务发现的疑问/</id>
<published>2018-12-25T14:02:41.000Z</published>
<updated>2019-01-20T05:39:33.407Z</updated>
<content type="html"><![CDATA[<ol><li>数据更新机制,变更通知型还是client每次都主动获取?</li></ol><ol><li>变更通知的话,如何通知wsgi 管理的服务;每次请求获取不太显示,如数据库、redis配置</li></ol>]]></content>
<summary type="html">
<ol>
<li>数据更新机制,变更通知型还是client每次都主动获取?</li>
</ol>
<ol>
<li>变更通知的话,如何通知wsgi 管理的服务;每次请求获取不太显示,如数据库、redis配置</li>
</ol>
</summary>
<category term="微服务" scheme="http://yoursite.com/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
<category term="服务发现" scheme="http://yoursite.com/tags/%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/"/>
</entry>
<entry>
<title>URL命名规范</title>
<link href="http://yoursite.com/2018/12/25/URL%E5%91%BD%E5%90%8D%E8%A7%84%E8%8C%83/"/>
<id>http://yoursite.com/2018/12/25/URL命名规范/</id>
<published>2018-12-25T14:02:41.000Z</published>
<updated>2019-01-20T05:39:33.373Z</updated>
<content type="html"><![CDATA[<h2 id="请求方式"><a href="#请求方式" class="headerlink" title="请求方式"></a>请求方式</h2><p>项目中统一只采用 get、post 两种方式</p><h3 id="get-查询请求"><a href="#get-查询请求" class="headerlink" title="get: 查询请求"></a>get: 查询请求</h3><p>参数不通过body,直接在放在URL上</p><h3 id="post-涉及修改数据的请求"><a href="#post-涉及修改数据的请求" class="headerlink" title="post: 涉及修改数据的请求"></a>post: 涉及修改数据的请求</h3><p>参数采用json格式,但URL也放上向get请求一样放上参数,用于请求日志查看、数据分析</p><h2 id="URL拼接方式"><a href="#URL拼接方式" class="headerlink" title="URL拼接方式"></a>URL拼接方式</h2><h3 id="业务统一"><a href="#业务统一" class="headerlink" title="业务统一"></a>业务统一</h3><p>第一个字符串代表具体的业务,如:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">/login/user/<int:user_id></span><br><span class="line">/login/user/<str:user_name></span><br><span class="line">/login/user/<int:phone></span><br></pre></td></tr></table></figure></p><p>login代表用户登陆的一系列请求。<br>这样做的好处一是统计的时候方便查看,二是万一要对某项业务做限制处理,只要用/login/去匹配就行。</p><h3 id="内部统一"><a href="#内部统一" class="headerlink" title="内部统一"></a>内部统一</h3><h4 id="根据内容查找弹幕"><a href="#根据内容查找弹幕" class="headerlink" title="根据内容查找弹幕"></a>根据内容查找弹幕</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`GET /barrage/lectures/get_barrages_by_content`</span><br></pre></td></tr></table></figure><p>查找统一用search,需要查找的关键词用q作为参数传递<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`GET /barrage/lectures/search`</span><br></pre></td></tr></table></figure></p><p>加上参数<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`/barrage/lectures/search?q=a`</span><br></pre></td></tr></table></figure></p><h4 id="获取弹幕数量"><a href="#获取弹幕数量" class="headerlink" title="获取弹幕数量"></a>获取弹幕数量</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`GET /barrage/lectures/barrage_num`</span><br></pre></td></tr></table></figure><p>数量统一用count表示<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`GET /barrage/lectures/barrage_count`</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<h2 id="请求方式"><a href="#请求方式" class="headerlink" title="请求方式"></a>请求方式</h2><p>项目中统一只采用 get、post 两种方式</p>
<h3 id="get-查询请求"><a href="#get-查询请
</summary>
<category term="代码规范" scheme="http://yoursite.com/categories/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
<category term="代码规范" scheme="http://yoursite.com/tags/%E4%BB%A3%E7%A0%81%E8%A7%84%E8%8C%83/"/>
</entry>
<entry>
<title>关于过进程的配置管理</title>
<link href="http://yoursite.com/2018/12/20/flask%E5%88%B7%E6%96%B0%E5%86%85%E5%AD%98%E9%85%8D%E7%BD%AE/"/>
<id>http://yoursite.com/2018/12/20/flask刷新内存配置/</id>
<published>2018-12-20T14:02:41.000Z</published>
<updated>2019-03-17T15:09:33.364Z</updated>
<content type="html"><![CDATA[<h4 id="需求:"><a href="#需求:" class="headerlink" title="需求:"></a>需求:</h4><p>程序中的配置,怎么能够实时更新。<br>其实我想解决的是业务配置的管理,可是想一想,无论业务配置还是服务的配置,<br>其本质也还都配置的管理,可以参考服务发现那套逻辑。</p><blockquote><p>目前服务发现更多采用的是分布式k/v,直接用redis还是少数,这里只是简单列举思路</p></blockquote><h4 id="方案"><a href="#方案" class="headerlink" title="方案"></a>方案</h4><ol><li>对外暴露一个接口,用于刷新配置</li><li>定时加载配置到服务中</li><li>每次接口调用都实时去加载配置</li></ol><h4 id="各方案优缺点"><a href="#各方案优缺点" class="headerlink" title="各方案优缺点"></a>各方案优缺点</h4><ol><li><p>对外暴露一个接口,用于刷新配置</p><blockquote><p>本质上就是进程间通讯</p></blockquote><p> <br>缺点:</p><ul><li><p>稍微麻烦:要写接口,还要让对方调用,如果有多个进程,需要多次调用。</p><p><br>优点:</p></li><li>实时性强</li><li>对业务系统的消耗最低</li></ul></li><li><p>定时加载配置到服务中<br> <br>缺点:</p><ul><li>需要等待刷新的时间,配置人员要等待,不具备实时性</li><li><p>无法很好的把控程序刷新时机,如果要同时改多个配置,可能会有配置改到一半就被刷新的风险</p><p><br>优点:</p></li><li>开发时省事</li></ul></li><li><p>每次接口调用都实时去加载配置<br> <br>缺点:</p><ul><li><p>对业务系统的消耗较高</p><p><br>优点:</p></li><li><p>简单,但是配置多或访问量大的情况不适合<br><br>优化:</p></li><li>数据量大的情况:将配置更新时间存在redis中,每次访问redis,和本地的更新时间做比较,不一致则更新。</li><li>数据量小的情况:将配置存在redis中,做比较,不一致则更新。</li><li>并发量较大:加上内存过期机制,若实时性较高,可以只保存2-3秒;还是采用多级缓存的思路。</li></ul></li></ol><h4 id="以往的方案"><a href="#以往的方案" class="headerlink" title="以往的方案"></a>以往的方案</h4><p>采用第一种。<br>用的twisted或tornado异步处理请求,每个进程分别对应两个端口,分别对内和对外提供服务。<br>对内服务主要用于刷新配置和设置临时变量。<br>如果要刷新配置,只要调用对应的内部端口即可。</p><h4 id="新的问题"><a href="#新的问题" class="headerlink" title="新的问题"></a>新的问题</h4><p>目前团队用的flask,采用wsgi管理多个进程,而多个进程的端口都是同一个。<br>怎么样才能让所有的进程都同步到最新的配置呢?</p><h4 id="目前的结论"><a href="#目前的结论" class="headerlink" title="目前的结论"></a>目前的结论</h4><ul><li>方案2是个坑;flask 可以用方案3;并发量特别大的时候,采用方案1。</li><li>方案1要实现同一端口的多进程调用,只能对wsgi再封装,如非必要,先放弃。</li></ul><h4 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h4><ul><li><a href="https://www.jianshu.com/p/75e1107530b1" target="_blank" rel="noopener">https://www.jianshu.com/p/75e1107530b1</a></li></ul>]]></content>
<summary type="html">
<h4 id="需求:"><a href="#需求:" class="headerlink" title="需求:"></a>需求:</h4><p>程序中的配置,怎么能够实时更新。<br>其实我想解决的是业务配置的管理,可是想一想,无论业务配置还是服务的配置,<br>其本质也还都
</summary>
<category term="微服务" scheme="http://yoursite.com/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/"/>
<category term="服务发现" scheme="http://yoursite.com/categories/%E5%BE%AE%E6%9C%8D%E5%8A%A1/%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/"/>
<category term="服务发现" scheme="http://yoursite.com/tags/%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0/"/>
<category term="wsgi" scheme="http://yoursite.com/tags/wsgi/"/>
<category term="flask" scheme="http://yoursite.com/tags/flask/"/>
</entry>
<entry>
<title>git多人协作流程</title>
<link href="http://yoursite.com/2018/12/20/git%E5%A4%9A%E4%BA%BA%E5%8D%8F%E4%BD%9C%E6%B5%81%E7%A8%8B/"/>
<id>http://yoursite.com/2018/12/20/git多人协作流程/</id>
<published>2018-12-20T14:02:41.000Z</published>
<updated>2019-01-20T05:39:33.377Z</updated>
<content type="html"><![CDATA[<blockquote><p>git是一个管理代码的工具,具体的介绍及使用方法可以查看下面链接,在此不做叙述。</p><ul><li><a href="https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001373962845513aefd77a99f4145f0a2c7a7ca057e7570000" target="_blank" rel="noopener">https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001373962845513aefd77a99f4145f0a2c7a7ca057e7570000</a></li></ul><p>本文主要讲述团队协作中,git的使用流程。</p></blockquote><h2 id="1-每做一个新功能时,先从master拉一个新分支"><a href="#1-每做一个新功能时,先从master拉一个新分支" class="headerlink" title="1. 每做一个新功能时,先从master拉一个新分支"></a>1. 每做一个新功能时,先从master拉一个新分支</h2><p><code>git checkout -b 你的名字/功能名称</code> 如 <code>git checkout -b ltj/test_git_add_branch</code></p><h2 id="2-功能开发…提交代码到本地分支"><a href="#2-功能开发…提交代码到本地分支" class="headerlink" title="2. 功能开发…提交代码到本地分支"></a>2. 功能开发…提交代码到本地分支</h2><h2 id="3-上传本地分支到远程分支"><a href="#3-上传本地分支到远程分支" class="headerlink" title="3. 上传本地分支到远程分支"></a>3. 上传本地分支到远程分支</h2><p><code>git push origin ltj/test_git_add_branch remotes/origin/ltj/test_git_add_branch</code></p><h2 id="4-rebase,和master代码同步"><a href="#4-rebase,和master代码同步" class="headerlink" title="4. rebase,和master代码同步"></a>4. rebase,和master代码同步</h2><p>(相当于把master合并到当前分支,但更友好)<br><code>git fetch</code></p><p><code>git rebase origin/master</code></p><p>如果有冲突,需要解决冲突,提交,之后再继续rebase</p><p><code>git add . ; git rebase --continue</code></p><p>这个过程一直持续循环到没有冲突为止</p><p>将rebase后的代码推送到远程分支</p><p><code>git push origin ltj/test_git_add_branch</code></p><p>如果rebase一直报错,可以强行推送到远程,但不建议用</p><p><code>git push origin ltj/test_git_add_branch -f</code></p><h2 id="5-发出合并请求,等master合并该分支"><a href="#5-发出合并请求,等master合并该分支" class="headerlink" title="5. 发出合并请求,等master合并该分支"></a>5. 发出合并请求,等master合并该分支</h2><p>到gitlab上进行页面操作</p><h2 id="6-过一段时间确认无误后"><a href="#6-过一段时间确认无误后" class="headerlink" title="6. 过一段时间确认无误后"></a>6. 过一段时间确认无误后</h2><p>删除远程分支<br><code>git push origin --delete ltj/test_git_add_branch</code></p><p>删除本地分支<br><code>git branch -D ltj/fix_paging</code></p><h2 id="可能遇到的问题:"><a href="#可能遇到的问题:" class="headerlink" title="可能遇到的问题:"></a>可能遇到的问题:</h2><ul><li>合并冲突 <a href="https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001375840202368c74be33fbd884e71b570f2cc3c0d1dcf000" target="_blank" rel="noopener">https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001375840202368c74be33fbd884e71b570f2cc3c0d1dcf000</a></li></ul>]]></content>
<summary type="html">
<blockquote>
<p>git是一个管理代码的工具,具体的介绍及使用方法可以查看下面链接,在此不做叙述。</p>
<ul>
<li><a href="https://www.liaoxuefeng.com/wiki/0013739516305929606dd1836124
</summary>
<category term="git" scheme="http://yoursite.com/categories/git/"/>
<category term="git" scheme="http://yoursite.com/tags/git/"/>
</entry>
<entry>
<title>聊一聊flask的get</title>
<link href="http://yoursite.com/2018/12/20/%E8%81%8A%E4%B8%80%E8%81%8Aflask%E7%9A%84get/"/>
<id>http://yoursite.com/2018/12/20/聊一聊flask的get/</id>
<published>2018-12-20T14:02:41.000Z</published>
<updated>2019-01-20T05:39:33.407Z</updated>
<content type="html"><![CDATA[<h2 id="获取参数的方式"><a href="#获取参数的方式" class="headerlink" title="获取参数的方式"></a>获取参数的方式</h2><h4 id="1-通过的URL获取,如:zoo-id,cat-id"><a href="#1-通过的URL获取,如:zoo-id,cat-id" class="headerlink" title="1. 通过的URL获取,如:zoo_id,cat_id"></a>1. 通过的URL获取,如:zoo_id,cat_id</h4><h4 id="2-通过-request-args-get-获取,如:name"><a href="#2-通过-request-args-get-获取,如:name" class="headerlink" title="2. 通过 request.args.get 获取,如:name"></a>2. 通过 request.args.get 获取,如:name</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">@BP.route('/zoo/<int:zoo_id>/cat/<int:cat_id>', methods=['GET'])</span><br><span class="line">def get_cat_info(zoo_id, cat_id):</span><br><span class="line"> """ """</span><br><span class="line"> name = request.args.get('name', type=str, default=None)</span><br></pre></td></tr></table></figure><h2 id="get-获取json格式"><a href="#get-获取json格式" class="headerlink" title="get 获取json格式"></a>get 获取json格式</h2><h4 id="1-通过URL传递"><a href="#1-通过URL传递" class="headerlink" title="1. 通过URL传递"></a>1. 通过URL传递</h4><p>这种方法虽然比较麻烦,但是接口较为通用<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@BP.route('/zoo/<int:zoo_id>/cats', methods=['GET'])</span><br><span class="line">def get_cat_list(zoo_id):</span><br><span class="line"> """ """</span><br><span class="line">ids = request.args.get('ids', type=str, default=0)</span><br><span class="line">ids = json.loads(ids)</span><br></pre></td></tr></table></figure></p><p>测试方法<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">url = '/zoo/<int:zoo_id>/cats?ids=%s' % json.dumps([1,2,3])</span><br><span class="line">request.get(url)</span><br></pre></td></tr></table></figure></p><h4 id="2-通过body传递"><a href="#2-通过body传递" class="headerlink" title="2. 通过body传递"></a>2. 通过body传递</h4><p>get_json比较简单,获取到的已经是json格式了,可以直接使用。<br>但get一般都不会通过body来传参,非一般规范,可以和项目协商后,统一采用一种方式。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">@BP.route('/zoo/<int:zoo_id>/cats', methods=['GET'])</span><br><span class="line">def get_cat_list(zoo_id):</span><br><span class="line">params = request.get_json()</span><br><span class="line">id = params.get("id")</span><br></pre></td></tr></table></figure></p><p>测试方法<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">url = '/zoo/<int:zoo_id>/cats'</span><br><span class="line">request.get(url, json={"ids":[20]})</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<h2 id="获取参数的方式"><a href="#获取参数的方式" class="headerlink" title="获取参数的方式"></a>获取参数的方式</h2><h4 id="1-通过的URL获取,如:zoo-id,cat-id"><a href="#1-通过的UR
</summary>
<category term="Python" scheme="http://yoursite.com/categories/Python/"/>
<category term="flask" scheme="http://yoursite.com/categories/Python/flask/"/>
<category term="flask" scheme="http://yoursite.com/tags/flask/"/>
</entry>
<entry>
<title>Python的with原理</title>
<link href="http://yoursite.com/2018/12/03/Python%E7%9A%84with%E5%8E%9F%E7%90%86/"/>
<id>http://yoursite.com/2018/12/03/Python的with原理/</id>
<published>2018-12-03T09:02:41.000Z</published>
<updated>2019-01-20T05:39:33.373Z</updated>
<content type="html"><![CDATA[<h3 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h3><p>对一个文件进行操作,我们可以这样写:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">f = open(file, mode)</span><br><span class="line">...</span><br><span class="line">f.close() #关闭文件</span><br></pre></td></tr></table></figure></p><p>但是可能会在文件操作的过程中触发异常,导致<code>f.close()</code>没有被执行,于是我们对其做出以下改良:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">f = open(file, mode)</span><br><span class="line">try:</span><br><span class="line"> ...</span><br><span class="line">finally:</span><br><span class="line"> f.close()</span><br></pre></td></tr></table></figure></p><p>嗯…貌似有点繁琐,能不能用一个简单点的方法呢?这时<code>with</code>就出现了:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">with open(file, mode) as f:</span><br><span class="line"> ...</span><br></pre></td></tr></table></figure></p><p>相当于用with包裹后,自动处理了文件的关闭。</p><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><p>那具体是怎么实现的呢?<br>我们先来看看有关上下文管理器的概念:</p><ul><li><p>上下文管理协议(Context Management Protocol)</p><blockquote><p>包含方法 <strong>enter</strong>() 和 <strong>exit</strong>(),支持<br>该协议的对象要实现这两个方法。</p></blockquote></li><li><p>上下文管理器(Context Manager)</p><blockquote><p>支持上下文管理协议的对象,这种对象实现了<br><strong>enter</strong>() 和 <strong>exit</strong>() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,<br>负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,<br>也可以通过直接调用其方法来使用。</p></blockquote></li><li><p>运行时上下文(runtime context)</p><blockquote><p>由上下文管理器创建,通过上下文管理器的 <strong>enter</strong>() 和<br><strong>exit</strong>() 方法实现,<strong>enter</strong>() 方法在语句体执行之前进入运行时上下文,<strong>exit</strong>() 在<br>语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。</p></blockquote></li></ul><h3 id="with-语句执行过程"><a href="#with-语句执行过程" class="headerlink" title="with 语句执行过程"></a>with 语句执行过程</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">context_manager = context_expression</span><br><span class="line">exit = type(context_manager).__exit__ </span><br><span class="line">value = type(context_manager).__enter__(context_manager)</span><br><span class="line">exc = True # True 表示正常执行,即便有异常也忽略;False 表示重新抛出异常,需要对异常进行处理</span><br><span class="line">try:</span><br><span class="line"> try:</span><br><span class="line"> target = value # 如果使用了 as 子句</span><br><span class="line"> with-body # 执行 with-body</span><br><span class="line"> except:</span><br><span class="line"> # 执行过程中有异常发生</span><br><span class="line"> exc = False</span><br><span class="line"> # 如果 __exit__ 返回 True,则异常被忽略;如果返回 False,则重新抛出异常</span><br><span class="line"> # 由外层代码对异常进行处理</span><br><span class="line"> if not exit(context_manager, *sys.exc_info()):</span><br><span class="line"> raise</span><br><span class="line">finally:</span><br><span class="line"> # 正常退出,或者通过 statement-body 中的 break/continue/return 语句退出</span><br><span class="line"> # 或者忽略异常退出</span><br><span class="line"> if exc:</span><br><span class="line"> exit(context_manager, None, None, None)</span><br><span class="line"> # 缺省返回 None,None 在布尔上下文中看做是 False</span><br></pre></td></tr></table></figure><h3 id="自定义上下文管理器"><a href="#自定义上下文管理器" class="headerlink" title="自定义上下文管理器"></a>自定义上下文管理器</h3><p>现在试着自己实现一个上下文管理器,实现对文件资源的释放<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">#/usr/bin/python</span><br><span class="line"># -*- coding: utf-8 -*-</span><br><span class="line"># 自行定義 Context Manager</span><br><span class="line">class File(object):</span><br><span class="line"> def __init__(self, filename, mode):</span><br><span class="line"> # 設定檔名與開檔模式</span><br><span class="line"> self.filename = filename</span><br><span class="line"> self.mode = mode</span><br><span class="line"></span><br><span class="line"> # 配給資源(開啟檔案)</span><br><span class="line"> def __enter__(self):</span><br><span class="line"> print("開啟檔案:" + self.filename)</span><br><span class="line"> self.open_file = open(self.filename, self.mode)</span><br><span class="line"> return self.open_file</span><br><span class="line"></span><br><span class="line"> # 回收資源(關閉檔案)</span><br><span class="line"> def __exit__(self, type, value, traceback):</span><br><span class="line"> print("關閉檔案:" + self.filename)</span><br><span class="line"> self.open_file.close()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">with File("file.txt", "w") as f:</span><br><span class="line"> print("寫入檔案...")</span><br><span class="line"> f.write("Hello, world.")</span><br></pre></td></tr></table></figure></p><p>另一种实现方式<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">#/usr/bin/python</span><br><span class="line"># -*- coding: utf-8 -*-</span><br><span class="line">from contextlib import contextmanager</span><br><span class="line"></span><br><span class="line"># 自行定義 Context Manager</span><br><span class="line">@contextmanager</span><br><span class="line">def open_file(name, mode):</span><br><span class="line"> # 配給資源(開啟檔案)</span><br><span class="line"> f = open(name, mode)</span><br><span class="line"> yield f</span><br><span class="line"> # 回收資源(關閉檔案)</span><br><span class="line"> f.close()</span><br><span class="line"></span><br><span class="line">with open_file('file.txt', 'w') as f:</span><br><span class="line"> f.write("Hello, world.")</span><br></pre></td></tr></table></figure></p><h3 id="对with的总结"><a href="#对with的总结" class="headerlink" title="对with的总结"></a>对with的总结</h3><blockquote><p>with适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源,比如文件使用后自动关闭、线程中锁的自动获取和释放等。</p></blockquote><h3 id="nested-同时使用多个with嵌套"><a href="#nested-同时使用多个with嵌套" class="headerlink" title="nested 同时使用多个with嵌套"></a>nested 同时使用多个with嵌套</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">with nested(A(), B(), C()) as (X, Y, Z):</span><br><span class="line"> # with-body code here</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">with A() as X:</span><br><span class="line"> with B() as Y:</span><br><span class="line"> with C() as Z:</span><br><span class="line"> # with-body code here</span><br></pre></td></tr></table></figure><p>需要注意的是,发生异常后,如果某个上下文管理器的 <strong>exit</strong>() 方法对异常处理返回 False,则更外层的上下文管理器不会监测到异常。</p><p>参考</p><ul><li><a href="https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/index.html" target="_blank" rel="noopener">https://www.ibm.com/developerworks/cn/opensource/os-cn-pythonwith/index.html</a></li><li><a href="https://blog.gtwang.org/programming/python-with-context-manager-tutorial/" target="_blank" rel="noopener">https://blog.gtwang.org/programming/python-with-context-manager-tutorial/</a></li></ul>]]></content>
<summary type="html">
<h3 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h3><p>对一个文件进行操作,我们可以这样写:<br><figure class="highlight plain"><table><tr><td c
</summary>
<category term="Python" scheme="http://yoursite.com/categories/Python/"/>
<category term="Python" scheme="http://yoursite.com/tags/Python/"/>
</entry>
<entry>
<title>nginx频率限制模块之HttpLimitReqModul</title>
<link href="http://yoursite.com/2018/11/30/nginx%E9%A2%91%E7%8E%87%E9%99%90%E5%88%B6%E6%A8%A1%E5%9D%97%E4%B9%8BHttpLimitReqModul/"/>
<id>http://yoursite.com/2018/11/30/nginx频率限制模块之HttpLimitReqModul/</id>
<published>2018-11-30T04:26:41.000Z</published>
<updated>2018-12-03T11:21:15.448Z</updated>
<content type="html"><![CDATA[<p>HttpLimitReqModul为nginx官方自带的<strong>限制单位时间内请求次数</strong>的功能模块,通过<strong>limit_req_zone</strong>和<strong>limit_req</strong>指令配合使用来达到限制。一旦对应的累计连接超过指定数量,就会返回503错误。<br>可以利用该规则对IP和URL进行访问频率的限制。</p><blockquote><ul><li>limit_conn_zone 定义IP或url访问的频率限制规则<br>只能配置在 http{} </li><li>limit_conn 启用对应的规则<br>可以配置于http{},server{},location{} </li></ul></blockquote><h4 id="限制规则"><a href="#限制规则" class="headerlink" title="限制规则"></a>限制规则</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">limit_req_zone $binary_remote_addr zone=ip_second:10m rate=10r/s;</span><br><span class="line">limit_req_zone $request_uri zone=url_minute:10m rate=1r/m;</span><br></pre></td></tr></table></figure><ul><li>limit_req_zone 可以理解为命令声明,类似于 <code>int x=0</code> 中的 int<h5 id="限制类型"><a href="#限制类型" class="headerlink" title="限制类型"></a>限制类型</h5></li><li>$binary_remote_addr 代表限制IP</li><li>$request_uri 代表限制URL<h5 id="规则"><a href="#规则" class="headerlink" title="规则"></a>规则</h5></li><li><code>zone=ip_second:10m rate=10r/s</code>相当于<code>zone=限制规则变量名:内存占用上限 频率=次数r/秒</code><h5 id="内存占用上限"><a href="#内存占用上限" class="headerlink" title="内存占用上限"></a>内存占用上限</h5></li><li>1m大约可以存1.6W个ip回话</li><li>10m—-16W<h5 id="频率限制"><a href="#频率限制" class="headerlink" title="频率限制"></a>频率限制</h5>采用漏桶原理,简单理解就是,<code>rate=10r/s</code> 这十个请求平均分配在1秒内,也就是100ms最多能通过一次请求,而不是前面100ms接收10次,后面才不能接收。</li></ul><h4 id="限制范围"><a href="#限制范围" class="headerlink" title="限制范围"></a>限制范围</h4><p>当 limit_req 设置后,对应的 limit_req_zone 规则才会生效,而且只在 limit_req 声明的范围内生效<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">limit_req zone=限制规则变量名 burst=5 nodelay;</span><br></pre></td></tr></table></figure></p><h5 id="全局生效"><a href="#全局生效" class="headerlink" title="全局生效"></a>全局生效</h5><p>将此命令放在 http{} 层,紧接着 limit_req_zone 之后</p><h5 id="局部生效"><a href="#局部生效" class="headerlink" title="局部生效"></a>局部生效</h5><p>将此命令放在 server{} 或 location{} 层,则在此之外的层级不受影响,还可以在不同的层级设置不同的规则<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">server{</span><br><span class="line">limit_req zone=限制规则变量名 burst=2 nodelay;</span><br><span class="line">}</span><br><span class="line">server{</span><br><span class="line">limit_req zone=限制规则变量名 burst=5 nodelay;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h5 id="burst"><a href="#burst" class="headerlink" title="burst"></a>burst</h5><p>burst 代表令牌数量,至少要为1,即可以在limit_req_zone规则的基础上,额外可以请求的数量。<br>令牌消耗后,以 limit_req_zone 定义的时间单位恢复。</p><h4 id="频率单位"><a href="#频率单位" class="headerlink" title="频率单位"></a>频率单位</h4><p>只能控制 秒(rate=100r/s) 和 分钟(rate=100r/m) 级别</p><h4 id="案例分析"><a href="#案例分析" class="headerlink" title="案例分析"></a>案例分析</h4><p>多个限制时,只要有一个符合条件,就会触发限制</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">http {</span><br><span class="line"> #限制ip</span><br><span class="line"> limit_req_zone $binary_remote_addr zone=ip_second:10m rate=10r/s;</span><br><span class="line"> limit_req zone=ip_second burst=5 nodelay;</span><br><span class="line"> limit_req_zone $binary_remote_addr zone=ip_minute:100m rate=10r/m;</span><br><span class="line"> limit_req zone=ip_minute burst=50 nodelay;</span><br><span class="line"></span><br><span class="line"> #限制url</span><br><span class="line"> limit_req_zone $request_uri zone=url_minute:10m rate=1r/m;</span><br><span class="line"> limit_req zone=url_minute burst=1 nodelay;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>一秒内请求100次</p><blockquote><p>ab -n 100 -c 10 url</p></blockquote><p>只会有51个成功,<code>rate=10r/m</code>代表一分钟10个请求,系统会默认把这十个请求平均分配在60秒内,即6秒一个请求,所以1秒内,只能用掉这里的1个名额,剩下50个名额用的是<code>burst=50</code>令牌的名额。</p><h4 id="自定义返回码"><a href="#自定义返回码" class="headerlink" title="自定义返回码"></a>自定义返回码</h4><p>自定义限制规则生效后的返回码<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">http {</span><br><span class="line"> limit_conn_status 503;</span><br><span class="line"></span><br><span class="line"> limit_req_zone $binary_remote_addr zone=ip_second:10m rate=100r/s;</span><br><span class="line"> limit_req zone=ip_second burst=5 nodelay;</span><br><span class="line"> limit_req_zone $binary_remote_addr zone=ip_minute:100m rate=10r/m;</span><br><span class="line"> limit_req zone=ip_minute burst=50 nodelay;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html">
<p>HttpLimitReqModul为nginx官方自带的<strong>限制单位时间内请求次数</strong>的功能模块,通过<strong>limit_req_zone</strong>和<strong>limit_req</strong>指令配合使用来达到限制。一旦对
</summary>
<category term="nginx" scheme="http://yoursite.com/categories/nginx/"/>
<category term="nginx" scheme="http://yoursite.com/tags/nginx/"/>
<category term="网络安全" scheme="http://yoursite.com/tags/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8/"/>
</entry>
</feed>