Repository navigation
Expand file tree
/
Copy pathatom.xml
More file actions
469 lines (236 loc) · 474 KB
/
Copy pathatom.xml
File metadata and controls
469 lines (236 loc) · 474 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>IntYou's Blog</title>
<subtitle>Change The World As you Know</subtitle>
<link href="https://intyouss.github.io/atom.xml" rel="self"/>
<link href="https://intyouss.github.io/"/>
<updated>2023-08-22T08:04:17.364Z</updated>
<id>https://intyouss.github.io/</id>
<author>
<name>IntYou</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>kratos接入kafka</title>
<link href="https://intyouss.github.io/2023/08/22/kratos%E6%8E%A5%E5%85%A5kafka/"/>
<id>https://intyouss.github.io/2023/08/22/kratos%E6%8E%A5%E5%85%A5kafka/</id>
<published>2023-08-22T00:33:21.000Z</published>
<updated>2023-08-22T08:04:17.364Z</updated>
<content type="html"><![CDATA[<blockquote><p>今天开始准备制作聊天功能,希望使用kafka进行消息的读写操作,保障消息的可靠性,以及对mysql的持久化要求</p></blockquote><h2 id="Kafka-基础概念"><a href="#Kafka-基础概念" class="headerlink" title="Kafka 基础概念"></a>Kafka 基础概念</h2><h3 id="Producer-和-Consumer"><a href="#Producer-和-Consumer" class="headerlink" title="Producer 和 Consumer"></a>Producer 和 Consumer</h3><blockquote><p>对于kafka来说,有两种基本类型: Producer 和 Consumer</p><p>Producer 简单来说就是用来生产消息或者源信息的,而Consumer 则负责消费消息,使用消息</p></blockquote><h3 id="Topic-和-Partition"><a href="#Topic-和-Partition" class="headerlink" title="Topic 和 Partition"></a>Topic 和 Partition</h3><ol><li><p>Topic</p><blockquote><p>在kafka中,消息根据Topic进行分类,每个Topic对应一个消息队列。</p><p>从大的来讲,我们可以将每个服务划分为一个Topic,数据按照服务进行分类处理。</p><p>从小的来看,我们也能细化每个服务的增删改查操作,每个操作维护一个Topic,进行更细致的划分</p></blockquote></li><li><p>Partition</p><blockquote><p>Partition是为了解决Topic内数据传输方式不同,导致单个Topic出现吞吐量不足的问题</p><p>他将单个Topic分化为多个块,对内部以不同方式传输的数据进行分块处理,例如将发送消息与删除消息分为两块进行,提高了未分块时串行输出的效率</p></blockquote></li></ol><h3 id="Broker-和-Cluster"><a href="#Broker-和-Cluster" class="headerlink" title="Broker 和 Cluster"></a>Broker 和 Cluster</h3><blockquote><p>一个kafka服务器称为一个Broker,多个Broker组成一个Cluster集群</p><p>其中集群内某个 Broker 会成为集群控制器(Cluster Controller),它负责管理集群,包括分配分区到 Broker、监控 Broker 故障等。在集群内,一个分区由一个 Broker 负责,这个 Broker 也称为这个分区的 Leader;当然一个分区可以被复制到多个 Broker 上来实现冗余,这样当存在 Broker 故障时可以将其分区重新分配到其他 Broker 来负责</p></blockquote><h2 id="Docker-Compose-安装-Kafka"><a href="#Docker-Compose-安装-Kafka" class="headerlink" title="Docker-Compose 安装 Kafka"></a>Docker-Compose 安装 Kafka</h2><blockquote><p>这里我们直接使用docker-compose安装kafka,简便快捷</p><p>当然,kafka依赖于zookeeper,虽然已经有版本不需要依赖,但我还是使用一般模式,目前仅使用单机,也就不再提及集群</p></blockquote><ol><li><p>配置docker-compose文件</p><div class="highlight-container" data-rel="Docker"><figure class="iseeu highlight docker"><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">version: <span class="string">'3'</span></span><br><span class="line">services:</span><br><span class="line"> zookeeper:</span><br><span class="line"> image: confluentinc/cp-zookeeper:<span class="number">6.2</span>.<span class="number">0</span></span><br><span class="line"> container_name: zookeeper</span><br><span class="line"> ports:</span><br><span class="line"> - <span class="string">"2181:2181"</span></span><br><span class="line"> environment:</span><br><span class="line"> ZOOKEEPER_CLIENT_PORT: <span class="number">2181</span></span><br><span class="line"> volumes:</span><br><span class="line"> - ../../../data/zookeeper/data:/data <span class="comment"># 挂载到项目data目录</span></span><br><span class="line"></span><br><span class="line"> kafka:</span><br><span class="line"> image: confluentinc/cp-kafka:<span class="number">6.2</span>.<span class="number">0</span></span><br><span class="line"> container_name: kafka</span><br><span class="line"> depends_on:</span><br><span class="line"> - zookeeper</span><br><span class="line"> ports:</span><br><span class="line"> - <span class="string">"9092:9092"</span></span><br><span class="line"> environment:</span><br><span class="line"> KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:<span class="number">9092</span></span><br><span class="line"> KAFKA_ZOOKEEPER_CONNECT: zookeeper:${ZOOKEEPER_PORT}</span><br><span class="line"> volumes:</span><br><span class="line"> - ../../../data/kafka/data:/data <span class="comment"># 挂载到项目data</span></span><br></pre></td></tr></table></figure></div></li><li><p>使用命令安装(Linux)</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><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"><span class="comment"># 下载docker-compose文件内依赖容器,并后台运行</span></span><br><span class="line">sudo docker-compose up -d</span><br></pre></td></tr></table></figure></div></li></ol><h2 id="使用Kafka-go在Kratos中操作Kafka"><a href="#使用Kafka-go在Kratos中操作Kafka" class="headerlink" title="使用Kafka-go在Kratos中操作Kafka"></a>使用Kafka-go在Kratos中操作Kafka</h2><blockquote><p>因为我们的服务框架为Kratos,所以就在Kratos中进行操作‘</p><p>对于Kafka-go,是我个人喜欢使用的一个操作kafka的三方</p></blockquote><h3 id="在聊天服务配置文件中添加kafka配置"><a href="#在聊天服务配置文件中添加kafka配置" class="headerlink" title="在聊天服务配置文件中添加kafka配置"></a>在聊天服务配置文件中添加kafka配置</h3><blockquote><p>在聊天服务中,我使用kafka-go的Writer,Reader Api进行写入与读取</p><ol><li><p><strong>易于使用</strong>:<code>Writer</code> 和 <code>Reader</code> 提供了简单的接口和方法,使得发送和接收消息变得非常直观和易于理解。它们封装了 Kafka 的复杂性,提供了一种更简单的方式来与 Kafka 进行交互。</p></li><li><p><strong>高级功能</strong>:<code>kafka-go</code> 库的 <code>Writer</code> 和 <code>Reader</code> 支持许多高级功能,例如批量写入消息、异步写入、读取消息的偏移量控制等。这些功能使得处理和管理消息变得更加灵活和高效。</p></li><li><p><strong>性能优化</strong>:<code>kafka-go</code> 库的 <code>Writer</code> 和 <code>Reader</code> 实现了一些性能优化策略,例如批量发送和异步处理,以提高消息的吞吐量和性能。它们经过了优化和测试,可以在高负载的情况下提供良好的性能表现。</p></li></ol></blockquote><p>我们看看官方给我们的实例</p><ul><li><p>Reader</p><div class="highlight-container" data-rel="Go"><figure class="iseeu highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// make a new reader that consumes from topic-A, </span></span><br><span class="line"><span class="comment">// partition 0, at offset 42</span></span><br><span class="line">r := kafka.NewReader(kafka.ReaderConfig{</span><br><span class="line"> Brokers: []<span class="type">string</span>{<span class="string">"localhost:9092"</span>},</span><br><span class="line"> Topic: <span class="string">"topic-A"</span>,</span><br><span class="line"> Partition: <span class="number">0</span>,</span><br><span class="line"> MaxBytes: <span class="number">10e6</span>, <span class="comment">// 10MB</span></span><br><span class="line">})</span><br><span class="line">r.SetOffset(<span class="number">42</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> {</span><br><span class="line"> m, err := r.ReadMessage(context.Background())</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> }</span><br><span class="line"> fmt.Printf(<span class="string">"message at offset %d: %s = %s\n"</span>, m.Offset,</span><br><span class="line"> <span class="type">string</span>(m.Key), <span class="type">string</span>(m.Value))</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> err := r.Close(); err != <span class="literal">nil</span> {</span><br><span class="line"> log.Fatal(<span class="string">"failed to close reader:"</span>, err)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li><li><p>Writer</p><div class="highlight-container" data-rel="Go"><figure class="iseeu highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// make a writer that produces to topic-A, using the </span></span><br><span class="line"><span class="comment">// least-bytes distribution</span></span><br><span class="line">w := &kafka.Writer{</span><br><span class="line">Addr: kafka.TCP(<span class="string">"localhost:9092"</span>),</span><br><span class="line">Topic: <span class="string">"topic-A"</span>,</span><br><span class="line">Balancer: &kafka.LeastBytes{},</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">err := w.WriteMessages(context.Background(),</span><br><span class="line">kafka.Message{</span><br><span class="line">Key: []<span class="type">byte</span>(<span class="string">"Key-A"</span>),</span><br><span class="line">Value: []<span class="type">byte</span>(<span class="string">"Hello World!"</span>),</span><br><span class="line">},</span><br><span class="line">kafka.Message{</span><br><span class="line">Key: []<span class="type">byte</span>(<span class="string">"Key-B"</span>),</span><br><span class="line">Value: []<span class="type">byte</span>(<span class="string">"One!"</span>),</span><br><span class="line">},</span><br><span class="line">kafka.Message{</span><br><span class="line">Key: []<span class="type">byte</span>(<span class="string">"Key-C"</span>),</span><br><span class="line">Value: []<span class="type">byte</span>(<span class="string">"Two!"</span>),</span><br><span class="line">},</span><br><span class="line">)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> log.Fatal(<span class="string">"failed to write messages:"</span>, err)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> err := w.Close(); err != <span class="literal">nil</span> {</span><br><span class="line"> log.Fatal(<span class="string">"failed to close writer:"</span>, err)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><blockquote><p>我们看到这两个配置使用的方法并不相同(NewReader和Writer{}),因为官方把NewWriter方法废弃了,由于某些安全原因</p></blockquote></li></ul><p>在这个两个连接实例中,我们不难得出我们的配置必备的是哪些东西</p><blockquote><p>localhost:9092 broker地址</p><p>topic 分类名</p><p>partition 分块号</p><p>我们可以添加读取与写入超时时间,提高连接使用效率</p></blockquote><p>configs/config.yaml</p><div class="highlight-container" data-rel="Yaml"><figure class="iseeu highlight yaml"><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"><span class="attr">kafka:</span></span><br><span class="line"> <span class="attr">addr:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span><span class="string">:9092</span></span><br><span class="line"> <span class="attr">topic:</span> <span class="string">"message"</span></span><br><span class="line"> <span class="attr">partition:</span> <span class="number">0</span></span><br><span class="line"> <span class="attr">read_timeout:</span> <span class="number">0.</span><span class="string">2s</span></span><br><span class="line"> <span class="attr">write_timeout:</span> <span class="number">0.</span><span class="string">2s</span></span><br></pre></td></tr></table></figure></div><p>conf/conf.proto</p><div class="highlight-container" data-rel="Protobuf"><figure class="iseeu highlight protobuf"><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></pre></td><td class="code"><pre><span class="line"> <span class="keyword">message </span><span class="title class_">Bootstrap</span> {</span><br><span class="line"> Kafka kafka = <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">message </span><span class="title class_">Kafka</span> {</span><br><span class="line"> <span class="type">string</span> addr = <span class="number">1</span>;</span><br><span class="line"> <span class="type">string</span> topic = <span class="number">2</span>;</span><br><span class="line"> <span class="type">string</span> partition = <span class="number">3</span>;</span><br><span class="line"> google.protobuf.Duration read_timeout = <span class="number">4</span>;</span><br><span class="line"> google.protobuf.Duration write_timeout = <span class="number">5</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></div><h3 id="设置Kafka连接"><a href="#设置Kafka连接" class="headerlink" title="设置Kafka连接"></a>设置Kafka连接</h3><blockquote><p>因为是在Kratos中使用,我们将Kafka连接建立写在服务data层内,并合并至NewData中,为data层提供操作</p></blockquote><p>我们首先整合一下Writer和Reader类型,创建一个新的Conn结构体</p><div class="highlight-container" data-rel="Go"><figure class="iseeu highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> KafkaConn <span class="keyword">struct</span> {</span><br><span class="line"> writer *kafka.Writer</span><br><span class="line"> reader *kafka.Reader</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在Data中引入,并小写属性隐藏连接,为提供给外部文件做准备</span></span><br><span class="line"><span class="keyword">type</span> Data <span class="keyword">struct</span> {</span><br><span class="line"> db *gorm.DB</span><br><span class="line"> kfk *KafkaConn</span><br><span class="line"> log *log.Helper</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>添加一个KafkaConn的New方法</p><div class="highlight-container" data-rel="Go"><figure class="iseeu highlight go"><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"><span class="function"><span class="keyword">func</span> <span class="title">NewKafkaConn</span><span class="params">(c *conf.Data)</span></span> *KafkaConn {</span><br><span class="line">writer := kafka.Writer{</span><br><span class="line">Addr: kafka.TCP(c.Kafka.Addr),</span><br><span class="line">Topic: c.Kafka.Topic,</span><br><span class="line">Balancer: &kafka.LeastBytes{},</span><br><span class="line">WriteTimeout: c.Kafka.WriteTimeout.AsDuration(),</span><br><span class="line">ReadTimeout: c.Kafka.ReadTimeout.AsDuration(),</span><br><span class="line">}</span><br><span class="line">reader := kafka.NewReader(kafka.ReaderConfig{</span><br><span class="line">Brokers: []<span class="type">string</span>{c.Kafka.Addr},</span><br><span class="line">Topic: c.Kafka.Topic,</span><br><span class="line">MaxBytes: <span class="number">10e6</span>, <span class="comment">// 10MB</span></span><br><span class="line">})</span><br><span class="line"><span class="keyword">return</span> &KafkaConn{</span><br><span class="line">writer: &writer,</span><br><span class="line">reader: reader,</span><br><span class="line">}</span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">// 在wire中添加New方法,使用kratos自带的依赖注入工具进行处理</span></span><br><span class="line"><span class="keyword">var</span> ProviderSet = wire.NewSet(NewData, NewKafkaConn)</span><br></pre></td></tr></table></figure></div><blockquote><p>kratos集成了wire依赖注入工具,直接生成代码确实好用</p><p>在服务入口文件中使用 <code>wire</code> 完成操作</p></blockquote><p>接下来,你就可以在外部文件中引入KafkaConn进行生产与消费</p><blockquote><p>wait… 还有一小步,初始化开启一个goroutine进行消费者循环</p></blockquote><h3 id="初始化消费者循环读取及生产者"><a href="#初始化消费者循环读取及生产者" class="headerlink" title="初始化消费者循环读取及生产者"></a>初始化消费者循环读取及生产者</h3><ul><li><p>消费者</p><div class="highlight-container" data-rel="Go"><figure class="iseeu highlight go"><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"><span class="comment">// InitStoreMessageQueue 初始化聊天记录存储队列</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *messageRepo)</span></span> InitStoreMessageQueue() {</span><br><span class="line"> ctx := context.Background()</span><br><span class="line"> <span class="keyword">for</span> {</span><br><span class="line"> msg, err := r.data.kfk.reader.ReadMessage(ctx)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> r.log.Errorf(<span class="string">"read message error, err: %v"</span>, err)</span><br><span class="line"> }</span><br><span class="line"> value := msg.Value</span><br><span class="line"> <span class="keyword">var</span> mg *Message</span><br><span class="line"> err = json.Unmarshal(value, mg)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> r.log.Errorf(<span class="string">"json unmarshal error, err: %v"</span>, err)</span><br><span class="line"> }</span><br><span class="line"> err = r.InsertMessage(ctx, mg.FromUserId, mg.ToUserId, mg.Content)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> r.log.Errorf(<span class="string">"insert message error, err: %v"</span>, err)</span><br><span class="line"> }</span><br><span class="line"> r.log.Infof(<span class="string">"message: UserId-%v to UserId-%v: %v "</span>, mg.FromUserId, mg.ToUserId, mg.Content)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在biz层使用接口,并创建</p><div class="highlight-container" data-rel="Go"><figure class="iseeu highlight go"><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"><span class="function"><span class="keyword">func</span> <span class="title">NewMessageUsecase</span><span class="params">(</span></span></span><br><span class="line"><span class="params"><span class="function"> repo MessageRepo, logger log.Logger)</span></span> *MessageUsecase {</span><br><span class="line"><span class="keyword">go</span> repo.InitStoreMessageQueue()</span><br><span class="line"><span class="keyword">return</span> &MessageUsecase{</span><br><span class="line">repo: repo,</span><br><span class="line">log: log.NewHelper(log.With(logger)),</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li><li><p>生产者</p><div class="highlight-container" data-rel="Go"><figure class="iseeu highlight go"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// PublishMessage 生产者发送消息</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r *messageRepo)</span></span> PublishMessage(ctx context.Context, userId, toUSerId <span class="type">uint32</span>, content <span class="type">string</span>) <span class="type">error</span> {</span><br><span class="line">mg := &Message{</span><br><span class="line">FromUserId: userId,</span><br><span class="line">ToUserId: toUSerId,</span><br><span class="line">Content: content,</span><br><span class="line">CreatedAt: time.Now().UnixMilli(),</span><br><span class="line">}</span><br><span class="line">byteValue, err := json.Marshal(mg)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"><span class="keyword">return</span> fmt.Errorf(<span class="string">"json marshal error, err: %w"</span>, err)</span><br><span class="line">}</span><br><span class="line">err = r.data.kfk.writer.WriteMessages(ctx, kafka.Message{</span><br><span class="line">Value: byteValue,</span><br><span class="line">})</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"><span class="keyword">return</span> fmt.Errorf(<span class="string">"write message error, err: %w"</span>, err)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="literal">nil</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li></ul><p>到此为止,我们就建立了一个可以持续接收消息,并存储到数据库的kafka消息队列啦</p>]]></content>
<summary type="html"><blockquote>
<p>今天开始准备制作聊天功能,希望使用kafka进行消息的读写操作,保障消息的可靠性,以及对mysql的持久化要求</p>
</blockquote>
<h2 id="Kafka-基础概念"><a href="#Kafka-基础概念" class="h</summary>
<category term="学习" scheme="https://intyouss.github.io/tags/%E5%AD%A6%E4%B9%A0/"/>
</entry>
<entry>
<title>Mysql添加新用户并赋权</title>
<link href="https://intyouss.github.io/2023/05/17/Mysql%E6%B7%BB%E5%8A%A0%E6%96%B0%E7%94%A8%E6%88%B7%E5%B9%B6%E8%B5%8B%E6%9D%83/"/>
<id>https://intyouss.github.io/2023/05/17/Mysql%E6%B7%BB%E5%8A%A0%E6%96%B0%E7%94%A8%E6%88%B7%E5%B9%B6%E8%B5%8B%E6%9D%83/</id>
<published>2023-05-17T13:49:32.000Z</published>
<updated>2023-05-17T14:06:31.307Z</updated>
<content type="html"><![CDATA[<ol><li><p>Mysql下创建新的用户并分配权限(应先转换到mysql数据库use mysql)</p><blockquote><p>语法:create user ‘用户名‘@’主机名’ identified by ‘密码’;</p></blockquote></li><li><p>给用户分配权限</p><blockquote><p>语法:grant 权限 on 数据库.数据表 to ‘用户’ @ ‘主机名’;</p></blockquote></li></ol>]]></content>
<summary type="html"><ol>
<li><p>Mysql下创建新的用户并分配权限(应先转换到mysql数据库use mysql)</p>
<blockquote>
<p>语法:create user ‘用户名‘@’主机名’ identified by ‘密码’;</p>
</blockquote>
<</summary>
<category term="数据库" scheme="https://intyouss.github.io/tags/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
</entry>
<entry>
<title>Python</title>
<link href="https://intyouss.github.io/2023/04/28/Python/"/>
<id>https://intyouss.github.io/2023/04/28/Python/</id>
<published>2023-04-28T13:49:29.000Z</published>
<updated>2023-04-29T05:03:50.243Z</updated>
<content type="html"><![CDATA[<h2 id="列举布尔值为-False-的常见值"><a href="#列举布尔值为-False-的常见值" class="headerlink" title="列举布尔值为 False 的常见值"></a>列举布尔值为 False 的常见值</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">0</span>,[],(),{},<span class="string">''</span>,<span class="literal">False</span>,<span class="literal">None</span></span><br></pre></td></tr></table></figure></div><h2 id="is-和-x3D-x3D-的区别"><a href="#is-和-x3D-x3D-的区别" class="headerlink" title="is 和 == 的区别"></a>is 和 == 的区别</h2><blockquote><p>is 比较的是两个对象的内存地址,== 比较的是两个对象的值</p></blockquote><h2 id="现有字典-dict-请按字典中的-value-值进行排序"><a href="#现有字典-dict-请按字典中的-value-值进行排序" class="headerlink" title="现有字典 dict 请按字典中的 value 值进行排序"></a>现有字典 dict 请按字典中的 value 值进行排序</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sorted</span>(<span class="built_in">dict</span>.items(), key=<span class="keyword">lambda</span> x:x[<span class="number">1</span>])</span><br></pre></td></tr></table></figure></div><h2 id="请反转字符串-aStr"><a href="#请反转字符串-aStr" class="headerlink" title="请反转字符串 aStr"></a>请反转字符串 aStr</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">print</span>(<span class="string">'aStr'</span>[::-<span class="number">1</span>])</span><br></pre></td></tr></table></figure></div><h2 id="请按-list1-中元素的-age-由大到小排序"><a href="#请按-list1-中元素的-age-由大到小排序" class="headerlink" title="请按 list1 中元素的 age 由大到小排序"></a>请按 list1 中元素的 age 由大到小排序</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">list1 = [{<span class="string">'name'</span>: <span class="string">'a'</span>, <span class="string">'age'</span>: <span class="number">20</span>}, {<span class="string">'name'</span>: <span class="string">'b'</span>, <span class="string">'age'</span>: <span class="number">13</span>}]</span><br><span class="line"><span class="built_in">sorted</span>(list1, key=<span class="keyword">lambda</span> x:x[<span class="string">'age'</span>], reverse=<span class="literal">True</span>)</span><br></pre></td></tr></table></figure></div><h2 id="常用的字符串格式化哪几种"><a href="#常用的字符串格式化哪几种" class="headerlink" title="常用的字符串格式化哪几种"></a>常用的字符串格式化哪几种</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">s1 = <span class="string">"%s dadada"</span>%name <span class="comment"># 占位符</span></span><br><span class="line">s2 = <span class="string">"{} dadada"</span>.<span class="built_in">format</span>(name) <span class="comment"># format</span></span><br><span class="line">s3 = <span class="string">"{name} dadada"</span> <span class="comment"># f-string</span></span><br></pre></td></tr></table></figure></div><h2 id="下面代码的输出结果将是什么"><a href="#下面代码的输出结果将是什么" class="headerlink" title="下面代码的输出结果将是什么"></a>下面代码的输出结果将是什么</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="built_in">list</span> = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>]</span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">list</span>[<span class="number">10</span>:])</span><br><span class="line"></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">上面的代码将输出[],不会产生 IndexError 错误。如果尝试用超出成员数量的 index 来获</span></span><br><span class="line"><span class="string">取某个列表的成员。例如,尝试获取 list[10] 和之后的成员,会导致 IndexError</span></span><br><span class="line"><span class="string">'''</span></span><br></pre></td></tr></table></figure></div><h2 id="写一个列表生成式,产生一个公差为11的等差数列"><a href="#写一个列表生成式,产生一个公差为11的等差数列" class="headerlink" title="写一个列表生成式,产生一个公差为11的等差数列"></a>写一个列表生成式,产生一个公差为11的等差数列</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[x*<span class="number">11</span> <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]</span><br></pre></td></tr></table></figure></div><h2 id="给定两个列表,怎么找出他们相同的元素和不同的元素"><a href="#给定两个列表,怎么找出他们相同的元素和不同的元素" class="headerlink" title="给定两个列表,怎么找出他们相同的元素和不同的元素"></a>给定两个列表,怎么找出他们相同的元素和不同的元素</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">list1 = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]</span><br><span class="line">list2 = [<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]</span><br><span class="line">set1 = <span class="built_in">set</span>(list1)</span><br><span class="line">set2 = <span class="built_in">set</span>(list2)</span><br><span class="line"><span class="built_in">print</span>(set1&set2)</span><br><span class="line"><span class="built_in">print</span>(set1^set2)</span><br></pre></td></tr></table></figure></div><h2 id="请写出一段-Python-代码实现删除一个-list-里面的重复元素"><a href="#请写出一段-Python-代码实现删除一个-list-里面的重复元素" class="headerlink" title="请写出一段 Python 代码实现删除一个 list 里面的重复元素"></a>请写出一段 Python 代码实现删除一个 list 里面的重复元素</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">li = [<span class="string">'b'</span>, <span class="string">'a'</span>, <span class="string">'f'</span>, <span class="string">'f'</span>, <span class="string">'k'</span>, <span class="string">'b'</span>]</span><br><span class="line">li1 = <span class="built_in">list</span>(<span class="built_in">set</span>(li))</span><br></pre></td></tr></table></figure></div><h2 id="列举字符串,列表,元组,字典每个常用的五种方法"><a href="#列举字符串,列表,元组,字典每个常用的五种方法" class="headerlink" title="列举字符串,列表,元组,字典每个常用的五种方法"></a>列举字符串,列表,元组,字典每个常用的五种方法</h2><blockquote><p>字符串:replace,strip,split,reverse,upper,lower,join …..</p><p>列表:append,remove,pop,insert,index,sort,count …..</p><p>元组:index,count,len(),dir()</p><p>字典:get,key,values,pop,popitems,clear,update,items …..</p></blockquote><h2 id="什么是反射?以及应用场景"><a href="#什么是反射?以及应用场景" class="headerlink" title="什么是反射?以及应用场景"></a>什么是反射?以及应用场景</h2><blockquote><p>反射是指在程序运行时动态的获取一个对象的信息以及操作对象的能力。在python中,反射可以通过内置函数 getattr(),hasattr(),setattr() 和 delattr() 来实现。这些函数可以让我们在运行时获取对象的属性和方法,或者动态的设置对象的属性和方法。它使得我们可以更加灵活的编写代码,动态的创建和操作对象,提高了代码的可扩展性和可维护性</p></blockquote><h2 id="简述-Python-的深浅拷贝"><a href="#简述-Python-的深浅拷贝" class="headerlink" title="简述 Python 的深浅拷贝"></a>简述 Python 的深浅拷贝</h2><blockquote><p>copy():浅拷贝,指创建一个新的对象,但是这个新对象只是原始对象的一个副本,其中包含了原始对象中的所有元素的引用。这意味着,如果你修改了原始对象的一个元素,那么这个元素也会被修改,因为它们共享同一个引用</p><p>deepcopy():深拷贝,指创建一个新的对象,这个新对象包含了原始对象中所有元素的副本。这意味着,如果你修改了原始对象中的元素,新对象并不会被修改,因为它们不再共享一个引用</p></blockquote><h2 id="Python-垃圾回收机制"><a href="#Python-垃圾回收机制" class="headerlink" title="Python 垃圾回收机制"></a>Python 垃圾回收机制</h2><blockquote><ol><li><p>引用计数</p><ul><li>每个对象都有一个引用计数器,当对象被引用时,计数器+1,当对象不再被引用时,计数器-1。当计数器为0时,对象被认为是垃圾,将自动回收这些垃圾对象的内存</li></ul><p>优点:简单直观,实时</p><p>缺点:维护引用计数需要消耗一定的资源。循环引用时,无法回收。</p></li><li><p>标记-清除</p><ul><li>不改动真实的引用计数,而是将集合中对象的引用计数复制一份副本,改动该对象引用的副本。对于副本做的任何改动都不会影响到对象生命周期的维护</li></ul></li><li><p>分代回收</p><ul><li>将系统中的所有内存块根据其存活时间划分为不同的集合。没一个集合就成为一个’代’,垃圾收集的频率随着’代’的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。</li></ul></li></ol></blockquote><h2 id="如何打乱一个排好序的-list-对象-alist"><a href="#如何打乱一个排好序的-list-对象-alist" class="headerlink" title="如何打乱一个排好序的 list 对象 alist"></a>如何打乱一个排好序的 list 对象 alist</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">import</span> random</span><br><span class="line">random.shuffle(alist)</span><br></pre></td></tr></table></figure></div><h2 id="从-0-99-这-100-个数中随机取出-10-个,要求不能重复,可以自己设计数据结构"><a href="#从-0-99-这-100-个数中随机取出-10-个,要求不能重复,可以自己设计数据结构" class="headerlink" title="从 0-99 这 100 个数中随机取出 10 个,要求不能重复,可以自己设计数据结构"></a>从 0-99 这 100 个数中随机取出 10 个,要求不能重复,可以自己设计数据结构</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">print</span>([x*x <span class="keyword">for</span> x <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">1</span>,<span class="number">11</span>)])</span><br></pre></td></tr></table></figure></div><h2 id="介绍一下-try-except-的用法和作用"><a href="#介绍一下-try-except-的用法和作用" class="headerlink" title="介绍一下 try except 的用法和作用"></a>介绍一下 try except 的用法和作用</h2><blockquote><p>try except 是 python 中的异常处理机制,用于在程序运行时捕获并处理异常。当程序运行时遇到错误或异常时,如果没有进行异常处理,程序将会崩溃并抛出错误信息。使用 try except 可以使程序在遇到异常时不会崩溃,而是执行指定的异常处理代码</p></blockquote><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">try</span>:</span><br><span class="line"> <span class="comment"># 可能会出现异常的代码块</span></span><br><span class="line"><span class="keyword">except</span> ExceptionType:</span><br><span class="line"> <span class="comment"># 异常处理代码块</span></span><br><span class="line"><span class="keyword">finally</span>:</span><br><span class="line"> <span class="comment"># 如果没有异常,执行的代码块</span></span><br></pre></td></tr></table></figure></div><h2 id="在-python-中如何抛出,捕获,处理异常"><a href="#在-python-中如何抛出,捕获,处理异常" class="headerlink" title="在 python 中如何抛出,捕获,处理异常"></a>在 python 中如何抛出,捕获,处理异常</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">raise</span> Exception <span class="comment"># 抛出异常</span></span><br><span class="line"><span class="keyword">try</span> ...<span class="keyword">except</span> <span class="comment"># 捕获异常</span></span><br><span class="line"><span class="keyword">try</span> ... <span class="keyword">except</span> ... <span class="keyword">else</span> ... <span class="keyword">finally</span> ... <span class="keyword">with</span> <span class="comment"># 处理异常</span></span><br></pre></td></tr></table></figure></div><h2 id="python-递归的最大层数"><a href="#python-递归的最大层数" class="headerlink" title="python 递归的最大层数"></a>python 递归的最大层数</h2><blockquote><p>最大层数为 998</p></blockquote><h2 id="列表推导式和生成器表达式的区别"><a href="#列表推导式和生成器表达式的区别" class="headerlink" title="列表推导式和生成器表达式的区别"></a>列表推导式和生成器表达式的区别</h2><blockquote><p>列表推导式和生成器表达式都可以用来生成一个新的列表,但是他们的输出结果不同。</p><p>列表推导式会立即生成一个新的列表,而生成器表达式则是在需要时才生成一个新的元素。这意味着生成器表达式可以更高效的处理大量数据,因为它不需要一次性生成整个列表。</p></blockquote><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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 class="built_in">print</span>([i % <span class="number">2</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>)]) <span class="comment"># 列表推导式直接输出</span></span><br><span class="line">a = (i % <span class="number">2</span> <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">10</span>))</span><br><span class="line">b = a.__next__()</span><br><span class="line">c = a.__next__()</span><br><span class="line"><span class="built_in">print</span>(b, c) <span class="comment"># 生成器推导式,需要时一个一个输出</span></span><br></pre></td></tr></table></figure></div><h2 id="什么是闭包"><a href="#什么是闭包" class="headerlink" title="什么是闭包"></a>什么是闭包</h2><blockquote><p>闭包是指一个函数可以访问并操作其外部函数作用域中的变量,即使该外部函数已经返回。这是通过在内部函数中创建对外部函数作用域中变量的引用来实现的。这种引用使得外部函数的变量在内部函数中仍然可用,即使外部函数已经完成执行并且其作用域已经销毁。</p><p>在 python 中,闭包通常用于创建函数工厂,即返回另一个函数的函数。这种函数可以访问其外部函数的变量,并且每次调用该函数时,它都会创建一个新的闭包,其中包含对外部函数作用域的新引用</p></blockquote><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="comment"># 函数内参数不会因为外部变量值发生改变而改变</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">outer_function</span>():</span><br><span class="line"> num = <span class="number">1</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">inner_function</span>():</span><br><span class="line"> <span class="built_in">print</span>(num)</span><br><span class="line"> <span class="keyword">return</span> inner_function</span><br><span class="line"></span><br><span class="line">closure = outer_function()</span><br><span class="line">num = <span class="number">1000</span> <span class="comment"># 不会改变内部 num</span></span><br><span class="line"><span class="built_in">print</span>(closure()) <span class="comment"># Output: 1</span></span><br></pre></td></tr></table></figure></div><h2 id="请实现一个装饰器,限制该函数被调用的频率,如-10-秒一次"><a href="#请实现一个装饰器,限制该函数被调用的频率,如-10-秒一次" class="headerlink" title="请实现一个装饰器,限制该函数被调用的频率,如 10 秒一次"></a>请实现一个装饰器,限制该函数被调用的频率,如 10 秒一次</h2><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">import</span> time</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">time_pay</span>(<span class="params">func</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">inner</span>():</span><br><span class="line"> time.sleep(<span class="number">10</span>)</span><br><span class="line"> <span class="keyword">return</span> func()</span><br><span class="line"> <span class="keyword">return</span> inner</span><br><span class="line"></span><br><span class="line"><span class="meta">@time_pay</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func1</span>():</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'aaa'</span>)</span><br><span class="line"></span><br><span class="line">func1()</span><br></pre></td></tr></table></figure></div><h2 id="python-函数调用的时候参数的传递方式是值传递还是引用传递"><a href="#python-函数调用的时候参数的传递方式是值传递还是引用传递" class="headerlink" title="python 函数调用的时候参数的传递方式是值传递还是引用传递"></a>python 函数调用的时候参数的传递方式是值传递还是引用传递</h2><blockquote><p>python 的参数传递有:位置参数,可变参数,关键字参数</p><p>函数的传值是值传递还是引用传递,要分情况</p><ol><li><p>不可变参数用值传递</p><ul><li>像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因此在函数内部修改这个值并不会影响到原来的值</li></ul></li><li><p>可变参数用引用传递</p><ul><li>可变参数在传递至只是传递了一个指向元对象的引用,因此在函数内部修改这个对象会影响到原来的值</li></ul></li></ol></blockquote><h2 id="递归函数停止的条件"><a href="#递归函数停止的条件" class="headerlink" title="递归函数停止的条件"></a>递归函数停止的条件</h2><blockquote><p>一般是定义在递归函数内部,在递归调用前要做一个条件判断,根据条件判断的结果选择是否继续调用自身,还是终止</p></blockquote><h2 id="MRO-是什么"><a href="#MRO-是什么" class="headerlink" title="MRO 是什么"></a>MRO 是什么</h2><blockquote><p>MRO 是 python 中的方法解析顺序,它决定了在多重继承中的方法调用顺序。python 使用 C3 线性化算法来计算 MRO</p></blockquote><h2 id="C3-算法是什么"><a href="#C3-算法是什么" class="headerlink" title="C3 算法是什么"></a>C3 算法是什么</h2><blockquote><p>C3 算法是为了解决原来基于深度优先搜索算法不满足本地优先级,和单调性问题。它是一种启发式搜索算法,可以用于解决许多优化问题,如路劲规划和调度问题。C3 算法的核心是将搜索空间分解为多个子空间,并在每个子空间中使用深度优先搜索算法。在搜索过程中,C3 算法会根据当前搜索状态的本地优先级和单调性来选择下一个搜索状态,以尽可能快的找到最优解</p><p>本地优先级:指声明时父类的顺序,比如 C(A, B),如果访问 C 类对象属性时,应该根据声明顺序,优先查找 A 类,然后再查找 B 类</p><p>单调性:如果在 C 的解析顺序中,A 排在 B 的前面,那么在 C 的所有子类里,也必须满足这个顺序</p></blockquote>]]></content>
<summary type="html"><h2 id="列举布尔值为-False-的常见值"><a href="#列举布尔值为-False-的常见值" class="headerlink" title="列举布尔值为 False 的常见值"></a>列举布尔值为 False 的常见值</h2><div class="h</summary>
<category term="面试" scheme="https://intyouss.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>软件设计师</title>
<link href="https://intyouss.github.io/2023/04/21/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%B8%88/"/>
<id>https://intyouss.github.io/2023/04/21/%E8%BD%AF%E4%BB%B6%E8%AE%BE%E8%AE%A1%E5%B8%88/</id>
<published>2023-04-21T08:35:10.000Z</published>
<updated>2023-04-27T14:16:30.111Z</updated>
<content type="html"><![CDATA[<h2 id="下午题"><a href="#下午题" class="headerlink" title="下午题"></a>下午题</h2><hr><h3 id="数据流图"><a href="#数据流图" class="headerlink" title="数据流图"></a>数据流图</h3><blockquote><p>也称数据流程图(DFD),它是一种便于用户理解,分析系统数据流程的图形工具。他摆脱了系统的物理内容,精确的在逻辑上描述系统的功能,输入,输出和数据存储等,是系统逻辑模型的重要组成部分</p></blockquote><ol><li><p>数据流图的基本图形元素</p><blockquote><p>数据流图中的基本图形元素包括数据流,加工,数据存储和外部实体。</p><p>其中,数据流,加工和数据存储用于构建软件系统内部的数据处理模型;外部实体表示存在于系统之外的对象,用来帮助用户理解系统数据的来源和去向</p></blockquote><ul><li><p>DFD 的基本图形元素</p><p><img lazyload src="/images/loading.svg" data-src="/images/DFD.png"></p><ul><li><p>外部实体内一般标记为 E</p></li><li><p>数据存储内一般标记为 D</p></li><li><p>加工内一般标记为 P</p></li></ul></li></ul></li><li><p>元素含义</p><ul><li><p>外部实体:当前系统之外的人,物,外部系统</p></li><li><p>数据存储:存储加工的输出数据和提供加工的输入数据</p></li><li><p>加工:将输入数据处理后得到输出数据,一个加工至少有一个输入数据流和一个输出数据流</p><ul><li><p>加工只有输入没有输出称为:黑洞</p></li><li><p>加工只有输出没有输入称为:白洞</p></li><li><p>加工的输入数据不足以产生输出数据:灰洞</p></li></ul></li><li><p>数据流:数据流有一组固定成分的数据组成,表示数据的流向,<strong>数据流的起点或终点必须有一个是加工</strong> ,在 DFD 中,数据流的流向可分为几种</p><ul><li><p>从一个加工流向另一个加工</p></li><li><p>从加工流向数据存储(写)</p></li><li><p>从数据存储流向加工(读)</p></li><li><p>从外部实体流向加工(输入)</p></li><li><p>从加工流向外部实体(输出)</p></li></ul></li></ul></li></ol><h4 id="题目答案规范"><a href="#题目答案规范" class="headerlink" title="题目答案规范"></a>题目答案规范</h4><h5 id="问题一"><a href="#问题一" class="headerlink" title="问题一"></a>问题一</h5><h6 id="答题格式"><a href="#答题格式" class="headerlink" title="答题格式"></a>答题格式</h6><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E6%B5%81%E9%97%AE%E9%A2%981.png"></p><h5 id="问题二"><a href="#问题二" class="headerlink" title="问题二"></a>问题二</h5><blockquote><p>特殊一:如果说明文章内没有详细说明存储于某文件或某表,则可 __根据数据流上文字__,直接在其后添加 <strong>文件</strong> 或 <strong>表</strong> 即可</p><p>特殊二:如果与数据存储相关联的数据流上没有文字,可在说明文章中获取文件霍或表名</p><p>其他:与问题一大同小异</p></blockquote><h6 id="答题格式-1"><a href="#答题格式-1" class="headerlink" title="答题格式"></a>答题格式</h6><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E6%B5%81%E9%97%AE%E9%A2%982.png"></p><h5 id="问题三"><a href="#问题三" class="headerlink" title="问题三"></a>问题三</h5><blockquote><p>方法(一起使用)</p><ol><li><p>父图子图平衡(不一定100%找到缺失数据流):父图中有的数据流,必须在子图中存在。若不存在,即找到一条缺失数据流</p><ul><li>在数据流上文字中,<strong>符号 /</strong> 的含义是指分别有两条数据流输入或输出(重点关注)</li></ul></li><li><p>加工必须有输入输出(不一定100%找到所有缺失数据流):查找图中加工是否都有 __输入输出__,若无其中任意一种,则为缺失</p></li><li><p>数据守恒(阅读理解,不一定100%找到所有缺失数据流):将说明文章与图对比,看是否所有功能都在图中体现,若没有体现则缺失</p></li></ol></blockquote><h6 id="答题格式-2"><a href="#答题格式-2" class="headerlink" title="答题格式"></a>答题格式</h6><blockquote><p>起点与终点若都有字母或者汉字,请使用相同的字型,如(起点:D3,终点:3)。若两者题中只给出不同字型,就按实际书写</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E6%B5%81%E9%97%AE%E9%A2%983.png"></p><h5 id="问题四"><a href="#问题四" class="headerlink" title="问题四"></a>问题四</h5><blockquote><p>推测考两部分</p><ul><li><p>结构化语言</p></li><li><p>根据说明,写出数据流组成</p></li></ul></blockquote><h6 id="结构化语言类"><a href="#结构化语言类" class="headerlink" title="结构化语言类"></a>结构化语言类</h6><blockquote><p>结构化语言是一种介于自然语言和形式化语言之间的半形式化语言,是自然语言的一个受限子集</p><p>结构化语言没有严格的语法,他的结构通常可以分为内层和外层。外层有严格的语法,内层的语法比较灵活,可以接近于自然语言的描述</p><ol><li><p>外层:用来描述控制结构,采用顺序,选择和重复3种基本结构</p><ul><li><p>顺序结构:一组祈使语句,选择语句,重复语句的顺序排列。祈使语句是指至少包含一个动词即一个名词。指出要执行的动作及接受动作的对象</p></li><li><p><strong>选择结构:一般用 IF-THEN-ELSE-ENDIF,CASE-OF-ENDCASE等关键词(重点)</strong></p></li><li><p>重复结构:一般用 DO-WHILE-ENDDO,REPEAT-UNTIL等关键词</p></li></ul></li><li><p>内层:一般采用祈使语句的自然语言短语,使用数字字典中的名词和有限的自定义词,其动词含义要具体,尽量不用形容词来修饰,还可以使用一些简单的算法运算和逻辑运算符号</p></li></ol></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E6%B5%81%E9%97%AE%E9%A2%984%E7%BB%93%E6%9E%84%E5%8C%96%E8%AF%AD%E8%A8%80%E7%B1%BB.png"></p><h6 id="组成类"><a href="#组成类" class="headerlink" title="组成类"></a>组成类</h6><blockquote><p>数据流由具体的数据属性构成,采用 <strong>符号 +</strong> 表示,__=__ 表示组成(被定义为),__:__ 表示有多个属性(与),__{}__ 表示其中属性出现多次,__()__ 表示其中属性可选等。</p><p>例:一个学生至少有一个家长,可以有多个家长,即 <strong>1{家长 ID}*</strong> </p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E6%B5%81%E9%97%AE%E9%A2%984%E7%BB%84%E6%88%90%E7%B1%BB.png"></p><h3 id="数据库设计"><a href="#数据库设计" class="headerlink" title="数据库设计"></a>数据库设计</h3><hr><h4 id="E-R-图"><a href="#E-R-图" class="headerlink" title="E-R 图"></a>E-R 图</h4><blockquote><p>椭圆:属性,内有属性名</p><p>矩形:实体,内有实体名</p><p>棱形:联系,内有联系字段,实体和实体之间的联系</p></blockquote><ol><li><p>实体</p><ul><li><p>弱实体</p><blockquote><p>在现实世界中有一种特殊的联系,这种联系代表实体间的所有关系,例如职工与家属的联系,家属总是属于某职工的。这种实体对于另一些实体具有很强的依赖关系,即一个实体的存在必须以另一个实体为前提,将这类实体称为弱实体</p><p>通常用 <strong>双线矩形</strong> 表示弱实体</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E5%BC%B1%E5%AE%9E%E4%BD%93%E4%B8%8E%E4%BE%9D%E8%B5%96%E8%81%94%E7%B3%BB.png"></p></li><li><p>子实体</p><blockquote><p>父类和子类之间具有继承关系</p><p>形状和橡皮擦一样,并且 <strong>连接线上带有一个圆圈</strong> (如果没有圆圈,就看是否像橡皮擦)</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E7%88%B6%E7%B1%BB%E5%92%8C%E5%AD%90%E7%B1%BB%E7%9A%84%E5%85%B3%E7%B3%BB.png"></p></li></ul></li><li><p>属性</p><ul><li><p>简单属性和复合属性</p><blockquote><p>简单属性是原子的,不可再分的;复合属性可以细分为更小的部分</p></blockquote></li></ul></li><li><p>联系</p><ul><li><p>两个不同实体之间的联系</p><ol><li><p>一对一(1:1)</p></li><li><p>一对多(1:n)</p></li><li><p>多对多(m:n)</p></li></ol></li><li><p>两个以上不同实体集之间的联系</p><ol><li><p>1:1:1</p></li><li><p>1:1:n</p></li><li><p>1:m:n</p></li><li><p>r:m:n</p></li></ol></li></ul></li></ol><h4 id="题目答案规范-1"><a href="#题目答案规范-1" class="headerlink" title="题目答案规范"></a>题目答案规范</h4><h5 id="问题一-1"><a href="#问题一-1" class="headerlink" title="问题一"></a>问题一</h5><blockquote><p>根据需求分析结果 答题</p></blockquote><ol><li><p>第一种</p><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1%E9%97%AE%E9%A2%981%E7%AC%AC%E4%B8%80%E7%A7%8D.png"></p></li><li><p>第二种</p><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1%E9%97%AE%E9%A2%981%E7%AC%AC%E4%BA%8C%E7%A7%8D.png"></p></li><li><p>第三种</p><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1%E9%97%AE%E9%A2%981%E7%AC%AC%E4%B8%89%E7%A7%8D.png"></p></li></ol><h5 id="问题二-1"><a href="#问题二-1" class="headerlink" title="问题二"></a>问题二</h5><blockquote><p>先根据 <strong>需求分析结果</strong> 对比查看是否缺少属性,再根据 <strong>概念模型设计</strong> 查看一对多等关系中是否有关系连接此实体(将1方的主码加到多方的属性中)</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1%E9%97%AE%E9%A2%982.png"></p><h3 id="UML"><a href="#UML" class="headerlink" title="UML"></a>UML</h3><hr><h4 id="关系"><a href="#关系" class="headerlink" title="关系"></a>关系</h4><ol><li><p>依赖关系</p><blockquote><p>依赖是两个事物间的语义关系,其中一个事物(独立事物)发生变化会影响另一个事物(依赖事物)的语义。在图形上,把一个依赖画出一条 __可能有方向的虚线__,代表 <strong>左事物依赖于右事物</strong></p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E4%BE%9D%E8%B5%96%E5%85%B3%E7%B3%BB.png"></p></li><li><p>关联关系(重点)</p><blockquote><p>可以分成两种关系:<strong>聚合</strong> 和 <strong>组合</strong> </p><p>关联关系是一种结构关系,他描述了一组链,链是对象之间的连接。</p><p><strong>聚合</strong> 是一种特殊类型的关联,他描述了整体与部分间的结构关系。整体消失了,部分仍然存在</p><p><strong>组合</strong> 是整体消失了,部分也会消失</p><p>在关联上可以标注 <strong>重复度</strong> 和 <strong>角色</strong></p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E5%85%B3%E8%81%94%E5%85%B3%E7%B3%BB.png"></p><blockquote><p>组合的图形是在聚合的基础上,空心棱形变为实心棱形</p><p>关联上的字符是重复度:0..1 指 0个或者一个,0..* 指 0个或者多个</p></blockquote></li><li><p>泛化关系</p><blockquote><p>泛化是一种特殊/一般关系,特殊元素(子元素)的对象可替代一般元素(父元素)d的对象。用这种方法,子元素共享了父元素的结构和行为。在图形上,把一个泛化关系画成一条带有空心箭头的实线,它指向父元素</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E6%B3%9B%E5%8C%96%E5%85%B3%E7%B3%BB.png"></p></li><li><p>实现关系</p><blockquote><p>实现是类元之间的语义关系,其中一个类元指定了由另一个类元保证执行的契约。在图形上,把一个实现关系画成一条带有空心箭头的虚线</p><p>在两种情况下会使用实现关系:</p><ol><li><p>在接口和实现它们的类或构件之间</p></li><li><p>在用例和实现它们的协作之间</p></li></ol></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E5%AE%9E%E7%8E%B0%E5%85%B3%E7%B3%BB.png"></p></li></ol><h4 id="UML-中的图"><a href="#UML-中的图" class="headerlink" title="UML 中的图"></a>UML 中的图</h4><blockquote><p>顶点代表事物,弧代表关系</p></blockquote><ol><li><p>类图</p><blockquote><p>类图展现了一组对象,接口,协作和他们之间的关系。在面向对象系统的建模中所建立的最常见的图就是类图。类图给出系统的静态设计视图。包含主动类的类图给出了系统的静态进程视图</p><p>+:public 公有的</p><p>-:private 私有的</p><p>#:protected 受保护的</p><p>~:package 包</p></blockquote><p>类图中通常包括下述内容:</p><ul><li><p>类</p></li><li><p>接口</p></li><li><p>协作</p></li><li><p>依赖,泛化和关联关系</p></li></ul><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E7%B1%BB%E5%9B%BE%E4%B8%AD%E7%9A%84%E7%B1%BB.png"></p><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E7%B1%BB%E5%9B%BE.png"></p></li><li><p>用例图</p><blockquote><p>用例图展现了一组用例,参与者以及它们之间的关系</p></blockquote><p>用例图通常包括下述内容</p><ul><li><p>用例:椭圆</p></li><li><p>参与者:人形</p></li><li><p>用例之间的扩展关系(<< extend >>) 和 包含关系(<< include >>),参与者和用例之间的关联关系,用例与用例以及参与者与参与者之间的泛式关系</p></li></ul><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E7%94%A8%E4%BE%8B%E5%9B%BE.png"></p><ol><li><p>包含关系</p><blockquote><p>一个用例包含另一个用例,使用 <strong>虚线加箭头,并在上有<< include >> 字段</strong> ,虚线起点包含终点</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E7%94%A8%E4%BE%8B%E5%9B%BE%E5%8C%85%E5%90%AB%E5%85%B3%E7%B3%BB.png"></p></li><li><p>扩展关系</p><blockquote><p>一个用例执行的时候,可能会发生一些特殊的情况或可选的情况,这种情况就是这个用例的扩展用例。使用 <strong>虚线加箭头,并在上有<< extend >> 字段</strong> ,虚线起点是终点的扩展</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E7%94%A8%E4%BE%8B%E5%9B%BE%E6%89%A9%E5%B1%95%E5%85%B3%E7%B3%BB.png"></p></li><li><p>泛化关系</p><blockquote><p>使用 <strong>实线加空心箭头</strong> ,虚线起点是终点的子类</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E7%94%A8%E4%BE%8B%E5%9B%BE%E6%B3%9B%E5%8C%96%E5%85%B3%E7%B3%BB.png"></p></li></ol></li></ol><h4 id="题目答案规范-2"><a href="#题目答案规范-2" class="headerlink" title="题目答案规范"></a>题目答案规范</h4><h5 id="问题一-2"><a href="#问题一-2" class="headerlink" title="问题一"></a>问题一</h5><blockquote><p>查看说明,寻找组合答案</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E9%97%AE%E9%A2%981.png"></p><h5 id="问题二-2"><a href="#问题二-2" class="headerlink" title="问题二"></a>问题二</h5><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E9%97%AE%E9%A2%982.png"></p><h5 id="问题三-1"><a href="#问题三-1" class="headerlink" title="问题三"></a>问题三</h5><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E9%97%AE%E9%A2%983.png"></p><p><img lazyload src="/images/loading.svg" data-src="/images/UML%E9%97%AE%E9%A2%98%E4%B8%892.png"></p><h3 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h3><hr><blockquote><p>此问题需注意强制类型转换,例:Resume a = (Resume) c.Clone()</p></blockquote><h4 id="简单工厂模式"><a href="#简单工厂模式" class="headerlink" title="简单工厂模式"></a>简单工厂模式</h4><blockquote><p>简单工厂模式属于创建型模式,但不属于23种设计模式之一</p><p>定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类</p><p>在简单工厂模式中用于被创建实例的方法通常为静态方法,因此简单工厂模式又被成为静态工厂方法</p></blockquote><p>三类角色</p><ul><li><p>工厂(核心):负责实现创建所有产品的内部逻辑,工厂类可以被外界直接调用,创建所需对象</p></li><li><p>抽象产品:工作类所创建的所有对象的父类,封装了产品对象的公共方法,所有的具体产品为其子类对象</p></li><li><p>具体产品:简单工厂模式的创建目标,所有被创建的对象都是某个具体类的实例。它要实现抽象产品中声明的抽象方法</p></li></ul><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%AE%80%E5%8D%95%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F.png"></p><h4 id="工厂方法-Factory-Method"><a href="#工厂方法-Factory-Method" class="headerlink" title="工厂方法(Factory Method)"></a>工厂方法(Factory Method)</h4><blockquote><p>定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E5%B7%A5%E5%8E%82%E6%96%B9%E6%B3%95.png"></p><p>适用场景</p><ul><li><p>当一个类不知道它所必须创建的对象的类的时候</p></li><li><p>当一个类希望由它的子类来指定他所创建的对象的时候</p></li><li><p>当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候</p></li></ul><h4 id="抽象工厂"><a href="#抽象工厂" class="headerlink" title="抽象工厂"></a>抽象工厂</h4><blockquote><p>提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82.png"></p><p>适用场景</p><ul><li><p>一个系统要独立于它的产品的创建,组合和表示时</p></li><li><p>一个系统要由多个产品系列中的一个来匹配时</p></li><li><p>当要强调一系列相关的产品对象的设计以便进行联合使用时</p></li><li><p>当提供一个产品类库,只想显示它们的接口而不是实现时</p></li></ul><h4 id="生成器-Builder"><a href="#生成器-Builder" class="headerlink" title="生成器(Builder)"></a>生成器(Builder)</h4><blockquote><p>将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%94%9F%E6%88%90%E5%99%A8.png"></p><p>适用场景</p><ul><li><p>当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时</p></li><li><p>当构造过程必须允许被构造的对象有不同的表示时</p></li></ul><h4 id="原型-Prototype"><a href="#原型-Prototype" class="headerlink" title="原型(Prototype)"></a>原型(Prototype)</h4><blockquote><p>用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E5%8E%9F%E5%9E%8B.png"></p><p>适用场景</p><ul><li><p>当一个系统应该独立于它的产品创建,构成和表示时</p></li><li><p>当要实例化的类是在运行时刻指定时</p></li><li><p>为了避免创建一个与产品类层次平行的工厂类层次时</p></li><li><p>当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们,可能比每次用合适的状态手工实例化该类更方便</p></li></ul><h4 id="适配器-Adapter"><a href="#适配器-Adapter" class="headerlink" title="适配器(Adapter)"></a>适配器(Adapter)</h4><blockquote><p>将一个类的接口转换成客户希望的另一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作</p><p>结构</p><ul><li><p>类适配器使用多重继承对一个接口与另一个接口进行匹配</p></li><li><p>对象适配器依赖于对象组合</p></li></ul></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%B1%BB%E9%80%82%E9%85%8D%E5%99%A8.png"></p><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E5%AF%B9%E8%B1%A1%E9%80%82%E9%85%8D%E5%99%A8.png"></p><p>适用场景</p><ul><li><p>想使用一个已经存在的类,而他的接口不符合要求</p></li><li><p>想创建一个可以服用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作</p></li><li><p>(仅适用于对象适配器)想使用一个已经存在的子类,但是不可能对每一个都进行子类化以匹配他们的接口。对象适配器可以适配它的父类接口</p></li></ul><h4 id="桥接-Bridge"><a href="#桥接-Bridge" class="headerlink" title="桥接(Bridge)"></a>桥接(Bridge)</h4><blockquote><p>将抽象部分与其实现部分分离,使它们都可以独立地变化</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E6%A1%A5%E6%8E%A5.png"></p><p>适用场景</p><ul><li><p>不希望在抽象和它的实现部分之间有一个固定的绑定关系。</p></li><li><p>类的抽象以及它的实现都应该可以通过生成子类的方法加以扩充。这是 Bridge 模式使得开发者可以对不同得抽象接口和实现部分进行组合,并分别对它们进行扩充</p></li><li><p>对一个抽象得实现部分的修改应对客户不产生影响,即客户代码不必重新编译</p></li><li><p>(C++)想对客户完全隐藏抽象的实现部分</p></li><li><p>有许多类要生产的类层次结构</p></li><li><p>想在多个对象间共享实现(可能使用引用计数),但同时要求客户并不知道这一点</p></li></ul><h4 id="组合-Composite"><a href="#组合-Composite" class="headerlink" title="组合(Composite)"></a>组合(Composite)</h4><blockquote><p>将对象组合成树型结构以表示”部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%BB%84%E5%90%88.png"></p><p>适用场景</p><ul><li><p>想表示对象的部分-整体层次结构</p></li><li><p>希望用户忽略组合对象与单个对象的不同,用户将统一的使用组合结构中的所有对象</p></li></ul><h4 id="装饰器-Decorator"><a href="#装饰器-Decorator" class="headerlink" title="装饰器(Decorator)"></a>装饰器(Decorator)</h4><blockquote><p>动态地给一个对象添加一些额外的职责。就增加功能而言,Decorator 模式比生成子类更加灵活</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E8%A3%85%E9%A5%B0.png"></p><p>适用场景</p><ul><li><p>在不影响其他对象的情况下,以动态,透明的方式给单个对象添加职责</p></li><li><p>处理那些可以撤销的职责</p></li><li><p>当不能采用生成子类的方式进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,由于类定义被隐藏,或类定义不能用于生成子类</p></li></ul><h4 id="享元-Flyweight"><a href="#享元-Flyweight" class="headerlink" title="享元(Flyweight)"></a>享元(Flyweight)</h4><blockquote><p>运用共享技术有效的支持大量细粒度的对象</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%BA%AB%E5%85%83.png"></p><p>适用场景</p><ul><li><p>一个应用程序使用了大量的对象</p></li><li><p>完全由于使用大量的对象,造成很大的存储开销</p></li><li><p>对象的大多数状态都可变为外部状态</p></li><li><p>如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象</p></li><li><p>应用程序不依赖于对象标识。由于 Flyweight 对象可以被共享,所以对于概念上明显有别的对象,标识测试将返回真值</p></li></ul><h4 id="命令-Command"><a href="#命令-Command" class="headerlink" title="命令(Command)"></a>命令(Command)</h4><blockquote><p>将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E5%91%BD%E4%BB%A4.png"></p><p>适用场景</p><ul><li><p>抽象出待执行大的动作以参数化某对象。Command 模式是过程语言中的回调(Callback)机制的一个面向对象的替代品</p></li><li><p>在不同的时刻指定,排列和执行请求。一个 Command 对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可以将负责该请求的命令对象传递给另一个不同的进程并在那实现该请求</p></li><li><p>支持取消操作。Command 的 Execute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个 Unexecute 操作,该操作取消上一次 Execute 调用的效果。 执行大的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用 Unexecute 和 Execute 来实现重数不限的 “取消” 和 “重做”。</p></li><li><p>支持修改日志。这样当系统崩溃时,这些修改可以被重做一遍。在 Command 接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用 Execute 操作重新执行它们</p></li><li><p>用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务(Transaction)的信息系统中很常见。Command 模式提供了对事务进行建模的方法。Command 有一个公共接口,使得可以用同一种方式调用所有的事务,同时使用该模式也易于添加新事务以扩展系统</p></li></ul><h4 id="观察者-Observer"><a href="#观察者-Observer" class="headerlink" title="观察者(Observer)"></a>观察者(Observer)</h4><blockquote><p>定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E8%A7%82%E5%AF%9F%E8%80%85.png"></p><p>适用场景</p><ul><li><p>当一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两者封装在独立的对象中以使它们可以各自独立的改变和复用</p></li><li><p>当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变时</p></li><li><p>当一个对象必须通知其他对象,而它又不能假定其他对象是谁,即不希望这些对象是紧耦合的</p></li></ul><h4 id="状态模式-State"><a href="#状态模式-State" class="headerlink" title="状态模式(State)"></a>状态模式(State)</h4><blockquote><p>允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%8A%B6%E6%80%81%E6%A8%A1%E5%BC%8F.png"></p><p>适用场景</p><ul><li><p>一个对象的行为决定于它的状态,并且他必须在允运行时刻根据状态改变它的行为</p></li><li><p>一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State 模式将每一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化</p></li></ul><h4 id="策略模式-Strategy"><a href="#策略模式-Strategy" class="headerlink" title="策略模式(Strategy)"></a>策略模式(Strategy)</h4><blockquote><p>定义一系列的算法,把他们一个个封装起来,并且使它们可以相互替换。此模式使得算法可以独立于使用它们的客户而变化</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E7%AD%96%E7%95%A5%E6%A8%A1%E5%BC%8F.png"></p><p>适用场景</p><ul><li><p>许多相关的类仅仅是行为有异。”策略”提供了一种用多个行为中的一个行为来配置一个类的方法</p></li><li><p>需要使用一个算法的不同变体。例如,定义一些反映不同空间的空间/时间权衡的算法。当这些变体实现为一个算法的类层次时,可以使用策略模式</p></li><li><p>算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的,与算法相关的数据结构</p></li><li><p>一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,将相关的条件分支移入它们各自的 Strategy 类中,以代替这些条例语句</p></li></ul><h4 id="访问者-Visitor"><a href="#访问者-Visitor" class="headerlink" title="访问者(Visitor)"></a>访问者(Visitor)</h4><blockquote><p>表示一个作用于某对象结构中的各元素的操作。它允许在不改变各元素的类的前提下定义作用于这些元素的新操作</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E8%AE%BF%E9%97%AE%E8%80%85.png"></p><p>适用场景</p><ul><li><p>一个对象结构包含很多类对象,它们有不同的接口,而用户想对这些对象实施一些依赖于其具体类的操作</p></li><li><p>需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而又想要避免这些操作”污染”这些对象的类。Visitor 使得用户可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作</p></li><li><p>定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好</p></li></ul><h4 id="中介者-Mediator"><a href="#中介者-Mediator" class="headerlink" title="中介者(Mediator)"></a>中介者(Mediator)</h4><blockquote><p>用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变他们之间地交互</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B8%AD%E4%BB%8B%E8%80%85.png"></p><p>适用场景</p><ul><li><p>一组对象以定义良好但是复杂地方式进行通信,产生地相互依赖关系结构混乱且难以理解</p></li><li><p>一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象</p></li><li><p>想定制一个分布在多个类中地行为,而又不想生成太多的子类</p></li></ul>]]></content>
<summary type="html"><h2 id="下午题"><a href="#下午题" class="headerlink" title="下午题"></a>下午题</h2><hr>
<h3 id="数据流图"><a href="#数据流图" class="headerlink" title="数据流图"></</summary>
<category term="证书" scheme="https://intyouss.github.io/tags/%E8%AF%81%E4%B9%A6/"/>
</entry>
<entry>
<title>Redis</title>
<link href="https://intyouss.github.io/2023/04/17/Redis/"/>
<id>https://intyouss.github.io/2023/04/17/Redis/</id>
<published>2023-04-17T01:28:36.000Z</published>
<updated>2023-04-23T13:19:45.909Z</updated>
<content type="html"><![CDATA[<h2 id="Redis-概述"><a href="#Redis-概述" class="headerlink" title="Redis 概述"></a>Redis 概述</h2><hr><h3 id="NoSQL"><a href="#NoSQL" class="headerlink" title="NoSQL"></a>NoSQL</h3><hr><blockquote><p>非关系型数据库</p></blockquote><h4 id="类型"><a href="#类型" class="headerlink" title="类型"></a>类型</h4><blockquote><ol><li><p>键值存储数据库</p><blockquote><p>key-value</p></blockquote></li><li><p>列存储数据库</p><blockquote><p>关系型数据库是典型的行存储数据库(MySQL),按行存储的数据在物理层面占用的时连续的存储空间,不适合海量数据存储。而按列存储则可实现分布式存储,适合海量存储(HBase)</p></blockquote></li><li><p>文档型数据库</p><blockquote><p>NoSQL 与 关系型数据库的结合(MangoDB)</p></blockquote></li><li><p>图像(Graph)数据库</p><blockquote><p>用于存放一个节点关系的数据库(Neo4J)</p></blockquote></li></ol></blockquote><h3 id="Redis的用途"><a href="#Redis的用途" class="headerlink" title="Redis的用途"></a>Redis的用途</h3><hr><h4 id="数据缓存"><a href="#数据缓存" class="headerlink" title="数据缓存"></a>数据缓存</h4><blockquote><p>减小 RT,而且降低 DBMS 的压力</p></blockquote><h5 id="缓存分类"><a href="#缓存分类" class="headerlink" title="缓存分类"></a>缓存分类</h5><h6 id="实时同步缓存"><a href="#实时同步缓存" class="headerlink" title="实时同步缓存"></a>实时同步缓存</h6><blockquote><p>指DBMS中数据更新后,Redis缓存中存放的相关数据会被立即清除,以促使再有对该数据的访问到来时,必须先从DBMS中查询获取到的最新数据,然后再写入到Redis</p></blockquote><h6 id="阶段性同步缓存"><a href="#阶段性同步缓存" class="headerlink" title="阶段性同步缓存"></a>阶段性同步缓存</h6><blockquote><p>指Redis缓存中的数据允许在一段时间内与DBMS中的数据不完全一致,而这个时间段就是这个缓存数据的过期时间</p></blockquote><h3 id="Redis-的-IO-模型"><a href="#Redis-的-IO-模型" class="headerlink" title="Redis 的 IO 模型"></a>Redis 的 IO 模型</h3><hr><blockquote><p>Redis 处理客户端请求所采用的处理架构,称为Redis 的 IO 模型</p></blockquote><h4 id="单线程模型"><a href="#单线程模型" class="headerlink" title="单线程模型"></a>单线程模型</h4><blockquote><p>对于 Redis 3.0 及其以前版本,Redis 的 IO模型采用的是纯粹的单线程模型,即所有客户端的请求全部由一个线程处理,Redis 的单线程模型采用了多路复用技术</p></blockquote><h5 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h5><blockquote><p>可维护性高,性能高,不存在并发读写情况,所以也就不存在执行顺序的不确定性,不存在线程切换开销,不存在死锁问题,不存在为了数据安全而进行的枷锁/解锁开销问题</p></blockquote><h5 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h5><blockquote><p>性能会受到影响,且由于单线程只使用一个处理器,所以会形成处理器浪费</p></blockquote><h4 id="多线程模型"><a href="#多线程模型" class="headerlink" title="多线程模型"></a>多线程模型</h4><blockquote><p>从 Redis 6.0 版本开始。处理客户端请求的仍是单线程模型,但对于一些比较耗时担又不影响对客户端的响应的操作,就由后台其他线程来处理。</p></blockquote><h5 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h5><blockquote><p>其结合了多线程与单线程的优点,避开了他们的不足</p></blockquote><h5 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h5><blockquote><p>该模型并非是一个真正意义上的多线程,因为真正处理任务的线程仍是单线程。所以,其对性能也是有些影响的</p></blockquote><h2 id="Redis-的配置"><a href="#Redis-的配置" class="headerlink" title="Redis 的配置"></a>Redis 的配置</h2><hr><h3 id="INCLUDE"><a href="#INCLUDE" class="headerlink" title="INCLUDE"></a>INCLUDE</h3><hr><blockquote><p>指定要在当前配置文件中包含的配置文件。这样做的目的主要是便于配置信息管理,可以将不同场景的配置都进行单独定义,然后在当前核心配置文件中根据不同场景选择包含进不同的配置文件。</p><p><em>想要使用外部配置文件覆盖,可以在Redis配置文件的文件末尾 include</em></p><p>例:<code>include /xxx/xxx.conf</code></p></blockquote><h3 id="MODULES"><a href="#MODULES" class="headerlink" title="MODULES"></a>MODULES</h3><hr><blockquote><p>加载不同的第三方模块,来增强,扩展 Redis 的功能</p><p>例:<code>loadmodule /xxx/xxx/xxx/xxx.so</code></p></blockquote><h3 id="NETWORK"><a href="#NETWORK" class="headerlink" title="NETWORK"></a>NETWORK</h3><hr><blockquote><p>网络配置</p></blockquote><h4 id="tcp-backlog"><a href="#tcp-backlog" class="headerlink" title="tcp-backlog"></a>tcp-backlog</h4><blockquote><p>tcp-backlog 是一个 TCP 连接队列,其主要用于解决高并发场景下客户端慢连接问题。配置中设置的值就是这个队列的长度。该队列与 TCP 连接的三次握手有关。不同的 Linux 内核,backlog 队列中存放的元素(客户端连接)类型是不同的</p></blockquote><ul><li><p>Linux 内核 2.2 版本之前</p><blockquote><p>该队列中存放的是已完成了第一次握手的所有客户端连接,其中就包含已完成三次握手的客户端连接。当然,此时的 backlog 队列中的连接也具有两种状态:未完成三次握手的连接状态为 <code>SYN_RECEIVED</code> ,已完成三次握手的连接状态为<code>ESTABLISHED</code> 。只有 <code>ESTABLISHED</code> 状态的连接才会被 Redis 处理。</p></blockquote></li><li><p>Linux 内核 2.2 版本之后</p><blockquote><p>TCP 系统中维护了两个队列:<code>SYN_RECEIVED</code> 队列与 <code>ESTABLISHED</code> 队列。<code>SYN_RECEIVED</code> 队列中存放的是未完成的三次握手的连接,<code>ESTABLISHED</code> 队列中存放的是已完成三次握手的连接。此时的 backlog 就是 <code>ESTABLISHED</code> 队列</p></blockquote></li><li><p>TCP 中的 backlog 队列的长度在 Linux 中由内核参数 somaxconn 来决定。在 Redis 中该队列的长度由 Redis 配置文件设置 与 somaxconn 共同决定:取它们中的最小值。</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><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"><span class="comment"># 查看 Linux 内核中 somaxconn 的值</span></span><br><span class="line"><span class="built_in">cat</span> /proc/sys/net/core/somaxconn</span><br></pre></td></tr></table></figure></div><blockquote><p>生产环境下(特别是高并发场景下),backlog 的值最好要大一些,否则可能会影响系统性能</p></blockquote><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><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"><span class="comment"># 修改somaxconn的值</span></span><br><span class="line">vim /etc/sysctl.conf</span><br><span class="line"></span><br><span class="line"><span class="comment"># 添加</span></span><br><span class="line">net.core.somaxconn=2048</span><br><span class="line"></span><br><span class="line"><span class="comment"># 动态重新加载配置</span></span><br><span class="line">sysctl -p</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="GENERAL"><a href="#GENERAL" class="headerlink" title="GENERAL"></a>GENERAL</h3><hr><blockquote><p>通用配置</p></blockquote><h4 id="daemonize"><a href="#daemonize" class="headerlink" title="daemonize"></a>daemonize</h4><blockquote><p>控制 Redis 启动是否采用守护进程方式,即是否是后台启动</p></blockquote><h4 id="pidfile"><a href="#pidfile" class="headerlink" title="pidfile"></a>pidfile</h4><blockquote><p>指定 Redis 允许是 pid 写入的文件,无论 Redis 是否采用守护进程方式启动,pid 都会写入到该配置文件</p><p>注意</p><ul><li><p>如果没有配置 pid 文件,不同的启动方式,pid 文件的产生效果是不同的</p><ul><li><p>采用守护进程方式启动(后台启动,daemonize 为 yes)——pid 文件为<code>/var/run/redis.pid</code></p></li><li><p>采用前台启动——不生产 pid 文件</p></li></ul></li></ul></blockquote><h4 id="loglevel"><a href="#loglevel" class="headerlink" title="loglevel"></a>loglevel</h4><blockquote><p>日志等级</p></blockquote><h4 id="logfile"><a href="#logfile" class="headerlink" title="logfile"></a>logfile</h4><blockquote><p>指定日志文件。如果设置为空串,则强制将日志记录到标准输出设备(显示器)。如果使用的是前台启动方式,设置为空串,则意味着会将日志发送到设备<code>/dev/null(空设备)</code></p></blockquote><h4 id="databases"><a href="#databases" class="headerlink" title="databases"></a>databases</h4><blockquote><p>设置数据库数量</p></blockquote><h4 id="security"><a href="#security" class="headerlink" title="security"></a>security</h4><blockquote><p>用户设置 ACL 权限,Redis 访问密码相关配置。该模块中最常用的就是 requirepass 属性</p></blockquote><h3 id="CLIENTS"><a href="#CLIENTS" class="headerlink" title="CLIENTS"></a>CLIENTS</h3><hr><blockquote><p>设置与客户端相关的属性</p></blockquote><h4 id="maxclients"><a href="#maxclients" class="headerlink" title="maxclients"></a>maxclients</h4><blockquote><p>设置Redis可并发处理的客户端连接数量,默认为10000,如果达到该最大连接数,则会拒绝再来的新连接,并返回一个异常消息:已达到最大连接数</p><p>注意</p><ul><li>该值不能超过 Linux 系统支持的可打开的文件描述符最大数量阈值</li></ul></blockquote><h3 id="MEMORY-MANAGEMENT"><a href="#MEMORY-MANAGEMENT" class="headerlink" title="MEMORY MANAGEMENT"></a>MEMORY MANAGEMENT</h3><hr><blockquote><p>控制最大可用内存及相关移除问题</p></blockquote><h4 id="maxmemory"><a href="#maxmemory" class="headerlink" title="maxmemory"></a>maxmemory</h4><blockquote><p>将内存使用限制设置为指定的字节数,当达到内存限制时,REdis 将根据选择的逐出策略 maxmemory-poilcy 尝试删除符合条件的key</p><p>如果不能按照逐出策略移除key,则会给写操作命令返回error,但对于只读的命令是没有影响的</p></blockquote><h3 id="Threaded-I-x2F-O"><a href="#Threaded-I-x2F-O" class="headerlink" title="Threaded I/O"></a>Threaded I/O</h3><blockquote><p>配置 Redis 对多线程 IO 模型的支持</p></blockquote><h2 id="Redis-命令"><a href="#Redis-命令" class="headerlink" title="Redis 命令"></a>Redis 命令</h2><hr><h3 id="Redis-基本命令"><a href="#Redis-基本命令" class="headerlink" title="Redis 基本命令"></a>Redis 基本命令</h3><hr><ol><li><p><code>dbsize</code> 查询当前数据库内容长度</p></li><li><p><code>flushdb</code> 删除当前数据库中所有数据</p></li><li><p><code>flushall</code> 删除所有数据库中所有数据</p></li><li><p><code>ping</code> 心跳命令,检测客户端与 Redis 的连接是否正常</p></li><li><p><code>set <key> <value></code> 将指定key-value 写入到当前DB</p></li><li><p><code>get <key></code> 读取指定key的value值</p></li><li><p><code>select 5</code> 选择数据库</p></li><li><p><code>quit/exit</code> 退出Redis客户端</p></li><li><p><code>redis-cli shutdown</code> 关闭Redis服务</p></li></ol><h3 id="Key操作命令"><a href="#Key操作命令" class="headerlink" title="Key操作命令"></a>Key操作命令</h3><hr><h4 id="keys"><a href="#keys" class="headerlink" title="keys"></a>keys</h4><ul><li>格式 <code>KEYS pattern</code></li></ul><blockquote><p>查找所有符合给定模式 pattern 的 key, pattern 为正则表达式</p><p>KEYS 的速度非常快,但在一个大的数据库中使用它可能会阻塞当前服务器的服务。所以生产环境中一般不使用该命令,而使用 scan 命令代替</p></blockquote><h4 id="exists"><a href="#exists" class="headerlink" title="exists"></a>exists</h4><ul><li>格式 <code>EXISTS key</code></li></ul><blockquote><p>检查给定 key 是否存在</p><p>若 key 存在,返回 1,反之 0</p></blockquote><h4 id="del"><a href="#del" class="headerlink" title="del"></a>del</h4><ul><li>格式 <code>DEL key [key ...]</code></li></ul><blockquote><p>删除给定的一个或多个 key。不存在的 key 会被忽略</p><p>返回被删除 key 的数量</p></blockquote><h4 id="rename"><a href="#rename" class="headerlink" title="rename"></a>rename</h4><ul><li>格式 <code>RENAME key newKey</code></li></ul><blockquote><p>将 key 改为 newKey</p><p>当 key 与 newKey 相同,或者 key 不存在时,返回一个错误。当 newkey 已经存在时,RENAME 命令将覆盖旧值。改名成功后提示 OK,失败时候返回一个错误</p></blockquote><h4 id="move"><a href="#move" class="headerlink" title="move"></a>move</h4><ul><li>格式 <code>MOVE key db</code></li></ul><blockquote><p>将当前数据库的 key 移动到给定的数据库 db 当中</p><p>如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果,移动成功返回 1,反之 0</p></blockquote><h4 id="type"><a href="#type" class="headerlink" title="type"></a>type</h4><ul><li>格式 <code>TYPE key</code></li></ul><blockquote><p>返回 key 所储存的值的类型</p><p>返回值有以下六种</p><ul><li><p>none(key 不存在)</p></li><li><p>string</p></li><li><p>list</p></li><li><p>set</p></li><li><p>zset</p></li><li><p>hash</p></li></ul></blockquote><h4 id="expire-与-pexpire"><a href="#expire-与-pexpire" class="headerlink" title="expire 与 pexpire"></a>expire 与 pexpire</h4><ul><li>格式 <code>EXPIRE key seconds</code></li></ul><blockquote><p>为给定 key 设置生存时间。当 key 过期时(生存时间为 0),它会被自动删除。expire 的时间单位为秒,pexpire 的时间单位为毫秒。在 Redis 中,带有生存时间的 key 被称为’易失的’(volatile)</p></blockquote><h4 id="ttl-与-pttl"><a href="#ttl-与-pttl" class="headerlink" title="ttl 与 pttl"></a>ttl 与 pttl</h4><ul><li>格式 <code>TTL key</code></li></ul><blockquote><p>TTL,time to live,返回给定 key 的剩余生存时间</p><p>其返回值存在三种可能</p><ul><li><p>当 key 不存在时,返回 -2</p></li><li><p>当 key 存在但没有设置剩余生存时间时,返回 -1</p></li><li><p>否则,返回 key 的剩余生存时间,ttl 命令返回的时间单位为秒,而 pttl 命令返回的时间单位为毫秒</p></li></ul></blockquote><h4 id="persist"><a href="#persist" class="headerlink" title="persist"></a>persist</h4><ul><li>格式 <code>persist key</code></li></ul><blockquote><p>去除给定 key 的生存时间,将这个 key 从’易失的’转换成’持久的’</p><p>当生存时间移除成功时,返回 1;若 key 不存在或 key 没有设置生存时间,则返回 0</p></blockquote><h4 id="randomkey"><a href="#randomkey" class="headerlink" title="randomkey"></a>randomkey</h4><ul><li>格式 <code>RANDOMKEY</code></li></ul><blockquote><p>从当前数据库中随机返回(不删除)一个key</p><p>当前数据库不为空时,返回一个 key。当数据库为空时,返回nil</p></blockquote><h4 id="scan"><a href="#scan" class="headerlink" title="scan"></a>scan</h4><ul><li>格式 <code>SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]</code></li></ul><blockquote><p>用于迭代数据库中的 key</p><ul><li><p>cursor:本次迭代开始的游标</p></li><li><p>pattern:本次迭代要匹配的 key 的模式</p></li><li><p>count:本次迭代要从数据集里返回多少元素,默认值为 10</p></li><li><p>type:本次迭代要返回的 value 的类型,默认为所有类型</p></li></ul><p>使用间断的,负数,超出范围的或者其他非正常的游标来执行增量式迭代</p><p>不会造成服务器崩溃。</p><p>当数据量很大时,count 的数据量的指定可能会不起作用,Redis 会自动调整每次的遍历数目。由于 scan 命令每次执行都只会返回少量元素,所以该命令可以用于生产环境</p></blockquote><h3 id="String-型-Value-操作命令"><a href="#String-型-Value-操作命令" class="headerlink" title="String 型 Value 操作命令"></a>String 型 Value 操作命令</h3><hr><h4 id="set"><a href="#set" class="headerlink" title="set"></a>set</h4><ul><li>格式 <code>SET key value [EX seconds | PX milliseconds] [NX|XX]</code></li></ul><blockquote><p>SET 除了可以直接将 key 的值设为 value 外,还可以指定一些参数</p><ul><li><p>EX seconds:为当前 key 设置过期时间,单位秒。等价于 SETEX 命令</p></li><li><p>PX milliseconds:为当前 key 设置过期时间,单位毫秒。等价于 PSETEX 命令</p></li><li><p>NX:指定的 key 不存在才会设置成功,用于添加指定的 key。等价于 SETNX 命令</p></li><li><p>XX:指定的 key 必须存在才会设置成功,用于更新指定 key 的 value</p></li></ul></blockquote><h4 id="getset"><a href="#getset" class="headerlink" title="getset"></a>getset</h4><ul><li>格式 <code>GETSET key value</code></li></ul><blockquote><p>将给定 key 的值设置为value,并返回 key 的旧值</p><p>当 key 存在但不是字符串类型时,返回一个错误;当 key 不存在时,返回nil</p></blockquote><h4 id="mset-与-msetnx"><a href="#mset-与-msetnx" class="headerlink" title="mset 与 msetnx"></a>mset 与 msetnx</h4><ul><li>格式 <code>MSET/MSETNX key value [key value ...]</code></li></ul><blockquote><p>同时设置一个或多个 key-value 对</p><p>如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX 命令;他只会在所有给定 key 都不存在的情况下进行操作操作,某些给定 key 被更新而另一些给定 key 没有改变的情况不可发生</p></blockquote><h4 id="mget"><a href="#mget" class="headerlink" title="mget"></a>mget</h4><ul><li>格式 <code>MGET key [key ...]</code></li></ul><blockquote><p>返回(一个或多个)给定 key 的值</p><p>如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil,因此,该命令永不失败</p></blockquote><h4 id="append"><a href="#append" class="headerlink" title="append"></a>append</h4><ul><li>格式 <code>APPEND key value</code></li></ul><blockquote><p>如果 key 已经存在并且是一个字符串,APPEND 命令将 value 追加到 key 原来的值的末尾。如果 key 不存在,append 就简单的将给定的 key 设为 value,就像执行 SET key value 一样</p><p>追加之后,返回 key 中字符串的长度</p></blockquote><h4 id="incr-与-decr"><a href="#incr-与-decr" class="headerlink" title="incr 与 decr"></a>incr 与 decr</h4><ul><li>格式 <code>incr key</code> 或 <code>decr key</code></li></ul><blockquote><p>increment 自动递增,将 key 中存储的数字值增一</p><p>decrement 自动递减,将 key 中存储的数字值减一</p><p>如果 key 不存在,那么 key 的值会先被初始化为 0,然后在执行增一/减一操作。如果值不能表示为数字,那么返回一个错误提示。如果执行正确,则返回增一/减一后的值</p></blockquote><h4 id="incrby-与-decrby"><a href="#incrby-与-decrby" class="headerlink" title="incrby 与 decrby"></a>incrby 与 decrby</h4><ul><li>格式 <code>incrby key increment</code> 或 <code>decrby key decrement</code></li></ul><blockquote><p>将 key 中存储的数字值增加/减少指定的数值,这个数值只能是整数,可以是负数,但不能是小数</p><p>如果 key 不存在,那么 key 的值会先被初始化 0,然后再执行操作,同 incr/decr</p></blockquote><h4 id="incrbyfloat"><a href="#incrbyfloat" class="headerlink" title="incrbyfloat"></a>incrbyfloat</h4><ul><li>格式 <code>incrbyfloat key increment</code></li></ul><blockquote><p>为 key 中所储存的值加上浮点数增量,没有 decrbyfloat</p></blockquote><h4 id="strlen"><a href="#strlen" class="headerlink" title="strlen"></a>strlen</h4><ul><li>格式 <code>strlen key</code></li></ul><blockquote><p>查看 key 中字符串长度</p></blockquote><h4 id="getrange"><a href="#getrange" class="headerlink" title="getrange"></a>getrange</h4><ul><li>格式 <code>getrange key start end</code></li></ul><blockquote><p>返回 key 中字符串值的子字符串,字符串的截取范围由 start 和 end 两个偏移量决定,包括 start 和 end 在内</p><p>end 必须要比 start 大,支持负数偏移量,表示从字符串最后开始计数,-1 表示最后一个字符,以此类推</p></blockquote><h4 id="setrange"><a href="#setrange" class="headerlink" title="setrange"></a>setrange</h4><ul><li>格式 <code>setrange key offset value</code></li></ul><blockquote><p>用 offset 参数替换给定 key 所储存的字符串值 str,从偏移量 offset 开始</p><p>当 offset 值大于 str 长度时,中间使用零字节\x00填充,对于不存在的 key 当作空串处理</p></blockquote><h3 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h3><hr><h4 id="数据缓存-1"><a href="#数据缓存-1" class="headerlink" title="数据缓存"></a>数据缓存</h4><blockquote><p>Redis 作为数据缓存层,MySQL 作为数据存储层。应用服务器首先从 Redis 中获取数据,如果缓存层中没有,则从 MySQL 中获取后先存入缓存层再返回给应用服务器</p></blockquote><h4 id="共享-Session"><a href="#共享-Session" class="headerlink" title="共享 Session"></a>共享 Session</h4><blockquote><p>对于一个分布式应用系统,如果将类似用户登陆信息这样的 Session 数据保存在提供登陆服务的服务器中,那么如果用户再次提交像收藏,支付等请求时可能会出现的问题:在提供收藏,支付等服务的服务器中并没有该用户的 Session 数据,从而导致该用户需要重新登录。对于用户来说这是不能接受的。</p><p>此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从 Redis 中查找相应的 Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。这样就不会引发’重新登录问题’</p></blockquote><h4 id="限制器"><a href="#限制器" class="headerlink" title="限制器"></a>限制器</h4><blockquote><p>Redis 可以结合 key 的过期时间与 incr 命令来完成限速,防止 DoS 攻击</p></blockquote><h3 id="Hash-型-Value-操作命令"><a href="#Hash-型-Value-操作命令" class="headerlink" title="Hash 型 Value 操作命令"></a>Hash 型 Value 操作命令</h3><hr><blockquote><p>Hash 表就是一个映射表 Map,也是由键-值对构成的,为了与整体的 key 进行区分,这里的键称为 field,值称为 value。注意,Redis 的 Hash 表中的 field-value 对均为 String 类型</p></blockquote><h4 id="hset"><a href="#hset" class="headerlink" title="hset"></a>hset</h4><ul><li>格式 <code>HSET key field value</code></li></ul><blockquote><p>将 hash 表 key 中的域 field 的值设为 value</p><p>如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作,返回1。已存在则覆盖,返回0</p></blockquote><h4 id="hget"><a href="#hget" class="headerlink" title="hget"></a>hget</h4><ul><li>格式 <code>hget key <field></code></li></ul><blockquote><p>同 String</p></blockquote><h4 id="hmset"><a href="#hmset" class="headerlink" title="hmset"></a>hmset</h4><ul><li>格式 <code>hmset key field value [field value ...]</code></li></ul><blockquote><p>同 String</p></blockquote><h4 id="hmget"><a href="#hmget" class="headerlink" title="hmget"></a>hmget</h4><ul><li>格式 <code>hmget key field [field ...]</code></li></ul><blockquote><p>同 String</p></blockquote><h4 id="hgetall"><a href="#hgetall" class="headerlink" title="hgetall"></a>hgetall</h4><ul><li>格式 <code>hgetall key</code></li></ul><blockquote><p>获取 key 中所有 field 和 value 值</p></blockquote><h4 id="hkeys"><a href="#hkeys" class="headerlink" title="hkeys"></a>hkeys</h4><ul><li>格式 <code>hkeys key</code></li></ul><blockquote><p>获取 key 中所有 field 值</p></blockquote><h4 id="hlen"><a href="#hlen" class="headerlink" title="hlen"></a>hlen</h4><ul><li>格式 <code>hlen key</code></li></ul><blockquote><p>获取 key 的 长度</p></blockquote><h4 id="略-大多与String命令同"><a href="#略-大多与String命令同" class="headerlink" title="略(大多与String命令同)"></a>略(大多与String命令同)</h4><h4 id="应用场景-1"><a href="#应用场景-1" class="headerlink" title="应用场景"></a>应用场景</h4><blockquote><p>存储对象数据,对对象属性的修改再=在 Redis 中就可以直接完成,其不像 String 型 Value 存储对象,那个对象是序列化过的,例如序列化为 JSON 串,对对象属性值的修改需要先反序列化为对象后再修改,修改后再序列化为 JSON 串后写入到 Redis</p></blockquote><h3 id="List-型-Value-操作命令"><a href="#List-型-Value-操作命令" class="headerlink" title="List 型 Value 操作命令"></a>List 型 Value 操作命令</h3><hr><h4 id="lpush-x2F-rpush"><a href="#lpush-x2F-rpush" class="headerlink" title="lpush/rpush"></a>lpush/rpush</h4><ul><li>格式 <code>LPUSH key value [value ...]</code> 或 <code>RPUSH key value [value ...]</code></li></ul><blockquote><p>表头/表尾添加元素</p></blockquote><h4 id="linsert"><a href="#linsert" class="headerlink" title="linsert"></a>linsert</h4><ul><li>格式 <code>LINSERT key BEFORE|AFTER pivot value</code></li></ul><blockquote><p>将值 value 插入到列表 ley 当中,位于元素 pivot 之前或之后</p></blockquote><h4 id="lpop-x2F-rpop"><a href="#lpop-x2F-rpop" class="headerlink" title="lpop/rpop"></a>lpop/rpop</h4><ul><li>格式 <code>LPOP key [count]</code> 或 <code>RPOP key [count]</code></li></ul><blockquote><p>从列表 key 的表头/表尾移除 count 个元素,并返回移除的元素,count 默认值 1</p><p>当 key 不存在时,返回 nil</p></blockquote><h4 id="blpop-x2F-brpop"><a href="#blpop-x2F-brpop" class="headerlink" title="blpop/brpop"></a>blpop/brpop</h4><ul><li>格式 <code>Blpop key [key ...] timeout</code> 或 <code>brpop key [key ..] timeout</code></li></ul><blockquote><p>列表的阻塞式(blocking)弹出,命令。它们是 LPOP/RPOP 的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLpop/Brpop 命令阻塞,知道等待 timeout 超时或发现可弹出元素为止</p></blockquote><h4 id="rpoplpush"><a href="#rpoplpush" class="headerlink" title="rpoplpush"></a>rpoplpush</h4><ul><li>格式 <code>rpoplpush source destnation</code></li></ul><blockquote><p>将source 列表的尾元素,放到 destnation 列表的表头</p></blockquote><h4 id="brpoplpush"><a href="#brpoplpush" class="headerlink" title="brpoplpush"></a>brpoplpush</h4><blockquote><p>rpoplpush 的阻塞版本</p></blockquote><h4 id="lrem"><a href="#lrem" class="headerlink" title="lrem"></a>lrem</h4><ul><li>格式 <code>lrem key count value</code></li></ul><blockquote><p>根据参数 count 的值,移除列表中与参数 value 相等的元素</p><p>count值为下列几种</p><ul><li><p>count > 0:从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count</p></li><li><p>count < 0:从表尾开始向表头搜索</p></li><li><p>count = 0:移除表中所有与 value 相等的值</p></li></ul><p>返回被移除元素的数量,当 key 不存在时,返回 0</p></blockquote><h4 id="ltrim"><a href="#ltrim" class="headerlink" title="ltrim"></a>ltrim</h4><ul><li>格式 <code>LTRIM key start stop</code></li></ul><blockquote><p>对一个列表进行修剪,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除</p><p>下表参数 start 和 stop 都以 0 为底,如果 start > stop,返回空列表,如果 stop < end,Redis 将 stop 的值设置为 end</p></blockquote><h4 id="应用场景-2"><a href="#应用场景-2" class="headerlink" title="应用场景"></a>应用场景</h4><blockquote><p>通过构建不同的数据结构来实现相应的业务功能</p><ul><li><p>列(lpush + lpop)</p></li><li><p>队列(lpush + rpop)</p></li><li><p>阻塞式消息队列(lpush + brpop)</p></li><li><p>动态有限集合(lpush + ltrim)</p></li></ul></blockquote><h3 id="Set-型-Value-操作命令"><a href="#Set-型-Value-操作命令" class="headerlink" title="Set 型 Value 操作命令"></a>Set 型 Value 操作命令</h3><hr><blockquote><p>Set 型的底层是 Hash 表,Set 中的元素具有无序性与不可重复性</p></blockquote><h4 id="sadd"><a href="#sadd" class="headerlink" title="sadd"></a>sadd</h4><ul><li>格式 <code>SADD key member [member ...]</code></li></ul><blockquote><p>将一个或多个 member 元素加入到集合 key 中,已经存在于集合的 member 元素将被忽略</p><p>key 不存在,则创建。当 key 不是集合类型时,返回一个错误</p></blockquote><h4 id="smembers"><a href="#smembers" class="headerlink" title="smembers"></a>smembers</h4><ul><li>格式 <code>SMEMBERS key</code></li></ul><blockquote><p>查看 key 中的所有值</p></blockquote><h4 id="scard"><a href="#scard" class="headerlink" title="scard"></a>scard</h4><ul><li>格式 <code>SCARD key</code></li></ul><blockquote><p>返回 Set 集合的长度</p></blockquote><h4 id="sismember"><a href="#sismember" class="headerlink" title="sismember"></a>sismember</h4><ul><li>格式 <code>SISMEMBER key member</code></li></ul><blockquote><p>判断 member 元素是否是集合 key 的成员</p></blockquote><h4 id="smove"><a href="#smove" class="headerlink" title="smove"></a>smove</h4><ul><li>格式 <code>SMOVE source destination member</code></li></ul><blockquote><p>将 member 元素从 source 集合移动到 destination 集合</p></blockquote><h4 id="srem"><a href="#srem" class="headerlink" title="srem"></a>srem</h4><ul><li>格式 <code>SREM key member [member ...]</code></li></ul><blockquote><p>移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略</p></blockquote><h4 id="srandmember"><a href="#srandmember" class="headerlink" title="srandmember"></a>srandmember</h4><ul><li>格式 <code>srandmember key [count]</code></li></ul><blockquote><p>在 key 中随机 count 个元素,count 默认为 1</p><p>count 为正数时,且小于集合长度,那么返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合长度,返回整个集合。如果 count 为负数,那么返回一个包含 count 绝对值个元素的数组,但数组中的元素可能会出现重复</p></blockquote><h4 id="spop"><a href="#spop" class="headerlink" title="spop"></a>spop</h4><ul><li>格式 <code>SPOP key [count]</code></li></ul><blockquote><p>移除并返回集合中的 count 个随机元素,count 必须为正数,且默认值为 1</p></blockquote><h4 id="sdiff"><a href="#sdiff" class="headerlink" title="sdiff"></a>sdiff</h4><ul><li>格式 <code>SDIFF key key2</code></li></ul><blockquote><p>差集,返回 key 相对于 key2 不同的元素</p></blockquote><h4 id="sdiffstore"><a href="#sdiffstore" class="headerlink" title="sdiffstore"></a>sdiffstore</h4><ul><li>格式 <code>SDIFFSTORE destination key key2</code></li></ul><blockquote><p>差集存储,将 key 相对于 key2 的差集存储在 destation 集合中</p></blockquote><h4 id="sinter"><a href="#sinter" class="headerlink" title="sinter"></a>sinter</h4><ul><li>格式 <code>SINTER key key2</code></li></ul><blockquote><p>交集,返回 key 与 key2 的交集</p></blockquote><h4 id="sinterstore"><a href="#sinterstore" class="headerlink" title="sinterstore"></a>sinterstore</h4><ul><li>格式 <code>SINTERSTROE destination key key2</code></li></ul><blockquote><p>交集存储,将 key 与 key2 的交集存储在 destination 集合中</p></blockquote><h4 id="sunion"><a href="#sunion" class="headerlink" title="sunion"></a>sunion</h4><blockquote><p>并集,同上</p></blockquote><h4 id="sunionstore"><a href="#sunionstore" class="headerlink" title="sunionstore"></a>sunionstore</h4><blockquote><p>并集存储,同上</p></blockquote><h4 id="应用场景-3"><a href="#应用场景-3" class="headerlink" title="应用场景"></a>应用场景</h4><blockquote><ul><li><p>动态黑白名单</p></li><li><p>有限随机数(spop 或 srandmember)</p></li><li><p>用户画像(sadd/sinter/sinterstore)</p></li></ul></blockquote><h3 id="有序-Set-型-Value-操作命令-ZSet"><a href="#有序-Set-型-Value-操作命令-ZSet" class="headerlink" title="有序 Set 型 Value 操作命令(ZSet)"></a>有序 Set 型 Value 操作命令(ZSet)</h3><blockquote><p>Redis 存储数据的 Value 可以是一个有序的 Set,这个有序的 Set 中的每个元素均 String 类型,有序 Set 与 Set 的不同之处是,有序 Set 中的每个元素都有一个分值 scor,Redis 会根据 score 的值对集合进行由小到大的排序。其与 Set 集合要求相同,元素不能重复,但元素的 score 可以重复,由于该类型的所有命令均是 z 开头,所有该 Set 也称为 ZSet</p></blockquote><h4 id="zadd"><a href="#zadd" class="headerlink" title="zadd"></a>zadd</h4><ul><li>格式 <code>ZADD key score member [[score member] ...]</code></li></ul><blockquote><p>将一个或多个 member 元素以及其 score 值加入到有序 key 中的适当位置</p></blockquote><h4 id="zrange"><a href="#zrange" class="headerlink" title="zrange"></a>zrange</h4><ul><li>格式 <code>zrange key start end [withscores]</code></li></ul><blockquote><p>将集合 key 中从 start 到 end 的元素切片输出,添加 withscores 显示 key 内元素的 score 值</p></blockquote><h4 id="zrangebyscore-与-zrevrangebyscore"><a href="#zrangebyscore-与-zrevrangebyscore" class="headerlink" title="zrangebyscore 与 zrevrangebyscore"></a>zrangebyscore 与 zrevrangebyscore</h4><ul><li><p>格式 <code>zrangebyscore key min max [withscores] [LIMIT offset count]</code></p></li><li><p>格式 <code>zrangebyscore key max min [withscores] [LIMIT offset count]</code></p></li></ul><blockquote><p>将集合 key 中 score > min,score <= max 的 元素输出,limit 按照前部分查询到的结果 从下标为 offset 开始,切片 count 数量元素并返回</p><p>zrevrangebyscore 是 zrangebyscore 的反向</p></blockquote><h4 id="zcard"><a href="#zcard" class="headerlink" title="zcard"></a>zcard</h4><blockquote><p>查询 key 集合的长度,同Set</p></blockquote><h4 id="zcount"><a href="#zcount" class="headerlink" title="zcount"></a>zcount</h4><ul><li>格式 <code>zcount key start end</code></li></ul><blockquote><p>计数 key 集合中下标 > start,下标 <= end 的元素个数 </p></blockquote><h4 id="zscore"><a href="#zscore" class="headerlink" title="zscore"></a>zscore</h4><ul><li>格式 <code>zscore key field</code></li></ul><blockquote><p>查询 key 集合中 field 元素的 score 值</p></blockquote><h4 id="zrank-与-zrevrank"><a href="#zrank-与-zrevrank" class="headerlink" title="zrank 与 zrevrank"></a>zrank 与 zrevrank</h4><ul><li>格式 <code>zrank key field</code> 或 <code>zrevrank key field</code></li></ul><blockquote><p>查询 key 中 field 元素的排名,zrevrank 倒序排名</p></blockquote><h4 id="zrem-与-zremrangerank"><a href="#zrem-与-zremrangerank" class="headerlink" title="zrem 与 zremrangerank"></a>zrem 与 zremrangerank</h4><blockquote><p>删除 key 中元素,zremrangerank 删除 key 中一个范围内的元素</p></blockquote><h4 id="zremrangebyscore"><a href="#zremrangebyscore" class="headerlink" title="zremrangebyscore"></a>zremrangebyscore</h4><blockquote><p>删除 key 中 score 在一个范围内的元素</p></blockquote><h4 id="zrangebylex"><a href="#zrangebylex" class="headerlink" title="zrangebylex"></a>zrangebylex</h4><ul><li>格式 <code>zrangebylex key (min [max</code></li></ul><blockquote><p>按照第一个字母排序切片,min 和 max 前必须添加符号</p></blockquote><h4 id="zlexcount"><a href="#zlexcount" class="headerlink" title="zlexcount"></a>zlexcount</h4><blockquote><p>根据字母返回计数</p></blockquote><h3 id="简单动态字符串-SDS-String类型底层"><a href="#简单动态字符串-SDS-String类型底层" class="headerlink" title="简单动态字符串 SDS(String类型底层)"></a>简单动态字符串 SDS(String类型底层)</h3><hr><blockquote><p>无论是 Redis 的 Key 还是 Value,其基础数据类型都是字符串。例如,Hash 型 Value 的 field 与 value 类型,List,Set,ZSet 型 Value 的元素的类型等都是字符串。Redis 虽然是使用标准 C 语言开发,但并没有直接使用 C语言中传统的字符串表示,而是自定义了一种字符串。这种字符串本身的结构比较简单,但功能却非常强大,称为简单动态字符串 SDS</p><p>注意,Redis 中的所有字符串并不都是 SDS,也会出现 C 字符串,C 字符串只会出现在字符串’字面常量’中,并且该字符串不可能发生变化</p></blockquote><h4 id="SDS-结构"><a href="#SDS-结构" class="headerlink" title="SDS 结构"></a>SDS 结构</h4><blockquote><p>buf[] 字节数组 + len 数组中已使用字节数量 + free 数组中未使用的字节数量</p></blockquote><h4 id="SDS-的优势"><a href="#SDS-的优势" class="headerlink" title="SDS 的优势"></a>SDS 的优势</h4><ol><li><p>防止’字符串长度获取’性能瓶颈</p></li><li><p>保证二进制安全(‘\0’)</p></li><li><p>减少内存再分配次数</p><blockquote><p>SDS 采用了 <strong>空间预分配策略</strong> 与 <strong>惰性空间释放策略</strong> 来避免内存再分配问题</p><p>空间预分配策略是指,每次 SDS 进行空间扩展时,程序不但为其分配所需空间,还会为其分配额外的未使用空间,以减少内存再分配次数,而额外分配的未使用空间大小取决于空间扩展后 SDS 的 len 属性</p><ul><li><p>如果 len 属性值小于 1M,那么分配的未使用空间 free 的大小与 len 属性值相同</p></li><li><p>如果 len 属性值大于等于 1M,那么分配的未使用空间 free 的大小固定是 1M</p></li></ul><p>SDS 对于空间释放采用的是惰性空间释放策略,该策略是指,SDS 字符串长度如果缩短,那么多出的未使用空间将暂时不释放,而是增加到 free 中,以使后期扩展 SDS 时减少内存再分配次数</p><p>如果要释放 SDS 的未使用空间,则可通过 sdsREmoveFreeSpace() 函数来释放</p></blockquote></li><li><p>兼容 C 函数</p><blockquote><p>SDS 底层数组 buf[] 中的字符串仍以空字符’\0’结尾</p></blockquote></li></ol><h3 id="集合的底层实现原理"><a href="#集合的底层实现原理" class="headerlink" title="集合的底层实现原理"></a>集合的底层实现原理</h3><hr><h4 id="两种实现的选择"><a href="#两种实现的选择" class="headerlink" title="两种实现的选择"></a>两种实现的选择</h4><blockquote><p>对于 Hash 与 ZSet 集合,其底层的实现实际有两种:压缩列表 zipList 与 跳跃列表 skipList,这两种实现对于用户来说是透明的,但用户写入不同的数据,系统会自动使用不同的实现。只有同时满足以配置文件 redis.conf 中相关集合元素数量阈值两个条件,使用的就是压缩列表 zipList,只要有一个条件不满足使用的就是跳跃列表 skipList</p><p>例如,对于 ZSet 集合中这两个条件如下</p><ul><li><p>集合元素个数小于 redis.conf 中 zset-max-ziplist-entries 属性的值,其默认值为 128</p></li><li><p>每个集合元素大小都小于 redis.conf 中 zset-max-ziplist-value 属性的值,其默认值为 64字节</p></li></ul></blockquote><h4 id="zipList"><a href="#zipList" class="headerlink" title="zipList"></a>zipList</h4><ol><li><p>什么是 zipList</p><blockquote><p>通常称为压缩列表,是一个经过 <em>特殊编码</em> 的用于存储字符串或整数的双向链表,其底层数据结构由三部分构成:head,entries 与 end,这三部分在内存上是连续存放的</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/ziplist.png"></p></li><li><p>head</p><blockquote><p>head 分为三部分</p><ol><li><p>zlbytes:占4个字节,用于存放 zipList 列表整体数据结构所占的字节数,包括 zlbytes 本身的长度</p></li><li><p>zltail:占4个字节,用于存放 zipList 中最后一个 entry 在整个数据结构中的偏移量(字节)。该数据的存在可以快速定位列表的尾 entry 位置,以方便操作</p></li><li><p>zllen:占2个字节,用于存放列表包含的 entry 个数。由于其只有 16 位,所以 zipList 最多可以含有的 entry 个数为 2^16 -1 = 65535 个</p></li></ol></blockquote></li><li><p>entries</p><blockquote><p>entries 是真正的列表,有很多的列表元素 entry 构成。由于不同的元素类型,数值的不同,从而导致每个 entry 的长度不同</p><p>每个 entry 由三部分组成</p><ol><li><p>prevlength:该部分用于记录上一个 entry 的长度,以实现逆序遍历,默认长度为 1 字节,只要上一个 entry 的长度 < 254 字节,prevlength 就占 1 字节,否则其会自动扩展为 3 字节长度</p></li><li><p>encoding:该部分用于标志后面的 data 的具体类型。如果 data 为整数类型,encoding 固定长度为 1 字节。如果 data 为字符串类型,则 encoding 长度可能会是 1 字节,2 字节或5 字节,data 字符串不同的长度,对应着不同的 encoding 长度</p></li><li><p>data:真正存储的数据。数据类型只能是整数类型或字符串类型。不同的数据占用的字节长度不同</p></li></ol></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/entry%E7%9A%84%E7%BB%93%E6%9E%84.png"></p></li><li><p>end</p><blockquote><p>end只包含一部分,称为zlend,占 1字节,值固定为255,表示一个 zipList 列表的结束</p></blockquote></li></ol><h4 id="listPack"><a href="#listPack" class="headerlink" title="listPack"></a>listPack</h4><blockquote><p>对于 zipList,实现复杂,为了逆序遍历,每个 entry 中包含前一个 entry 的长度,这样会导致在 zipList 中间修改或者插入 entry 时需要进行级联更新。在高并发的写操作场景下会极度降低 Redis 的性能。为了实现更紧凑,更快的解析,更简单的实现,重写实现了 zipList,并命名为 listPack</p><p>在 Redis 7.0 中,已经将 zipList 全部替换为了 listPack,但为了兼容性,在配置中也保留了 zipList 的相关属性</p></blockquote><ol><li><p>什么是 listPack</p><blockquote><p>listPack 与 zipList 的区别在 head 与每个 entry 的结构上。表示列表结束的end 与 zipList 的 zlend 是相同的,占一个字节,且8位全为1</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/listpack%E7%BB%93%E6%9E%84.png"></p></li><li><p>head</p><blockquote><p>head 由两部分构成</p><ol><li><p>totalBytes:占 4个字节,用于存放 listPack 列表整体数据结构所占的字节数,包括 totalBytes 本身长度</p></li><li><p>elemNum:占 2字节,用于存放列表包含的 entry 个数,其意义与 zipList 中 zllen 的相同</p></li></ol><p>与 zipList 的 head 相比,没有了记录最后一个 entry 偏移量的 zltail。</p></blockquote></li><li><p>entries</p><blockquote><p>与 zipList 不同的是,没有了记录前一个 entry 长度的 prevlength,而增加了记录当前 entry 长度的 element-total-len。而这个改变仍然可以实现逆序遍历,但却避免了由于在列表中间修改或插入 entry 时引发的级联更新</p><p>每个 entry 仍由三部分构成</p><ol><li><p>encoding:该部分用于标志后面的 data 的具体类型,如果 data 位整数类型,encoding 长度可能会是1,2,3,4,5 或 9字节,不同的字节长度,其标识位不同。如果 data 为字符串类型,则 encoding 长度可能会是 1,2 或 5 字节,data 字符串不同的长度,对应着不同的 encoding 长度</p></li><li><p>data</p></li><li><p>element-total-len:该部分用于记录当前 entry 的长度,用于实现逆序遍历。由于其特殊的记录方式,使其本身占有的字节数据可能会是1,2,3,4 或 5字节</p></li></ol></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/entry%E7%9A%84%E7%BB%86%E8%87%B4%E7%BB%93%E6%9E%84.png"></p></li></ol><h4 id="skipList"><a href="#skipList" class="headerlink" title="skipList"></a>skipList</h4><ol><li><p>什么时 skipList</p><blockquote><p>skipList 简称跳表,是一种 <strong>随机化</strong> 的数据结构,基于 <strong>并联</strong> 的链表,实现简单,查找效率较高,简单来说跳表也是链表的一种,只不过它在链表的基础上增加了跳跃功能,也正是这个功能,使得在查找元素时,能够提供较高的效率</p></blockquote></li><li><p>skipList 原理</p><p><img lazyload src="/images/loading.svg" data-src="/images/skiplist.png"></p><blockquote><p>每个节点随机分层,每多一层多一层链表,减少比较的次数</p></blockquote></li></ol><h4 id="quickList"><a href="#quickList" class="headerlink" title="quickList"></a>quickList</h4><ol><li><p>什么是 quickList</p><blockquote><p>quickList 快速列表,quickList 本身是一个双向无循环链表,他的每一个节点都是一个 zipList,从 Redis 3.2 版本开始,对于 List 的底层实现,使用 quickList 替代了 zipList 和 linkedList</p><p>quickList 本质上是 zipList 和 linkedList 的混合体,其将 linkedList 按段切分,每一段使用 zipList 来紧凑存储若干真正的数据元素,多个 zipList 之间使用双向指针串接起来,当然,对于每个 zipList 中最多可存放多大容量的数据元素,在配置文件中通过 list-max-ziplist-size 属性可以指定</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/quicklist.png"></p></li><li><p>检索操作</p><blockquote><p>对于 List 元素的检索,都是以其索引 index 为依据的。quickList 由一个个的 zipList 构成,每个 zipList 的 zllen 中记录的就是当前 zipList 中包含的 entry 的个数,即包含的真正数据元素的个数。根据要检索的 index,从 quickList 的头节点开始,逐个对 zipList 的 zllen 做 sum 求和,直到找到第一个求和后 sum 大于 index 的 zipList,那么要检索的这个元素就在这个 zipList 中</p></blockquote></li><li><p>插入操作</p><blockquote><p>假设要插入的元素的大小为 indexBytes</p><ol><li><p>若 indexBytes + zlBytes <= zipListMaxSize,则直接将元素插入到当前找到的 zipList 中即可</p></li><li><p>若 indexBytes + zlbytes > zipListMaxSize,且插入的位置在该 zipList 的首部位置,此时需要再查看该 zipList 的前一个 zipList 的大小 prev_zlBytes</p><ul><li><p>若 indexBytes + prev_zlBytes <= zipListMaxSize,则直接将元素插入到前一个 zipList 的尾部即可</p></li><li><p>若 indexBytes + prev_zlBytes > zipListMaxSize,则将元素自己构建为一个 zipList,连接到 quickList</p></li></ul></li><li><p>若 indexBytes + zlbytes > zipListMaxSize,且插入的位置在该 zipList 的尾部位置,此时需要再查看该 zipList 的下一个 zipList 的大小 next_zlBytes</p><ul><li><p>若 indexBytes + next_zlBytes <= zipListMaxSize,则直接将元素插入到下一个 zipList 的首部即可</p></li><li><p>若 indexBytes + next_zlBytes > zipListMaxSize,则将元素自己构建为一个 zipList,连接到 quickList</p></li></ul></li><li><p>若 indexBytes + zlbytes > zipListMaxSize,且插入的位置在该 zipList 的中间位置,此时需要将该 zipList 分割为两个 zipList 并连接到 quickList,然后再将元素直接插入到分割出的两个 zipList 中的前一个 zipList 的尾部位置</p></li></ol></blockquote></li><li><p>删除操作</p><blockquote><p>直接将节点删除即可</p><p>注意,如果 entry 只有一个元素,需要将 head 和 end 一起删除</p></blockquote></li></ol><h4 id="key-与-value-中元素的数量"><a href="#key-与-value-中元素的数量" class="headerlink" title="key 与 value 中元素的数量"></a>key 与 value 中元素的数量</h4><ul><li><p>Redis 最多可以处理 2^32 个 key,每个 Redis 实例至少可以处理 2.5 亿个 key</p></li><li><p>每个 Hash,List,Set,ZSet 集合都可以包含 2^32 个元素</p></li></ul><h3 id="Redis-事务"><a href="#Redis-事务" class="headerlink" title="Redis 事务"></a>Redis 事务</h3><hr><blockquote><p>Redis 的事务的本质是一组命令的批处理。这组命令在执行过程中会被顺序的,一次性全部执行完毕,只要没有出现语法错误,这组命令在执行期间是不会被中断</p></blockquote><h4 id="Redis-事务特性"><a href="#Redis-事务特性" class="headerlink" title="Redis 事务特性"></a>Redis 事务特性</h4><blockquote><p>redis 的事务仅保证了数据的一致性,不具有像 DBMS 一样的 ACID 特性</p></blockquote><ul><li><p>这组命令中的某些命令的执行失败不会影响其他命令的执行,不会引发 <strong>回滚</strong> 。即不具备原子性</p></li><li><p>这组命令通过乐观锁机制实现了简单的隔离性。没有复杂的隔离级别</p></li><li><p>这组命令的执行结果是被写入到内存的,是否持久取决于 Redis 的持久化策略,与事务无关</p></li></ul><h4 id="Redis-事务实现"><a href="#Redis-事务实现" class="headerlink" title="Redis 事务实现"></a>Redis 事务实现</h4><ol><li><p>三个命令</p><ul><li><p>multi:开启事务</p></li><li><p>exec:执行事务</p></li><li><p>discard:取消事务</p></li></ul><blockquote><p>exec 与 discard 不可共存</p></blockquote></li></ol><h2 id="Redis-持久化"><a href="#Redis-持久化" class="headerlink" title="Redis 持久化"></a>Redis 持久化</h2><hr><blockquote><p>Redis 具有持久化功能,其会按照设置以 <strong>快照</strong> 或 <strong>操作日志</strong> 的形式将数据持久化到磁盘</p><p>根据持久化使用技术不同,分为两种:RDB 与 AOF</p></blockquote><h3 id="持久化-钝化-基本原理"><a href="#持久化-钝化-基本原理" class="headerlink" title="持久化(钝化)基本原理"></a>持久化(钝化)基本原理</h3><hr><p><img lazyload src="/images/loading.svg" data-src="/images/Redis%E6%8C%81%E4%B9%85%E5%8C%96%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86.png"></p><blockquote><p>RDB 与 AOF 可以共存,RDB 是默认持久化技术,而如果开启 AOF,则 AOF 的优先级更高</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/%E6%8C%81%E4%B9%85%E5%8C%96%E6%96%87%E4%BB%B6%E7%9A%84%E5%8A%A0%E8%BD%BD.png"></p><h3 id="RDB-持久化"><a href="#RDB-持久化" class="headerlink" title="RDB 持久化"></a>RDB 持久化</h3><hr><blockquote><p>是指将内存中某一时刻的数据快照 <strong>全量</strong> 写入到指定的 rdb 文件的持久化技术。RDB 持久化是默认开启的。当 Redis 启动时会自动读取 RDB 快照文件,将数据从硬盘载入到内存,以恢复 Redis 关机前的数据库状态</p></blockquote><h4 id="持久化的执行"><a href="#持久化的执行" class="headerlink" title="持久化的执行"></a>持久化的执行</h4><blockquote><p>RDB 持久化的执行有三种方式:手动 save 命令,手动 bgsave 命令,与自动条件触发</p></blockquote><ol><li><p>手动 save 命令</p><blockquote><p>通过在 redis-cli 客户端中执行 save 命令可立即进行一次持久化保存。save 命令在执行期间会阻塞 redis-server 进程,直至持久化过程完毕。而在 redis-server 进程阻塞期间,redis 不能处理任何读写请求,无法对外提供服务</p></blockquote></li><li><p>手动 bgsave 命令</p><blockquote><p>通过在 redis-cli 客户端中执行 bgsave 命令可立即进行一次持久化保存。不同于 save 的是,可以后台允许 save。bgsave 命令会使服务器进程 redis-server 生成一个子进程,由该子进程负责完成保存过程。在子进程进行保存过程中,不会阻塞 redis-server 进程对客户端读写请求的处理</p></blockquote></li><li><p>自动条件触发</p><blockquote><p>本质仍是 bgsave 命令的执行。只不过是用户通过在配置文件中做相应的设置后,Redis 会根据设置信息自动调用 bgsave 命令执行。</p></blockquote></li><li><p>查看上次持久化的时间</p><ul><li>格式:<code>lastsave</code></li></ul></li></ol><h4 id="RDB-优化配置"><a href="#RDB-优化配置" class="headerlink" title="RDB 优化配置"></a>RDB 优化配置</h4><blockquote><p>参考配置文件 redis.conf 中的 SNAPSHOTTING 字段内容</p></blockquote><h4 id="RDB-文件结构"><a href="#RDB-文件结构" class="headerlink" title="RDB 文件结构"></a>RDB 文件结构</h4><blockquote><p>RDB 持久化文件 dump.rdb 分为五部分</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/RDB%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84.png"></p><ol><li><p>SOF</p><blockquote><p>SOF 是一个常量,一个字符串 REDIS,仅包含这五个字符,其长度为 5。用于 标识 RDB 文件的开始,以便在加载 RDB 文件时可以迅速判断出文件是否是 RDB 文件</p></blockquote></li><li><p>rdb_version</p><blockquote><p>一个整数,长度为 4 字节,标识 RDB 文件的版本号</p></blockquote></li><li><p>databases</p><blockquote><p>内部包含任意多个非空数据库。而每个 database 由三部分组成</p><ul><li><p>SODB:常量,占1字节,用于标识一个数据库的开始</p></li><li><p>db_number:数据库编号</p></li><li><p>key_value_pairs:当前数据库中的键值对数据</p></li></ul><p><img lazyload src="/images/loading.svg" data-src="/images/RDB%E6%96%87%E4%BB%B6%E4%B8%AD%E6%AF%8F%E4%B8%AA%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E7%BB%93%E6%9E%84.png"></p><p>每个 key_value_pairs 又由很多个用于描述键值对的数据构成</p><ul><li><p>VALUE_TYPE:常量,占1字节,用于标识该键值对中 value 的类型</p></li><li><p>EXPIRETIME_UNIT:常量,占1字节,用于标识过期时间的单位是秒还是毫秒</p></li><li><p>time:当前 key-value 的过期时间</p></li></ul><p><img lazyload src="/images/loading.svg" data-src="/images/%E5%B8%A6%E4%B8%8E%E4%B8%8D%E5%B8%A6%E6%97%B6%E9%97%B4%E7%9A%84%E9%94%AE%E5%80%BC%E5%AF%B9%E5%AF%B9%E6%AF%94.png"></p></blockquote></li><li><p>EOF</p><blockquote><p>一个常量,长度为 1,用于标识 RDB 数据的结束,校验和的开始</p></blockquote></li><li><p>check_sum</p><blockquote><p>校验和 check_sum 用于判断 RDB 文件中的内容是否出现数据异常,其采用的是 CRC 校验算法</p></blockquote></li></ol><h4 id="RDB-持久化过程"><a href="#RDB-持久化过程" class="headerlink" title="RDB 持久化过程"></a>RDB 持久化过程</h4><blockquote><p>对于 Redis 默认的 Redis 持久化,在进行 bgsave 持久化时,redis-server 进程会 fork 出一个 bgsave 子进程,由该子进程以异步方式负责完成持久化。而在持久化过程中,redis-server 进程不会阻塞,其会继续接收并处理用户的读写请求。</p><p>bgsave 子进程的详细工作原理如下</p><ul><li><p>由于子进程可以继承父进程的所有资源,且父进程不能拒绝子进程的继承权。所以,bgsave 子进程有权读取到 redis-server 进程写入到内存中的用户数据,使得将内存数据持久化到 dump.rdb 成为可能</p></li><li><p>bgsave 子进程在持久化时首先会将内存中得全量数据 copy 到磁盘中的一个 RDB 临时文件,copy 结束后,在将该文件 rename 为 dump.rdb,替换掉原来的同名文件</p></li><li><p>不过,在进行持久化过程中,如果 redis-server 进程接收到了用户写请求,则系统会将内存中发生数据修改的物理块 copy 出一个副本。等内存中的全量数据 copy 结束后,会再将副本中的数据 copy 到 RDB 临时文件。这个副本的生成是由于 Linux 系统的写时复制技术(Copy-On-Write)实现的</p></li></ul></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/RDB%E6%8C%81%E4%B9%85%E5%8C%96%E8%BF%87%E7%A8%8B.png"></p><h3 id="AOF-持久化"><a href="#AOF-持久化" class="headerlink" title="AOF 持久化"></a>AOF 持久化</h3><hr><blockquote><p>AOF,是指 Redis 将每一次的写操作都以日志的形式记录到一个 AOF 文件中的持久性技术。当需要恢复内存数据时,将这些写操作重新执行一次,便会恢复到之前的内存数据状态</p></blockquote><h4 id="AOF-基础配置"><a href="#AOF-基础配置" class="headerlink" title="AOF 基础配置"></a>AOF 基础配置</h4><ol><li><p>AOF 的开启</p><blockquote><p>在 redis.conf 配置文件内将 <code>appendonly</code> 字段设置为 yes</p></blockquote></li><li><p>文件名配置</p><blockquote><p>包括三类多个文件</p><ul><li><p>基本文件:可以是 RDF 格式也可以是 AOF 格式。其存放的内容是由 RDB 转为 AOF 当时内存的快照数据。该文件可以有多个</p></li><li><p>增量文件:以操作日志形式记录转为 AOF 后的写入操作。该文件可以有多个</p></li><li><p>清单文件:用于维护 AOF 文件的创建顺序,保障激活时的应用顺序。该文件只有一个</p></li></ul></blockquote></li><li><p>混合式持久化开启</p><blockquote><p>对于基本文件可以是 RDB 格式也可以是 AOF 格式。通过 <code>aof-use-rdb-preamble</code> 属性可以选择。其默认值是 yes,即默认 AOF 持久化的基本文件为 rdb 格式文件,也就是默认采用混合式持久化</p></blockquote></li></ol><h4 id="AOF-文件格式"><a href="#AOF-文件格式" class="headerlink" title="AOF 文件格式"></a>AOF 文件格式</h4><ol><li><p>Redis 协议</p><blockquote><p>增量文件扩展名为.aof,采用 AOF 格式。AOF 格式其实就是 Redis 通讯协议格式,AOF 持久化文件的本质就是基于 Redis 通讯协议的文本,将命令以纯文本的方式写入到文件中</p><p>Redis 协议规定,Redis 文本是以行来划分,每行以\r\n 行结束。每一行都有一个我消息头,以表示消息类型。消息头由六种不同的符号表示</p><ul><li><p>(+):表示一个正确的状态信息</p></li><li><p>(-):表示一个错误信息</p></li><li><p>(*):表示消息体总共有多少行,不包括当前行</p></li><li><p>($):表示下一行消息数据的长度,不包括换行符长度\r\n</p></li><li><p>(空):表示一个消息数据</p></li><li><p>(:):表示返回一个数值</p></li></ul></blockquote></li></ol><h4 id="Rewrite-机制"><a href="#Rewrite-机制" class="headerlink" title="Rewrite 机制"></a>Rewrite 机制</h4><blockquote><p>为了防止 AOF 文件由于太大而占用大量的磁盘空间,降低性能,Redis 引入了 Rewrite 机制来对 AOF 文件进行压缩</p></blockquote><ol><li><p>何为 rewrite</p><blockquote><p>所谓 Rewrite 其实就是对 AOF 文件进行重写整理。当 Rewrite 开启后,主进程 redis-server 创建出一个子进程 bgrewriteaof,由该子进程完成 rewrite 过程。其首先对现有 aof 文件进行 rewrite 计算,将计算结果写入到一个临时文件,写入完毕后,再 rename 该临时文件为原 aof 文件名,覆盖原有文件</p></blockquote></li><li><p>rewrite 计算</p><blockquote><p>其遵循以下策略</p><ul><li><p>读操作命令不写入文件</p></li><li><p>无效命令不写入文件</p></li><li><p>过期数据不写入文件</p></li><li><p>多条命令合并写入文件</p></li></ul></blockquote></li><li><p>手动开启 rewrite</p><ul><li>格式 <code>bgrewriteaof</code></li></ul></li></ol><h4 id="AOF-优化配置"><a href="#AOF-优化配置" class="headerlink" title="AOF 优化配置"></a>AOF 优化配置</h4><ol><li><p>appendfsync</p><blockquote><p>当客户端提交写操作命令后,该命令就会写入到 aof_buf 中,而 aof_buf 中的数据持久化到磁盘 AOF 文件的过程称为数据同步。</p><p>可以采用以下三种不同的数据同步策略</p></blockquote><ul><li><p>always:写操作命令写入 aof_buf 后会立即调用 fsync() 系统函数,将其追加到 AOF 文件。该策略效率较低,但相对比较安全,不会丢失太多的数据。(生产情况不要使用)</p></li><li><p>no:写操作命令写入 aof_buf 后什么都不做,不会调用 fsync() 函数。而将 aof_buf 中的数据同步磁盘的操作由操作系统负责。Linux 系统默认同步周期为 30 秒,效率较高。</p></li><li><p>everysec:默认策略。写操作命令写入 aof_buf 后并不直接调用 fsync(),而是每秒调用一次 fsync() 系统函数来完成同步。该策略兼顾了性能与安全,是一种折中方案</p></li></ul></li><li><p>no-appendfsync-on-rewrite</p><blockquote><p>前提是 appendfsync 设置为 always 或 everysec</p><p>该属性用于指定,当主进程创建了子进程正在执行 bgsave 或 bgrewriteaof 时,主进程是否不调用 fsync() 来做数据同步</p><p>如果调用 fsync(),在需要同步的数据量非常大时,会阻塞主进程对外提供服务,可以将此命令设置为 yes,关闭 rewrite 操作,但这样也就和 <code>appendfsync no</code> 一样了</p></blockquote></li><li><p>aof-rewrite-incremental-fsync</p><blockquote><p>当 bgrewriteaof 在执行过程也是先将 rewrite 计算的结果写入到了 aof_rewrite_buf 缓存中,然后当缓存中数据达到一定量后就会调用 fsync() 进行刷盘操作,即数据同步,将数据写入到临时文件。该属性用于控制 fsync() 每次刷盘的数据量最大不超过 4MB。这样可以避免由于单次刷盘量过大而引发长时间堵塞</p></blockquote></li><li><p>aof-timestamp-enabled</p><blockquote><p>该属性设置为 yes 则会开启在 AOF 文件中增加时间戳的显示功能,可方便按照时间对数据进行恢复。但该方法可能会与 AOF 解析器不兼容,所以默认值为 no,不开启</p></blockquote></li><li><p>aof-load-truncated</p><blockquote><p>AOF 文件可能会在加载到内存时被截断(当操作系统崩溃时,极有可能发生。但是当 Redis 崩溃,但系统没有崩溃时,不会发生这个情况),如果此命令设置为 yes,会重新加载 AOF 文件</p></blockquote></li></ol><h4 id="AOF-持久化过程"><a href="#AOF-持久化过程" class="headerlink" title="AOF 持久化过程"></a>AOF 持久化过程</h4><p><img lazyload src="/images/loading.svg" data-src="/images/AOF%E6%8C%81%E4%B9%85%E5%8C%96%E8%BF%87%E7%A8%8B.png"></p><h3 id="RDB-与-AOF-对比"><a href="#RDB-与-AOF-对比" class="headerlink" title="RDB 与 AOF 对比"></a>RDB 与 AOF 对比</h3><hr><h4 id="RDB-优势与不足"><a href="#RDB-优势与不足" class="headerlink" title="RDB 优势与不足"></a>RDB 优势与不足</h4><h5 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h5><ul><li><p>RDB 文件较小(快照)</p></li><li><p>数据恢复快</p></li></ul><h5 id="不足"><a href="#不足" class="headerlink" title="不足"></a>不足</h5><ul><li><p>数据安全性较差</p></li><li><p>写时复制会降低性能</p></li><li><p>RDB 文件可读性较差</p></li></ul><h4 id="AOF-优势与不足"><a href="#AOF-优势与不足" class="headerlink" title="AOF 优势与不足"></a>AOF 优势与不足</h4><h5 id="优势-1"><a href="#优势-1" class="headerlink" title="优势"></a>优势</h5><ul><li><p>数据安全性高</p></li><li><p>AOF 文件可读性高</p></li></ul><h5 id="不足-1"><a href="#不足-1" class="headerlink" title="不足"></a>不足</h5><ul><li><p>AOF 文件比较大(日志)</p></li><li><p>写操作会影响性能</p></li><li><p>数据恢复较慢(重新执行命令)</p></li></ul><h4 id="持久化技术的选型"><a href="#持久化技术的选型" class="headerlink" title="持久化技术的选型"></a>持久化技术的选型</h4><blockquote><p>官方推荐使用 RDB 与 AOF 混合式持久化。</p><p>若对数据安全性要求不高,则推荐使用纯 RDB 持久化方式</p><p>不推荐使用纯 AOF 持久化方式。</p><p>若 Redis 仅用于缓存,无需使用任何持久化技术</p></blockquote><h2 id="Redis-主从集群"><a href="#Redis-主从集群" class="headerlink" title="Redis 主从集群"></a>Redis 主从集群</h2><hr><blockquote><p>为了避免 Redis 的单点故障问题,我们可以搭建一个 Redis 集群,将数据备份到集群中的其他节点上。若一个 Redis 节点宕机,则由集群中的其他节点顶上</p></blockquote><h3 id="主从集群搭建"><a href="#主从集群搭建" class="headerlink" title="主从集群搭建"></a>主从集群搭建</h3><hr><blockquote><p> Redis 的主从集群是一个”一主多从”的读写分离集群。集群中的 Master 节点负责处理客户端的读写请求,而 Slave 节点仅能处理客户端的读请求。之所以要将集群搭建为读写分离模式,主要原因是对于数据库集群,写操作压力一般较小,压力大多来源于读操作请求。所以,只有一个接待你负责处理写操作请求即可</p></blockquote><h4 id="伪集群搭建与配置"><a href="#伪集群搭建与配置" class="headerlink" title="伪集群搭建与配置"></a>伪集群搭建与配置</h4><blockquote><p>在采用单线程 IO 模型时,为了提高处理器的利用率,一般会在一个主机中安装多台 Redis,构建一个 Redis 主从伪集群。</p><p>演示搭建配置:一个 Master 与 两个 Slave</p></blockquote><ol><li><p>复制 redis.conf</p></li><li><p>修改 redis.conf</p><ul><li><p>设置 masterauth</p><blockquote><p>主从 Redis 数据库的密码需要一致</p></blockquote></li><li><p>设置 repl-disable-tcp-nodelay</p><blockquote><p>该属性用于设置是否禁用 TCP 特性 tcp-nodelay。设置为 yes 则禁用,此时 Master 与 Slave 间的通信会产生延迟,但使用的 TCP 包数量会较少,占用的网络带宽会较小。反之,则网络延时变小,TCP 包数量较多,占用网络带宽较大</p></blockquote><p>tcp-nodelay</p><blockquote><p>为了充分复用网络带宽,TCP 总是希望发送尽可能大的数据块。为了达到该目的,TCP 中使用了一个叫 Nagle 的算法。</p><p>网络在接收到要发送的数据后,不直接发送,而是等待数据量足够大时在一次性发送出去(延迟发送)</p><p>此命令是 TCP 协议中 Nagle 算法的开关</p></blockquote></li></ul></li><li><p>新建配置文件</p><blockquote><p>将 redis.conf 中的共有属性,写入新的配置文件中,并引用源 redis.conf 文件</p></blockquote><ul><li><p><code>include redis.conf</code> 引用源文件</p></li><li><p><code>pidfile /var/run/redis_xxx.pid <redis_xxx> 不能和源 redis.conf 中的相同</code> </p></li><li><p><code>port <端口号> 自定义端口号</code></p></li><li><p><code>dbfilename dumpxxxx.rdb <dumpxxxx.rdb> 不能和源 redis.conf 中的相同</code></p></li><li><p><code>appendfilename appendonlyxxxx.aof <appendonlyxxxx.aof> 不能相同</code> AOF文件配置</p></li><li><p><code>replice-priority xx <xx> 为优先级数值,优先级越小,选举时优先为主 redis</code></p></li></ul></li><li><p>设置主从关系</p><blockquote><p>当主从库宕机后重新启动,需要重新设置主从关系</p></blockquote><ul><li><p>格式 <code>slaveof <ip> <port></code></p><blockquote><p>设置本机的主 Redis</p></blockquote></li></ul></li></ol><h4 id="分级管理"><a href="#分级管理" class="headerlink" title="分级管理"></a>分级管理</h4><blockquote><p>若 Redis 主从集群中的 Slave 较多时,它们的数据同步过程会对 Master 形成较大的性能压力。此时可以对 Slave 进行分级管理</p><p>使用 slaveof 低级 Slave 指向高级 Slave 即可</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="/images/Redis%E4%B8%BB%E4%BB%8E%E9%9B%86%E7%BE%A4%E7%9A%84%E5%88%86%E7%BA%A7%E7%AE%A1%E7%90%86.png"></p><h4 id="容灾冷处理"><a href="#容灾冷处理" class="headerlink" title="容灾冷处理"></a>容灾冷处理</h4><blockquote><p>在 Master/Slave 的 Redis 集群中,如果 Master 宕机有两种处理方式</p><ul><li><p>通过手动角色调整,使 Slave 晋升为 Master 的 <strong>冷处理</strong> </p></li><li><p>使用哨兵模式,实现 Redis 集群的高可用 HA,即 <strong>热处理</strong></p></li></ul></blockquote><h3 id="主从复制原理"><a href="#主从复制原理" class="headerlink" title="主从复制原理"></a>主从复制原理</h3><hr><blockquote><p>当一个 Redis 节点(slave 节点)接收到类似 slaveof ip port 的命令后直至其可以从 master 持续复制数据</p></blockquote><h4 id="主从复制过程"><a href="#主从复制过程" class="headerlink" title="主从复制过程"></a>主从复制过程</h4><ol><li><p>保存 master 地址</p><blockquote><p>当 slave 接收到 slaveof 指令后,slave 会立即将新的 master 的地址保存下来</p></blockquote></li><li><p>建立连接</p><blockquote><p>slave 中维护着一个定时任务,该定时任务会尝试着发送请求,与该 master 建立 socket 连接。</p><ul><li><p>如果连接成功,slave会发送 ping 命令进行首次通信。</p></li><li><p>如果连接失败,重新让定时任务发出连接请求。</p></li><li><p>如果在接收请求时接收到 slaveof no one 命令,slave 转 master 命令,连接立即停止</p></li></ul></blockquote></li><li><p>身份验证</p><blockquote><p>master 在接收到 slave 的 ping 命令后对 slave 进行身份验证</p><ul><li><p>如果验证通过,master 向 slave 发送连接成功响应</p></li><li><p>如果验证失败,拒绝连接</p></li></ul></blockquote></li><li><p>数据同步</p><blockquote><p>slave 接收到 master 的连接成功响应后,slave 向 master 发出数据同步请求。master 在接收到数据同步请求后 fork 出一个子进程进行数据持久化。持久化完毕后 master 再 fork 出一个子进程将 master 的持久化文件发送给 slave。slave 接收到 master 的数据并写入到本地持久化文件</p><ul><li>如果数据同步过程中 master 主进程又有写操作发生,master 将数据写入到本地内存的同时又将数据写入到同步缓存。当 master 的持久化文件数据发送完毕后,master 将同步缓存中的数据发送给 slave(数据补偿),slave 将数据追加到本地持久化文件中,数据同步完毕</li></ul></blockquote></li><li><p>恢复数据</p><blockquote><p>数据同步完毕后 slave 读取本地持久化文件,恢复内存数据,对外服务。对外服务中 master 持续接收到写操作,会以增量方式发送给 slave</p></blockquote></li></ol><p><img lazyload src="/images/loading.svg" data-src="/images/%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6%E6%81%A2%E5%A4%8D%E6%95%B0%E6%8D%AE1.png"></p><p><img lazyload src="/images/loading.svg" data-src="/images/%E4%B8%BB%E4%BB%8E%E5%A4%8D%E5%88%B6%E6%81%A2%E5%A4%8D%E6%95%B0%E6%8D%AE2.png"></p>]]></content>
<summary type="html"><h2 id="Redis-概述"><a href="#Redis-概述" class="headerlink" title="Redis 概述"></a>Redis 概述</h2><hr>
<h3 id="NoSQL"><a href="#NoSQL" class="heade</summary>
<category term="Redis Study" scheme="https://intyouss.github.io/tags/Redis-Study/"/>
</entry>
<entry>
<title>MySQL</title>
<link href="https://intyouss.github.io/2023/04/12/MySQL/"/>
<id>https://intyouss.github.io/2023/04/12/MySQL/</id>
<published>2023-04-12T03:42:33.000Z</published>
<updated>2023-04-15T10:33:43.897Z</updated>
<content type="html"><![CDATA[<h2 id="SQL"><a href="#SQL" class="headerlink" title="SQL"></a>SQL</h2><hr><blockquote><p>结构化查询语言,用于存取,查询,更新数据以及管理关系型数据库系统</p></blockquote><ul><li><p>在不同的数据库产品中遵循SQL的通用规范,但是也对SQL有一些不同的改进,形成了一些数据库的专有指令</p><ul><li><p>MySQL: limit</p></li><li><p>SQLServer: top</p></li><li><p>Oracle: rownum</p></li></ul></li></ul><h3 id="SQL分类"><a href="#SQL分类" class="headerlink" title="SQL分类"></a>SQL分类</h3><hr><blockquote><p>根据SQL指令完成的数据库操作的不同,可以将SQL指令分类</p></blockquote><ul><li><p>DDL Data Definition Language 数据定义语言</p><ul><li>用于完成对数据库对象(数据库,数据表,视图,索引等)的创建,删除,修改</li></ul></li><li><p>DML Data Manipulation Language 数据操作语言</p><ul><li>用于完成对数据表中的数据的添加,删除,修改操作</li></ul></li><li><p>DQL Data Query Language 数据查询语言</p><ul><li>用于将数据表中的数据查询出来</li></ul></li><li><p>DCL Data Control Language 数据控制语言</p><ul><li>用于完成事务管理等控制性操作</li></ul></li></ul><h3 id="SQL基本语法"><a href="#SQL基本语法" class="headerlink" title="SQL基本语法"></a>SQL基本语法</h3><hr><blockquote><p>在MySQL Command Line Client 或者 navicat 等工具中都能编写SQL指令</p></blockquote><ul><li><p>SQL 指令不区分大小写</p></li><li><p>每条SQL表达式结束之久都以<code>;</code>结束</p></li><li><p>SQL 关键字之间以空格进行分割</p></li><li><p>SQL 之间可以不限制换行(可以有空格的地方就可以换行)</p></li></ul><h3 id="DDL"><a href="#DDL" class="headerlink" title="DDL"></a>DDL</h3><hr><h4 id="DDL-数据库操作"><a href="#DDL-数据库操作" class="headerlink" title="DDL-数据库操作"></a>DDL-数据库操作</h4><hr><blockquote><p>使用DDL语句可以创建,查询,修改,删除数据库</p></blockquote><h5 id="查询数据库"><a href="#查询数据库" class="headerlink" title="查询数据库"></a>查询数据库</h5><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">show</span> databases;</span><br><span class="line"></span><br><span class="line">## 显示此数据库创建<span class="keyword">SQL</span>指令</span><br><span class="line"><span class="keyword">show</span> <span class="keyword">create</span> database <span class="operator"><</span>dbName<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div><h5 id="创建数据库"><a href="#创建数据库" class="headerlink" title="创建数据库"></a>创建数据库</h5><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 创建数据库 dbName表示创建的数据库名称</span><br><span class="line"><span class="keyword">create</span> database <span class="operator"><</span>dbName<span class="operator">></span>;</span><br><span class="line"></span><br><span class="line">## 创建数据库,防止数据库存在报错,当不存在时创建</span><br><span class="line"><span class="keyword">create</span> database if <span class="keyword">not</span> <span class="keyword">exists</span> <span class="operator"><</span>dbName<span class="operator">></span>;</span><br><span class="line"></span><br><span class="line">## 在创建数据库的同时指定数据库的字符集(编码格式,utf8可选)</span><br><span class="line"><span class="keyword">create</span> database <span class="operator"><</span>dbName<span class="operator">></span> <span class="type">character</span> <span class="keyword">set</span> <span class="operator"><</span>utf8<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div><h5 id="修改数据库"><a href="#修改数据库" class="headerlink" title="修改数据库"></a>修改数据库</h5><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 修改数据库的字符集</span><br><span class="line"><span class="keyword">alter</span> database <span class="operator"><</span>dbName<span class="operator">></span> <span class="type">character</span> <span class="keyword">set</span> <span class="operator"><</span>utf8<span class="operator">></span>; # utf8 gbk</span><br></pre></td></tr></table></figure></div><h5 id="删除数据库"><a href="#删除数据库" class="headerlink" title="删除数据库"></a>删除数据库</h5><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 删除数据库,防止数据库不存在报错</span><br><span class="line"><span class="keyword">drop</span> database if <span class="keyword">exists</span> <span class="operator"><</span>dbName<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div><h5 id="使用-x2F-切换数据库"><a href="#使用-x2F-切换数据库" class="headerlink" title="使用/切换数据库"></a>使用/切换数据库</h5><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">use <span class="operator"><</span>dbName<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div><ul><li><p>创建数据表</p><blockquote><p>数据表实际上就是一个二位的表格,是由多列组成,表格的每一列时表格的一个字段</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> <span class="operator"><</span>dbTableName<span class="operator">></span>(</span><br><span class="line"> <span class="operator"><</span>TableName<span class="operator">></span> <span class="type">char</span>(<span class="number">8</span>) <span class="keyword">not</span> <span class="keyword">null</span> <span class="keyword">unique</span>, # 格式,下为例子</span><br><span class="line"> T_name <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> T_gender <span class="type">char</span>(<span class="number">2</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> T_age <span class="type">int</span> <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> T_tel <span class="type">char</span>(<span class="number">11</span>) <span class="keyword">not</span> <span class="keyword">null</span> <span class="keyword">unique</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure></div></li><li><p>查询数据表列</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> tables; </span><br></pre></td></tr></table></figure></div></li><li><p>查询表结构</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">desc</span> <span class="operator"><</span>tableName<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div></li><li><p>删除数据表</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">drop</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span>;</span><br><span class="line"></span><br><span class="line">## 当数据表存在时删除数据表</span><br><span class="line"><span class="keyword">drop</span> <span class="keyword">table</span> if <span class="keyword">exists</span> <span class="operator"><</span>tableName<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div></li><li><p>修改数据表</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 修改表名</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span> rename <span class="keyword">to</span> <span class="operator"><</span>newTableName<span class="operator">></span>;</span><br><span class="line"></span><br><span class="line">## 数据表也是有字符集的,默认字符集与数据库一致</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="type">character</span> <span class="keyword">set</span> <span class="operator"><</span>utf8<span class="operator">></span>;</span><br><span class="line"></span><br><span class="line">## 添加字段</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">add</span> <span class="operator"><</span>columnName<span class="operator">></span> <span class="type">varchar</span>(<span class="number">200</span>);</span><br><span class="line"></span><br><span class="line">## 修改字段的列表和类型</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span> change <span class="operator"><</span>oldColumnName<span class="operator">></span> <span class="operator"><</span>newColumnName<span class="operator">></span> <span class="operator"><</span>type<span class="operator">></span>;</span><br><span class="line"></span><br><span class="line">## 只修改字段类型</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span> modify <span class="operator"><</span>columnName<span class="operator">></span> <span class="operator"><</span>newType<span class="operator">></span>;</span><br><span class="line"></span><br><span class="line">## 删除字段</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">drop</span> <span class="operator"><</span>columnName<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="MySQL-数据类型"><a href="#MySQL-数据类型" class="headerlink" title="MySQL 数据类型"></a>MySQL 数据类型</h3><hr><ul><li><p>数值类型</p><blockquote><p>tinyint smallint mediumint int/integer bigint float double decimal</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">table</span> books(</span><br><span class="line"> stock tinyint </span><br><span class="line">);</span><br></pre></td></tr></table></figure></div></blockquote></li><li><p>字符类型</p></li><li><p>日期类型</p><blockquote><p>在MySQL数据库中,我们可以使用字符串来存储时间,但是如果我们需要基于时间字段进行查询操作(查询在某个时间段内的数据)就不变于查询实现</p></blockquote></li></ul><h3 id="字段约束"><a href="#字段约束" class="headerlink" title="字段约束"></a>字段约束</h3><hr><blockquote><p>在创建数据表的时候,指定的对数据表的列的数据限制性的要求(对表的列中的数据进行限制)</p></blockquote><ul><li><p>保证数据有效性</p></li><li><p>保证数据完整性</p></li><li><p>保证数据正确性</p></li></ul><h4 id="字段常见的约束"><a href="#字段常见的约束" class="headerlink" title="字段常见的约束"></a>字段常见的约束</h4><hr><ul><li><p>非空约束</p><blockquote><p>限制此列的值必须提供,不能null</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">table</span> books(</span><br><span class="line"> a <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">not</span> <span class="keyword">null</span> # 添加<span class="keyword">not</span> <span class="keyword">null</span>字段</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div></li><li><p>唯一约束</p><blockquote><p>在表中的多条数据,此列的值不能重复</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">table</span> books(</span><br><span class="line"> a <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">unique</span> # 添加<span class="keyword">unique</span>字段</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div></li><li><p>主键约束</p><blockquote><p>非空+唯一,能够唯一标识数据表中的一条数据</p><p>主键 ——数据表中记录的唯一标识,在一张表中只能有一个主键(主键可以是一列,也可以是多个列的集合)</p><p>当一个字段声明为主键之后,添加数据时</p><ul><li><p>此字段数据不能为空</p></li><li><p>此字段数据不能重复</p></li></ul></blockquote><ul><li><p>创建表时添加主键约束</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">table</span> books(</span><br><span class="line"> a <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">primary</span> key # 添加<span class="keyword">primary</span> key字段</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div><p>或</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">create</span> <span class="keyword">table</span> books(</span><br><span class="line"> a <span class="type">varchar</span>(<span class="number">20</span>),</span><br><span class="line"> b <span class="type">int</span>,</span><br><span class="line"> <span class="keyword">primary</span> key(a) # 最后定义</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div></li><li><p>删除数据表主键约束</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">drop</span> <span class="keyword">primary</span> key;</span><br></pre></td></tr></table></figure></div></li><li><p>创建表之后添加主键约束</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> <span class="operator"><</span>tableName<span class="operator">></span> modify <span class="operator"><</span>cloumnName<span class="operator">></span> <span class="operator"><</span>type<span class="operator">></span> <span class="keyword">primary</span> key</span><br></pre></td></tr></table></figure></div></li><li><p>主键自增长</p><blockquote><p>在我们创建一张数据表时,如果数据表中有列可以作为主键(例如: 学生表的学号)我们可以直接使这个列为主键;当有些数据表中没有合适的列作为主键时,我们可以额外定义一个与记录本身无关的列(ID)作为主键,此列无具体含义主要用于表示一条记录,在MySQL中我们可以将此列定义为int,同时设置自动增长,当我们向数据表中新增一条记录时,无需提供ID列的值,它会自动生成</p></blockquote><ul><li><p>定义主键自动增长</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">table</span> types(</span><br><span class="line"> # 关键词添加 auto_increment,一定要在主键中,字段名自定义</span><br><span class="line"> <span class="operator"><</span>type_id<span class="operator">></span> <span class="type">int</span> <span class="keyword">primary</span> key auto_increment,</span><br><span class="line"> type_name <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> type_remark <span class="type">varchar</span>(<span class="number">100</span>)</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div><p>注意:自动增长从1开始,每添加一条记录,自动的增长的列会自+1,当我们把某条记录删除之后再添加数据,自动增长的数据也不会重复生成(自动增长只保证唯一性,不保证连续性)</p></li></ul></li><li><p>联合主键</p><blockquote><p>将数据表中的多列组合在一起设置为表的主键</p></blockquote><ul><li><p>定义联合主键</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">table</span> grades(</span><br><span class="line"> stu_num <span class="type">char</span>(<span class="number">8</span>),</span><br><span class="line"> course_id <span class="type">int</span>,</span><br><span class="line"> score <span class="type">int</span>,</span><br><span class="line"> <span class="keyword">primary</span> key(stu_num, course_id) # 添加联合主键</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div><p>注意:在实际企业项目的数据库设计中,联合主键使用的频率并不高;当一个数据表中没有明确的字段可以作为主键时,我们可以额外添加一个ID字段作为主键</p></li></ul></li></ul></li><li><p>外键约束:建立不同表之间的关联关系</p></li></ul><h3 id="DML"><a href="#DML" class="headerlink" title="DML"></a>DML</h3><hr><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> T(</span><br><span class="line"> T_num <span class="type">char</span>(<span class="number">8</span>) <span class="keyword">primary</span> key,</span><br><span class="line"> T_name <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> T_gender <span class="type">char</span>(<span class="number">2</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> T_age <span class="type">int</span> <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> T_tel <span class="type">char</span>(<span class="number">11</span>) <span class="keyword">not</span> <span class="keyword">null</span> <span class="keyword">unique</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure></div><h4 id="插入数据"><a href="#插入数据" class="headerlink" title="插入数据"></a>插入数据</h4><hr><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 像数据表中指定的列添加数据(不允许为空的列必须提供数据)</span><br><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> <span class="operator"><</span>tableName<span class="operator">></span>(columnName, columnName ...) </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">value</span> (value1, value2 ...);</span><br></pre></td></tr></table></figure></div><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 数据表名后的字段名列表顺序可以不与表中一致</span><br><span class="line">## 但是<span class="keyword">value</span>中值的顺序必须与表名后的字段名顺序对应</span><br><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> T(T_num, T_name, T_gender, T_age, T_tel) </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">values</span>(<span class="string">'12'</span>, <span class="string">'1234'</span>, <span class="string">'12'</span>, <span class="number">8</span>, <span class="string">'123'</span>);</span><br><span class="line"></span><br><span class="line">## 当要向表中的所有列添加数据时,数据表名后面的字段列表可以省略,</span><br><span class="line">## 但是<span class="keyword">value</span>中的值的顺序要与数据表定义的字段顺序保持一致</span><br><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> T <span class="keyword">values</span>(<span class="string">'12'</span>, <span class="string">'1234'</span>, <span class="string">'12'</span>, <span class="number">8</span>, <span class="string">'123'</span>);</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="删除数据"><a href="#删除数据" class="headerlink" title="删除数据"></a>删除数据</h4><hr><blockquote><p>从数据表中删除满足特定条件(所有)的数据</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">delete</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> [<span class="keyword">where</span> conditions];</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="修改数据"><a href="#修改数据" class="headerlink" title="修改数据"></a>修改数据</h4><hr><blockquote><p>对数据表中以及添加的记录进行修改</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">update</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">set</span> columnName<span class="operator">=</span><span class="keyword">value</span>, columnName1<span class="operator">=</span>value2...</span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> [<span class="keyword">where</span> conditions]</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="DQL"><a href="#DQL" class="headerlink" title="DQL"></a>DQL</h3><hr><blockquote><p>从数据表中提取满足特定条件的记录</p><ul><li><p>单表查询</p></li><li><p>多表联合查询</p></li></ul></blockquote><h4 id="查询基础"><a href="#查询基础" class="headerlink" title="查询基础"></a>查询基础</h4><hr><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">select</span> 关键字后指定要显示查询到的记录的那些列</span><br><span class="line"><span class="keyword">select</span> columnName1[,columnName2,...] <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span>;</span><br><span class="line"></span><br><span class="line">## 查询所有列</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span>; # 不建议使用</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="where-子句"><a href="#where-子句" class="headerlink" title="where 子句"></a>where 子句</h4><hr><blockquote><p>在删除,修改及查询的语句后都可以添加where子句(条件),用于筛选满足条件的数据进行操作</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">delete</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">where</span> conditions</span><br><span class="line"></span><br><span class="line"><span class="keyword">update</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">set</span> ... <span class="keyword">where</span> conditions</span><br><span class="line"></span><br><span class="line"><span class="keyword">select</span> ... <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">where</span> conditions</span><br></pre></td></tr></table></figure></div><h4 id="多条件查询"><a href="#多条件查询" class="headerlink" title="多条件查询"></a>多条件查询</h4><hr><blockquote><p>在 where 子句中,可以将多个条件通过逻辑预算符进行连接,通过多个条件来筛选要操作的数据</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## <span class="keyword">and</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">where</span> <span class="operator"><</span>columnName<span class="operator">>=</span><span class="keyword">value</span> <span class="keyword">and</span> <span class="operator"><</span>columnName<span class="operator">>=</span><span class="keyword">value</span></span><br><span class="line"></span><br><span class="line">## <span class="keyword">or</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">where</span> <span class="operator"><</span>columnName<span class="operator">>=</span><span class="keyword">value</span> <span class="keyword">or</span> <span class="operator"><</span>columnName<span class="operator">>=</span><span class="keyword">value</span></span><br><span class="line"></span><br><span class="line">## <span class="keyword">not</span> <span class="keyword">between</span> ... <span class="keyword">and</span> ...</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">where</span> <span class="operator"><</span>columnName<span class="operator">></span> <span class="keyword">not</span> <span class="keyword">between</span> a <span class="keyword">and</span> b</span><br></pre></td></tr></table></figure></div><h4 id="LIKE-子句"><a href="#LIKE-子句" class="headerlink" title="LIKE 子句"></a>LIKE 子句</h4><hr><blockquote><p>在 where 子句的条件中,我们可以使用like关键字来实现模糊查询</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">where</span> <span class="operator"><</span>columnName<span class="operator">></span> <span class="keyword">like</span> <span class="string">'reg'</span></span><br></pre></td></tr></table></figure></div><ul><li><p>在like关键字后的reg表达式中,为正则表达式的使用</p><ul><li><p>% 表示任意多个字符【%o%包含字母o】</p></li><li><p>_表示任意一个字符 【_o% 第二个字母为o】</p></li></ul></li></ul></li></ul><h3 id="对查询结果的处理"><a href="#对查询结果的处理" class="headerlink" title="对查询结果的处理"></a>对查询结果的处理</h3><hr><h4 id="设置查询的列"><a href="#设置查询的列" class="headerlink" title="设置查询的列"></a>设置查询的列</h4><blockquote><p>声明显示查询结果的指定列</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">select</span> columnName1,columnName2... </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">where</span> <span class="operator"><</span>columnName<span class="operator">></span> <span class="operator">></span> <span class="number">20</span></span><br></pre></td></tr></table></figure></div><h4 id="计算列"><a href="#计算列" class="headerlink" title="计算列"></a>计算列</h4><blockquote><p>对从数据表中查询的记录的列进行运算之后显示出来</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> columnName1,x <span class="operator">-</span> columnName2... <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span></span><br></pre></td></tr></table></figure></div><h4 id="字段别名"><a href="#字段别名" class="headerlink" title="字段别名"></a>字段别名</h4><blockquote><p>我们可以为查询结果的列名,去一个语义性更强的别名</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="operator"><</span>columnName<span class="operator">></span> <span class="keyword">as</span> <span class="operator"><</span>xxx<span class="operator">></span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span></span><br></pre></td></tr></table></figure></div><h4 id="消除重复行"><a href="#消除重复行" class="headerlink" title="消除重复行"></a>消除重复行</h4><blockquote><p>从查询的结果中将重复的记录消除</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> <span class="keyword">distinct</span> <span class="operator"><</span>columnName<span class="operator">></span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> # 关键字<span class="keyword">distinct</span></span><br></pre></td></tr></table></figure></div><h4 id="排序"><a href="#排序" class="headerlink" title="排序"></a>排序</h4><blockquote><p>将查询到的结果按照一定规则升序/降序排列</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 单字段排序</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> <span class="keyword">where</span> conditions </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">order</span> <span class="keyword">by</span> <span class="operator"><</span>columnName<span class="operator">></span> <span class="keyword">asc</span><span class="operator">|</span><span class="keyword">desc</span> # 关键字段 <span class="keyword">order</span> <span class="keyword">by</span> ... <span class="keyword">asc</span><span class="operator">|</span><span class="keyword">desc</span></span><br></pre></td></tr></table></figure></div><ul><li><p>order by columnName 表示将查询结果按照指定的列排序(置于where条件后)</p></li><li><p>asc 升序(默认)</p></li><li><p>desc 降序</p></li></ul><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 多字段排序,先满足第一个排序规则,后满足下一个</span><br><span class="line">... <span class="keyword">by</span> <span class="operator"><</span>columnName<span class="operator">></span> <span class="keyword">asc</span>, <span class="operator"><</span>columnName<span class="operator">></span> <span class="keyword">desc</span></span><br></pre></td></tr></table></figure></div></li></ul><h3 id="聚合函数"><a href="#聚合函数" class="headerlink" title="聚合函数"></a>聚合函数</h3><hr><blockquote><p>SQL 中提供了一些可以对查询记录的列进行计算的函数</p></blockquote><ul><li><p><code>count()</code> 统计函数,统计满足条件的指定字段值的个数(记录数)</p></li><li><p><code>max()</code> 获取最大值</p></li><li><p><code>min()</code> 获取最小值</p></li><li><p><code>sum()</code> 计算和,指定列的总和</p></li><li><p><code>avg()</code> 求平均值</p></li></ul><h3 id="日期函数-和-字符串函数"><a href="#日期函数-和-字符串函数" class="headerlink" title="日期函数 和 字符串函数"></a>日期函数 和 字符串函数</h3><hr><h4 id="日期函数"><a href="#日期函数" class="headerlink" title="日期函数"></a>日期函数</h4><blockquote><p>当我们向日期类型的列表添加数据时,可以通过字符串类型赋值(字符串的格式必须为yyyy-MM-dd hh:mm:ss)</p><p>如果我们想要获取当前系统时间到日期类型的列,可以使用<code>now()</code>或<code>sysdate()</code></p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> T <span class="keyword">values</span>(<span class="string">'1'</span>,<span class="string">'2'</span>,now());</span><br></pre></td></tr></table></figure></div><h4 id="字符串函数"><a href="#字符串函数" class="headerlink" title="字符串函数"></a>字符串函数</h4><blockquote><p>通过SQL指令对字符串进行处理</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line">## 拼接字符串 concat</span><br><span class="line"><span class="keyword">select</span> concat(xxx,<span class="string">'-'</span>,aaa) <span class="keyword">from</span> T;</span><br><span class="line"></span><br><span class="line">## 将字段的值转换成大写</span><br><span class="line"><span class="keyword">select</span> <span class="built_in">upper</span>(xxx) <span class="keyword">from</span> T;</span><br><span class="line"></span><br><span class="line">## 将字段的值转换为小写</span><br><span class="line"><span class="keyword">select</span> <span class="built_in">lower</span>(xxx) <span class="keyword">from</span> T;</span><br><span class="line"></span><br><span class="line">## 从指定列中截取部分显示 <span class="keyword">start</span>从<span class="number">1</span>开始,len是长度</span><br><span class="line"><span class="keyword">select</span> xxx, <span class="built_in">substring</span>(xxxx,<span class="keyword">start</span>,len) <span class="keyword">from</span> T;</span><br></pre></td></tr></table></figure></div><h3 id="分组查询"><a href="#分组查询" class="headerlink" title="分组查询"></a>分组查询</h3><hr><blockquote><p>将数据表中的列记录按照指定的列进行分组</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">select</span> 分组字段<span class="operator">/</span>聚合函数 <span class="keyword">from</span> 表名 [<span class="keyword">where</span> 条件] </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">group</span> <span class="keyword">by</span> 分组字段 [<span class="keyword">having</span> 条件] [<span class="keyword">order</span> <span class="keyword">by</span> 排序字段]</span><br></pre></td></tr></table></figure></div><ul><li><p>select 后通常显示分组字段和聚合函数</p></li><li><p>语句执行顺序:</p><ol><li><p>先根据where条件从数据库查询记录</p></li><li><p>group by对查询记录进行分组</p></li><li><p>执行having对分组后的数据进行筛选</p></li></ol></li></ul></li></ul><h3 id="分页查询"><a href="#分页查询" class="headerlink" title="分页查询"></a>分页查询</h3><hr><blockquote><p>当数据表中的记录比较多的时候,如果一次性全部查询出来显示给用户,用户的可读性、体验性就不太好,因此我们可以将这些数据分页进行展示</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> ... <span class="keyword">from</span> ... <span class="keyword">where</span> ... limit param1,param2</span><br></pre></td></tr></table></figure></div><ul><li><p>param1 int 表示获取查询语句的结果中的第一条数据的索引(索引从0开始)</p></li><li><p>param2 int 表示获取的查询记录的条数(如果剩下的数据条数<param2,则返回剩下的条目)</p></li></ul></li><li><p>案例:对数据表中的学生信息进行分页显示,总共有10条数据,我们每页显示3条</p><blockquote><p>总记录数 count 10</p><p>每页显示 pageSize 3</p><p>总页数 pageCount = count%pageSize==0? count/pageSize + 1</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 查询第一页</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> T limit <span class="number">0</span>,<span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">## 通用表达式</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="operator"><</span>tableName<span class="operator">></span> [<span class="keyword">where</span> ...] </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> limit (pageNum<span class="number">-1</span>)<span class="operator">*</span>pageSize,pageSize;</span><br></pre></td></tr></table></figure></div></li></ul><h2 id="数据表的关联关系"><a href="#数据表的关联关系" class="headerlink" title="数据表的关联关系"></a>数据表的关联关系</h2><hr><h3 id="关联关系介绍"><a href="#关联关系介绍" class="headerlink" title="关联关系介绍"></a>关联关系介绍</h3><hr><blockquote><p>MySQL是一个关系型数据库,不仅可以存储数据,还可以维护数据与数据之间的关系——通过在数据表中添加字段建立外键约束</p></blockquote><ul><li><p>数据与数据之间的关系</p><ul><li><p>一对一关联</p></li><li><p>一对多关联</p></li><li><p>多对一关联</p></li><li><p>多对多关联</p></li></ul></li></ul><h3 id="一对一关联"><a href="#一对一关联" class="headerlink" title="一对一关联"></a>一对一关联</h3><hr><blockquote><p>一个实体对应另一个实体</p></blockquote><ul><li><p>方案一 主键关联——两张数据表中主键相同的数据为相互对应的数据</p></li><li><p>方案二 唯一外键——在任意一张表中添加一个字段外键约束与另一张表主键关联,并且将外键添加唯一约束</p></li></ul><h3 id="一对多-与-多对一-关联"><a href="#一对多-与-多对一-关联" class="headerlink" title="一对多 与 多对一 关联"></a>一对多 与 多对一 关联</h3><hr><blockquote><p>一个实体对应多个实体</p></blockquote><ul><li>方案 在多的一段添加外键,与一的一端主键进行关联</li></ul><h3 id="多对多关联"><a href="#多对多关联" class="headerlink" title="多对多关联"></a>多对多关联</h3><hr><blockquote><p>多个实体对应其他多个实体</p></blockquote><ul><li>方案 额外创建一张关系表来维护多对多关联——在关系表中定义两个外键,分别与两个数据表的主键进行关联</li></ul><h3 id="外键约束"><a href="#外键约束" class="headerlink" title="外键约束"></a>外键约束</h3><hr><blockquote><p>将一个列添加外键约束与另一张表的主键(唯一列)进行关联之后,这个外键约束的列添加的数据必须要在关联的主键字段中存在</p></blockquote><ul><li><p>案例:学生表 与 班级表 (在学生表中添加外键与班级表的主键进行关联)</p><blockquote><ol><li><p>先创建班级表</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">create</span> <span class="keyword">table</span> classes(</span><br><span class="line"> class_id <span class="type">int</span> <span class="keyword">primary</span> key auto_increment,</span><br><span class="line"> class_name <span class="type">varchar</span>(<span class="number">40</span>) <span class="keyword">not</span> <span class="keyword">null</span> <span class="keyword">unique</span>,</span><br><span class="line"> class_remark <span class="type">varchar</span>(<span class="number">200</span>)</span><br><span class="line">);</span><br></pre></td></tr></table></figure></div></li><li><p>创建学生表(在学生表中添加外键与班级表中的主键进行关联)</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line">## 方式一</span><br><span class="line">## 在创建表的时候,添加外键约束</span><br><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> students(</span><br><span class="line"> stu_num <span class="type">char</span>(<span class="number">8</span>) <span class="keyword">primary</span> key,</span><br><span class="line"> stu_name <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> stu_gender <span class="type">char</span>(<span class="number">2</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> stu_age <span class="type">int</span> <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> cid <span class="type">int</span>,</span><br><span class="line"> <span class="keyword">constraint</span> FK_STUDENTS_CLASSES <span class="keyword">foreign</span> key(cid) <span class="keyword">references</span> </span><br><span class="line"> classes(class_id) ## 添加外键约束</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">## 方式二</span><br><span class="line">## 在创建表之后,为cid添加外键约束</span><br><span class="line"><span class="keyword">create</span> <span class="keyword">table</span> students(</span><br><span class="line"> stu_num <span class="type">char</span>(<span class="number">8</span>) <span class="keyword">primary</span> key,</span><br><span class="line"> stu_name <span class="type">varchar</span>(<span class="number">20</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> stu_gender <span class="type">char</span>(<span class="number">2</span>) <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> stu_age <span class="type">int</span> <span class="keyword">not</span> <span class="keyword">null</span>,</span><br><span class="line"> cid <span class="type">int</span>,</span><br><span class="line">);</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> students <span class="keyword">add</span> <span class="keyword">constraint</span> FK_STUDENTS_CLASSES </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">foreign</span> key(cid) <span class="keyword">references</span> classes(class_id);</span><br><span class="line"></span><br><span class="line">#删除外键约束</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> students <span class="keyword">drop</span> <span class="keyword">foreign</span> key FK_STUDENTS_CLASSES;</span><br></pre></td></tr></table></figure></div></li><li><p>向班级表添加班级信息</p></li><li><p>向学生表中添加学生信息</p><blockquote><p>添加学生的时候,设置给cid外键列的值必须在其关联的主表classes的class_id列存在</p></blockquote></li></ol></blockquote></li></ul><h3 id="级联"><a href="#级联" class="headerlink" title="级联"></a>级联</h3><hr><blockquote><p>当学生表中存在学生信息关联班级表的某条记录时,就不能对班级表的这条记录进行修改,删除。</p><p>如果一定要修改,需:</p><ol><li><p>将被引用的记录内外键cid设置为null</p></li><li><p>再修改原记录内的主键class_id</p></li><li><p>将cid中设置为null的记录重新修改关联为你所需要的新的class_id</p></li></ol></blockquote><blockquote><p>我们可以使用级联操作来实现:</p><ol><li><p>在添加外键时,设置 <em>级联修改</em> 和 <em>级联删除</em> </p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 删除原有的外键</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> students <span class="keyword">drop</span> <span class="keyword">foreign</span> key FK_STUDENTS_CLASSES;</span><br><span class="line"></span><br><span class="line">## 重新添加外键,并设置级联修改和级联删除</span><br><span class="line">## <span class="keyword">ON</span> <span class="keyword">UPDATE</span> 级联修改 <span class="keyword">ON</span> <span class="keyword">DELETE</span> 级联修改</span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">table</span> students <span class="keyword">add</span> <span class="keyword">constraint</span> FK_STUDENTS_CLASSES </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">foreign</span> key(cid) <span class="keyword">references</span> classes(class_id)</span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">ON</span> <span class="keyword">UPDATE</span> CASCADE <span class="keyword">ON</span> <span class="keyword">DELETE</span> CASCADE;</span><br></pre></td></tr></table></figure></div></li><li><p>删除class_id 的班级信息,学生表引用此班级信息的记录也会被同步删除</p></li></ol></blockquote><h2 id="连接查询"><a href="#连接查询" class="headerlink" title="连接查询"></a>连接查询</h2><hr><blockquote><p>通过连接查询从多张数据表提取数据</p><p>再MySQL中可以使用join实现多表的联合查询——连接查询,join按照其功能不同分为三个操作:</p><ul><li><p>inner join 内连接</p></li><li><p>left join 左连接</p></li><li><p>right join 又连接</p></li></ul></blockquote><h3 id="内连接-INNER-JOIN"><a href="#内连接-INNER-JOIN" class="headerlink" title="内连接 INNER JOIN"></a>内连接 INNER JOIN</h3><hr><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> ... <span class="keyword">from</span> tableName1 <span class="keyword">inner</span> <span class="keyword">join</span> tableName2</span><br></pre></td></tr></table></figure></div></li><li><p>笛卡尔积</p><blockquote><p>笛卡尔积(A集合&B集合):使用A中的每个记录一次关联B中每个记录,笛卡尔积=A总数*B总数</p><p>如果直接执行,会获取两种数据表中的数据集合的笛卡尔积(一次使用tableName1 表中的每一条记录去匹配tableName2中的每一条数据)</p></blockquote></li><li><p>内连接条件</p><blockquote><p>两张表用inner join连接查询之后生成的笛卡尔积数据中很多都是无意义的,我们需要消除这个缺点</p></blockquote><ul><li><p>使用 <code>on</code> 设置两张表连接查询的匹配条件</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 使用where设置过滤条件:先生成笛卡尔积再从笛卡尔积中过滤数据(效率很低)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> students <span class="keyword">INNER</span> <span class="keyword">JOIN</span> classes </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">where</span> students.cid <span class="operator">=</span> classes.class_id;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 使用ON设置连接查询条件:先判断连接条件是否成立,如果成立两张表的数据进行</span></span><br><span class="line"><span class="comment">-- 组合生成一条结果记录</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> students <span class="keyword">INNER</span> <span class="keyword">JOIN</span> classes </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">ON</span> students.cid <span class="operator">=</span> classes.class_id;</span><br></pre></td></tr></table></figure></div></li><li><p>结果:只获取两种表中匹配条件成立的数据,任何一张表再另一张表如果没有找到对应匹配则不会出现在查询结果中</p></li></ul></li></ul><h3 id="左连接-LEFT-JOIN"><a href="#左连接-LEFT-JOIN" class="headerlink" title="左连接 LEFT JOIN"></a>左连接 LEFT JOIN</h3><hr><blockquote><p>显示左表中的所有数据,如果再右表中存在与左表记录满足匹配条件的数据,则进行匹配,如果左表不存在匹配数据,则显示为NULL</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> lefttable <span class="keyword">LEFT</span> <span class="keyword">JOIN</span> righttable </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">ON</span> 匹配条件 [<span class="keyword">where</span> 条件];</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="右连接-RIGHT-JOIN"><a href="#右连接-RIGHT-JOIN" class="headerlink" title="右连接 RIGHT JOIN"></a>右连接 RIGHT JOIN</h3><hr><blockquote><p>显示右表中的所有数据,如果再左表中存在与右表记录满足匹配条件的数据,则进行匹配,如果右表不存在匹配数据,则显示为NULL</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> lefttable <span class="keyword">RIGHT</span> <span class="keyword">JOIN</span> righttable </span><br><span class="line"> <span class="operator">-</span><span class="operator">></span> <span class="keyword">ON</span> 匹配条件 [<span class="keyword">where</span> 条件];</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="数据表别名"><a href="#数据表别名" class="headerlink" title="数据表别名"></a>数据表别名</h3><hr><blockquote><p>如果在连接查询的多张表中存在相同名字的字段,我们可以使用<code>表名,字段名</code> 来进行区分,如果表名太长则不便于SQL语句的编写,我们可以使用数据表别名</p></blockquote><ul><li><p>示例</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">select</span> s.<span class="operator">*</span>,c.class_name</span><br><span class="line"><span class="keyword">from</span> students s</span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span> classes c ## 空格<span class="operator">+</span>别名</span><br><span class="line"><span class="keyword">ON</span> s.cid <span class="operator">=</span> c.class_id;</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="子查询-x2F-嵌套查询"><a href="#子查询-x2F-嵌套查询" class="headerlink" title="子查询 / 嵌套查询"></a>子查询 / 嵌套查询</h3><hr><blockquote><p>先进行一次查询,第一次查询的结果作为第二次查询的源条件</p></blockquote><ul><li><p>示例一</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 先查询括号里面的</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> students <span class="keyword">where</span> cid <span class="operator">=</span> (</span><br><span class="line"><span class="keyword">select</span> class_id <span class="keyword">from</span> classes <span class="keyword">where</span> class_name<span class="operator">=</span><span class="string">'Java2105'</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure></div></li><li><p>示例二</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="comment">-- 如果子查询返回的结果时多个值(单列多行),条件使用IN</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> students <span class="keyword">where</span> cid </span><br><span class="line"><span class="keyword">IN</span> (<span class="keyword">select</span> class_id <span class="keyword">from</span> classes <span class="keyword">where</span> class_name <span class="keyword">LIKE</span> <span class="string">'Java%'</span>);</span><br></pre></td></tr></table></figure></div></li><li><p>示例三</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="comment">-- (多行多列)先查询cid=1班级中的所有学生信息,将这些信息作为一个整体虚拟表</span></span><br><span class="line"><span class="comment">-- 在基于这个虚拟表查询性别为男的学生信息('虚拟表'需要别名)</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> (<span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> students <span class="keyword">where</span> cid<span class="operator">=</span><span class="number">1</span>) t </span><br><span class="line"><span class="keyword">where</span> t.stu_gender<span class="operator">=</span><span class="string">'男'</span>;</span><br></pre></td></tr></table></figure></div></li></ul><h2 id="存储过程"><a href="#存储过程" class="headerlink" title="存储过程"></a>存储过程</h2><hr><h3 id="存储过程介绍"><a href="#存储过程介绍" class="headerlink" title="存储过程介绍"></a>存储过程介绍</h3><hr><ul><li><p>SQL 指令执行过程</p><blockquote><ol><li><p>编写并允许SQL指令</p></li><li><p>通过数据库连接将SQL指令发送到数据库</p></li><li><p>数据库接收SQL指令至SQL引擎</p></li><li><p>SQL引擎编译并执行SQL指令</p></li><li><p>将执行SQL的结果通过数据库连接返回给用户端</p></li><li><p>用户端显示查询结果</p></li></ol></blockquote><ul><li><p>存在的问题</p><blockquote><ol><li><p>如果我们需要重复多次执行相同的SQL,SQL执行都需要通过连接传递到MySQL,并且需要经过编译和执行的步骤</p></li><li><p>如果我们需要连续执行多个SQL指令,第二个SQL指令往往需要使用第一个SQL指令执行的结果作为参数</p></li></ol></blockquote></li></ul></li><li><p>SQL指令执行过程存在问题解决方法——存储过程(按需配置)</p><blockquote><p>将能够完成特定功能的SQL指令进行封装(SQL指令集),编译之后存储在数据库服务器上,并且为之去一个名字,客户端可以通过名字直接调用这个SQL指令集,获取执行结果</p></blockquote><ul><li><p>步骤</p><blockquote><ol><li><p>通过存储过程的名字进行调用</p></li><li><p>向数据库传输存储过程的名字</p></li><li><p>再数据库中通过存储过程的名字查询对应的指令集</p></li><li><p>执行指令集</p></li><li><p>返回执行结果</p></li><li><p>显示查询结果</p></li></ol></blockquote></li><li><p>优点</p><blockquote><ol><li><p>SQL指令无需客户端编写,通过网络传送,可以节省网络开销,同时避免SQL指令再网络传输过程中被恶意修改</p></li><li><p>存储过程经过编译创建并保存在数据库中的,执行过程无需重复的进行编译操作,对SQL指令的执行过程进行了性能提升</p></li><li><p>存储过程中多个SQL指令之间存在逻辑关系,支持流程控制语句(分支,循环),可以实现更为复杂的业务</p></li></ol></blockquote></li><li><p>缺点</p><blockquote><ol><li><p>存储过程是根据不同的数据库进行编译,创建并存储在数据库中;当我们需要切换到其他的数据库产品时,需要重新编写针对于新数据库的存储过程</p></li><li><p>存储过程受限于数据库产品,如果需要高性能的优化会成为一个问题</p></li><li><p>在互联网的项目中,如果需要数据库的高(连接)并发访问,使用存储过程会增加数据库的连接执行时间(因为我们将复杂的业务交给了数据库进行处理)</p></li></ol></blockquote></li></ul></li></ul><h3 id="创建存储过程"><a href="#创建存储过程" class="headerlink" title="创建存储过程"></a>创建存储过程</h3><hr><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line">## 创建一个存储过程实现加法运算</span><br><span class="line"><span class="keyword">create</span> <span class="keyword">procedure</span> <span class="operator"><</span>procName<span class="operator">></span>(</span><br><span class="line"> [<span class="keyword">IN</span> param1 <span class="type">int</span>, <span class="keyword">IN</span> param2 <span class="type">int</span>,...,<span class="keyword">OUT</span> param3 <span class="type">int</span>]</span><br><span class="line">)</span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line"> <span class="comment">-- SQL语句</span></span><br><span class="line"><span class="keyword">end</span>;</span><br></pre></td></tr></table></figure></div></li><li><p>案例</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">procedure</span> test1(<span class="keyword">IN</span> a <span class="type">int</span>, <span class="keyword">IN</span> b <span class="type">int</span>, <span class="keyword">OUT</span> c <span class="type">int</span>)</span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line"> <span class="keyword">SET</span> c <span class="operator">=</span> a<span class="operator">+</span>b;</span><br><span class="line"><span class="keyword">end</span>;</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="调用存储过程"><a href="#调用存储过程" class="headerlink" title="调用存储过程"></a>调用存储过程</h4><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">set</span> <span class="variable">@m</span> <span class="operator">=</span> <span class="number">0</span>; ## 定义变量<span class="variable">@m</span></span><br><span class="line"><span class="keyword">call</span> test1(<span class="number">3</span>, <span class="number">4</span>, <span class="variable">@m</span>) ## 将<span class="variable">@m</span>传递给c</span><br><span class="line"><span class="comment">-- 显示变量值</span></span><br><span class="line"><span class="keyword">select</span> <span class="variable">@m</span> <span class="keyword">from</span> DUAL;</span><br></pre></td></tr></table></figure></div><h3 id="存储过程中变量的使用"><a href="#存储过程中变量的使用" class="headerlink" title="存储过程中变量的使用"></a>存储过程中变量的使用</h3><hr><blockquote><p>局部变量 和 用户变量</p></blockquote><h4 id="局部变量"><a href="#局部变量" class="headerlink" title="局部变量"></a>局部变量</h4><blockquote><p>定义在存储过程中的变量,只能在存储过程内部使用</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 创建一个存储过程,计算 输入参数的平方 与 输入参数/2 之和</span></span><br><span class="line"><span class="keyword">create</span> <span class="keyword">procedure</span> test2(<span class="keyword">IN</span> a <span class="type">int</span>, <span class="keyword">OUT</span> c <span class="type">int</span>)</span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line"> <span class="keyword">declare</span> x <span class="type">int</span> <span class="keyword">default</span> <span class="number">0</span>; ## 定义x <span class="type">int</span>类型 默认值为<span class="number">0</span></span><br><span class="line"> <span class="keyword">declare</span> y <span class="type">int</span> <span class="keyword">default</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">set</span> x <span class="operator">=</span> a<span class="operator">*</span>a; ## 设置x的值</span><br><span class="line"> <span class="keyword">set</span> y <span class="operator">=</span> a<span class="operator">/</span><span class="number">2</span>;</span><br><span class="line"> <span class="keyword">set</span> r <span class="operator">=</span> x<span class="operator">+</span>y;</span><br><span class="line"><span class="keyword">end</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">select</span> x <span class="keyword">from</span> DUAL; ## 无结果,局部变量不会出现在DUAL内</span><br></pre></td></tr></table></figure></div><blockquote><p>局部变量要定义在存储过程中,而且必须定义在存储过程开始</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">declare</span> <span class="operator"><</span>变量名<span class="operator">></span> <span class="operator"><</span>变量类型<span class="operator">></span> [<span class="keyword">default</span> 默认值];</span><br></pre></td></tr></table></figure></div><h4 id="用户变量"><a href="#用户变量" class="headerlink" title="用户变量"></a>用户变量</h4><blockquote><p>相当于全局变量,定义的用户变量可以通过<code>select @attrName from DUAL</code>查询到</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="comment">-- 用户变量直接使用set设置</span></span><br><span class="line"><span class="comment">-- 用户变量需要舒勇@来修饰</span></span><br><span class="line"><span class="keyword">set</span> <span class="variable">@n</span><span class="operator">=</span><span class="number">1</span>;</span><br><span class="line"><span class="keyword">call</span> test2(<span class="number">6</span>,<span class="variable">@n</span>);</span><br><span class="line"><span class="keyword">select</span> <span class="variable">@n</span> <span class="keyword">from</span> DUAL;</span><br></pre></td></tr></table></figure></div><ul><li><p>注意</p><blockquote><p>因为用户变量相当于全局变量,可以在SQL指令以及多个存储过程中共享,用户变量过多会导致程序不易理解,在开发中少使用</p></blockquote></li></ul><h5 id="设置变量值"><a href="#设置变量值" class="headerlink" title="设置变量值"></a>设置变量值</h5><blockquote><p>无论时局部变量还是用户变量,都是使用<code>set</code> 关键字修改值</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">set</span> <span class="variable">@n</span><span class="operator">=</span><span class="number">1</span>;</span><br><span class="line"><span class="comment">-- 或</span></span><br><span class="line"><span class="keyword">set</span> y <span class="operator">=</span> a<span class="operator">+</span>b;</span><br></pre></td></tr></table></figure></div><ul><li><p>在SQL语句中赋值给变量</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">create</span> <span class="keyword">procedure</span> test3(<span class="keyword">OUT</span> c <span class="type">int</span>)</span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line"> <span class="keyword">select</span> <span class="built_in">count</span>(stu_num) <span class="keyword">INTO</span> c <span class="keyword">from</span> students;</span><br><span class="line"> ## 使用select...INTO关键字段,赋值给c</span><br><span class="line"><span class="keyword">end</span>;</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="存储过程的参数"><a href="#存储过程的参数" class="headerlink" title="存储过程的参数"></a>存储过程的参数</h3><hr><blockquote><p>MySQL存储过程的参数:IN / OUT / INOUT</p></blockquote><h4 id="IN-输入参数"><a href="#IN-输入参数" class="headerlink" title="IN 输入参数"></a>IN 输入参数</h4><blockquote><p>在调用存储过程中传递数据给存储过程的参数(在调用的过程中必须为具有实际值的变量 或者 字面值)</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">procedure</span> test4(</span><br><span class="line"> <span class="keyword">IN</span> snum <span class="type">char</span>(<span class="number">8</span>), <span class="keyword">IN</span> sname <span class="type">varchar</span>(<span class="number">20</span>), <span class="keyword">IN</span> gender <span class="type">char</span>(<span class="number">2</span>), <span class="keyword">IN</span> age <span class="type">int</span>,</span><br><span class="line"> <span class="keyword">IN</span> cid <span class="type">int</span>, <span class="keyword">IN</span> remark <span class="type">varchar</span>(<span class="number">255</span>))</span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line"> <span class="keyword">insert</span> <span class="keyword">into</span> students(stu_num,stu_name,stu_gender,stu_age,cid,remark)</span><br><span class="line"> <span class="keyword">values</span>(snum,sname,gender,age,cid,remark);</span><br><span class="line"><span class="keyword">end</span>;</span><br></pre></td></tr></table></figure></div><h4 id="OUT-输出参数"><a href="#OUT-输出参数" class="headerlink" title="OUT 输出参数"></a>OUT 输出参数</h4><blockquote><p>将存储过程中产生的数据返回给过程调用者</p></blockquote><h4 id="INOUT-输入输出参数"><a href="#INOUT-输入输出参数" class="headerlink" title="INOUT 输入输出参数"></a>INOUT 输入输出参数</h4><blockquote><p>输入与输出都在同一个参数上传递</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">procedure</span> test6(<span class="keyword">INOUT</span> str <span class="type">varchar</span>(<span class="number">20</span>))</span><br><span class="line"><span class="keyword">begin</span></span><br><span class="line"> <span class="keyword">select</span> stu_name <span class="keyword">INTO</span> str <span class="keyword">from</span> students <span class="keyword">where</span> stu_num<span class="operator">=</span>str;</span><br><span class="line"><span class="keyword">end</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">set</span> <span class="variable">@name</span><span class="operator">=</span><span class="string">'aa'</span>;</span><br><span class="line"><span class="keyword">call</span> test6(<span class="variable">@name</span>);</span><br><span class="line"><span class="keyword">select</span> <span class="variable">@name</span> <span class="keyword">from</span> dual;</span><br></pre></td></tr></table></figure></div><h3 id="存储过程中的流程控制"><a href="#存储过程中的流程控制" class="headerlink" title="存储过程中的流程控制"></a>存储过程中的流程控制</h3><hr><blockquote><p>在存储过程中支持流程控制语句用于实现逻辑的控制</p></blockquote><h4 id="分支语句"><a href="#分支语句" class="headerlink" title="分支语句"></a>分支语句</h4><ul><li><p><code>if-then-else</code></p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 单分支</span></span><br><span class="line">if conditions <span class="keyword">then</span></span><br><span class="line"> <span class="comment">-- SQL</span></span><br><span class="line"><span class="keyword">end</span> if;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 双分支</span></span><br><span class="line">if confitions <span class="keyword">then</span></span><br><span class="line"> <span class="comment">-- SQL1</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> <span class="comment">-- SQL2</span></span><br><span class="line"><span class="keyword">end</span> if;</span><br></pre></td></tr></table></figure></div></li><li><p><code>case</code></p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">case</span> 变量名</span><br><span class="line"><span class="keyword">when</span> value1 <span class="keyword">then</span></span><br><span class="line"> <span class="comment">-- SQL1</span></span><br><span class="line"><span class="keyword">when</span> value2 <span class="keyword">then</span></span><br><span class="line"> <span class="comment">-- SQL2</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line"> <span class="comment">-- SQL3</span></span><br><span class="line"><span class="keyword">end</span> <span class="keyword">case</span>;</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="循环语句"><a href="#循环语句" class="headerlink" title="循环语句"></a>循环语句</h4><ul><li><p><code>while</code></p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">declare</span> i <span class="type">int</span>;</span><br><span class="line"><span class="keyword">set</span> i <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">while i <span class="operator"><</span> num do</span><br><span class="line"> <span class="comment">--SQL</span></span><br><span class="line"> <span class="keyword">set</span> i <span class="operator">=</span> i <span class="operator">+</span> <span class="number">1</span>;</span><br><span class="line"><span class="keyword">end</span> while;</span><br></pre></td></tr></table></figure></div></li><li><p><code>repeat</code></p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">declare</span> i <span class="type">int</span>;</span><br><span class="line"><span class="keyword">set</span> i <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">repeat</span><br><span class="line"> <span class="comment">--SQL</span></span><br><span class="line"> <span class="keyword">insert</span> <span class="keyword">into</span> classes(class_name,remark) </span><br><span class="line"> <span class="keyword">values</span>(CONCAT(<span class="string">'Python'</span>,i),<span class="string">'....'</span>);</span><br><span class="line"> <span class="keyword">set</span> i <span class="operator">=</span> i<span class="operator">+</span><span class="number">1</span></span><br><span class="line">until i <span class="operator">></span> num <span class="keyword">end</span> repeat;</span><br></pre></td></tr></table></figure></div></li><li><p><code>loop</code></p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">declare</span> i <span class="type">int</span>;</span><br><span class="line"><span class="keyword">set</span> i <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">myloop:loop</span><br><span class="line"> <span class="comment">--SQL</span></span><br><span class="line"> if i <span class="operator">=</span> num <span class="keyword">then</span></span><br><span class="line"> leave myloop;</span><br><span class="line"> <span class="keyword">end</span> if;</span><br><span class="line"><span class="keyword">end</span> loop;</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="存储过程管理"><a href="#存储过程管理" class="headerlink" title="存储过程管理"></a>存储过程管理</h3><hr><h4 id="查询存储过程"><a href="#查询存储过程" class="headerlink" title="查询存储过程"></a>查询存储过程</h4><blockquote><p>存储过程是属于某个数据库的,也就是说当我们将存储过程创建在某个数据库之后,只能在当前数据库调用此存储过程</p><p>查询:查询某个数据库中有哪些存储过程</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="comment">-- 根据数据库名查询数据库内存储过程</span></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">procedure</span> status [<span class="keyword">where</span> db<span class="operator">=</span>数据库名];</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 查询某数据库中特定存储过程的实现细节</span></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">create</span> <span class="keyword">procedure</span> 数据库名.存储过程;</span><br></pre></td></tr></table></figure></div><h4 id="修改存储过程"><a href="#修改存储过程" class="headerlink" title="修改存储过程"></a>修改存储过程</h4><blockquote><p>修改存储过程的特征 / 特性</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">alter</span> <span class="keyword">procedure</span> <span class="operator"><</span>proc_name<span class="operator">></span> 特征<span class="number">1</span> [特征<span class="number">2</span> 特征<span class="number">3</span> ...];</span><br></pre></td></tr></table></figure></div><ul><li><p>存储过程的特征参数</p><ul><li><p><code>CONTAINS SQL</code> 表示子程序包含 SQL 语句,但不包含读或写数据的语句</p></li><li><p><code>NO SQL</code> 表示子程序中不包含 SQL 语句</p></li><li><p><code>READS SQL DATA</code> 表示子程序中不包含 SQL 语句</p></li><li><p><code>MODIFIES SQL DATA</code> 表示子程序中包含写数据的语句</p></li><li><p><code>SQL SECURITY</code> {DEFINER | INVOKER} 指明谁有权限来执行</p><ul><li><p>DEFINER 表示只有定义者自己才能够执行</p></li><li><p>INVOKER 表示调用者可以执行</p></li></ul></li><li><p><code>COMMENT 'string'</code> 表示注释信息</p></li></ul></li></ul><h4 id="删除存储过程"><a href="#删除存储过程" class="headerlink" title="删除存储过程"></a>删除存储过程</h4><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="comment">-- drop 删除数据库中的对象</span></span><br><span class="line"><span class="comment">-- delete 删除数据表中的数据</span></span><br><span class="line">dro <span class="keyword">procedure</span> proc_test1</span><br></pre></td></tr></table></figure></div><h3 id="游标"><a href="#游标" class="headerlink" title="游标"></a>游标</h3><hr><blockquote><p>创建一个存储过程,需要返回查询语句查询到的多条数据</p></blockquote><p><code>concat_ws('~', x, xx, aa) = x~xx~aa</code> 拼接 </p><h4 id="游标的概念"><a href="#游标的概念" class="headerlink" title="游标的概念"></a>游标的概念</h4><blockquote><p>游标可以用来一次取出查询结果中的每一条数据,逐个读取</p></blockquote><h4 id="游标的使用步骤"><a href="#游标的使用步骤" class="headerlink" title="游标的使用步骤"></a>游标的使用步骤</h4><ol><li><p>声明游标</p><ul><li><p>声明游标语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">declare</span> cursor_name <span class="keyword">CURSOR</span> <span class="keyword">FOR</span> <span class="operator"><</span>查询语句<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div></li></ul></li><li><p>打开游标</p><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">open</span> cursor_name;</span><br></pre></td></tr></table></figure></div></li></ul></li><li><p>使用游标</p><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">set</span> i<span class="operator">=</span><span class="number">0</span>;</span><br><span class="line">while i<span class="operator"><</span>num do</span><br><span class="line"> <span class="keyword">FETCH</span> cursor_name <span class="keyword">INTO</span> bname,bauthor,bprice; ## 循环取出</span><br><span class="line"> <span class="keyword">set</span> i<span class="operator">=</span>i<span class="operator">+</span><span class="number">1</span>;</span><br><span class="line"><span class="keyword">end</span> while;</span><br></pre></td></tr></table></figure></div></li></ul></li><li><p>关闭游标</p><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">close</span> cursor_name;</span><br></pre></td></tr></table></figure></div></li></ul></li></ol><h2 id="触发器"><a href="#触发器" class="headerlink" title="触发器"></a>触发器</h2><hr><h3 id="触发器介绍"><a href="#触发器介绍" class="headerlink" title="触发器介绍"></a>触发器介绍</h3><hr><blockquote><p>触发器是一种特殊的存储过程,能够完成特定功能,存储在数据库服务器上的SQL片段,但是触发器无需调用,当对数据表中的数据执行DML操作时自动触发这个SQL片段的执行,无需手动调用</p><p>在MySQL中只有执行insert\delete\update操作才能触发触发器的执行</p></blockquote><h3 id="触发器的使用"><a href="#触发器的使用" class="headerlink" title="触发器的使用"></a>触发器的使用</h3><hr><h4 id="创建触发器"><a href="#创建触发器" class="headerlink" title="创建触发器"></a>创建触发器</h4><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">trigger</span> tri_name</span><br><span class="line"><span class="operator"><</span>before<span class="operator">|</span>after<span class="operator">></span> <span class="comment">--定义触发时机</span></span><br><span class="line"><span class="operator"><</span><span class="keyword">insert</span><span class="operator">|</span><span class="keyword">delete</span><span class="operator">|</span><span class="keyword">update</span><span class="operator">></span> <span class="comment">--定义DML类型</span></span><br><span class="line"><span class="keyword">ON</span> <span class="operator"><</span>table_name<span class="operator">></span></span><br><span class="line"><span class="keyword">for</span> <span class="keyword">each</span> <span class="type">row</span> <span class="comment">--声明为行级触发器(只要操作一条记录就触发触发器一次)</span></span><br><span class="line"><span class="comment">--SQL语句</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--例</span></span><br><span class="line"><span class="keyword">create</span> <span class="keyword">trigger</span> tri_test1</span><br><span class="line">before <span class="keyword">insert</span> <span class="keyword">on</span> students</span><br><span class="line"><span class="keyword">for</span> <span class="keyword">each</span> <span class="type">row</span></span><br><span class="line"><span class="comment">--SQL语句</span></span><br></pre></td></tr></table></figure></div></li></ul><h4 id="查看触发器"><a href="#查看触发器" class="headerlink" title="查看触发器"></a>查看触发器</h4><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> triggers;</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="删除触发器"><a href="#删除触发器" class="headerlink" title="删除触发器"></a>删除触发器</h4><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">drop</span> <span class="keyword">trigger</span> tri_name;</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="NEW-与-OLD"><a href="#NEW-与-OLD" class="headerlink" title="NEW 与 OLD"></a>NEW 与 OLD</h3><hr><blockquote><p>我们可以通过<code>NEW</code> 和 <code>OLD</code> 关键字在触发器中货期触发器的MDML操作的数据</p><ul><li><p><code>NEW</code>:在触发器中用于获取insert操作添加的数据,update操作修改后的记录</p></li><li><p><code>OLD</code>:在触发器中用于获取delete操作删除前的数据,update操作修改前的数据</p></li></ul></blockquote><h4 id="NEW"><a href="#NEW" class="headerlink" title="NEW"></a>NEW</h4><ul><li><p>insert操作</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">create</span> <span class="keyword">trigger</span> tri_test1</span><br><span class="line">before <span class="keyword">insert</span> <span class="keyword">on</span> students</span><br><span class="line"><span class="keyword">for</span> <span class="keyword">each</span> <span class="type">row</span></span><br><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> stulogs(<span class="type">time</span>, log_text) </span><br><span class="line"><span class="keyword">values</span>(now(), concat(<span class="string">'添加'</span>, NEW.stu_name, <span class="string">'学生信息'</span>));</span><br></pre></td></tr></table></figure></div></li><li><p>update操作</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">trigger</span> tri_test2</span><br><span class="line">after <span class="keyword">update</span> <span class="keyword">on</span> students <span class="keyword">for</span> <span class="keyword">each</span> <span class="type">row</span></span><br><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> stulogs(<span class="type">time</span>, log_text) </span><br><span class="line"><span class="keyword">values</span>(now(), concat(<span class="string">'修改'</span>, NEW.stu_num, NEW.stu_name));</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="OLD"><a href="#OLD" class="headerlink" title="OLD"></a>OLD</h4><ul><li><p>delete操作</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">trigger</span> tri_test2</span><br><span class="line">after <span class="keyword">delete</span> <span class="keyword">on</span> students <span class="keyword">for</span> <span class="keyword">each</span> <span class="type">row</span></span><br><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> stulogs(<span class="type">time</span>, log_text) </span><br><span class="line"><span class="keyword">values</span>(now(), concat(<span class="string">'删除'</span>, OLD.stu_num, <span class="string">'学生信息'</span>));</span><br></pre></td></tr></table></figure></div></li><li><p>update操作</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">trigger</span> tri_test2</span><br><span class="line">after <span class="keyword">update</span> <span class="keyword">on</span> students <span class="keyword">for</span> <span class="keyword">each</span> <span class="type">row</span></span><br><span class="line"><span class="keyword">insert</span> <span class="keyword">into</span> stulogs(<span class="type">time</span>, log_text) </span><br><span class="line"><span class="keyword">values</span>(now(), concat(<span class="string">'修改'</span>, OLD.stu_name, NEW.stu_name));</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><hr><h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><ul><li><p>触发器时自动执行的,当对触发器相应的表执行响应的DML操作时立即执行</p></li><li><p>触发器可以实现表中的数据的级联操作(关联操作),有利于保证数据库的完整性</p></li><li><p>触发器可以对DML操作的数据进行更为复杂的合法性校验</p></li></ul><h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><ul><li><p>使用触发器实现的业务逻辑如果出现问题,将难以定位,后期维护难</p></li><li><p>如果大量使用,容易导致代码结构杂乱,增加了程序的复杂性</p></li><li><p>当触发器操作的数据量比较大时,执行效率会大大降低</p></li></ul><h4 id="使用建议"><a href="#使用建议" class="headerlink" title="使用建议"></a>使用建议</h4><ul><li><p>在互联网项目中,应避免使用触发器</p></li><li><p>对于并发量不大的项目可以选择使用存储过程,但是在互联网引用中不提倡使用存储过程(原因:存储过程将实现业务逻辑交给数据库处理,一则增加了数据库的负载,二则不利于数据库的迁移)</p></li></ul><h2 id="视图"><a href="#视图" class="headerlink" title="视图"></a>视图</h2><hr><h3 id="视图的概念"><a href="#视图的概念" class="headerlink" title="视图的概念"></a>视图的概念</h3><hr><blockquote><p>视图就是由数据库中一张表或者多张表根据特定的条件查询出的数据构造成的虚拟表</p></blockquote><h3 id="视图的作用"><a href="#视图的作用" class="headerlink" title="视图的作用"></a>视图的作用</h3><hr><ul><li><p>安全性</p><blockquote><p>如果我们直接将数据表授权给用户操作,那么用户可以操作数据表中的所有数据,假如我们想要对数据表中的部分数据进行保护,可以将公开的数据生成视图,授权用户访问视图;用户通过查询视图可以获取数据表中公开的数据,从而达到将数据表中的部分数据对用户隐藏</p></blockquote></li><li><p>简单性</p><blockquote><p>如果我们需要查询的数据来源于多张数据表,可以使用多表连接查询来实现;我们通过视图将这些连表查询的结果对用户开放,用户则可以直接通过查询视图获取多表数据,操作更便捷</p></blockquote></li></ul><h3 id="创建视图"><a href="#创建视图" class="headerlink" title="创建视图"></a>创建视图</h3><hr><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="keyword">create</span> <span class="keyword">view</span> view_name</span><br><span class="line"><span class="keyword">AS</span></span><br><span class="line"><span class="comment">-- SQL语句</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--例</span></span><br><span class="line"><span class="keyword">create</span> <span class="keyword">view</span> view_test1</span><br><span class="line"><span class="keyword">AS</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> students <span class="keyword">where</span> stu_genter<span class="operator">=</span><span class="string">'男'</span>; ## 单表</span><br><span class="line"></span><br><span class="line"><span class="keyword">create</span> <span class="keyword">view</span> view_test2</span><br><span class="line"><span class="keyword">AS</span></span><br><span class="line"><span class="keyword">select</span> s.stu_name,b.book_name,borrow_num</span><br><span class="line"><span class="keyword">from</span> books b <span class="keyword">inner</span> <span class="keyword">join</span> records r <span class="keyword">inner</span> <span class="keyword">join</span> student s</span><br><span class="line"><span class="keyword">on</span> b.book_id<span class="operator">=</span>r.bid <span class="keyword">and</span> r.snum<span class="operator">=</span>s.stu_num; ## 多表</span><br><span class="line"></span><br><span class="line"><span class="comment">--查询视图</span></span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> view_test2;</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="视图数据的特性"><a href="#视图数据的特性" class="headerlink" title="视图数据的特性"></a>视图数据的特性</h3><hr><ul><li><p>查询操作</p><blockquote><p>如果在数据表中添加了新的数据,而且这个数据满足创建视图时的查询语句条件,通过查询视图也可以查询出新增的数据;当删除原表中男足查询条件的数据时,也会从视图中消失</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></ul><h4 id="使用建议-1"><a href="#使用建议-1" class="headerlink" title="使用建议"></a>使用建议</h4><blockquote><p>对复杂查询简化操作,并且不会对数据进行修改的情况下可以使用视图</p></blockquote><h3 id="查询视图结构"><a href="#查询视图结构" class="headerlink" title="查询视图结构"></a>查询视图结构</h3><hr><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">desc</span> view_test2;</span><br></pre></td></tr></table></figure></div><h3 id="修改视图结构"><a href="#修改视图结构" class="headerlink" title="修改视图结构"></a>修改视图结构</h3><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">--方式1</span></span><br><span class="line"><span class="keyword">create</span> <span class="keyword">OR</span> REPLACE <span class="keyword">view</span> view_test1 # 关键段 <span class="keyword">OR</span> REPLACE 若存在则覆盖</span><br><span class="line"><span class="keyword">AS</span></span><br><span class="line"><span class="comment">-- SQL语句</span></span><br><span class="line"></span><br><span class="line"><span class="comment">--方式2</span></span><br><span class="line"><span class="keyword">alter</span> <span class="keyword">view</span> view_test1</span><br><span class="line"><span class="keyword">AS</span></span><br><span class="line"><span class="comment">-- SQL语句</span></span><br></pre></td></tr></table></figure></div><h3 id="删除视图"><a href="#删除视图" class="headerlink" title="删除视图"></a>删除视图</h3><hr><blockquote><p>删除数据表时会同时删除数据表中的数据,但是删除视图时不会影响原表中的数据</p></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">drop</span> <span class="keyword">view</span> view_test1</span><br></pre></td></tr></table></figure></div><h2 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h2><hr><blockquote><p>数据库是用来存储数据的,在互联网应用中数据库中存储的数据可能会很多(大数据),数据表中数据的查询速率会随着数据量的增长逐渐变慢,从而导致响应用户请求的速度变慢——用户体验差</p></blockquote><h3 id="索引的介绍"><a href="#索引的介绍" class="headerlink" title="索引的介绍"></a>索引的介绍</h3><hr><blockquote><p>索引就是用来提高数据表中数据的查询效率的</p><p>将数据表中某一列/某几列的值取出来构造成便于查找的结构进行存储,生成数据表的目录,当我们进行数据查询时,则现在目录中进行查找得到对应数据的地址,然后再到数据表中根据地址快速获取数据记录,避免全表扫描</p></blockquote><h3 id="索引的分类"><a href="#索引的分类" class="headerlink" title="索引的分类"></a>索引的分类</h3><hr><blockquote><p>MySQL中的索引根据创建索引的列的不同分为:</p><ul><li><p>主键索引:在主键字段创建的索引</p></li><li><p>唯一索引:在唯一约束字段创建的索引,可以为NULL</p></li><li><p>普通索引:没有限制,在普通字段创建的索引</p></li><li><p>组合索引:两个及以上的字段创建的索引</p></li></ul></blockquote><ul><li><p>说明</p><blockquote><ol><li><p>在创建数据表时,将字段声明为主键时,会自动在主键字段创建主键索引</p></li><li><p>在创建数据表时,将字段声明为唯一键,会自动在唯一字段创建唯一索引</p></li></ol></blockquote></li></ul><h3 id="创建索引"><a href="#创建索引" class="headerlink" title="创建索引"></a>创建索引</h3><hr><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">unique</span> 唯一索引,无 普通索引</span><br><span class="line"><span class="keyword">create</span> [<span class="keyword">unique</span>] index index_索引名 <span class="keyword">on</span> 表名(字段名);</span><br><span class="line"></span><br><span class="line">## 组合索引,需要多个字段值</span><br><span class="line"><span class="keyword">create</span> index index_索引名 <span class="keyword">on</span> 表名(字段名<span class="number">1</span>,<span class="number">2</span>);</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="全文索引"><a href="#全文索引" class="headerlink" title="全文索引"></a>全文索引</h4><blockquote><p>通过此索引进行全文检索操作</p></blockquote><ul><li><p>语法</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> fulltext index index_索引名 <span class="keyword">on</span> 表名(字段名);</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="索引的使用"><a href="#索引的使用" class="headerlink" title="索引的使用"></a>索引的使用</h3><hr><blockquote><p>索引创建完成之后无需调用,当根据创建索引的列进行数据查询的时候,会自动使用索引</p><p>组合索引需要根据创建索引的所有字段进行查询时触发</p></blockquote><ul><li><p>在命令行中查看查询语句的查询规划</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">explain <span class="operator"><</span><span class="keyword">SQL</span>语句<span class="operator">></span>\G;</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="查看索引"><a href="#查看索引" class="headerlink" title="查看索引"></a>查看索引</h3><hr><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="comment">--方案1 ,</span></span><br><span class="line"><span class="keyword">show</span> <span class="keyword">create</span> <span class="keyword">table</span> tb_testindex\G;</span><br><span class="line"><span class="comment">--方案2 查询数据表的索引</span></span><br><span class="line"><span class="keyword">show</span> indexes <span class="keyword">from</span> tb_testindex;</span><br><span class="line"><span class="comment">--方案3 查询索引</span></span><br><span class="line"><span class="keyword">show</span> keys <span class="keyword">from</span> tb_testindex;</span><br></pre></td></tr></table></figure></div><h3 id="删除索引"><a href="#删除索引" class="headerlink" title="删除索引"></a>删除索引</h3><hr><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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"><span class="comment">--索引是建立在表的字段上的,不同的表可能会出现相同名称的索引,</span></span><br><span class="line"><span class="comment">--因此删除索引时需要指定表名</span></span><br><span class="line"><span class="keyword">drop</span> index index_test3 <span class="keyword">on</span> tb_testindex;</span><br></pre></td></tr></table></figure></div><h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><hr><h4 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h4><blockquote><ol><li><p>索引大大降低了数据库服务器在执行查询操作时扫描的数据</p></li><li><p>索引可以避免服务器排序,将随机IO变成顺序IO</p></li></ol></blockquote><h4 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h4><blockquote><ol><li><p>索引是根据数据表列创建的,当数据表中数据发生DML操作时,索引页也需要更新</p></li><li><p>索引文件也会占用磁盘空间</p></li></ol></blockquote><h4 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h4><blockquote><ol><li><p>数据表中数据不多时,全表扫描可能更快,不要使用索引</p></li><li><p>数据量大但是DML操作很频繁,不建议使用</p></li><li><p>不要在数据重复高度高的列上创建索引</p></li><li><p>创建索引之后,要注意查询SQL语句的编写,避免索引失效</p></li></ol></blockquote><h2 id="数据库事务"><a href="#数据库事务" class="headerlink" title="数据库事务"></a>数据库事务</h2><hr><h3 id="数据库事务介绍"><a href="#数据库事务介绍" class="headerlink" title="数据库事务介绍"></a>数据库事务介绍</h3><hr><blockquote><p>我们把完成特定的业务的多个数据库DML操作步骤称为一个数据库事务</p></blockquote><h3 id="数据库事务的特性"><a href="#数据库事务的特性" class="headerlink" title="数据库事务的特性"></a>数据库事务的特性</h3><hr><blockquote><p>ACID特性</p></blockquote><h4 id="原子性"><a href="#原子性" class="headerlink" title="原子性"></a>原子性</h4><blockquote><p>Atomicity :一个事务中的多个DML操作,要么同时执行成功,要么同时失败</p></blockquote><h4 id="一致性"><a href="#一致性" class="headerlink" title="一致性"></a>一致性</h4><blockquote><p>Consistency:事务执行之前和事务执行之后,数据库中的数据是一致的,完整性和一致性不能破坏</p></blockquote><h4 id="隔离性"><a href="#隔离性" class="headerlink" title="隔离性"></a>隔离性</h4><blockquote><p>Isolation:数据库允许多个事务同时进行(张三借Java书同时允许李四借),多个并行的事务之间不能相互影响</p></blockquote><h4 id="持久性"><a href="#持久性" class="headerlink" title="持久性"></a>持久性</h4><blockquote><p>Durability:事务完成之后,对数据得到操作是永久的</p></blockquote><h3 id="MySQL事务管理"><a href="#MySQL事务管理" class="headerlink" title="MySQL事务管理"></a>MySQL事务管理</h3><hr><h4 id="自动提交与手动提交"><a href="#自动提交与手动提交" class="headerlink" title="自动提交与手动提交"></a>自动提交与手动提交</h4><blockquote><p>在MySQL中,默认DML指令的执行是自动提交的,当我们执行一个DML指令之后,自动同步到数据库</p></blockquote><h4 id="事务管理"><a href="#事务管理" class="headerlink" title="事务管理"></a>事务管理</h4><blockquote><p>开启事务,关闭自动提交</p><ul><li><p>当开始事务第一个操作之前执行<code>start transaction</code> 开启事务</p></li><li><p>依次执行事务中的每个DML操作</p></li><li><p>如果在执行的过程中的任何位置出现异常,则执行<code>rollback</code> 回滚事务</p></li><li><p>如果所有操作都执行成功,则在最后执行<code>commit</code> 提交</p></li></ul></blockquote><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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 class="keyword">start</span> transaction; ## 开始事务</span><br><span class="line"><span class="comment">--SQL语句</span></span><br><span class="line"><span class="keyword">rollback</span>; ## 错误则回滚</span><br><span class="line"><span class="comment">--SQL语句</span></span><br><span class="line"><span class="keyword">commit</span>; ## 全部正确则提交</span><br></pre></td></tr></table></figure></div><h4 id="事务隔离级别"><a href="#事务隔离级别" class="headerlink" title="事务隔离级别"></a>事务隔离级别</h4><h5 id="MySQL数据库事务隔离级别"><a href="#MySQL数据库事务隔离级别" class="headerlink" title="MySQL数据库事务隔离级别"></a>MySQL数据库事务隔离级别</h5><ul><li><p>读未提交</p><blockquote><p>T2可以读取T1执行但未提交的数据,可能会导致出现脏读</p><p>脏读:一个事务读取到了另一个事务中未提交的数据</p></blockquote></li><li><p>读已提交</p><blockquote><p>T2只能读取T1已经提交的数据,可以避免脏读,但可能会导致不可重复读(虚读)</p><p>虚读:在同一个事务中,两次查询操作读取到数据不一致</p></blockquote></li><li><p>可重复读</p><blockquote><p>T2执行第一次查询后,在事务结束之前其他事务不能修改对应数据,避免了不可重复读(虚读),但可能导致幻读</p><p>幻读:T2对数据表中的数据进行修改然后查询,在查询之前T1像数据表中新增了一条数据,就导致T2以为修改了所有数据,但却查询除了与修改不一致的数据(T1事务新增的数据)</p></blockquote></li><li><p>串行化</p><blockquote><p>同时只允许一个事务对数据表进行操作,避免了脏读,虚读,幻读问题</p></blockquote></li></ul><table><thead><tr><th align="center">隔离级别</th><th align="center">脏读</th><th align="center">不可重复读(虚读)</th><th align="center">幻读</th></tr></thead><tbody><tr><td align="center">read uncommitted</td><td align="center">√</td><td align="center">√</td><td align="center">√</td></tr><tr><td align="center">read committed</td><td align="center">×</td><td align="center">√</td><td align="center">√</td></tr><tr><td align="center">repeatable read</td><td align="center">×</td><td align="center">×</td><td align="center">√</td></tr><tr><td align="center">serializable</td><td align="center">×</td><td align="center">×</td><td align="center">×</td></tr></tbody></table><h5 id="设置数据库事务隔离级别"><a href="#设置数据库事务隔离级别" class="headerlink" title="设置数据库事务隔离级别"></a>设置数据库事务隔离级别</h5><blockquote><p>通过设置数据库默认的事务隔离级别来控制事务之间的隔离性,也可以通过客户端与数据库连接设置来设置事务间的隔离性(在应用程序中设置)</p><p>MySQL数据库默认的隔离级别为<code>可重复读</code></p></blockquote><ul><li><p>查看MySQL数据库默认的隔离级别</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> @<span class="variable">@transaction</span>_isolation;</span><br></pre></td></tr></table></figure></div></li><li><p>设置MySQL默认隔离级别</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> session transaction isolation level <span class="operator"><</span>read committed<span class="operator">></span>;</span><br></pre></td></tr></table></figure></div></li></ul><h2 id="数据库设计"><a href="#数据库设计" class="headerlink" title="数据库设计"></a>数据库设计</h2><hr><h3 id="数据库设计流程"><a href="#数据库设计流程" class="headerlink" title="数据库设计流程"></a>数据库设计流程</h3><hr><ol><li><p><strong>根据应用系统功能,分析数据实体</strong>(实体,就是要存储的数据对象)</p><ul><li>电商系统:商品,用户,订单</li></ul></li><li><p><strong>提取实体的数据项</strong>(数据项,就是实体的属性)</p><ul><li>商品(商品名称,商品图片)</li></ul></li><li><p><strong>根据数据库设计三范式规范视图的数据项</strong></p><blockquote><p>检查实体的数据是否满足数据库设计三范式,如果实体的数据项不满足三范式,可能会导致数据的冗余,从而引起数据维护困难,破环数据一致性等问题</p></blockquote></li><li><p><strong>绘制E-R图</strong>(实体关系图,直观的展示实体与实体之间的关系)</p></li><li><p><strong>数据库建模</strong></p><ul><li><p>三线图进行数据表设计</p></li><li><p>PowerDesigner</p></li><li><p>PDMan</p></li></ul></li><li><p><strong>建库建表</strong>(编写SQL指令创建数据库,数据表)</p></li><li><p><strong>添加测试数据,SQL测试</strong></p></li></ol><h4 id="数据库设计三范式"><a href="#数据库设计三范式" class="headerlink" title="数据库设计三范式"></a>数据库设计三范式</h4><ul><li><p>第一范式(1NF):要求数据表中的字段(列)不可再分</p></li><li><p>第二范式(2NF):不存在非关键字段对关键字段的部分依赖</p></li><li><p>第三范式(3NF):不存在非关键字段不存在传递依赖</p></li></ul><h4 id="绘制E-R图"><a href="#绘制E-R图" class="headerlink" title="绘制E-R图"></a>绘制E-R图</h4><blockquote><p>E-R实体关系图,用于直观的体现实体与实体之间的关联关系(一对一,一对多,多对一,多对多)</p></blockquote><h5 id="E-R图基本图例"><a href="#E-R图基本图例" class="headerlink" title="E-R图基本图例"></a>E-R图基本图例</h5><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Lenovo\AppData\Roaming\marktext\images\2023-04-15-17-26-47-image.png"></p><ul><li><p>示例</p><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Lenovo\AppData\Roaming\marktext\images\2023-04-15-17-27-15-image.png"></p></li></ul><h5 id="三线图"><a href="#三线图" class="headerlink" title="三线图"></a>三线图</h5><blockquote><p>统一数据实体的表结构</p></blockquote><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Lenovo\AppData\Roaming\marktext\images\2023-04-15-17-40-28-image.png"></p><h4 id="数据库建模"><a href="#数据库建模" class="headerlink" title="数据库建模"></a>数据库建模</h4><blockquote><ul><li><p>E-R 图 数据表设计 建库建表</p></li><li><p>PowerDesigner建模工具 导出数据表</p></li><li><p>PDMan建模工具</p></li></ul></blockquote><h5 id="PowerDesigner使用"><a href="#PowerDesigner使用" class="headerlink" title="PowerDesigner使用"></a>PowerDesigner使用</h5><ul><li><p>概念数据模型 - E-R 图</p></li><li><p>逻辑数据模型 - 体现实体主外键关联</p></li><li><p>物理数据模型</p><ul><li><p>可以对物理模型进行微调</p></li><li><p>可以通过物理数据模型生成建库建表的SQL语句</p></li></ul></li><li><p>面向对象模型</p><ul><li>可以根据语言设置,生成实体类</li></ul></li></ul><blockquote><p>在企业项目开发,通常是不会使用建模工具来生成数据表,实体类的,因为生成的代码规范不合乎代码需求</p></blockquote>]]></content>
<summary type="html"><h2 id="SQL"><a href="#SQL" class="headerlink" title="SQL"></a>SQL</h2><hr>
<blockquote>
<p>结构化查询语言,用于存取,查询,更新数据以及管理关系型数据库系统</p>
</blockquot</summary>
<category term="MySQL Study" scheme="https://intyouss.github.io/tags/MySQL-Study/"/>
</entry>
<entry>
<title>数据库</title>
<link href="https://intyouss.github.io/2023/04/12/%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
<id>https://intyouss.github.io/2023/04/12/%E6%95%B0%E6%8D%AE%E5%BA%93/</id>
<published>2023-04-12T03:26:11.000Z</published>
<updated>2023-04-20T05:19:35.721Z</updated>
<content type="html"><![CDATA[<h2 id="MySQL-为什么使用-B-树来作索引,对比-B-树它的优点和缺点是什么?"><a href="#MySQL-为什么使用-B-树来作索引,对比-B-树它的优点和缺点是什么?" class="headerlink" title="MySQL 为什么使用 B+ 树来作索引,对比 B 树它的优点和缺点是什么?"></a>MySQL 为什么使用 B+ 树来作索引,对比 B 树它的优点和缺点是什么?</h2><hr><blockquote><ul><li><p>优点</p><ol><li><p>B+ 树具有更高的扇出因子,这意味着它们可以在每个节点中存储更多的键。这减少了树的高度并提高了搜索性能。此外,因为B+树仅在其叶子节点中存储键,所以它们可以存储比B树更多的键,B树在内部和叶子节点中都存储键</p></li><li><p>B+树的另一个优点是它们更适合范围查询。因为所有叶子节点都连接在一起形成一个链表,所以很容易按顺序遍历树并检索某个范围内的所有键。相比之下,B树需要额外的遍历才能找到范围内的所有键</p></li></ol></li><li><p>缺点</p><ol><li>使用B+树需要更多的磁盘空间,因为叶子节点中有额外的指针。此外,由于需要维护叶子节点的链接列表,B+树可能需要更多的CPU时间来执行插入和删除操作</li></ol></li><li><p>总结</p><p>在MySQL中使用B+树而不是B树的决定可能是基于改善搜索性能和增加磁盘空间和CPU使用率之间的权衡</p></li></ul></blockquote><h2 id="数据库大的事务隔离级别有哪些-各有哪些优缺点?"><a href="#数据库大的事务隔离级别有哪些-各有哪些优缺点?" class="headerlink" title="数据库大的事务隔离级别有哪些?各有哪些优缺点?"></a>数据库大的事务隔离级别有哪些?各有哪些优缺点?</h2><hr><table><thead><tr><th align="center">隔离级别</th><th align="center">脏读</th><th align="center">不可重复读</th><th align="center">幻读</th></tr></thead><tbody><tr><td align="center">Read uncommitted</td><td align="center">√</td><td align="center">√</td><td align="center">√</td></tr><tr><td align="center">Read committed</td><td align="center">×</td><td align="center">√</td><td align="center">√</td></tr><tr><td align="center">Repeatable read</td><td align="center">×</td><td align="center">×</td><td align="center">√</td></tr><tr><td align="center">Serializable</td><td align="center">×</td><td align="center">×</td><td align="center">×</td></tr></tbody></table><h2 id="什么是数据库事务的特性?-简述数据库中的ACID分别是什么"><a href="#什么是数据库事务的特性?-简述数据库中的ACID分别是什么" class="headerlink" title="什么是数据库事务的特性?(简述数据库中的ACID分别是什么)"></a>什么是数据库事务的特性?(简述数据库中的ACID分别是什么)</h2><hr><blockquote><p>原子性,一致性,隔离性,持久性</p></blockquote><h2 id="什么情况下会发生死锁,如何解决死锁?"><a href="#什么情况下会发生死锁,如何解决死锁?" class="headerlink" title="什么情况下会发生死锁,如何解决死锁?"></a>什么情况下会发生死锁,如何解决死锁?</h2><hr><blockquote><p>死锁是指两个或多个事务相互等待对方释放资源,从而导致所有事务都无法继续执行的情况。在MySQL中,死锁通常发生在事务中使用了不同的锁定顺序时</p><p>解决方法</p><ol><li><p>进行系统的重新启动,代价很大,它意味着在这之前所有的进程已经完成的计算工作都无效,包括参与死锁的进程,以及未参与的进程</p></li><li><p>撤销进程,剥夺资源。终止参加死锁的进程,收回它们占有的资源,从而解除死锁。</p><ul><li><p>情况一:一次性撤销参与死锁的全部进程,剥夺全部进程</p></li><li><p>情况二:逐步撤销参与死锁的进程,逐步收回死锁进程占有的资源</p></li></ul><p>一般来说,选择逐步撤销的进程时要按照一定的原则进行,目的是撤销哪些代价最小的进程,比如按进程的优先级确定进程的代价;考虑进程允许是=时的代价和与此进程相关的外部作业的代价等因素</p></li><li><p>进程回退策略,即让参与死锁的进程回退到没有发生死锁前的某一点处,并由此点处继续执行,以求再次执行时不在发生死锁。</p></li></ol></blockquote><h2 id="简述乐观锁以及悲观锁的区别以及使用场景"><a href="#简述乐观锁以及悲观锁的区别以及使用场景" class="headerlink" title="简述乐观锁以及悲观锁的区别以及使用场景"></a>简述乐观锁以及悲观锁的区别以及使用场景</h2><hr><blockquote><p>定义</p><ul><li><p>悲观锁(Pessimistic Lock)</p><blockquote><p>每次获取数据的时候,都会担心数据被修改,所以每次获取数据的时候都会进行加锁,确保在自己使用的过程中数据不会被别人修改,使用完成后进行数据解锁。由于数据进行加锁,期间对该数据进行读写的其他线程都会进行等待</p></blockquote></li><li><p>乐观锁(Optimistic Lock)</p><blockquote><p>每次获取数据时,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁,但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行更新,如果数据没有被其他线程修改,则进行更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写操作</p></blockquote></li></ul><p>适合场景</p><ul><li><p>悲观锁</p><blockquote><p>比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量</p></blockquote></li><li><p>乐观锁</p><blockquote><p>比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增加,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量</p></blockquote></li></ul><p>总结</p><blockquote><p>两种锁各有优缺,读取频繁用乐观锁,写入频繁用悲观锁</p></blockquote></blockquote><h2 id="聚簇索引和非聚簇索引的区别,什么时候使用聚簇索引?"><a href="#聚簇索引和非聚簇索引的区别,什么时候使用聚簇索引?" class="headerlink" title="聚簇索引和非聚簇索引的区别,什么时候使用聚簇索引?"></a>聚簇索引和非聚簇索引的区别,什么时候使用聚簇索引?</h2><hr><blockquote><p>聚簇索引和非聚簇索引是用于提高数据库查询性能的两种索引类型</p><p>聚簇索引决定了表中数据的物理顺序。他根据索引列对数据行进行排序和存储。<em>一个表只能有一个聚簇索引</em></p><p>非聚簇索引是数据行之外的独立结构。它包括索引列的副本和指向相应的数据行指针。<em>一个表可以有多个非聚簇索引</em></p><p>聚簇索引最适合在表中频繁查询某个列的一定范围内的值时使用。这是因为数据时根据索引列进行物理排序的,因此检索一定范围内的值更快。而非聚簇索引是数据行之外的一个独立结构,最适合在表中频繁查询使用多个列时使用。在决定使用哪一种时,需要考虑数据修改的频率</p><p>注意</p><blockquote><p>在具有频繁插入或更新的表上创建聚簇索引可能会对性能产生负面影响。这是因为每次插入新行或更新现有行时,数据都必须进行物理重组</p></blockquote></blockquote><h2 id="简述脏读和幻读的发生场景,InnoDB-是如何解决幻读的?"><a href="#简述脏读和幻读的发生场景,InnoDB-是如何解决幻读的?" class="headerlink" title="简述脏读和幻读的发生场景,InnoDB 是如何解决幻读的?"></a>简述脏读和幻读的发生场景,InnoDB 是如何解决幻读的?</h2><hr><blockquote><p>脏读是指一个事务读取了另一个事务未提交的数据,如果另一个事务回滚了操作,那么读取的数据就是无效的。InnoDB通过实现MVCC(多版本并发控制)来避免脏读的发生。MVCC通过在每一行数据后面添加两个隐藏的列来实现,一个是创造时间,一个是过期时间。当一个事务开始时,他会创建一个视图,这个视图包含了当前时间点之前的所有数据。如果一个事务正在修改一行数据,那么这个行的过期时间就会被设置为当前时间,这样其他事务就无法读取到这个行的数据。如果一个事务正在读取一行数据,那么这个行的创建时间必须早于这个事务的开始时间,这样才能保证读取到的数据是有效的</p><p>幻读是指一个事务在读取某个范围内的数据时,另一个事务在这个范围内插入和新的数据,导致第一个事务读取到了之前不存在的数据。InnoDB通过实现间隙锁来避免幻读的发生。当一个事务执行SELECT语句时,InnoDB会对查询的范围进行加锁,这样其他事务就无法在这个范围内插入新的数据。如果一个事务要插入新的数据,那么他必须等待其他事务释放锁之后才能执行插入操作</p></blockquote><h2 id="唯一索引与普通索引的区别是什么-使用索引会有哪些优缺点?"><a href="#唯一索引与普通索引的区别是什么-使用索引会有哪些优缺点?" class="headerlink" title="唯一索引与普通索引的区别是什么?使用索引会有哪些优缺点?"></a>唯一索引与普通索引的区别是什么?使用索引会有哪些优缺点?</h2><hr><blockquote><p>唯一索引会限定数据唯一,而普通索引则没有</p><p>优点</p><ol><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><p>缺点</p><ol><li><p>索引会占用磁盘空间</p></li><li><p>索引会降低写入性能</p><blockquote><p>每次插入,更新或删除数据时,都需要更新索引</p></blockquote></li><li><p>索引可能会导致查询变慢</p><blockquote><p>如果索引被错误的使用,或者索引的选择不当</p></blockquote></li></ol></blockquote><h2 id="MySQL的索引什么时候会失效?"><a href="#MySQL的索引什么时候会失效?" class="headerlink" title="MySQL的索引什么时候会失效?"></a>MySQL的索引什么时候会失效?</h2><blockquote><ol><li><p>带有运算</p><blockquote><p>在where子句中使用=,+这些运算符号</p></blockquote></li><li><p>使用not in,in</p><blockquote><p>范围运算,会导致索引失效产生全表扫描</p></blockquote></li><li><p>like模糊查询,使用%开头的左模糊查询</p></li><li><p>使用or的时候</p><blockquote><p>因为or是一个并集,如果or条件中有一个不是索引,就会导致索引失效</p></blockquote></li><li><p>字段类型与SQL语句类型不匹配时</p><blockquote><p>如果列类型是字符串,则SQL语句中使用到该列的条件数据就必须用引号引用起来,否则会导致索引失效,原因是MySQL会进行隐式的数据类型转换,导致索引失效</p></blockquote></li><li><p>使用函数</p></li><li><p>复合索引违反最左原则时</p><div class="highlight-container" data-rel="Sql"><figure class="iseeu highlight sql"><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">## 组合索引a,b,c,需出现a</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="keyword">table</span> <span class="keyword">where</span> a<span class="operator">=</span><span class="string">''</span> <span class="keyword">and</span> b<span class="operator">=</span><span class="string">''</span> ## 索引可用</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> <span class="keyword">table</span> <span class="keyword">where</span> b<span class="operator">=</span><span class="string">''</span> <span class="keyword">and</span> c<span class="operator">=</span><span class="string">''</span> ## 索引不可用</span><br></pre></td></tr></table></figure></div><blockquote><p>使用复合索引(组合索引)时,需要遵循最左前缀原则,即查询条件必须包括复合索引的最左前缀列,才能使用该索引进行查询</p><p>最左前缀原则的意义在于,如果查询条件包含复合索引的最左前缀列,那么该索引可以帮助数据库快速定位到符合条件的数据,从而提高查询效率。如果查询条件不包含最左前缀列,那么该索引就无法使用,需要对所有数据进行扫描,这导致查询变慢</p></blockquote></li></ol></blockquote><h2 id="简述MySQL的主从同步机制"><a href="#简述MySQL的主从同步机制" class="headerlink" title="简述MySQL的主从同步机制"></a>简述MySQL的主从同步机制</h2><blockquote><p>指将一个MySQL数据库实例(主库)的数据同步到另一个MySQL数据库实例(从库)的过程。主从同步可以实现数据备份,负载均衡,读写分离等操作</p><p>原理</p><blockquote><p>主库将更新操作记录在二进制日记(binlog)中,从库连接主库,将主库的binlog复制到从库的中继日志(relay log)中,然后从库读取中继日志,将更新操作应用到从库的数据中。从库可以设置延迟时间,以便在主库出现故障时,从库可以称为新的主库,继续提供服务</p></blockquote><p>步骤</p><blockquote><ol><li><p>主库接收用户左CRUD操作,写入数据库,更新结果集到binlog中</p></li><li><p>主从同步时主找从的,从库IO发起请求,主库的主进程看从库 master change 中给的参数是否合法,如果合法主进程交给IO进程执行2操作,否则拒绝处理</p></li><li><p>主库根据 master 的位置点,从这个位置点的 binlog 日志一直到 binlog 日志最后,将其准备发送给从库</p></li><li><p>将找到的 binlog 日志发送给从库,并且还会发送新的日志点</p></li><li><p>从库接收到 binlog 日志,将其写入 relay-log(中继日志)中</p></li><li><p>从库IO进程再想 master info 保存主库传过来的最后的 binlog 日志的日志位置点</p></li><li><p>从库IO时循环发起的请求,发了再要,不会管 SQL 读取中继日志的操作。从库 IO 根据新的日志点,向主库发起请求,主库执行 3 4 操作,再发送新的 binlog 给从库,从库再执行5操作</p></li><li><p>其实第一次向 relay-log 中放数据时,SQL进程就已经知道,SQL进程将 relay-log 中的SQL语句转换成数据,写入从库,从而实现同步</p></li><li><p>从库 SQL 线程读取中继日志,并不会一次性读完,会把读取到的日志点存放到relay-log.info中</p></li></ol></blockquote><p>主从同步配置注意</p><blockquote><ol><li><p>主从库MySQL版本必须一致,否则可能会出现兼容性问题</p></li><li><p>主从库的字符集和校对规则必须一致,否则可能会出现乱码等问题</p></li><li><p>主从库的数据表结构必须一致,否则可能会出现同步失败等问题</p></li><li><p>主从库的数据同步需要保证网络稳定,否则可能会出现同步延迟等问题</p></li><li><p>主从库的数据同步需要保证主库的写入性能,否则可能会出现主库性能下降等问题</p></li></ol></blockquote></blockquote><h2 id="主从复制延迟"><a href="#主从复制延迟" class="headerlink" title="主从复制延迟"></a>主从复制延迟</h2><blockquote><p>指从库的数据更新操作与主库的数据更新操作之间存在一定的时间差,这可能导致从库的数据不一致</p><ul><li><p>网络延迟</p><blockquote><p>主库和从库之间的网络延迟可能会导致从库无法及时获取主库的更新操作</p></blockquote></li><li><p>从库负载过高</p><blockquote><p>同上</p></blockquote></li><li><p>主库负载过高</p><blockquote><p>如果主库的负载过高,可能会导致主库无法即时将更新操作记录到二进制日志中,从而导致从库无法及时获取更新操作</p></blockquote></li></ul></blockquote><h2 id="Redis-如何实现延时队列"><a href="#Redis-如何实现延时队列" class="headerlink" title="Redis 如何实现延时队列"></a>Redis 如何实现延时队列</h2><blockquote><p>可以使用 有序集合 sortedset 来存储任务,我们可以将任务的执行时间作为有序集合的 score,任务大的内容作为有序集合的 member。这样,我们就可以通过 score 的排序来获取最早需要执行的任务</p></blockquote><h2 id="简述-Redis-中如何防止缓存雪崩和缓存击穿"><a href="#简述-Redis-中如何防止缓存雪崩和缓存击穿" class="headerlink" title="简述 Redis 中如何防止缓存雪崩和缓存击穿"></a>简述 Redis 中如何防止缓存雪崩和缓存击穿</h2><h4 id="缓存击穿"><a href="#缓存击穿" class="headerlink" title="缓存击穿"></a>缓存击穿</h4><blockquote><p>是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力增大,造成过大压力</p><p>解决方法</p><ol><li><p>设置热点数据永不过期</p></li><li><p>加互斥锁</p></li></ol></blockquote><h4 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h4><blockquote><p>是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至 down 机。和缓存击穿不同的是,缓存击穿指并发查询同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查询数据库</p><p>解决方法</p><ol><li><p>缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生</p></li><li><p>如果缓存数据库是分布式部署,将热点数据均匀分布在不同的缓存数据库中</p></li><li><p>设置热点数据永不过期</p></li></ol></blockquote><h2 id="简述-Redis-持久化中-rdb-以及-aof-方案的优缺点"><a href="#简述-Redis-持久化中-rdb-以及-aof-方案的优缺点" class="headerlink" title="简述 Redis 持久化中 rdb 以及 aof 方案的优缺点"></a>简述 Redis 持久化中 rdb 以及 aof 方案的优缺点</h2><h4 id="RDB-方案"><a href="#RDB-方案" class="headerlink" title="RDB 方案"></a>RDB 方案</h4><blockquote><p>是将 Redis 内存中的数据定期保存到磁盘上,以保证数据的持久化</p></blockquote><h5 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h5><ul><li><p>RDB 方案适合大规模的数据恢复,因为 RDB 文件是一个快照,可以在 Redis 重启时快速的加载数据</p></li><li><p>RDB 方案对于 Redis 的性能影响较小,因为 RDB 文件是在后台异步生成的,不会影响 Redis 的正常操作</p></li></ul><h5 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h5><ul><li><p>RDB 方案不够实时,如果 Redis 发生故障,可能会丢失最后一次快照之后的数据</p></li><li><p>RDB 方案的文件较大,如果数据量较大,可能会占用较多的磁盘空间</p></li></ul><h4 id="AOF-方案"><a href="#AOF-方案" class="headerlink" title="AOF 方案"></a>AOF 方案</h4><blockquote><p>是将 Redis 的操作日志以追加的方式写入到磁盘上,以保证数据的持久化</p></blockquote><h5 id="优点-1"><a href="#优点-1" class="headerlink" title="优点"></a>优点</h5><ul><li><p>AOF 方案可以保证数据的实时性,因为每个操作都会被记录到 AOF 文件中</p></li><li><p>AOF 方案的文件较小,因为只记录了 Redis 的操作日志,而不是整个数据集</p></li></ul><h5 id="缺点-1"><a href="#缺点-1" class="headerlink" title="缺点"></a>缺点</h5><ul><li><p>AOF 方案不够适合大规模的数据恢复,因为 AOF 文件是一个操作日志,需要重新执行所有的操作才能恢复数据</p></li><li><p>AOF 方案对于 Redis 的性能影响较大,因为每个操作都需要写入到 AOF 文件中,会影响 Redis 的正常操作</p></li></ul>]]></content>
<summary type="html"><h2 id="MySQL-为什么使用-B-树来作索引,对比-B-树它的优点和缺点是什么?"><a href="#MySQL-为什么使用-B-树来作索引,对比-B-树它的优点和缺点是什么?" class="headerlink" title="MySQL 为什么使用 B+ 树来作</summary>
<category term="面试" scheme="https://intyouss.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>计算机网络</title>
<link href="https://intyouss.github.io/2023/04/09/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/"/>
<id>https://intyouss.github.io/2023/04/09/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/</id>
<published>2023-04-09T12:59:00.000Z</published>
<updated>2023-04-25T11:47:05.267Z</updated>
<content type="html"><![CDATA[<h1 id><a href="#" class="headerlink" title></a></h1><ol><li><p>简述 TCP 三次握手以及四次挥手的流程。为什么需要三次握手以及四次挥手?</p><blockquote><ul><li><p>三次握手</p><blockquote><p>指建立一个TCP连接时,需要客户端和服务器总共发送3个包</p><ol><li><p>客户端发送一个SYN包到服务器,并进入SYN-SEND状态</p></li><li><p>服务器收到SYN包后,回复一个SYN-ACK包,确认收到客户端的SYN包,并进入SYN-RECV状态</p></li><li><p>客户端收到服务器的SYN+ACK包后,发送一个ACK包给服务器,确认收到服务器的SYN+ACK包,此时连接建立成功</p></li></ol></blockquote></li><li><p>四次挥手</p><blockquote><p>指断开一个TCP连接时,需要客户端和服务器总共发送4个包</p><ol><li><p>客户端发送一个FIN包到服务器,表示客户端不在发送数据,并进入FIN-WAIT-1状态</p></li><li><p>服务器收到FIN包后,回复一个ACK包,确认收到客户端的FIN包,并进入CLOSE-WAIT状态</p></li><li><p>服务器再次发送一个FIN包给客户端,表示服务器不再发送数据,并进入LAST-ACK状态</p></li><li><p>客户端收到服务器的FIN包后,回复一个ACK包给服务器,确认收到服务器的FIN包,并进入TIME-WAIT状态。此时连接断开成功,而TIME-WAIT状态在等待2MSL后返回CLOSED状态</p></li></ol></blockquote></li></ul><p>三次握手是为了防止已失效的连接请求报文突然又传送到了服务端。</p><p>四次挥手是为了确保数据能够完整传输。</p><p>为什么TIME-WAIT状态还需要等待2MSL后才能返回到CLOSED状态?</p><blockquote><ol><li>无法保证最后发送的ACK报文会一定被对方收到,所以需要重发可能丢失的ACK报文</li><li>关闭连接一段时间后可能会出现相同的IP地址和端口建立新的连接,为了防止旧连接的重复分组在新连接已经终止后再现。2msl足以让分组最多存活msl秒被丢弃</li></ol></blockquote><p>为什么必须是三次握手,不能用两次握手进行连接?</p><blockquote><p>两次握手容易死锁!如果服务器的应答分组在传输中丢失,S将不知道建立什么样的序列号。C认为链接还未建立成功,将忽视S发来的任何数据分组,只等待连接确认。而S在发出的分组超时后,重复发送同样的分组,这样就形成了死锁</p></blockquote></blockquote></li><li><p>Restful 与 RPC 的区别是什么? Restful 的优点在哪里?</p><blockquote><p>Restful 和 RFC 的主要区别在于 Restful 是一种用于构建web服务的架构结构,而 RFC 是一系列描述互联网及其协议的技术文档</p><p>Restful 基于一组原则,强调使用 HTTP 方法和资源 URL 来表示和操作资源。它还强调无状态性,这意味这每个请求应包含完成请求所需的所有信息,而不依赖于任何先前的请求</p><p><code>Restful: GET /order/123</code></p><p>RFC 是一系列描述互联网及其协议的技术文档。这些文档又互联网工程任务组(IEIF)创建,并用于定义电子邮件,文件传输和网络协议等标准</p><p><code>RFC: Queryorder/order_id=123</code></p></blockquote><blockquote><p>Restful 优点在于它是一种简单易用,高度可扩展,可移植的架构风格,可以帮助开发人员更轻松地构建web服务</p></blockquote></li><li><p>HTTP 与 HTTPS 有什么区别?</p><blockquote><ol><li><p>HTTPS 协议需要到证书颁发机构CA申请证书,HTTP 不用申请</p></li><li><p>HTTP 是长文本传输协议,属于应用层信息传输。HTTPS 则是具有 SSL 加密安全性传输协议,对数据的传输进行加密,相当于 HTTP 的升级版</p></li><li><p>HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443</p></li><li><p>HTTP 的连接很简单,是无状态的;HTTPS 协议是由 SSL + HTTP 协议构建的可进行加密传输,身份认证的网络协议,比 HTTP 协议安全</p></li></ol></blockquote></li><li><p>HTTPS 的加密原理是什么?</p><blockquote><p>HTTPS 使用一种称为 SSL/TLS 的协议来加密数据传输。SSL/TLS 使用公钥加密和私钥解密的方法来保护数据的机密性和完整性</p><p>当客户端与服务器建立连接时,ta们会协商一种加密算法和密钥长度,然后使用该算法和密钥来加密和解密数据。这种加密方法可以防止中间人攻击和窃听,因为只有客户端和服务器知道密钥</p></blockquote></li><li><p>一次 HTTP 请求过程中发生了什么?</p><blockquote><p>HTTP 请求过程中发生了以下步骤:</p><ol><li><p>DNS 解析:客户端将 URL 解析为 IP 地址,以便与服务器建立连接。</p></li><li><p>建立 TCP 连接:客户端使用 TCP 协议与服务器建立连接,以便在客户端和服务器之间传输数据</p></li><li><p>发送 HTTP 请求:客户端向服务器发送 HTTP 请求,其中包括请求方法,请求头和请求体</p></li><li><p>服务器处理请求:服务器接收到 HTTP 请求后,会根据请求方法和请求 URL 来处理请求,并返回 HTTP 响应</p></li><li><p>接收 HTTP 响应:客户端接收到 HTTP 响应后,会解析响应头和响应体,并根据响应状态码来判断请求是否成功</p></li><li><p>关闭 TCP 连接:客户端和服务器之间的 TCP 连接在请求和响应完成后会被关闭</p></li></ol></blockquote></li><li><p>域名解析的过程是?</p><blockquote><ol><li><p>客户端向本地DNS服务器发送域名解析请求</p></li><li><p>如果本地DNS服务器缓存了该域名的IP地址,则直接返回该IP地址给客户端</p><p>如果本地DNS服务器没有缓存该域名的IP地址,则向根DNS服务器发送域名解析请求</p></li><li><p>根DNS服务器返回顶级域名服务器的IP地址给本地DNS服务器</p></li><li><p>本地DNS服务器向顶级域名服务器发送域名解析请求</p></li><li><p>顶级域名服务器返回次级域名服务器的IP地址给本地DNS服务器</p></li><li><p>本地DNS服务器向次级域名服务器发送域名解析请求</p></li><li><p>次级域名服务器返回该域名的IP地址给本地DNS服务器</p></li><li><p>本地DNS服务器将该IP地址缓存起来,并返回给客户端</p></li></ol></blockquote></li><li><p>TCP 与 UDP 在网络协议中的哪一层,他们之间有什么区别?</p><blockquote><p>TCP 和 UDP 是传输层协议,用于在网络上传输数据。</p><p>它们之间的主要区别在于它们如何传输数据以及它们提供的服务类型</p><p>TCP(传输控制协议):</p><blockquote><p>是一种面向连接的协议,它提供可靠的数据传输服务。它使用三次握手协议来建立连接,并使用确认和重传机制来确保数据的可靠传输。TCP 还提供流量控制和拥塞控制功能,以确保网络的稳定性和可靠性。TCP 通常用于需要可靠数据传输的应用程序</p></blockquote><p>UDP(用户数据报协议):</p><blockquote><p>是一种无连接的协议,它提供不可靠的数据传输服务。它不使用握手协议来建立连接,也不提供确认和重传机制。UDP 不提供流量控制和拥塞控制功能,因此他通常用于需要快速数据传输的应用程序</p></blockquote></blockquote></li><li><p>TCP 中拥塞控制的实现?</p><blockquote><p>慢启动:</p><blockquote><p>在TCP连接建立后,发送方会先发送少量数据,然后逐渐增加发送数据的速率,直到网络出现拥塞为止。这样可以避免发送方一开始就发送大量数据导致网络拥塞</p></blockquote><p>拥塞避免:</p><blockquote><p>一旦网络出现拥塞,TCP会减小发送窗口的大小,以避免发送过多的数据导致网络拥塞。同时,TCP还会动态调整发送窗口的大小,以适应网络的变化</p></blockquote><p>拥塞恢复</p><p>快速重传</p><p>快速恢复</p></blockquote></li><li><p>TCP 怎么保证可靠传递?</p><blockquote><p>校验和</p><p>确认应答与序列号</p><p>超时重传</p><p>连接管理</p><p>流量控制:</p><blockquote><ol><li><p>发送方维护一个发送窗口,用于存储已发送但未被确认的数据。发送方只有在接收到接收方的确认后才能将窗口向前移动,发送更多的数据</p></li><li><p>接收方维护一个接收窗口,用于存储已接收但未被处理的数据。接收方会向发送方发送确认消息,告诉发送方已经成功接收到数据,并将接收窗口向前移动,以便接收更多的数据</p></li><li><p>发送方根据接收方发送的确认消息来动态调整发送窗口的大小,如果接收方的接收窗口变小,发送方会减小发送窗口的大小,以避免发送过多的数据导致接收方无法处理。如果接收方的接收窗口变大,发送方会增大发送窗口的大小,以提高发送数据的速率</p></li></ol><p>避免了发送过多数据导致接收方无法处理的情况</p></blockquote><p>拥塞控制</p></blockquote></li><li><p>从系统层面上看,UDP如何保证尽量可靠?</p><blockquote><p>为了在系统级别上确保可靠性,UDP实现的几种技术。</p><p>其中一种技术是使用校验和来检测数据中的错误。这可以通过在UDP头中添加一个校验和字段来实现,该字段是基于发送的数据计算出来的。接收方可以重新计算校验和,并将其与头部中发送的校验和进行比较,以检测任何错误</p><p>另一种技术是在应用程序级别实现重传。这涉及到发送方跟踪接收方已成功接受的数据包,并重新传输任何未被确认的数据包。这可以使用UDP头中的序列号来实现</p><p>需要注意的是,虽然这些技术可以提高可靠性,但他并不能保证可靠性。UDP仍然是一种尽力而为的协议,数据包仍然可能在传输过程中丢失或损坏。</p></blockquote></li><li><p>TCP 的 keepalive 了解吗?说一说它和 http 的 keepalive 的区别</p><blockquote><p>在TCP中,keepalive用于检测连接是否仍然处于活动状态。如果连接在一段时间内没有活动,则会发送一个keepalive消息,以确保连接仍然存在。这可以防止连接因为长时间没有活动而被关闭</p><p>与 HTTP 的 keepalive 不同,TCP 的 keepalive 是在 TCP 层面上实现的,而 HTTP 的 keepalive 是在应用层面实现的。HTTP 的 keepalive 是指在一个 HTTP 连接中,客户端可以发送多个请求,而不需要每次都重新建立连接。这可以减少连接建立和断开的开销,提高网络性能。</p></blockquote></li><li><p>简述 HTTP 1.0 1.1 2.0 的主要区别</p><blockquote><p>HTTP1.0</p><ul><li>无状态,无连接</li></ul><p>HTTP1.1</p><ul><li><p>支持长连接</p></li><li><p>增加缓存处理</p></li><li><p>增加host字段</p></li><li><p>支持断点传输</p></li></ul><p>HTTP2.0</p><ul><li><p>二进制分帧</p></li><li><p>多路复用(连接共享)</p></li><li><p>头部压缩,encoder</p></li><li><p>服务器推送</p></li></ul></blockquote></li><li><p>HTTP 的方法有哪些?</p><blockquote><p>get post put delete patch</p></blockquote></li><li><p>简述 TCP 协议的延迟 ACK 和累计应答</p><blockquote><p>两者用于优化网络传输性能和可靠性</p><p>延迟 ACK 是指 TCP 接收方在接收到数据包后不立即发送 ACK 确认消息,而是等待一段时间(通常是200ms),以便在此期间接收到其他数据包时可以一起发送 ACK。这可以减少网络流量和减轻网络负载,从而提高性能。但是,延迟 ACK 也可能导致一些问题,例如在某些情况下可能会导致数据包重传</p><p>累计应答是指 TCP 接收方只发送一个 ACK 消息来确认接收到的所有数据包,而不是每个数据包都发送一个 ACK。这可以减少网络流量和减轻网络负载,从而提高性能。但是累计应答也可能导致一些问题,例如在某些情况下可能会导致发送方重传已经被接收方接受的数据包</p></blockquote></li><li><p>简述 TCP 的报文头部结构</p><blockquote><p>16位端口号,32位序号,32位确认号,4位头部长度,6位标志位,16位窗口大小,16位校验和,16位紧急指针,TCP 头部选项</p></blockquote></li><li><p>简述 TCP 半连接发生场景</p><blockquote><ol><li><p>服务器负载过高,无法处理新的连接请求。在这种情况下,服务器可能会忽略或拒绝新的连接请求,导致客户端无法建立完整的TCP连接</p></li><li><p>防火墙或其他网络设备阻止了 SYN + ACK 包的传输。在这种情况下,客户端可能会发送多个SYN包,但服务器仍然无法回复 SYN + ACK 包,导致客户端无法建立完整的 TCP 连接</p></li></ol><p>解决方法:</p><blockquote><ol><li><p>增加服务器的处理能力,以便能够处理更多的连接请求</p></li><li><p>检查网络设备和防火墙的配置,确保它们不会阻止 SYN + ACK 包的传输</p></li><li><p>在客户端和服务器之间使用心跳机制,以便在连接空闲时保持连接状态</p></li></ol></blockquote></blockquote></li><li><p>什么时 SYN flood?如何防范?</p><blockquote><p>SYN flood是一种网络攻击,攻击者通过发送大量的 SYN 包来占用服务器的资源,从而导致服务器无法处理正常的连接请求。</p><p>可以采取以下措施防范:</p><blockquote><ol><li><p>增加服务器的处理能力,以便能够处理更多的连接请求。这可以通过增加服务器的硬件资源(例如CPU, 内存,网络带宽等)或优化服务器的软件配置(例如调整服务器的网络栈参数,使用负载均衡等)来实现</p></li><li><p>使用防火墙或其他网络设备来过滤 SYN flood 攻击。这可以通过配置防火墙规则,使用 SYN cookies 等技术来实现。SYN cookies 是一种防范 SYN flood 攻击的技术,它可以在服务器端生成一个加密的 cookie,并将其发送给客户端。当客户端发送 SYN 包时,服务器可以验证 cookie 的有效性,从而防止 SYN flood 攻击</p></li></ol></blockquote></blockquote></li></ol>]]></content>
<summary type="html"><h1 id><a href="#" class="headerlink" title></a></h1><ol>
<li><p>简述 TCP 三次握手以及四次挥手的流程。为什么需要三次握手以及四次挥手?</p>
<blockquote>
<ul>
<li><p>三次握手</p></summary>
<category term="面试" scheme="https://intyouss.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>操作系统</title>
<link href="https://intyouss.github.io/2023/04/08/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/"/>
<id>https://intyouss.github.io/2023/04/08/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/</id>
<published>2023-04-08T03:07:24.000Z</published>
<updated>2023-04-10T10:14:51.825Z</updated>
<content type="html"><![CDATA[<ol><li><p>进程和线程之间有什么区别?</p><blockquote><p>进程是运行中的程序,线程是进程的内部的一个执行序列<br>进程是资源分配单元,线程是执行单元<br>进程间切换代价大,线程间切换代价小<br>进程拥有资源多,线程拥有资源少<br>多个线程共享进程资源</p><p>进程为啥切换代价比线程要大?</p><blockquote><p>物理地址就是真实的地址,这种寻址方式很容易破坏操作系统,而且使得操作系统中同时运行两个或以上的程序几乎是不可能的(第一个程序给物理内存地址赋值 10,第二个程序也同样给这个地址赋值为 100,那么第二个程序的赋值会覆盖掉第一个程序所赋的值,这会造成两个程序同时崩溃)。</p><p>当然,也不是完全不可能,有一种方式可以实现比较粗糙的并发。就是说,我们将空闲的进程存储在磁盘上,这样当它们不运行时就不会占用内存,当进程需要运行的时候再从磁盘上转到内存上来,不过很显然这种方式比较浪费时间。</p><p>于是,我们考虑,把所有进程对应的内存一直留在物理内存中,给每个进程分别划分各自的区域,这样,发生上下文切换的时候就切换到特定的区域<br>那问题还是很明显的,就是仍然没法避免破坏操作系统,因为各个进程之间可以随意读取、写入内容。</p><p>所以,我们需要一种机制对每个进程使用的地址进行保护,因此操作系统创造了一个新的内存模型,那就是虚拟地址空间, 就是说,每个进程都拥有一个自己的虚拟地址空间,并且独立于其他进程的地址空间,然后每个进程包含的栈、堆、代码段这些都会从这个地址空间中被分配一个地址,这个地址就被称为虚拟地址。底层指令写入的地址也是虚拟地址。</p><p>有了虚拟地址空间后,CPU 就可以通过虚拟地址转换成物理地址这样一个过程,来间接访问物理内存了。</p><p>地址转换需要两个东西,一个是 CPU 上的内存管理单元 MMU,另一个是内存中的页表,页表中存的虚拟地址到物理地址的映射。</p><p>但是呢,每次访问内存,都需要进行虚拟地址到物理地址的转换,对吧,这样的话,页表就会被频繁地访问,而页表又是存在于内存中的。所以说,访问页表(内存)次数太多导致其成为了操作系统地一个性能瓶颈。</p><p>于是,引入了转换检测缓冲区 TLB,也就是快表,其实就是一个缓存,把经常访问到的内存地址映射存在 TLB 中,因为 TLB 是在 CPU 的 MMU 中的嘛,所以访问起来非常快。</p><p>然后,正是因为 TLB 这个东西,导致了进程切换比线程切换慢。</p><p>由于进程切换会涉及到虚拟地址空间的切换,这就导致内存中的页表也需要进行切换,一个进程对应一个页表是不假,但是 CPU 中的 TLB 只有一个,页表切换后这个 TLB 就失效了。这样,TLB 在一段时间内肯定是无法被命中的,操作系统就必须去访问内存,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢。</p><p>而线程切换呢,由于不涉及虚拟地址空间的切换,所以也就不存在这个问题了。</p></blockquote></blockquote></li><li><p>进程间有哪些通信方式?</p><ol><li><p>管道:</p><blockquote><p>管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有关联的进程间使用,进程的关系通常是指父子进程关系。</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><li><p>共享内存通信:</p><blockquote><p>共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个远程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间简单通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。</p></blockquote></li><li><p>socket:</p><blockquote><p>套接字机制与其他机制不同的是,它可用于不同机器间的进程通信。</p></blockquote></li></ol></li><li><p>简述 socket 中 select 与 epoll 的使用场景以及区别?</p><blockquote><p>两个网络模型本质上都是IO多路复用的</p><p>select 需要不断的去轮询数据,时间复杂度为O(n),性能比较低,并且它的文件描述符也是有限制的。像常见的服务器软件 apache 用的就是这种网络类型。</p><p>而 epoll 则不一样,他不会去轮询,而是会注册一个监听事件,每当数据有变化的时候,就会通知,像 nginx 底层的网络模型就是用的 epoll ,这个也是为啥nginx 能处理高并发的主要原因。</p><p>表面上看 epoll 的性能最好,但是在连接数少并且连接都十分活跃的情况下,select 和 poll 的性能可能比 epoll 好,毕竟 epoll 的通知机制需要很多函数回调。</p><p>select 低效是因为每次它都需要轮询,但低效也是相对的,是情况而定,也可以通过良好的设计改善。</p></blockquote></li><li><p>epoll 中水平触发以及边缘触发有什么不同?</p><ul><li><p>Level_triggered(水平触发):</p><blockquote><p>当被监控的文件描述符上有可读写事件发生时,epoll_wait() 会通知处理程序去读写,如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait() 时,它还会通知你在没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量的你不需要读写的就绪文件描述符,而他们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!</p></blockquote></li><li><p>Edge_triggered(边缘触发):</p><blockquote><p>当被监控的文件描述符上有可读写事件发生时,epoll_wait() 会通知程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait() 时,它不会通知你,也就是它只会通知一次,知道该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!</p></blockquote></li></ul></li><li><p>Linux 进程调度中有哪些常见算法以及策略?</p><blockquote><ol><li><p>先来先服务和短作业(进程)优先调度算法</p></li><li><p>高优先权优先调度算法</p><ul><li><p>非抢占式优先权算法</p></li><li><p>抢占式优先权调度算法</p></li></ul></li></ol></blockquote></li></ol><blockquote><p>进程的状态有哪几种?</p><ol><li><p>运行态:</p><blockquote><p>该状态表明进程在实际占用CPU。</p></blockquote></li><li><p>就绪态:</p><blockquote><p>该状态下进程可以运行,但因为其他进程正在运行而暂时停止。</p></blockquote></li><li><p>阻塞态:</p><blockquote><p>该状态下进程不能运行,除非某种外部事件的发送。</p></blockquote></li></ol><p>运行态 –> 等待态</p><blockquote><p>往往是由于等待外设,等待主存等资源分配或等待人工干预而引起的</p></blockquote><p>等待态 –> 就绪态</p><blockquote><p>则是等待的条件已满足,只需分配到处理器后就能运行</p></blockquote><p>运行态 –> 就绪态</p><blockquote><p>不是由于自身原因,而是由外界原因使运行状态的进程让出处理器,这时候就变成就绪态。例如时间片用完,或有更高优先级的进程来抢占处理器等</p></blockquote><p>就绪态 –> 运行态</p><blockquote><p>系统按某种策略选中就绪队列中的一个进程占用处理器,此时就变成了运行态</p></blockquote></blockquote><ol start="6"><li><p>操作系统如何申请以及管理内存的?</p><blockquote><p>虚拟内存是现代操作系统普遍使用的一项技术,很多情况下,现有内存无法满足仅仅一个大进程的内存要求(比如很多游戏都是10G+的级别)。在早期的操作系统曾使用覆盖(overlays)来解决这个问题,将一个程序分为多个块,基本思想是先将块0加入内存,块0执行完成后,将块1加入内存。以此往复,这个解决方案最大的问题是需要程序员去程序进行分块,这是一个费时费力的过程。后来这个解决方案的修正版就是虚拟内存。</p><p>虚拟内存的基本思想是,每个进程有独立的逻辑地址空间,内存被分为大小相等的多个快,称为页。每个页都是一段连续的地址。对于进程来看,逻辑上貌似有很多内存空间,其中一部分对应物理内存的一块(称为页框,通常页和页框大小相等),还有一些没加载在内存中的对应在硬盘上。</p></blockquote></li><li><p>简述 Linux 系统态与用户态,什么时候会进入系统态?</p><blockquote><ol><li><p>系统调用</p></li><li><p>发生异常,当 cpu 正在执行运行在用户的程序的时候,突然发生某些预先不可知的异常事件,这个时候就会触发从当前用户态执行的进程转向内核执行相关的异常事件,典型的如缺页异常。</p></li><li><p>外围设备的中断,当外围设备完成用户的请求操作后,会像CPU发出中断信号,此时,CPU 就会暂停执行下一条即将要执行的指令,转而去执行中断信号对应的处理程序,如果先前执行的指令是在用户态下,则自然就发生从用户态到内核态的转换。</p></li></ol></blockquote></li><li><p>简述 LRU 算法及其实现方式</p><blockquote><p>Least Recently Used,最近最久未使用法,它是按照一个非常著名的计算机操作系统基础理论得来的:最近使用的页面数据会在未来一段时期内仍然被使用,已经很久没有使用的页面很有可能在未来较长的一段时间内仍然不会被使用。基于这个思想,会存在一种缓存淘汰机制,每次从内存中找到最久未使用的数据然后置换出来,从而存入新的数据!它的主要衡量指标是使用的时间,附加指标是使用的次数。在计算机中大量使用了这个机制,它的合理性在于优先筛选热点数据,所谓热点数据,就是最近最多使用的数据!因为,利用 LRU 我们可以解决很多实际开发中的问题,并且很符合业务。利用双向链表实现</p></blockquote></li><li><p>线程间有哪些通信方式</p><blockquote><p>共享变量,共享内存,共享数据库,消息队列</p></blockquote></li><li><p>简述同步与异步的区别,阻塞与非阻塞的区别</p><blockquote><ol><li><p>同步和异步</p><blockquote><p>同步和异步通常用来描述消息传递或通信的方式。</p><p>在同步通信中,发送方会等待接收方的响应,然后才继续发送下一条消息。相反,在异步通信中,发送方不会等待接收方的响应,而是继续发送下一条消息。</p></blockquote></li><li><p>阻塞与非阻塞</p><blockquote><p>阻塞与非阻塞通常用来描述系统调用的行为。系统调用是应用程序请求操作系统内核提供服务的一种机制,例如读写文件,创建进程等。</p><p>当一个应用程序发起一个阻塞的系统调用时,他会一直等待,直到该调用完成。相反,当一个应用程序发起一个非阻塞的系统调用时,如果该调用不能立即完成,他会立即返回。</p></blockquote></li></ol><p>总结</p><blockquote><p>同步:</p><blockquote><p>执行一个操作之后,等待结果,然后才继续执行后续的操作</p></blockquote><p>异步:</p><blockquote><p>执行一个操作之后,可以去执行其他的操作,然后等待通知再回来执行完的操作</p></blockquote><p>阻塞:</p><blockquote><p>进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作</p></blockquote><p>非阻塞:</p><blockquote><p>进程给CPU传达任务后,继续处理后续的操作,隔段时间再来询问之前的操作是否完成,这样的过程也叫轮询</p></blockquote></blockquote></blockquote></li><li><p>简述操作系统中的缺页中断</p><blockquote><p>虚拟内存实际上可以比物理内存大,当访问虚拟内存时,会访问MMU(内存管理单元)去匹配对应的物理地址,而如果虚拟内存的页并不存在于物理内存中,会产生缺页中断,从磁盘中取得缺的页放入内存中,如果内存已满,还会根据页的置换算法将磁盘中的页换出。</p></blockquote></li><li><p>简述操作系统中 malloc 的实现原理</p><blockquote><p>malloc 基本的实现原理就是维护一个内存空闲链表,当申请内存空间时,搜索内存空闲链表,找到适配的空闲内存空间,然后将空间分割成两个内存块,一个变成分配块,一个变成新的空闲块。如果没有搜索到,那么就会用sbrk()推进brk指针来申请内存空间。</p><p>搜索空闲块最常见的算法有:首次适配,下一次适配,最佳适配。</p><p>首次适配:</p><blockquote><p>第一次找到足够大的内存块就分配,这种方法会产生很多内存碎片</p></blockquote><p>下一次适配:</p><blockquote><p>等第二次找到足够大的内存块就分配,这样会产生比较少的内存碎片</p></blockquote><p>最佳适配:</p><blockquote><p>对堆进行彻底的搜索,重投开始,遍历所有块,使用数据区大小大于size且差值最小的块作为此次分配的块</p></blockquote></blockquote></li><li><p>简述 Linux 的 IO模型</p><blockquote><p>在linux操作系统中,共有五种IO模型</p><ul><li><p>阻塞IO</p><blockquote><p>应用程序在进行系统调用时会被阻塞,直到数据准备好并复制到用户空间后才返回</p></blockquote></li><li><p>非阻塞IO</p><blockquote><p>应用程序在进行系统调用时不会被阻塞,而是立即返回一个状态值。应用程序需要不断轮询内核以检查数据是否准备好</p></blockquote></li><li><p>多路复用IO</p><blockquote><p>应用程序可以同时监视多个文件描述符,当某个文件描述符就绪时,对其进行处理</p></blockquote></li><li><p>信号驱动IO</p><blockquote><p>应用程序向内核注册一个信号处理函数,当数据报准备好时,内核会向应用程序发送一个信号,应用程序对信号进行捕捉,并调用信号处理函数来获取数据报</p></blockquote></li><li><p>异步IO</p><blockquote><p>应用程序发起系统调用后立即返回,当内核将数据准备好并复制完成后会给应用程序发送通知</p></blockquote></li></ul><p>这些模型区别在于应用程序和内核之间的交互方式以及数据准备和拷贝的过程</p></blockquote></li></ol>]]></content>
<summary type="html"><ol>
<li><p>进程和线程之间有什么区别?</p>
<blockquote>
<p>进程是运行中的程序,线程是进程的内部的一个执行序列<br>进程是资源分配单元,线程是执行单元<br>进程间切换代价大,线程间切换代价小<br>进程拥有资源多,线程拥有资源少<br>多个线程</summary>
<category term="面试" scheme="https://intyouss.github.io/tags/%E9%9D%A2%E8%AF%95/"/>
</entry>
<entry>
<title>Django</title>
<link href="https://intyouss.github.io/2023/03/25/Django/"/>
<id>https://intyouss.github.io/2023/03/25/Django/</id>
<published>2023-03-25T04:37:08.000Z</published>
<updated>2023-04-30T11:27:50.773Z</updated>
<content type="html"><![CDATA[<h3 id="Django-MVT"><a href="#Django-MVT" class="headerlink" title="Django MVT"></a>Django MVT</h3><ul><li><p>网站设计的三层架构</p></li><li><p>借鉴于Java的MVC(C: Controller)</p></li></ul><p>M(Model数据模型: 数据层)V(View视图层: 控制层)T(Templete模板层(在MVC中为视图层))</p><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Lenovo\AppData\Roaming\marktext\images\2023-03-25-12-39-38-image.png"></p><h3 id="创建Django工程文件"><a href="#创建Django工程文件" class="headerlink" title="创建Django工程文件"></a>创建Django工程文件</h3><p>django-admin startproject 文件名</p><p>内部生成文件</p><ul><li><p>manage.py 内置django命令</p></li><li><p>settings 配置文件</p></li><li><p>urls 路由文件</p></li><li><p>wsgi 网关系统 在互联网中可以被其他计算机访问的文件</p></li></ul><h3 id="运行Django工程"><a href="#运行Django工程" class="headerlink" title="运行Django工程"></a>运行Django工程</h3><p>manage.py 内置测试服务器运行指令</p><p>python3 manage.py runserver (默认8000端口)</p><h3 id="创建子应用"><a href="#创建子应用" class="headerlink" title="创建子应用"></a>创建子应用</h3><p>python3 manage.py startapp 应用名</p><p>内部生成文件</p><ul><li><p>admin.py 与网站后台管理站点配置相关</p></li><li><p>apps.py 用于配置当前子应用的相关信息</p></li><li><p>mifrations 用于存储数据库迁移历史文件</p></li><li><p>models.py 用户保存数据库模型类</p></li><li><p>tests.py 用于开发测试用例,编写单元测试</p></li><li><p>views.py 用于编写web应用视图</p></li></ul><h3 id="Setting配置"><a href="#Setting配置" class="headerlink" title="Setting配置"></a>Setting配置</h3><ul><li><p>ALLOWED_HOSTS = [‘*’] 允许访问的主机</p></li><li><p>INSTALLED_APPS = [‘app’] 应用注册,app在创建后在此注册应用</p></li></ul><h3 id="外键的优缺点"><a href="#外键的优缺点" class="headerlink" title="外键的优缺点"></a>外键的优缺点</h3><h4 id="优点"><a href="#优点" class="headerlink" title="优点:"></a>优点:</h4><ul><li><p>由数据库自身保证数据一致性和完整性,数据更可靠</p></li><li><p>可以增加ER图的可读性</p></li><li><p>外键可以节省开发量</p></li></ul><h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点:"></a>缺点:</h4><ul><li><p>性能确实,有额外的开销</p></li><li><p>主键表被锁定时,会引发外键表也被锁</p></li><li><p>删除主键表的数据时,需先删除外键表的数据</p></li><li><p>修改外键表字段时,需重建外键约束</p></li><li><p>不能用于分布式环境</p></li><li><p>不容易做到数据解耦</p></li></ul><h4 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h4><ul><li><p>适用</p><p>内部系统,传统企业级,规模可控的环境</p></li><li><p>不适用</p><p>规模不可控的环境, 数量大</p></li></ul><p>on_delete:</p><ul><li><p>models.SET_NULL<br>置空模式,删除时,外键字段被设置为空,前提就是blank=True, null=True,定义该字段时,允许为空。理解:删除关联数据(子表),与之关联的值设置默认值为null(父表中),这个前提需要父表中的字段可以为空。<br>PS:外键写在多处,且写外键只能是主键,如没设置主键,django会自动帮你创</p></li><li><p>None:<br>删除关联表的数据时,当前表与关联表的filed的行为。</p></li><li><p>models.CASCADE:<br>表示级联删除,当关联表(子表)中的数据删除时,与其相对应的外键(父表)中的数据也删除。</p></li><li><p>models.DO_NOTHING:<br>你删你的,父亲(外键)不想管你</p></li><li><p>models.PROTECT:<br>保护模式,如采用这个方法,在删除关联数据时会抛出ProtectError错误</p></li><li><p>models.SET_DEFAULT:<br>设置默认值,删除子表字段时,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。</p></li><li><p>on_delete = models.SET(值):<br>删除关联数据时,自定义一个值,该值只能是对应指定的实体</p></li></ul><h3 id="视图"><a href="#视图" class="headerlink" title="视图"></a>视图</h3><ol><li><p>视图函数必须定义一个参数(通常命名为request)</p><ul><li>request: 用来接收客户端的请求信息的</li></ul></li><li><p>视图函数的返回值必须是一个HttpResponse的对象</p></li></ol><h4 id="视图使用的流程"><a href="#视图使用的流程" class="headerlink" title="视图使用的流程"></a>视图使用的流程</h4><ol><li><p>在应用的views.py定义视图函数</p></li><li><p>配置路由</p><ul><li><p>在项目目录的urls.py中关联应用下的urls.py</p></li><li><p>在应用的目录下定义一个urls.py文件</p></li><li><p>配置具体的访问规则</p><p><code>urlpatterns = [path('路由名', 包路径)]</code></p></li></ul></li></ol><h3 id="admin-Django默认后台"><a href="#admin-Django默认后台" class="headerlink" title="admin Django默认后台"></a>admin Django默认后台</h3><ol><li><p>配置admin账户密码</p><ul><li><code>python manage.py createsuperuser</code></li></ul></li></ol><h3 id="模板"><a href="#模板" class="headerlink" title="模板"></a>模板</h3><h4 id="模板的配置和使用步骤"><a href="#模板的配置和使用步骤" class="headerlink" title="模板的配置和使用步骤:"></a>模板的配置和使用步骤:</h4><ol><li><p>在项目目录下创建一个templates文件夹</p></li><li><p>在setting.py中TEMPLATES选项中配置项目模板的根路径</p><p><code>'DIRS': [BASE_DIR / 'templates']</code></p></li><li><p>在templates中创建和应用同名文件夹</p></li><li><p>在templates下应用同名的文件夹中创建html模板页面</p></li><li><p>在views.py中定义视图函数, 并返回html模板页面</p><ul><li><p>参数:</p><ul><li><p>第一个请求对象</p></li><li><p>第二个模板路径</p></li><li><p>第三个要渲染到模板中的数据(必须是字典)</p></li></ul></li></ul><p><code>return render(request, 'news/list.html', info)</code></p></li><li><p>配置路由访问规则</p></li></ol><h3 id="修改数据库为MySQL"><a href="#修改数据库为MySQL" class="headerlink" title="修改数据库为MySQL"></a>修改数据库为MySQL</h3><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 修改为:</span></span><br><span class="line">DATABASES = {</span><br><span class="line"> <span class="string">'default'</span>: {</span><br><span class="line"> <span class="string">'ENGINE'</span>: <span class="string">'django.db.backends.mysql'</span>,</span><br><span class="line"> <span class="string">'NAME'</span>: <span class="string">''</span>,</span><br><span class="line"> <span class="string">'USER'</span>:<span class="string">""</span>,</span><br><span class="line"> <span class="string">'PASSWORD'</span>:<span class="string">""</span>,</span><br><span class="line"> <span class="string">'HOST'</span>:<span class="string">"127.0.0.1"</span>,</span><br><span class="line"> <span class="string">'PORT'</span>: <span class="number">3306</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="创建模型"><a href="#创建模型" class="headerlink" title="创建模型"></a>创建模型</h3><ol><li><p>调用<code>from Django.db import models</code></p></li><li><p>创建模型类<code>class 模型名(models.Model)</code></p></li><li><p>设置属性</p></li><li><p>表名自定义:</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">class</span> <span class="title class_">模型名</span>(models.Model):</span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">Meta</span>:</span><br><span class="line"> <span class="comment"># set the table's name</span></span><br><span class="line"> db_table = <span class="string">'newinfo'</span></span><br><span class="line"> <span class="comment"># set the table's info</span></span><br><span class="line"> verbose_name = <span class="string">"new's info"</span></span><br></pre></td></tr></table></figure></div></li></ol><h3 id="模型表迁移"><a href="#模型表迁移" class="headerlink" title="模型表迁移"></a>模型表迁移</h3><p><code>python manage.py makemigration</code></p><p><code>python manage.py migrate</code></p><h3 id="自定义admin后台"><a href="#自定义admin后台" class="headerlink" title="自定义admin后台"></a>自定义admin后台</h3><ol><li><p>进入admin.py</p></li><li><p>新建类<code>class admin类(admin.ModelAdmin)</code></p></li><li><p>将模型中的属性都放入admin类中的list_display中</p><p><code>list_display = ['id', 'name']</code></p></li><li><p>注册绑定类<code>admin.site.register(模型名,admin模型名)</code></p></li></ol><h3 id="模型类的查询方法"><a href="#模型类的查询方法" class="headerlink" title="模型类的查询方法"></a>模型类的查询方法</h3><h4 id="精确查询"><a href="#精确查询" class="headerlink" title="精确查询"></a>精确查询</h4><ul><li><p>get方法: 查询一条数据</p><p>查询id为1的数据</p><p><code>NewsType.object.get(id=1)</code></p><p>特点: 只能查询唯一满足条件的数据</p><blockquote><p>没有找到符合条件的数据,直接报错(DoesNotExist)</p><p>查询出多条符合条件数据,也会报错(MultipleObjectReturned)</p></blockquote></li><li><p>filter方法: 根据条件过滤查询</p><blockquote><p>返回的值是一个quertSet对象(查询集)</p></blockquote></li><li><p>all方法: 返回模块类对应的表中所有数据</p><blockquote><p>返回的值是一个querSet对象(查询集)</p></blockquote></li><li><p>exclude方法: 查询不符合条件的数据</p><p>查询name不是实时政治的</p><p><code>NewsType.object.exclude(name='实时政治')</code></p></li><li><p>order_by: 排序方法</p><p>根据新闻的阅读量进行排序(升序)</p><p><code>NewsInfo.object.all().order_by('read')</code></p><p>(降序)</p><p><code>NewsInfo.object.all().order_by('-read')</code></p><ul><li><p>排序规则:</p><blockquote><p>默认是从小到大(升序)</p><p>降序排序在字段前面加一个’-‘</p></blockquote></li></ul></li><li><p>查询集:</p><ul><li><p>支持索引取值</p></li><li><p>支持切片</p></li><li><p>支持再次调用查询方法</p></li></ul></li></ul><h4 id="模糊查询"><a href="#模糊查询" class="headerlink" title="模糊查询"></a>模糊查询</h4><p>条件语法格式:</p><ul><li>模型类.object.filter(模型类属性名__条件名=值)</li></ul><blockquote><ul><li>包含: contains</li></ul><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="comment"># 查询名称里包含'娱乐'的新闻类别</span></span><br><span class="line">NewsInfo.objects.<span class="built_in">filter</span>(new_type__contains=<span class="string">'娱乐'</span>)</span><br></pre></td></tr></table></figure></div><ul><li>开头: startswith</li></ul><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="comment"># 查询以'国'开头的新闻类别</span></span><br><span class="line">NewsInfo.objects.<span class="built_in">filter</span>(new_type__startswith=<span class="string">'国'</span>)</span><br></pre></td></tr></table></figure></div><ul><li>结尾: endswith</li></ul><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="comment"># 查询以'资讯'结尾的新闻类别</span></span><br><span class="line">NewsInfo.objects.<span class="built_in">filter</span>(new_type__endswith=<span class="string">'资讯'</span>)</span><br></pre></td></tr></table></figure></div><ul><li>范围查询: in</li></ul><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="comment"># 查询id为1或3或5的新闻类别</span></span><br><span class="line">NewsInfo.objects.<span class="built_in">filter</span>(id__in=[<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>])</span><br></pre></td></tr></table></figure></div></blockquote><blockquote><p>比较查询</p><ul><li><p>大于: gt</p></li><li><p>小于: lt</p></li><li><p>大于等于: gte</p></li><li><p>小于等于: lte</p></li><li><p>字段值是否为Null: isnull</p><p>查询name不为空的字段</p><p><code>NewsType.objects.filter(name__isnull=False)</code></p></li></ul></blockquote><h4 id="F对象和Q对象"><a href="#F对象和Q对象" class="headerlink" title="F对象和Q对象"></a>F对象和Q对象</h4><ol><li><p>F对象: 用来比较查询数据的两个属性</p><blockquote><p>导入: <code>from django.db.models import F</code></p><p>查询 ‘阅读量’ 大于 ‘评论数量’两倍的新闻</p><p><code>NewsInfo.objects.filter(read__gt=F('comment')*2)</code></p><p>F对象查询结果可以直接进行算数运算</p></blockquote></li><li><p>Q对象: 逻辑查询(与或非)</p><blockquote><p>导入: <code>from django.db.models import Q</code></p><ol><li><p>逻辑与:</p><p>不使用Q对象:</p><p>查询阅读数量大于40,评论数量大于35的数据</p><p><code>NewsInfo.objects.filter(read__gt=40,comment__gt=35)</code></p><p>使用Q对象:</p><p><code>NewsInfo.objects.filter(Q(read__gt=40) & Q(comment__gt=35))</code></p></li><li><p>逻辑或:</p><p><code>NewsInfo.objects.filter(Q(read__gt=40) | Q(comment__gt=35))</code></p></li><li><p>逻辑非:</p><p>通过 exclude查询:</p><p><code>NewsType.object.exclude(name='实时政治')</code></p><p>通过Q对象:</p><p><code>NewsType.object.exclude(~Q(name='实时政治'))</code></p></li></ol></blockquote></li></ol><h4 id="模型类关联查询"><a href="#模型类关联查询" class="headerlink" title="模型类关联查询"></a>模型类关联查询</h4><ol><li><p>通过对象进行关联查询</p><p><code>单一查询.模型类名_set.操作()</code></p><p>由一到多的查询:</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">b = BookInfo.objects.get(<span class="built_in">id</span>=<span class="number">1</span>)</span><br><span class="line">b.hero_set.<span class="built_in">all</span>()</span><br></pre></td></tr></table></figure></div><p>由多到一的查询:</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">b = BookInfo.objects.get(<span class="built_in">id</span>=<span class="number">1</span>)</span><br><span class="line">b.book <span class="comment"># 属性名</span></span><br></pre></td></tr></table></figure></div></li><li><p>通过模型类进行关联查询</p><p>由一到多的查询:</p><p><code>关联模型类名小写__属性__比较方法 = 值</code></p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Book.objects.<span class="built_in">filter</span>(hero__name__contains=<span class="string">'峰'</span>)</span><br></pre></td></tr></table></figure></div><p>由多到一的查询:</p><p><code>关联字段__属性__条件运算符 = 值</code></p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Hero.objects.<span class="built_in">filter</span>(book__name=<span class="string">'天龙八部'</span>)</span><br></pre></td></tr></table></figure></div></li></ol><h3 id="增删改操作"><a href="#增删改操作" class="headerlink" title="增删改操作"></a>增删改操作</h3><h4 id="添加数据"><a href="#添加数据" class="headerlink" title="添加数据"></a>添加数据</h4><blockquote><p>通过模型类往数据库添加数据</p></blockquote><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">Book.objects.create(title=<span class="string">'新闻001'</span>) <span class="comment"># 方式一</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 方式二</span></span><br><span class="line">obj = Book()</span><br><span class="line">obj.title = <span class="string">'啦啦啦'</span></span><br><span class="line">obj.save()</span><br></pre></td></tr></table></figure></div><h4 id="删除数据"><a href="#删除数据" class="headerlink" title="删除数据"></a>删除数据</h4><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">obj = Book.objects.get(title=<span class="string">'xxx'</span>)</span><br><span class="line">obj.delete()</span><br></pre></td></tr></table></figure></div><h4 id="修改数据"><a href="#修改数据" class="headerlink" title="修改数据"></a>修改数据</h4><blockquote><p>通过模型类修改数据</p></blockquote><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">obj = Book.objects.get(title=<span class="string">'xxx'</span>)</span><br><span class="line">obj.title = <span class="string">'yyy'</span></span><br><span class="line">obj.save()</span><br></pre></td></tr></table></figure></div><h3 id="路由"><a href="#路由" class="headerlink" title="路由"></a>路由</h3><blockquote><p>项目下的 urls.py 是整个项目路由匹配的入口</p></blockquote><h4 id="添加路由"><a href="#添加路由" class="headerlink" title="添加路由"></a>添加路由</h4><blockquote><p>将应用中的 urls.py 的引入主 urls.py</p></blockquote><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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 class="keyword">from</span> django.urls <span class="keyword">import</span> path, include</span><br><span class="line">urlpatterns = [</span><br><span class="line"> path(<span class="string">'news/'</span>, include(<span class="string">'news.urls'</span>)),</span><br><span class="line"> path(<span class="string">'book/'</span>, include(<span class="string">'book.urls'</span>))</span><br><span class="line">]</span><br></pre></td></tr></table></figure></div><h4 id="url参数"><a href="#url参数" class="headerlink" title="url参数"></a>url参数</h4><ul><li><p>方式一:位置参数</p><blockquote><p>直接使用小括号,通过位置参数传递给视图</p></blockquote><ol><li><p>参数匹配</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">re_path(<span class="string">r'^index/(\d+)/$'</span>,views.show)</span><br></pre></td></tr></table></figure></div></li><li><p>视图中获取参数</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">def</span> <span class="title function_">show</span>(<span class="params">request, num</span>):</span><br><span class="line"> <span class="keyword">return</span> HttpResponse(<span class="string">f'aaaaa<span class="subst">{num}</span>'</span>)</span><br></pre></td></tr></table></figure></div></li></ol></li><li><p>方式二:关键字参数</p><ol><li><p>参数匹配</p><blockquote><p>其中 <code>?P<name1></code> 表示为这个参数定义的名称为 name1,可以是其他名称</p></blockquote><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">re_path(<span class="string">r'^index/(?P<name1>\d+)/$'</span>, show)</span><br></pre></td></tr></table></figure></div></li><li><p>视图中获取参数</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">def</span> <span class="title function_">show</span>(<span class="params">request, name1</span>):</span><br><span class="line"> <span class="keyword">return</span> HttpResponse(<span class="string">f'aaa<span class="subst">{name1}</span>'</span>)</span><br></pre></td></tr></table></figure></div></li></ol></li></ul><h3 id="错误视图"><a href="#错误视图" class="headerlink" title="错误视图"></a>错误视图</h3><blockquote><p>Django 内置处理 HTTP 错误的视图,主要错误及视图包括:</p><ul><li><p>404错误:url 匹配失败,找不到页面</p></li><li><p>500错误:server error 视图</p></li></ul></blockquote><p>如果想看到错误试图而不是调试信息,需要修改 <code>setting.py</code> 文件的 DEBUG 项</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">DEBUG = <span class="literal">False</span></span><br><span class="line">ALLOWED_HOSTS = [<span class="string">'*'</span>]</span><br></pre></td></tr></table></figure></div><ol><li><p>404错误视图配置</p><blockquote><p>将请求地址进行 url 匹配后,没有找到匹配的正则表达式,则调用 404 视图,这个视图会调用 404.html 的模板进行渲染。视图传递变量 request_path 给模板,表示导致错误的 URL</p></blockquote><ol><li><p>在 templates 中创建 404.html</p></li><li><p>在浏览器中访问没有定义的路由,触发错误</p></li></ol></li><li><p>500错误视图配置</p><blockquote><p>在视图中代码运行报错会发生500错误,调用内置错误视图,使用 <code>templates/500.html</code> 模板渲染</p></blockquote><ol><li><p>定义错误的视图</p></li><li><p>访问错误的视图</p></li></ol></li></ol><h3 id="HttpRequest-对象"><a href="#HttpRequest-对象" class="headerlink" title="HttpRequest 对象"></a>HttpRequest 对象</h3><ol><li><p>request 对象的属性</p><ul><li><p>path:表示请求的页面的完整路径,不包括域名和参数部分</p></li><li><p>method:表示请求使用的 HTTP 方法,常用值包括:’GET’, ‘POST’</p></li><li><p>encoding:表示提交的数据的编码方式</p></li><li><p>FILES:包含所有上传文件</p></li><li><p>COOKIES:包含所有的 cookie,key 和 value 都是字符串</p></li></ul></li></ol><h3 id="HttpResponse-对象"><a href="#HttpResponse-对象" class="headerlink" title="HttpResponse 对象"></a>HttpResponse 对象</h3><ol><li><p>初始化参数</p><ul><li><p>content:表示返回的内容字符串</p></li><li><p>charset:表示 response 采用的编码字符集,默认为 utf-8</p></li><li><p>status_code:返回的是HTTP响应状态码</p></li><li><p>content_type:指定返回数据的MIME类型,默认为’text/html’</p></li></ul></li></ol><h3 id="重定向"><a href="#重定向" class="headerlink" title="重定向"></a>重定向</h3><blockquote><p>当一个逻辑处理完成后,不需要向客户端呈现数据,而转到其他页面</p></blockquote><ol><li><p>定义视图</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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 class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponseRedirect</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义重定向视图</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">red</span>(<span class="params">request</span>):</span><br><span class="line"> <span class="keyword">return</span> HttpResponseRedirect(redirect_to=<span class="string">'/'</span>)</span><br></pre></td></tr></table></figure></div></li><li><p>配置 url</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">url(<span class="string">r'^red/$'</span>, views.red)</span><br></pre></td></tr></table></figure></div></li></ol><p>简写redirect</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render,redirect</span><br><span class="line"><span class="keyword">def</span> <span class="title function_">red</span>(<span class="params">request</span>):</span><br><span class="line"> <span class="keyword">return</span> redirect(<span class="string">'/'</span>)</span><br></pre></td></tr></table></figure></div><h3 id="类视图"><a href="#类视图" class="headerlink" title="类视图"></a>类视图</h3><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">from</span> django.views <span class="keyword">import</span> View</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">view</span>(<span class="title class_ inherited__">View</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get</span>(<span class="params">self, request</span>):</span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">post</span>(<span class="params">self, request</span>):</span><br><span class="line"> <span class="keyword">pass</span></span><br></pre></td></tr></table></figure></div><blockquote><p>在urls.py 中配置路由</p></blockquote><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">re_path(<span class="string">r'xxx/$'</span>, view.as_view())</span><br></pre></td></tr></table></figure></div><h3 id="模板-1"><a href="#模板-1" class="headerlink" title="模板"></a>模板</h3><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">IndexView</span>(<span class="title class_ inherited__">View</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get</span>(<span class="params">self, request</span>):</span><br><span class="line"> context = {</span><br><span class="line"> <span class="string">'xxx'</span> = <span class="string">'xxx'</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> render(request, <span class="string">'tempdemo/index.html'</span>, context)</span><br></pre></td></tr></table></figure></div><blockquote><p>在urls.py中配置路由</p></blockquote>]]></content>
<summary type="html"><h3 id="Django-MVT"><a href="#Django-MVT" class="headerlink" title="Django MVT"></a>Django MVT</h3><ul>
<li><p>网站设计的三层架构</p>
</li>
<li><p>借鉴</summary>
<category term="Django Study" scheme="https://intyouss.github.io/tags/Django-Study/"/>
</entry>
<entry>
<title>Linux</title>
<link href="https://intyouss.github.io/2023/03/20/Linux/"/>
<id>https://intyouss.github.io/2023/03/20/Linux/</id>
<published>2023-03-20T11:55:22.000Z</published>
<updated>2023-03-22T13:03:04.505Z</updated>
<content type="html"><![CDATA[<h3 id="ls"><a href="#ls" class="headerlink" title="ls"></a>ls</h3><p>当前目录下的所有内容(文件或文件夹)</p><p>默认目录home-家目录/home/‘username’</p><ol><li><p>命令参数[-a -l -h]</p><p>ls 目录 -查找目录下所有内容</p><p>-a-all 显示所有当前目录下所有内容(包括隐藏内容.)</p><p>-l-list 以列表方式加创建时间和权限组显示所有当前目录下所有内容</p><p>选项可以复合-al同时应用两类功能</p><p>-h 和-l混合使用 将内容容量以可读方式显示1KB,1MB等</p></li></ol><h3 id="cd"><a href="#cd" class="headerlink" title="cd"></a>cd</h3><p>切换工作目录Change Directory</p><p>cd [Linux路径] 无参数直接切换</p><p>不指定路径,直接回到home目录</p><h3 id="pwd"><a href="#pwd" class="headerlink" title="pwd"></a>pwd</h3><p>Print Work Directory</p><p>直接使用pwd打印当前工作目录路径 显示/home/xxx</p><h3 id="绝对路径"><a href="#绝对路径" class="headerlink" title="绝对路径"></a>绝对路径</h3><p>以根目录为起点,写出一个路径,以/为起点</p><h3 id="相对路径"><a href="#相对路径" class="headerlink" title="相对路径"></a>相对路径</h3><p>以当前目录为起点,写一个路径,不需要写/</p><h4 id="特殊路径符"><a href="#特殊路径符" class="headerlink" title="特殊路径符"></a>特殊路径符</h4><p>. 表示当前目录,比如 cd ./Desktop表示切换到当前目录下的Desktop目录内</p><p>.. 表示切换到上一级的某个目录</p><p>~ 表示HOME目录,比如cd ~即可退回HOME</p><h3 id="mkdir"><a href="#mkdir" class="headerlink" title="mkdir"></a>mkdir</h3><p>Make Directroy</p><p>创建目录,可以使用相对路径和绝对路径</p><p>makdir [-p] 路径</p><p>-p表示自动创建不存在的父目录</p><h3 id="ctrl-l-and-clear"><a href="#ctrl-l-and-clear" class="headerlink" title="ctrl+l and clear"></a>ctrl+l and clear</h3><p>清空屏幕</p><h3 id="touch"><a href="#touch" class="headerlink" title="touch"></a>touch</h3><p>创建文件</p><p>touch 路径</p><h3 id="cat"><a href="#cat" class="headerlink" title="cat"></a>cat</h3><p>查看文件内内容(全部打印)</p><p>cat 路径</p><h3 id="more"><a href="#more" class="headerlink" title="more"></a>more</h3><p>查看文件内内容(可翻页–more–)</p><p>more 路径</p><p>退出翻页查看(键盘q)</p><h3 id="cp"><a href="#cp" class="headerlink" title="cp"></a>cp</h3><p>copy</p><p>复制文件和文件夹</p><p>cp [-r] 参数1 参数2</p><p>-r 用于复制文件夹用,不输入即复制文件</p><p>参数1 表示被复制文件路径</p><p>参数2 表示要复制去的路径</p><h3 id="scp"><a href="#scp" class="headerlink" title="scp"></a>scp</h3><p>服务器之间传递文件</p><p>scp [-r] 参数1 参数2</p><ul><li><p>-r 用于复制文件夹</p></li><li><p>参数 本机路径或远程目标路径</p></li></ul><p>scp -r /export/server/jdk root@node2:/export/server/</p><p>将本机上的jdk文件夹,以root身份复制到node2的指定路径中</p><h3 id="mv"><a href="#mv" class="headerlink" title="mv"></a>mv</h3><p>move</p><p>移动文件夹和文件,改名</p><p>mv 参数1 参数2</p><p>参数与cp同</p><h3 id="rm"><a href="#rm" class="headerlink" title="rm"></a>rm</h3><p>remove</p><p>rm [-r -f] 参数1——n</p><p>删除文件和文件夹</p><p>-r 用于删除文件夹</p><p>-f 表示force 强制删除(不会弹出提示确认信息)</p><ul><li>普通用户不会弹出,root用户会弹出</li></ul><p>参数表示要删除的文件或文件夹路径空格隔开,可一起删除</p><h4 id="通配符"><a href="#通配符" class="headerlink" title="通配符*"></a>通配符*</h4><p>匹配任意内容</p><p>test* 匹配任何test开头的内容</p><p>*test * 匹配任何包含test的内容</p><p>*test 匹配test结尾的内容</p><p>rm -rf /* 强制删除根目录下所有内容</p><h3 id="which"><a href="#which" class="headerlink" title="which"></a>which</h3><p>查看命令的程序文件的路径</p><p>which [命令]</p><h3 id="find"><a href="#find" class="headerlink" title="find"></a>find</h3><p>搜索指定文件</p><p>find 起始路径 -name ‘查找文件名’</p><p>root用户全盘搜索</p><p>find 起始路径 -size +|-n [kMG]</p><ul><li><p>+,-表示大于和小于</p></li><li><p>n表示大小数字</p></li><li><p>kMG表示大小单位</p></li></ul><p>find / -size -10k</p><h3 id="grep"><a href="#grep" class="headerlink" title="grep"></a>grep</h3><p>从文件中通过关键字过滤文件行</p><p>grep [-n] ’关键字‘ 文件路径</p><h3 id="wc"><a href="#wc" class="headerlink" title="wc"></a>wc</h3><p>统计文件的行数</p><p>wc [-c -m -l -w] 文件路径(不加参数,显示行数,单词数,字节数,文件名)</p><p>-c 统计bytes数</p><p>-m 统计字符数</p><p>-l 统计行数</p><p>-w 统计单词数</p><h3 id="管道符"><a href="#管道符" class="headerlink" title="管道符(|)"></a>管道符(|)</h3><p>将管道符左边的结果,作为右边的输入</p><ul><li><p>cat test.txt | grep x</p></li><li><p>ls -lh /usr | wc -l</p></li><li><p>cat test.txt | grep x | grep xx</p></li></ul><h3 id="echo"><a href="#echo" class="headerlink" title="echo"></a>echo</h3><p>在命令行中输出指定内容</p><p>echo ‘输出的内容’</p><p>echo ``(反引号内输入命令) 输出命令结果 </p><h3 id="重定向符"><a href="#重定向符" class="headerlink" title="重定向符"></a>重定向符</h3><p>[>] 将左侧命令的结果,覆盖写入到符号右侧</p><ul><li>echo ‘hello’ > test.txt</li></ul><p>[>>] 将左侧命令的结果,追加写入符号右侧</p><ul><li>ls / > test.txt</li></ul><h3 id="tail"><a href="#tail" class="headerlink" title="tail"></a>tail</h3><p>可以查看文件尾部内容,跟踪文件最新更改</p><p>tail [-f -num] 路径</p><p>-f 表示持续跟踪</p><p>-num 表示查看尾部多少行,不填默认10行</p><ul><li>tail -5 test.txt</li></ul><h3 id="vi-x2F-vim"><a href="#vi-x2F-vim" class="headerlink" title="vi/vim"></a>vi/vim</h3><p>Linux中最经典的文本编辑器</p><p>vim是vi的加强版本,兼容vi的所有指令,不仅能编辑文本,还具有shell程序编辑的功能,可以不同颜色的字体来辨别语法的正确性</p><ol><li><p>命令模式 (Command mode)</p><p>所敲的按键编辑器都理解成命令,此模式下,不能自由进行文本编辑</p></li><li><p>输入模式(Insert mode)</p><p>此模式下,可以对文件内容进行自由编辑</p></li><li><p>底线命令模式(Last line mode)</p><p>以:开始,通常用于文件的保存和退出</p></li></ol><p>vim 文件路径</p><ul><li><p>如果文件存在打开,不存在则创建</p></li><li><p>从命令模式点击i进入输入模式</p></li><li><p>从输入模式使用esc退回命令模式</p></li><li><p>命令模式下按:进入底线命令模式</p><ul><li>wq w保存 q退出</li></ul></li></ul><p>命令模式命令</p><blockquote><p>$移动到行结尾</p><p>0移动到行开头</p><p>/进入搜索模式</p><p>n向下搜索</p><p>N向上搜索</p><p>yy复制当前行</p><p>p粘贴</p><p>dd删除当前行</p><p>ndd删除当前行和下面的n行</p><p>u撤销修改</p><p>ctrl+r反向撤销修改</p><p>gg跳到首行</p><p>G跳到最后一行</p><p>dG从当前行向下全部删除</p><p>dgg向上全部删除</p><p>d$从光标开始删除到本行行尾</p><p>d0从光标开始删除到本行行首</p></blockquote><p>底线命令模式命令</p><blockquote><p>:wq 保存退出</p><p>:q! 强制退出</p><p>:set nu 显示行号</p><p>:set paste设置粘贴模式</p></blockquote><h3 id="root-超级管理员"><a href="#root-超级管理员" class="headerlink" title="root(超级管理员)"></a>root(超级管理员)</h3><p>拥有最大的系统操作权限</p><h4 id="su"><a href="#su" class="headerlink" title="su"></a>su</h4><p>su - 默认切换root</p><p>su-root 密码 切换root管理员(-是为了在登录时加载环境变量)</p><p>exit 回退</p><p>root管理员切换其他账号不要密码</p><h4 id="sudo"><a href="#sudo" class="headerlink" title="sudo"></a>sudo</h4><p>临时以root身份执行命令</p><p>sudo 其他命令</p><p>只有认证的用户才能使用</p><ul><li><p>为普通用户配置认证</p><ul><li><p>切换到root用户,执行visudo命令打开/etc/sudoers</p></li><li><p>在文件最后添加:</p><p><code>intyou ALL=(ALL) NOPASSWD:ALL</code></p></li></ul></li></ul><h3 id="用户,用户组"><a href="#用户,用户组" class="headerlink" title="用户,用户组"></a>用户,用户组</h3><p>以下命令使用root用户执行</p><h4 id="用户组管理"><a href="#用户组管理" class="headerlink" title="用户组管理"></a>用户组管理</h4><p>groupadd 用户组名 创建用户组</p><p>groupdel 用户组名 删除用户组</p><h4 id="用户管理-Ubuntu"><a href="#用户管理-Ubuntu" class="headerlink" title="用户管理(Ubuntu)"></a>用户管理(Ubuntu)</h4><p>useradd [-g -d] 用户名</p><ul><li><p>-g 指定用户的组,不指定,会创建同名组并自动加入,指定组需要已存在, 如已知存在同名组,必须使用-g</p></li><li><p>-d指定用户HOME路径, 不指定, HOME目录默认在: /home/用户名</p></li><li><p>-m创建家目录</p></li></ul><p>userdel [-r] 用户名</p><ul><li>-r 删除用户的HOME目录,不使用, HOME目录保留</li></ul><p>id 用户名</p><ul><li>查看用户的用户组</li></ul><p>usermod -aG</p><p>将指定用户加入指定用户组</p><p>getent</p><p>getent passwd 查看当前系统中有哪些用户</p><p>getent group 查看当前系统中有哪些用户组</p><h3 id="认知权限信息"><a href="#认知权限信息" class="headerlink" title="认知权限信息"></a>认知权限信息</h3><p>ls -l 输出的信息分析</p><p>drwxr-xr-x intyou intyou</p><p>drwxr-xr-x - 表示权限管控细节</p><p>-代表无</p><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Lenovo\AppData\Roaming\marktext\images\2023-03-21-18-03-51-image.png"></p><p>第一块 ‘-‘表示文件 ‘d’表示文件夹 ‘l’表示软链接</p><p>除去第一块:</p><ul><li><p>前三块代表所属用户权限</p></li><li><p>中三块代表所属用户组权限</p></li><li><p>后三块代表其他用户权限</p></li></ul><p>r 读权限</p><p>w写权限</p><p>x执行权限</p><h3 id="chmod"><a href="#chmod" class="headerlink" title="chmod"></a>chmod</h3><p>修改文件,文件夹的权限信息</p><p>只有文件文件夹的所属用户或root用户可以修改</p><p>chmod [-R] 权限 文件和文件夹</p><ul><li>-R 对文件夹内的全部内容应用同样的操作</li></ul><p><code>chomd u=rwx,g=rx,o=x hello.txt</code>将文件权限修改为rwxr-x–x</p><p>u表示所属用户权限,g表示group组权限,o表示other其他用户权限</p><h4 id="快捷方式"><a href="#快捷方式" class="headerlink" title="快捷方式"></a>快捷方式</h4><p>权限的数字序号</p><p>权限可以用三位数字来代表,第一位数字表示用户权限,第二位表示用户组权限,第三位表示其他用户权限</p><p>命令从右到左依次增加</p><p>0:— 1:–x 2:-w-3:-wx 4:r– 5:r-x 6:rw- 7:rwx</p><h3 id="chown"><a href="#chown" class="headerlink" title="chown"></a>chown</h3><p>只能root用户执行</p><p>修改文件和文件夹的所属用户和用户组</p><p>chown [-R] [用户] [:] [用户组] 文件或文件夹</p><p>chown root hello.txt 将hello.txt所属用户修改为root</p><p>chown :root hello.txt 将hello.txt所属用户组修改为root</p><h3 id="小技巧"><a href="#小技巧" class="headerlink" title="小技巧"></a>小技巧</h3><h4 id="ctrl-c-强制停止"><a href="#ctrl-c-强制停止" class="headerlink" title="ctrl+c 强制停止"></a>ctrl+c 强制停止</h4><h4 id="ctrl-d-退出或登录"><a href="#ctrl-d-退出或登录" class="headerlink" title="ctrl+d 退出或登录"></a>ctrl+d 退出或登录</h4><ul><li>不能用于退出vi/vim</li></ul><h4 id="历史命令搜索"><a href="#历史命令搜索" class="headerlink" title="历史命令搜索"></a>历史命令搜索</h4><ul><li><p>history</p><p>history | grep ‘xxx’</p></li><li><p>!命令前缀 自动执行上一次匹配前缀的命令</p><p>python</p><p>!p</p></li><li><p>ctrl+r 输入内容去匹配历史命令</p></li></ul><h3 id="centos-下载包"><a href="#centos-下载包" class="headerlink" title="centos 下载包"></a>centos 下载包</h3><h4 id="yum"><a href="#yum" class="headerlink" title="yum"></a>yum</h4><p>yum [-y] [install | remove | search] 软件名称</p><ul><li><p>-y 自动确定, 无需手动确认安装或卸载过程</p></li><li><p>install 安装</p></li><li><p>remove 卸载</p></li><li><p>search 搜索</p></li></ul><p>yum命令需要root指令</p><h3 id="ubuntu下载包"><a href="#ubuntu下载包" class="headerlink" title="ubuntu下载包"></a>ubuntu下载包</h3><h4 id="apt"><a href="#apt" class="headerlink" title="apt"></a>apt</h4><p>apt [-y] [install | remove | search] 软件名称</p><p>与centos相同</p><h3 id="systemctl"><a href="#systemctl" class="headerlink" title="systemctl"></a>systemctl</h3><p>控制软件(内置或第三方)启动,停止,开机自启</p><p>systemctl start | stop | status | enable | disable 服务名</p><p>enable/disable 打开、关闭开机自启</p><h3 id="软链接"><a href="#软链接" class="headerlink" title="软链接"></a>软链接</h3><h4 id="ln"><a href="#ln" class="headerlink" title="ln"></a>ln</h4><p>在系统中创建软链接,可以将文件和文件夹链接到其他位置(快捷方式)</p><p>ln -s 参数1 参数2</p><p>-s 创建软链接</p><p>参数1 被链接的文件或文件夹</p><p>参数2 要链接去的目的地</p><p><code>ln -s /etc/yum.conf ~/yum.conf</code></p><h3 id="date"><a href="#date" class="headerlink" title="date"></a>date</h3><p>date [-d] [+格式化字符串]</p><ul><li><p>-d 按照给定的字符串显示日期,一般用于日期计算</p><p>支持 year month day hour minute second</p><p>date -d ‘+1 day’ ‘+%Y-%m-%d’</p></li><li><p>格式化字符串</p><ul><li><p>%Y 年</p></li><li><p>%y 年份后两位数字(00,99)</p></li><li><p>%m 月份</p></li><li><p>%d 日</p></li><li><p>%H 小时</p></li><li><p>%M 分钟</p></li><li><p>%S 秒</p></li><li><p>%s 自1970-01-01 00:00:00 到现在的秒数</p></li></ul><p>date ‘+%Y-%m-%d %H:%M:%S’</p></li></ul><h4 id="修改linux时区-东八区"><a href="#修改linux时区-东八区" class="headerlink" title="修改linux时区(东八区)"></a>修改linux时区(东八区)</h4><p>root权限</p><p>rm -f /etc/localtime</p><p>sudo ln -s /user/share/zoneinfo/Asia/Shanghai /etc/localtime</p><h4 id="ntp程序"><a href="#ntp程序" class="headerlink" title="ntp程序"></a>ntp程序</h4><p>联网自动校准系统时间</p><p>需要使用apt下载</p><p>使用systemctl enable ntp 开机自启</p><p>手动校准 ntpdate -u ntp.aliyun.com</p><h3 id="ifconfig"><a href="#ifconfig" class="headerlink" title="ifconfig"></a>ifconfig</h3><p>查看ip信息</p><h3 id="hostname"><a href="#hostname" class="headerlink" title="hostname"></a>hostname</h3><p>查看主机名</p><ul><li>hostname set-hostname 名字 修改主机名</li></ul><h3 id="在linux中固定ip地址"><a href="#在linux中固定ip地址" class="headerlink" title="在linux中固定ip地址"></a>在linux中固定ip地址</h3><p>在ubuntu22.04中应找到/etc/netplan中的yaml文件修改</p><blockquote><p><a class="link" href="https://www.cnblogs.com/liujiaxin2018/p/16287463.html">ubuntu 22.04如何配置静态IP、网关、DNS - 小鲨鱼2018 - 博客园 (cnblogs.com) <i class="fa-regular fa-arrow-up-right-from-square fa-sm"></i></a>`</p></blockquote><h3 id="ping"><a href="#ping" class="headerlink" title="ping"></a>ping</h3><p>检测指定网络服务器是否可联通状态</p><p>ping [-c num] ip或主机名</p><p>-c 检查的次数 不使用,将无限次</p><h3 id="wget"><a href="#wget" class="headerlink" title="wget"></a>wget</h3><p>可以在命令行内下载网络文件</p><p>wget [-b] url</p><p>-b 后台下载</p><p>参数: url 下载链接</p><p>不管成功下载或者不成功,都会创建文件</p><h3 id="curl"><a href="#curl" class="headerlink" title="curl"></a>curl</h3><p>可以发送网络请求 下载文件 获取网站状态</p><p>curl [-O] url</p><p>-O 用于下载文件,当url是下载链接时,可以使用此选项保存文件</p><h3 id="端口"><a href="#端口" class="headerlink" title="端口"></a>端口</h3><h4 id="查看端口占用"><a href="#查看端口占用" class="headerlink" title="查看端口占用"></a>查看端口占用</h4><ul><li><p>netstat</p><p>需要apt安装</p><p>netstat -anp|grep 端口号</p></li><li><p>nmap</p><p>需要apt安装</p><p>nmap 端口</p></li></ul><h3 id="进程"><a href="#进程" class="headerlink" title="进程"></a>进程</h3><h4 id="查看进程"><a href="#查看进程" class="headerlink" title="查看进程"></a>查看进程</h4><p>ps [-e -f]</p><ul><li><p>-e 显示全部的进程</p></li><li><p>-f 已完全格式化的形式展示信息</p></li></ul><p>固定用法ps -ef 列出全部进程的全部信息</p><p>例: ps -ef | grep tail</p><h4 id="关闭进程"><a href="#关闭进程" class="headerlink" title="关闭进程"></a>关闭进程</h4><p>kill [-9] 进程ID</p><p>-9 表示强制关闭进程</p><h3 id="查看系统占用"><a href="#查看系统占用" class="headerlink" title="查看系统占用"></a>查看系统占用</h3><p>top 查看cpu占用</p><ul><li><p>第一行: 命令名称 当前系统时间 up 6 min: 启动了6分钟 2users: 2个用户登录 load average: 0.06 0.17 0.13 1 5 15分钟负载</p></li><li><p>第二行: Task 进程数 running: 正在运行进程数 sleeping: 进程睡眠数 stopped停止进程数 zombie 僵尸进程数</p></li><li><p>第三行: %cpu cpu使用率 us 用户cpu使用率 sy 系统cpu使用率 ni高优先级进程占用cpu时间百分比 id空闲cpu率 wa IO等待cpu占用率 hi cpu硬件中断率 si CPU软件中断率 st 强制等待占用cpu率</p></li><li><p>第四行: kib Mem 物理内存 total总量 free空闲 used 使用 buff/cache buff和cache占用 kibSwap 虚拟内存(交换空间)</p></li></ul><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Lenovo\AppData\Roaming\marktext\images\2023-03-22-19-26-29-image.png"></p><p>选项:</p><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Lenovo\AppData\Roaming\marktext\images\2023-03-22-19-28-00-image.png"></p><p>交互式选项:</p><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Lenovo\AppData\Roaming\marktext\images\2023-03-22-19-31-28-image.png"></p><h3 id="磁盘信息监控"><a href="#磁盘信息监控" class="headerlink" title="磁盘信息监控"></a>磁盘信息监控</h3><p>查看硬盘使用情况</p><p>df [-h]</p><ul><li>-h 以更加人性化的单位显示</li></ul><p>iostat [-x] [num1][num2]</p><p>需要下载包</p><ul><li><p>-x 显示更多信息</p></li><li><p>num1 数字 刷新时间,num2 数字 刷新几次</p></li><li><p>tps 该设备每秒的传输次数,’一次传输’=’一次I/O请求’。多个逻辑请求可能会被合并为”一次I/O请求”</p></li><li><p>rKB/s 每秒发送到设备的读取请求数 wKB/s 每秒发送到设备的写入请求数 %util 磁盘利用率</p></li></ul><h3 id="网络状态监控"><a href="#网络状态监控" class="headerlink" title="网络状态监控"></a>网络状态监控</h3><p>sar -n DEV num1 num2</p><p>需要下载包</p><ul><li><p>-n 查看网络</p></li><li><p>DEV 查看网络接口</p></li></ul><h3 id="环境变量"><a href="#环境变量" class="headerlink" title="环境变量"></a>环境变量</h3><p>简化程序执行路径</p><p>env 查看当前系统中记录的环境变量</p><h4 id="符号"><a href="#符号" class="headerlink" title="$符号"></a>$符号</h4><p>$获取变量的值</p><blockquote><p>echo $PATH</p><p>echo ${PATH}ABC</p></blockquote><h4 id="自行设置环境变量"><a href="#自行设置环境变量" class="headerlink" title="自行设置环境变量"></a>自行设置环境变量</h4><ul><li><p>临时设置 export 变量名 = 变量值</p></li><li><p>永久生效</p><ul><li><p>针对当前用户生效,配置在当前用户的: ~/.bashrc文件中</p></li><li><p>针对所有用户生效, 配置在系统的: /etc/profile文件中</p></li><li><p>并通过语法: source 配置文件, 进行立刻生效</p></li></ul></li></ul><h4 id="自定义环境变量PATH"><a href="#自定义环境变量PATH" class="headerlink" title="自定义环境变量PATH"></a>自定义环境变量PATH</h4><p>export PATH=$PATH: 路径</p><h3 id="压缩、解压"><a href="#压缩、解压" class="headerlink" title="压缩、解压"></a>压缩、解压</h3><h5 id="tar-gz"><a href="#tar-gz" class="headerlink" title=".tar .gz"></a>.tar .gz</h5><p>gzip体积压缩格式.tar.gz</p><p>tar [-c -v -x -f -z -C] 参数1—n</p><ul><li><p>-c 创建压缩文件</p></li><li><p>-v 显示压缩,解压过程</p></li><li><p>-x 解压模式</p></li><li><p>-f 要创建的文件,或要解压的文件,必须在所有选项中处于最后一个</p></li><li><p>-z gzip模式 不使用就是普通tarball格式 一般处于第一个</p></li><li><p>-C 选择解压的目的地, 单独使用</p></li></ul><p>压缩:</p><p>tar -cvf test.tar 1.txt 2.txt</p><p>将1.txt 2.txt压缩到test.tar</p><p>解压:</p><p>tar -xvf test.tar -C 解压路径</p><p>将test.tar 解压到解压路径</p><h5 id="zip"><a href="#zip" class="headerlink" title=".zip"></a>.zip</h5><p>压缩:</p><p>zip [-r] 参数1—n</p><ul><li>被压缩的包含文件夹的时候</li></ul><p>解压:</p><p>unzip [-d] 参数</p><ul><li>-d 指定解压路径</li></ul>]]></content>
<summary type="html"><h3 id="ls"><a href="#ls" class="headerlink" title="ls"></a>ls</h3><p>当前目录下的所有内容(文件或文件夹)</p>
<p>默认目录home-家目录&#x2F;home&#x2F;‘username’</p>
<</summary>
<category term="Linux Study" scheme="https://intyouss.github.io/tags/Linux-Study/"/>
</entry>
<entry>
<title>3月9日项目问题</title>
<link href="https://intyouss.github.io/2023/03/09/3%E6%9C%889%E6%97%A5%E9%A1%B9%E7%9B%AE%E9%97%AE%E9%A2%98/"/>
<id>https://intyouss.github.io/2023/03/09/3%E6%9C%889%E6%97%A5%E9%A1%B9%E7%9B%AE%E9%97%AE%E9%A2%98/</id>
<published>2023-03-09T04:24:47.000Z</published>
<updated>2023-03-24T13:38:46.876Z</updated>
<content type="html"><![CDATA[<h4 id="读取yaml"><a href="#读取yaml" class="headerlink" title="读取yaml"></a>读取yaml</h4><p>引用<code>from ruamel import yaml</code></p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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">yaml.load(</span><br><span class="line"> Path(<span class="string">'C:\\Users\\Lenovo\\Desktop\\bot\\IntBot\\configs\\config.yaml'</span>).read_text(encoding=<span class="string">'utf-8'</span>),</span><br><span class="line"> Loader=yaml.Loader</span><br><span class="line">)</span><br></pre></td></tr></table></figure></div><h4 id="pydantic"><a href="#pydantic" class="headerlink" title="pydantic"></a>pydantic</h4><p><code>extra='ignore'</code>忽略额外属性</p><h3 id="flask-migrate数据迁移"><a href="#flask-migrate数据迁移" class="headerlink" title="flask-migrate数据迁移"></a>flask-migrate数据迁移</h3><p>因为在开发中,由于数据表模型的改动,数据经常被清除,因此可以使用数据迁移,保存旧数据</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask</span><br><span class="line"><span class="keyword">from</span> flask_sqlalchemy <span class="keyword">import</span> SQLAlchemy</span><br><span class="line"><span class="keyword">from</span> flask_migrate <span class="keyword">import</span> Migrate</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"><span class="comment"># 设置数据库连接地址</span></span><br><span class="line">DB_URI = <span class="string">'mysql+pymysql://root:123456@127.0.0.1:3306/web'</span></span><br><span class="line">app.config[<span class="string">'SQLALCHEMY_DATABASE_URI'</span>] = DB_URI</span><br><span class="line"><span class="comment"># 是否追踪数据库修改,一般不开启, 会影响性能</span></span><br><span class="line">app.config[<span class="string">'SQLALCHEMY_TRACK_MODIFICATIONS'</span>] = <span class="literal">False</span></span><br><span class="line"><span class="comment"># 是否显示底层执行的SQL语句</span></span><br><span class="line">app.config[<span class="string">'SQLALCHEMY_ECHO'</span>] = <span class="literal">True</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化db,关联flask 项目</span></span><br><span class="line">db = SQLAlchemy(app)</span><br><span class="line"><span class="comment"># 迁移组件初始化</span></span><br><span class="line">Migrate(app, db)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建模型</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Students</span>(db.Model):</span><br><span class="line"> __tablename__ = <span class="string">'students'</span> <span class="comment"># 数据库表名</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">id</span> = db.Column(db.Integer, primary_key=<span class="literal">True</span>)</span><br><span class="line"> name = db.Column(db.String(<span class="number">20</span>))</span><br><span class="line"> fullname = db.Column(db.String(<span class="number">30</span>))</span><br><span class="line"> nickname = db.Column(db.String(<span class="number">30</span>))</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">f"<Students(name='<span class="subst">{self.name}</span>', fullname='<span class="subst">{self.fullname}</span>', nickname='<span class="subst">{self.nickname}</span>')>"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Users</span>(db.Model):</span><br><span class="line"> __tablename__ = <span class="string">'user'</span> <span class="comment"># 数据库表名</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">id</span> = db.Column(db.Integer, primary_key=<span class="literal">True</span>)</span><br><span class="line"> name = db.Column(db.String(<span class="number">30</span>))</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__repr__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">f"<Users(id='<span class="subst">{self.<span class="built_in">id</span>}</span>', name='<span class="subst">{self.name}</span>')>"</span></span><br></pre></td></tr></table></figure></div><p>执行迁移命令,先设置环境变量, 如果启动文件是app.py 这步可以省略。</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">set</span> FLASK_APP=app.py <span class="comment"># 设置环境变量指定启动文件</span></span><br></pre></td></tr></table></figure></div><p>windows用set设置环境变量,linux 用export 设置环境变量</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> FLASK_APP=app.py <span class="comment"># 设置环境变量指定启动文件</span></span><br></pre></td></tr></table></figure></div><p>执行相关命令</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><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">flask db init <span class="comment"># 生成迁移文件夹 只执行一次</span></span><br><span class="line">flask db migrate <span class="comment"># ⽣成迁移版本, 保存到迁移文件夹中</span></span><br><span class="line">flask db upgrade <span class="comment"># 执行迁移</span></span><br></pre></td></tr></table></figure></div><h4 id="迁移命令"><a href="#迁移命令" class="headerlink" title="迁移命令"></a>迁移命令</h4><p>init 命令执行后,会生成migrations目录</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flask db init</span><br></pre></td></tr></table></figure></div><p>migrate 命令生成迁移脚本,保存到迁移文件夹中versions目录</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flask db migrate</span><br></pre></td></tr></table></figure></div><p>upgrade 命令执行迁移</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flask db upgrade</span><br></pre></td></tr></table></figure></div><p>执行完upgrade 命令才会在数据库中生成表</p><h3 id="虚拟环境直接复制迁移"><a href="#虚拟环境直接复制迁移" class="headerlink" title="虚拟环境直接复制迁移"></a>虚拟环境直接复制迁移</h3><ol><li><p>迁移到新电脑,将 虚拟环境文件夹内的pyvenv.cfg文件中home参数,写成新电脑python的路径</p></li><li><p>修改虚拟环境文件夹中Scripts中的activate内VIRTUAL_ENV参数修改成新电脑虚拟环境文件夹路径</p></li><li><p>修改虚拟文件夹中Scripts中的activate.bat内set “VIRTUAL_ENV参数改成新电脑虚拟环境文件夹路径</p></li></ol>]]></content>
<summary type="html"><h4 id="读取yaml"><a href="#读取yaml" class="headerlink" title="读取yaml"></a>读取yaml</h4><p>引用<code>from ruamel import yaml</code></p>
<div class=</summary>
<category term="Project" scheme="https://intyouss.github.io/tags/Project/"/>
</entry>
<entry>
<title>poetry使用</title>
<link href="https://intyouss.github.io/2023/03/09/poetry%E4%BD%BF%E7%94%A8/"/>
<id>https://intyouss.github.io/2023/03/09/poetry%E4%BD%BF%E7%94%A8/</id>
<published>2023-03-09T02:37:25.000Z</published>
<updated>2023-03-09T02:50:44.686Z</updated>
<content type="html"><![CDATA[<h3 id="Poetry"><a href="#Poetry" class="headerlink" title="Poetry"></a>Poetry</h3><hr><p>使用pyproject.toml管理整个项目的依赖</p><h4 id="安装poetry"><a href="#安装poetry" class="headerlink" title="安装poetry"></a>安装poetry</h4><ol><li><p>安装pipx为每个包自动创建隔离环境,方便管理</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install pipx</span><br></pre></td></tr></table></figure></div></li><li><p>安装poetry</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pipx install poetry</span><br></pre></td></tr></table></figure></div></li></ol><h4 id="项目中使用portry"><a href="#项目中使用portry" class="headerlink" title="项目中使用portry"></a>项目中使用portry</h4><ol><li><p>初始化pyproject.toml</p><p>在项目路径中使用:</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">poetry init</span><br></pre></td></tr></table></figure></div></li><li><p>创建项目虚拟环境</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">poetry <span class="built_in">env</span> use python</span><br></pre></td></tr></table></figure></div></li><li><p>进入虚拟环境</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">poetry shell</span><br></pre></td></tr></table></figure></div></li><li><p>添加新依赖</p><p>在pyproject.toml文件中添加如下内容:</p><div class="highlight-container" data-rel="Toml"><figure class="iseeu highlight toml"><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"><span class="section">[[tool.poetry.source]]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">"aliyun"</span></span><br><span class="line"><span class="attr">url</span> = <span class="string">"https://mirrors.aliyun.com/pypi/simple"</span></span><br></pre></td></tr></table></figure></div><p>添加普通依赖:</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">poetry add <pakge_name></span><br></pre></td></tr></table></figure></div><p>添加开发依赖:</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">poetry add <pakge_name> -D</span><br></pre></td></tr></table></figure></div></li><li><p>测试</p><p>使用pytest</p><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">poetry run pytest</span><br></pre></td></tr></table></figure></div></li></ol>]]></content>
<summary type="html"><h3 id="Poetry"><a href="#Poetry" class="headerlink" title="Poetry"></a>Poetry</h3><hr>
<p>使用pyproject.toml管理整个项目的依赖</p>
<h4 id="安装poetry"><</summary>
<category term="poetry" scheme="https://intyouss.github.io/tags/poetry/"/>
</entry>
<entry>
<title>JavaScript-React-3</title>
<link href="https://intyouss.github.io/2023/03/09/JavaScript-React-3/"/>
<id>https://intyouss.github.io/2023/03/09/JavaScript-React-3/</id>
<published>2023-03-08T16:58:39.000Z</published>
<updated>2023-03-08T17:42:29.875Z</updated>
<content type="html"><![CDATA[<h3 id="React"><a href="#React" class="headerlink" title="React"></a>React</h3><hr><p>父组件传递属性,需要在子组件获取才能使用:</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><div className={<span class="string">`card <span class="subst">${props.className}</span>`</span>}></div></span><br></pre></td></tr></table></figure></div><p>props.children 表示组件的标签体</p><p>useRef().current 表示当前的DOM</p><p>事件对象:</p><ul><li><p>event包含所有触发事件对象信息</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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"><span class="keyword">const</span> <span class="title function_">descChangeHandler</span> = (<span class="params">event</span>) => {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(event.<span class="property">target</span>.<span class="property">value</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li></ul>]]></content>
<summary type="html"><h3 id="React"><a href="#React" class="headerlink" title="React"></a>React</h3><hr>
<p>父组件传递属性,需要在子组件获取才能使用:</p>
<div class="highlight-conta</summary>
</entry>
<entry>
<title>JavaScript-React-2</title>
<link href="https://intyouss.github.io/2023/03/08/JavaScript-React-2/"/>
<id>https://intyouss.github.io/2023/03/08/JavaScript-React-2/</id>
<published>2023-03-08T06:43:21.000Z</published>
<updated>2023-03-08T13:21:43.063Z</updated>
<content type="html"><![CDATA[<h3 id="React"><a href="#React" class="headerlink" title="React"></a>React</h3><hr><h4 id="三大API"><a href="#三大API" class="headerlink" title="三大API"></a>三大API</h4><ol><li><p><code>React.createElement()</code>用来创建React元素,无法修改</p></li><li><p><code>React.createRoot()</code>用来创建React的根容器, 容器用来放置React元素</p></li><li><p><code>root.render()</code></p><ul><li><p>当首次调用时, 容器节点里的所有DOM元素都会被替换, 后续的调用则会使用React的DOM差分算法(DOM diffing alorithm) 进行高效更新</p></li><li><p>不会修改容器节点(只会修改容器的子节点), 可以在不覆盖现有子节点的情况下, 将组件插入已有的DOM节点中</p></li></ul></li></ol><h4 id="JSX"><a href="#JSX" class="headerlink" title="JSX"></a>JSX</h4><h5 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项:"></a>注意事项:</h5><ol><li><p>JSX不是字符串, 不要加引号</p></li><li><p>JSX中html标签应该小写, React组件应该大写开头</p></li><li><p>JSX中有且只有一个根标签</p></li><li><p>jSX的标签必须正确结束<code><input type='text' /></code>必须写斜杠或成对出现</p></li><li><p>在JSX中可以使用{}嵌入表达式</p><ul><li>有返回值就是表达式</li></ul></li><li><p>如果表达式是空值, Null, undefined, 布尔值, 不会显示</p></li><li><p>在JSX中, 属性可以直接在标签中设置</p><ul><li><p>class 需使用className代替</p></li><li><p>style中必须使用对象设置, 不能使用属性</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> div = <span class="language-xml"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">{{backgroundColor:</span>'<span class="attr">yellow</span>'}}></span><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></div></li></ul></li></ol><h5 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h5><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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"><span class="keyword">const</span> arr = [<span class="string">'1'</span>, <span class="string">'2'</span>, <span class="string">'3'</span>]</span><br><span class="line"><span class="keyword">const</span> list = <span class="language-xml"><span class="tag"><<span class="name">div</span>></span>{arr}<span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure></div><p>相当于直接渲染列表中的所有元素</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> list = <span class="language-xml"><span class="tag"><<span class="name">ul</span>></span>data.map(item => <span class="tag"><<span class="name">li</span>></span>{item}<span class="tag"></<span class="name">li</span>></span>)<span class="tag"></<span class="name">ul</span>></span></span></span><br></pre></td></tr></table></figure></div><h4 id="虚拟DOM"><a href="#虚拟DOM" class="headerlink" title="虚拟DOM"></a>虚拟DOM</h4><p>好处: </p><ol><li><p>降低API的复杂度</p></li><li><p>解决兼容问题</p></li><li><p>提升性能(减少DOM的不必要操作)</p></li></ol><div class="note note-info icon-padding"><i class="note-icon fa-solid fa-info-circle"></i><p>当我们在JSX中显示数组时, 数组中每一个元素都需要一个唯一的KEY, 否则会显示警告</p></div><p>JSX比较元素是否发生改变是按照位置比较的,如果将一个新元素插入到数组前,则可能造成重复元素渲染, 为了解决这个问题, 可以自定义唯一KEY, 将比较规则改成用KEY比较</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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"><ul></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">key</span>=<span class="string">'1'</span>></span>1<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"> <span class="language-xml"><span class="tag"><<span class="name">li</span> <span class="attr">key</span>=<span class="string">'3'</span>></span>2<span class="tag"></<span class="name">li</span>></span></span></span><br><span class="line"></ul></span><br></pre></td></tr></table></figure></div><div class="note note-warning icon-padding"><i class="note-icon fa-solid fa-exclamation-circle"></i><p>尽量不要使用索引做KEY, 索引会跟着元素顺序改变, 做无用功. 唯一不同就是无警告</p></div><h3 id="React-项目"><a href="#React-项目" class="headerlink" title="React 项目"></a>React 项目</h3><hr><p>public/index.html 是首页的模板; webpack会以index.html为模板生成index.html</p><p>src/index.js 是js的入口文件</p><h4 id="手动准备"><a href="#手动准备" class="headerlink" title="手动准备"></a>手动准备</h4><p><code>npm init -y</code>初始化</p><p><code>npm install react react-dom react-scripts -S</code>下载依赖</p><div class="note note-warning icon-padding"><i class="note-icon fa-solid fa-exclamation-circle"></i><p>需要使用<code>import ReactDOM from 'react-dom/client';</code>引入ReactDOM</p></div><p><code>npx react-scripts build</code>打包</p><p><code>npx react-scripts start</code>开发时测试</p><ul><li><p>这两个命令可以在packetage.json中加入’scripts’内置:</p><p><code>"start": "react-scripts start"</code> 命令行中使用<code>npm start</code></p><p><code>"build": "react-scripts build"</code> 命令行中使用<code>npm run build</code></p></li></ul><p>ESLint语法检查器: </p><ul><li><p>可以在packetage.json 中添加:</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><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">"eslintConfig": {</span><br><span class="line"> "extends": [</span><br><span class="line"> "react-app"</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li></ul><p>引入CSS<code>import './index.css'</code></p><h4 id="React组件"><a href="#React组件" class="headerlink" title="React组件"></a>React组件</h4><h5 id="函数组件-推荐"><a href="#函数组件-推荐" class="headerlink" title="函数组件(推荐)"></a>函数组件(推荐)</h5><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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"><span class="keyword">function</span> <span class="title function_">App</span>(<span class="params"></span>){<span class="comment">// 也可以使用箭头函数</span></span><br><span class="line"> retun <div><span class="title class_">Hello</span></div></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> root = <span class="title class_">ReactDOM</span>.<span class="title function_">createRoot</span>(<span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'root'</span>))</span><br><span class="line">root.<span class="title function_">render</span>(<span class="language-xml"><span class="tag"><<span class="name">App</span>/></span></span>)<span class="comment">// 一般写在入口文件中</span></span><br></pre></td></tr></table></figure></div><h6 id="事件"><a href="#事件" class="headerlink" title="事件"></a>事件</h6><p>在React中事件需要通过元素的属性来设置,和原生Js不一样, 在React中事件的属性需要使用驼峰命名法</p><p><code>onclick -> onClick</code></p><p><code>onchange -> onChange</code></p><p>属性值不能直接执行代码:</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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">onClink = {<span class="function">() =></span> {<span class="title function_">alert</span>(<span class="string">'123'</span>)}} <span class="comment">// 正确</span></span><br><span class="line">oClink = {clickHandler} <span class="comment">//函数不需要加括号,若返回值为函数可加</span></span><br></pre></td></tr></table></figure></div><ul><li><p>事件对象</p><ul><li><p>React事件中同样会传递事件对象, 可以在响应函数中定义参数来接收事件对象</p></li><li><p>React事件对象不是原生的事件对象,是经过React包装过的事件对象</p></li></ul></li></ul><p>取消行为:</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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"><span class="keyword">const</span> <span class="title function_">clickHandler</span> = (<span class="params">event</span>) => {</span><br><span class="line"> event.<span class="title function_">preventDefault</span>(); </span><br><span class="line"> <span class="comment">//取消默认行为(PS: 若放在<a href='#'>中,则取消跳转)</span></span><br><span class="line"> event.<span class="title function_">stopPropagation</span>()</span><br><span class="line"> <span class="comment">//取消冒泡</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li><p>动态传参</p><p>在组件间,父组件可以通过props(属性)向子组件传递数据(父传子)</p><ul><li>props(外部,父 -> 子)是只读属性, 不能修改</li></ul></li></ul><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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></pre></td><td class="code"><pre><span class="line"><<span class="title class_">LogItem</span> test=<span class="string">'123'</span> /></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">LogItem</span> = (<span class="params">props</span>) => {</span><br><span class="line"> <span class="comment">// 可以在函数组件的形参中定义一个props,props指定所有传递的参数</span></span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">div</span>></span>{props.test}<span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> <span class="comment">// 调用</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li><p>状态</p><p>在React中,当组件渲染完毕后,在修改组件中的变量,不会使组件重新渲染。因此必须重新渲染。</p><ul><li><p>state(自身)</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> { useState } <span class="keyword">from</span> <span class="string">'react'</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">LogItem</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="keyword">const</span> [count, setCounter] = <span class="title function_">useState</span>(<span class="number">1</span>) </span><br><span class="line"> <span class="comment">// 该函数会返回一个数组,数组第一个元素是初始值,第二个是一个函数</span></span><br><span class="line"> <span class="comment">// 初始值只是用来显示,修改不会触发渲染</span></span><br><span class="line"> <span class="comment">// 函数通常命名为setXxxx, 使用函数可以修改count触发渲染</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol><li><p>React会监控这个变量的变化,当state发生变化时,会自动触发组件的重新渲染</p></li><li><p>在函数组件中,我们需要通过钩子函数,获取state(useState)</p></li><li><p>State实际就是一个被React管理的变量</p></li><li><p>setXxxx只有在值与count不一样的时候才会渲染</p></li><li><p>当通过setState去修改一个state时,并不是修改当前state,是修改下一次渲染时的state时</p></li><li><p>setState()会触发组件的渲染,他是异步的,所以当调用setState需要用到旧值,可能出现错误。可以传递回调函数来修改state</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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"><span class="title function_">setCounter</span>(<span class="function"><span class="params">prevCounter</span> =></span> prevCounter + <span class="number">1</span>)</span><br><span class="line"><span class="comment">// 回调函数执行时,React会将最新的state值作为参数</span></span><br></pre></td></tr></table></figure></div></li></ol></li></ul></li></ul><div class="note-large notel-yellow"><div class="notel-title"><p>注意</p></div><div class="notel-content"><p>当useState为一个对象时,如果直接修改旧的State对象,对象地址不会改变,所以不会被渲染。可以使用浅拷贝</p> </div></div><p>PS: <code>setXxxx({...user, name:'aaa'})</code></p><ul><li><p>useRef()钩子函数</p><p>创建一个容器, 返回一个普通的JS对象,但是可以确保每次渲染都是同一个对象,减少对象的创建次数(当你需要一个对象不会因为渲染改变时使用)</p></li></ul><h5 id="类组件"><a href="#类组件" class="headerlink" title="类组件"></a>类组件</h5><p>必须继承React.Component</p><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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"><span class="keyword">class</span> <span class="title class_">App</span> <span class="keyword">extends</span> <span class="title class_ inherited__">React.Component</span>{</span><br><span class="line"> <span class="comment">// 类组件中,必须添加一个render()方法,且方法的返回值要是一个jsx</span></span><br><span class="line"> <span class="title function_">render</span>(<span class="params"></span>){</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag"><<span class="name">div</span>></span>aaa<span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li><p>类组件的props是存储在实例对象中,可以直接通过实例对象访问<code>this.props</code></p></li><li><p>类组件的state是存储在对象的state属性中,可以直接通过<code>this.state</code>访问</p><ul><li><p>通过<code>this.setState()对其进行修改</code></p></li><li><p>当我们通过this.setState()修改state时,只会修改设置的属性(只限于直接存储state的值)</p></li></ul></li></ul><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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">state = {</span><br><span class="line"> <span class="attr">count</span>: <span class="number">0</span></span><br><span class="line"> <span class="attr">test</span>: <span class="string">'aaa'</span></span><br><span class="line"> <span class="attr">obj</span>: {<span class="attr">name</span>: <span class="string">'aaa'</span>} <span class="comment">// 没有直接存储在state,会被覆盖</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li>在类组件中,响应函数是以类的方法定义的</li></ul><div class="highlight-container" data-rel="Jsx"><figure class="iseeu highlight jsx"><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></pre></td><td class="code"><pre><span class="line">clickHandler = <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">setState</span>(<span class="function"><span class="params">prevState</span> =></span> {</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="attr">count</span>: prevState + <span class="number">1</span> </span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li><p>createRef钩子函数</p><p>与useRef相同,都是创建一个不会变化的JS容器</p></li></ul>]]></content>
<summary type="html"><h3 id="React"><a href="#React" class="headerlink" title="React"></a>React</h3><hr>
<h4 id="三大API"><a href="#三大API" class="headerlink" title</summary>
<category term="React Study" scheme="https://intyouss.github.io/tags/React-Study/"/>
</entry>
<entry>
<title>JavaScript-React-1</title>
<link href="https://intyouss.github.io/2023/03/07/JavaScript-React-1/"/>
<id>https://intyouss.github.io/2023/03/07/JavaScript-React-1/</id>
<published>2023-03-07T14:03:33.000Z</published>
<updated>2023-03-08T06:42:48.329Z</updated>
<content type="html"><![CDATA[<h3 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h3><hr><h4 id="限制"><a href="#限制" class="headerlink" title="限制:"></a>限制:</h4><ul><li><p>只有一个参数的函数 参数 => 返回值</p></li><li><p>多个参数使用括号 (a,b) => 返回值</p></li><li><p>返回值必须是一个表达式</p></li><li><p>返回值是对象必须带括号</p></li><li><p>如需写语句,在后添加代码块</p></li></ul><div class="note note-info icon-padding"><i class="note-icon fa-solid fa-info-circle"></i><p>隐含参数arguments-表示当前函数的实参 arguments.lenth 获取实参数。剩余参数…args</p></div><h4 id="特性"><a href="#特性" class="headerlink" title="特性:"></a>特性:</h4><ul><li><p>箭头函数中没有arguments,可以使用…args</p></li><li><p>箭头函数的this总是看外层作用域的this</p></li><li><p>箭头函数中的this无法通过call(), apply(), bind()修改</p></li><li><p>箭头函数无法作为构造函数,无法new</p></li></ul><h3 id="模块化"><a href="#模块化" class="headerlink" title="模块化"></a>模块化</h3><hr><div class="note note-info icon-padding"><i class="note-icon fa-solid fa-info-circle"></i><p>默认情况下,script标签中不能使用import语句,必须设置script的type属性为module</p></div><h4 id="导出"><a href="#导出" class="headerlink" title="导出:"></a>导出:</h4><ul><li><p>默认导出</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> xxx;</span><br></pre></td></tr></table></figure></div></li></ul><p>一个模块里只有一个默认导出</p><ul><li><p>命名导出</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> {a,b,c}</span><br></pre></td></tr></table></figure></div></li></ul><h4 id="导入"><a href="#导入" class="headerlink" title="导入:"></a>导入:</h4><ul><li><p>默认导入</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> xxx <span class="keyword">from</span> <span class="string">'./m1.js'</span>;</span><br></pre></td></tr></table></figure></div></li></ul><p>导入默认模块时,变量名可以自主制定,无需使用模块中的变量名</p><ul><li><p>命名导入</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> {a,b,c} <span class="keyword">from</span> <span class="string">'./m1.js'</span></span><br></pre></td></tr></table></figure></div></li></ul><h3 id="类"><a href="#类" class="headerlink" title="类"></a>类</h3><h4 id="特殊方法"><a href="#特殊方法" class="headerlink" title="特殊方法"></a>特殊方法</h4><ul><li><p>构造函数</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title function_">constructor</span>(<span class="params"></span>){}</span><br></pre></td></tr></table></figure></div></li></ul><p>构造函数中,可以通过this来引用当前的对象</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><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"><span class="title function_">constructor</span>(<span class="params">name, age</span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h4 id="模式"><a href="#模式" class="headerlink" title="模式"></a>模式</h4><ul><li><p>严格模式</p><p>“use strict”</p><ol><li><p>类中的所有代码都会在严格模式下执行</p></li><li><p>函数的this不再是window,而是undefined</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> test = mc.<span class="property">fn</span></span><br></pre></td></tr></table></figure></div></li></ol></li></ul><div class="note note-warning icon-padding"><i class="note-icon fa-solid fa-exclamation-circle"></i><p>以方法形式调用时, this就是当前的实例。以函数形式调用, this是undefined</p></div><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><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"><span class="title function_">constructor</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">fn</span> = <span class="variable language_">this</span>.<span class="property">fn</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>) <span class="comment">//将fn方法的this绑定为当前实例</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>可以限制this绑定在自身</p><p>也可以使用箭头函数的形式调用,永远绑定为本身</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><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"><span class="title function_">constructor</span>(<span class="params"></span>){</span><br><span class="line"> fn = <span class="function">() =></span> {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h4 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h4><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><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"><span class="keyword">class</span> <span class="title class_">Animal</span>{}</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Dog</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Animal</span>{}</span><br></pre></td></tr></table></figure></div><p>在子类中重写父类构造函数时,应该在子类中第一时间调用父类构造函数,否则报错</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><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"><span class="title function_">constructor</span>(<span class="params">name, age</span>){</span><br><span class="line"> <span class="variable language_">super</span>(name, age); <span class="comment">//调用父类构造函数,并且需要加入父类参数</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h4 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h4><ul><li><p>动态属性</p><p>需要首先new一个实例才能使用</p></li><li><p>静态属性</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MyClass</span>{</span><br><span class="line"> <span class="keyword">static</span> name = <span class="string">'aaa'</span></span><br><span class="line"> <span class="comment">// 静态方法this不是实例对象而是当前类对象</span></span><br><span class="line"> <span class="keyword">static</span> fn = <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'aaa'</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li></ul><p>可以直接使用<code>MyClass.name</code></p><h3 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h3><hr><ul><li><p>map()</p><ol><li><p>可以根据原有的数组返回一个新数组</p></li><li><p>需要一个回调函数作为参数, 回调函数返回值会成为新数组中的元素</p><p>用法:</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><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"><span class="keyword">let</span> result = arr.<span class="title function_">map</span>(<span class="function">(<span class="params">item, index, array</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> item+<span class="number">2</span></span><br><span class="line">}) </span><br><span class="line"><span class="comment">// 第一个参数 当前元素</span></span><br><span class="line"><span class="comment">// 第二个参数 当前元素索引</span></span><br><span class="line"><span class="comment">// 第三个参数 当前数组</span></span><br></pre></td></tr></table></figure></div></li></ol></li><li><p>filter()</p><p>过滤器</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">result = arr.<span class="title function_">filter</span>(<span class="function">(<span class="params">item</span>) =></span> item % <span class="number">2</span> === <span class="number">0</span>)</span><br></pre></td></tr></table></figure></div><p>根据条件过滤元素</p><ul><li><p>find()</p><p>找第一个被过滤条件通过的元素</p></li></ul></li><li><p>reduce()</p><p>整合数组</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><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">arr.<span class="title function_">reduce</span>(<span class="function">(<span class="params">prev, curr</span>) =></span> {}, <span class="number">0</span>) </span><br><span class="line"><span class="comment">// 0的位置是定义第一次prev的值, prev 上一次合并后值, curr 当前值</span></span><br></pre></td></tr></table></figure></div></li></ul>]]></content>
<summary type="html"><h3 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h3><hr>
<h4 id="限制"><a href="#限制" class="headerlink" title="限制:"></a</summary>
<category term="React Study" scheme="https://intyouss.github.io/tags/React-Study/"/>
</entry>
<entry>
<title>3月7日学习记录</title>
<link href="https://intyouss.github.io/2023/03/07/3%E6%9C%887%E6%97%A5%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
<id>https://intyouss.github.io/2023/03/07/3%E6%9C%887%E6%97%A5%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</id>
<published>2023-03-06T16:23:24.000Z</published>
<updated>2023-03-20T17:48:11.429Z</updated>
<content type="html"><![CDATA[<h3 id="路由系统"><a href="#路由系统" class="headerlink" title="路由系统"></a>路由系统</h3><hr><h4 id="路由的两种写法"><a href="#路由的两种写法" class="headerlink" title="路由的两种写法"></a>路由的两种写法</h4><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">app.add_url_rule(<span class="string">'/index'</span>, <span class="string">'index'</span>, index)</span><br></pre></td></tr></table></figure></div><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">'/inddex'</span></span>)</span></span><br></pre></td></tr></table></figure></div><h4 id="路由加载源码的流程"><a href="#路由加载源码的流程" class="headerlink" title="路由加载源码的流程"></a>路由加载源码的流程</h4><ul><li><p>将URL和函数打包成为Rule对象</p></li><li><p>将Rule对象添加到map对象中</p></li><li><p>app_url_map = map对象</p></li></ul><h4 id="动态路由"><a href="#动态路由" class="headerlink" title="动态路由"></a>动态路由</h4><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="meta">@app.route(<span class="params"><span class="string">'/index/<string:name>'</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>(<span class="params">name</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">f'<span class="subst">{name}</span>'</span></span><br></pre></td></tr></table></figure></div><p>uuid类型 类似于xxxxx-xxxxxxx-xxxxxx-xxxxxx</p><h5 id="自定义路由"><a href="#自定义路由" class="headerlink" title="自定义路由"></a>自定义路由</h5><p>必须继承BaseConverter</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> werkzeug.routing <span class="keyword">import</span> BaseConverter</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">RegexConverter</span>(<span class="title class_ inherited__">BaseConverter</span>):</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, <span class="built_in">map</span>, regex</span>):</span><br><span class="line"> <span class="built_in">super</span>().__init__(<span class="built_in">map</span>)</span><br><span class="line"> self.regex = regex</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">to_python</span>(<span class="params">self, value</span>):</span><br><span class="line"> <span class="keyword">return</span> value</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">app.url_map.converters[<span class="string">'regex'</span>] = RegexConverter</span><br></pre></td></tr></table></figure></div><h3 id><a href="#" class="headerlink" title></a></h3><h3 id="视图"><a href="#视图" class="headerlink" title="视图"></a>视图</h3><hr><h4 id="FBV"><a href="#FBV" class="headerlink" title="FBV"></a>FBV</h4><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">'/user'</span>, method=[<span class="string">'GET'</span>, <span class="string">'POST'</span>]</span>)</span></span><br></pre></td></tr></table></figure></div><h4 id="CBV"><a href="#CBV" class="headerlink" title="CBV"></a>CBV</h4><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">UserView</span>(views, MethodView):</span><br><span class="line"> method = [<span class="string">'GET'</span>, <span class="string">'POST'</span>]</span><br><span class="line"> decorators = [test1, test2] //CBV添加装饰器</span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">get</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'get'</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">post</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'post'</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">app.add_url_rule(<span class="string">'/user'</span>, view_func=UserView.as_view(<span class="string">'user'</span>))</span><br></pre></td></tr></table></figure></div><h3 id="模板-jinja2"><a href="#模板-jinja2" class="headerlink" title="模板(jinja2)"></a>模板(jinja2)</h3><hr><h4 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h4><p>模板可继承</p><ol><li><p>被继承:</p><p><code>{% block content %} ? {% endbloack %}</code> </p></li><li><p>继承:</p><p><code>{% extends 'layout.html' %} {% block content %} ? {% endbloack %}</code></p></li><li><p>引入:</p><p><code>{% include 'from.html' %}</code></p></li></ol><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">def</span> <span class="title function_">dunc</span>(<span class="params">arg</span>):</span><br><span class="line"> <span class="keyword">return</span> arg</span><br><span class="line"></span><br><span class="line">render_template(<span class="string">'md.html'</span>, nums=nums, f=func) //可以传递函数名</span><br></pre></td></tr></table></figure></div><p>定义全局模板方法</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@app.template_glabol() //模板中全局使用 {{ func(<span class="params"><span class="string">'haha'</span></span>) }}</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">func</span>(<span class="params">arg</span>):</span><br><span class="line"> <span class="keyword">return</span> arg</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.template_filter() //模板全局使用 {{ <span class="string">'haha'</span>|f(<span class="params"><span class="string">'e'</span></span>) }}</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">f</span>(<span class="params">arg,h</span>):</span><br><span class="line"> <span class="keyword">return</span> arg + h</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">'/md/hg'</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>():</span><br><span class="line"> <span class="keyword">return</span> render_template(<span class="string">'md_hg.html'</span>)</span><br></pre></td></tr></table></figure></div><p>如果想要将一个方法限制为一个蓝图域中使用,只需将app修改即可</p><h3 id="特殊装饰器"><a href="#特殊装饰器" class="headerlink" title="特殊装饰器"></a>特殊装饰器</h3><hr><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="meta">@app.before_request //视图函数运行前运行</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">f1</span>():// 一旦返回值为真,则停止行为向下传播(判断用户登录等), 反之空,则继续</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'f1'</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.after_request //视图函数运行结束后运行</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">f2</span>(<span class="params">response</span>): //视图函数有返回值,就必须返回一个Responce</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">'f2'</span>)</span><br><span class="line"> <span class="keyword">return</span> response</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">'/index'</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>():</span><br><span class="line"> <span class="keyword">return</span> render_template(<span class="string">'index.html'</span>)</span><br></pre></td></tr></table></figure></div><p>或</p><p><code>app.before_request(func)</code>不加装饰器使用</p>]]></content>
<summary type="html"><h3 id="路由系统"><a href="#路由系统" class="headerlink" title="路由系统"></a>路由系统</h3><hr>
<h4 id="路由的两种写法"><a href="#路由的两种写法" class="headerlink" title</summary>
<category term="Flask Study" scheme="https://intyouss.github.io/tags/Flask-Study/"/>
</entry>
<entry>
<title>3月6日 学习记录</title>
<link href="https://intyouss.github.io/2023/03/06/3%E6%9C%886%E6%97%A5%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
<id>https://intyouss.github.io/2023/03/06/3%E6%9C%886%E6%97%A5%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</id>
<published>2023-03-06T05:21:41.000Z</published>
<updated>2023-03-09T04:24:23.379Z</updated>
<content type="html"><![CDATA[<h3 id="唯一的URL-x2F-重定向行为"><a href="#唯一的URL-x2F-重定向行为" class="headerlink" title="唯一的URL/重定向行为"></a>唯一的URL/重定向行为</h3><hr><p>route内的元素表示不同,将有不同的限制</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">'/projects/'</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">projects</span>():</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'a'</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">'/about'</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">about</span>():</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'b'</span></span><br></pre></td></tr></table></figure></div><p>如果使用没有斜杠的/projects访问,Flask会自动帮助添加斜杠,因此中规中矩,却有两种访问方式。 </p><p>如果使用有斜杠的/about/访问,将会返回一个404未找到错误,可以保证URL唯一性,并有助于搜索引擎重复索引同一个界面!</p><h3 id="flask的session"><a href="#flask的session" class="headerlink" title="flask的session"></a>flask的session</h3><hr><p>flask的session是以加密保存在浏览器上的,依赖于secret_key</p><h3 id="数据库连接池"><a href="#数据库连接池" class="headerlink" title="数据库连接池"></a>数据库连接池</h3><hr><p>使用数据库连接池可以很好的解决链接数据库时,重复连接的问题</p><p>可以将数据库连接封装成一个函数或者类,方便调用</p><h4 id="With上下文管理-简"><a href="#With上下文管理-简" class="headerlink" title="With上下文管理(简)"></a>With上下文管理(简)</h4><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">class</span> <span class="title class_">Foo</span>(<span class="title class_ inherited__">object</span>):</span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__enter__</span>(<span class="params">self</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="number">124</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__exit__</span>(<span class="params">self, exc_type, exc_val</span>):</span><br><span class="line"> <span class="keyword">pass</span></span><br><span class="line">obj = Foo()</span><br><span class="line"><span class="keyword">with</span> obj <span class="keyword">as</span> f:</span><br><span class="line"> <span class="built_in">print</span>(f)</span><br></pre></td></tr></table></figure></div><p>with一个对象会调用对象中的 属性__enter__ ,结束后调用属性__exit__。</p><p>使用上下文管理操控线程池的开关,可以实现线程自动化</p><h3 id="引入静态文件"><a href="#引入静态文件" class="headerlink" title="引入静态文件"></a>引入静态文件</h3><hr><div class="highlight-container" data-rel="Html"><figure class="iseeu highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">{{</span> <span class="attr">url_for</span>('<span class="attr">static</span>', <span class="attr">filename</span>=<span class="string">'mm.jpg'</span>) }} /></span></span><br></pre></td></tr></table></figure></div><p>或</p><div class="highlight-container" data-rel="Html"><figure class="iseeu highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">img</span> <span class="attr">src</span>=<span class="string">'/static/mm.jpg'</span> /></span></span><br></pre></td></tr></table></figure></div><p>在使用static_url_path=’/xx’修改static文件夹路径时,前一个可以在不做修改下实现自动识别static路径。后者需要一个一个修改,不利于使用。</p><h3 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h3><hr><p>使用函数加载配置文件:</p><p><code>app.config.from_object('config.settings')</code></p><p>使用配置文件:</p><p><code>app.config['xx']</code></p><ol><li><p>可以使用localsettings在本地测试, 覆盖settings, 保护原始文件</p><p>在settings添加:</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">from</span> localsettings <span class="keyword">import</span> *</span><br><span class="line"><span class="keyword">except</span> ImportError:</span><br><span class="line"> <span class="keyword">pass</span></span><br></pre></td></tr></table></figure></div></li></ol><p> 在上传服务器时,可以方便的在服务器和本地切换</p><p> 使用.gitgnore控制上传文件,忽略不需要的文件</p><ol start="2"><li><p>类配置文件</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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 class="keyword">class</span> <span class="title class_">Dev</span>(<span class="title class_ inherited__">obj</span>):</span><br><span class="line"> HOST = <span class="number">1.1</span><span class="number">.1</span><span class="number">.1</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Prod</span>(<span class="title class_ inherited__">obj</span>):</span><br><span class="line"> HOST = <span class="number">1.1</span><span class="number">.1</span><span class="number">.1</span></span><br></pre></td></tr></table></figure></div></li></ol><p> 使用类隔离开发环境</p><h3 id="小知识"><a href="#小知识" class="headerlink" title="小知识"></a>小知识</h3><hr><ol><li><p>进程是cpu中执行的最小单元,线程是cpu中资源调度的最小单元,一个进程内可以有多个线程</p></li><li><p>接口<br>其他语言 :<br>接口是以interface关键字开头,内部定义方法,但不具体实现,专门用于约束实现类<br>类中可以有多个接口继承 </p><p>python :<br>无接口,可以多继承<br>但不能继承多个类</p></li><li><p>构造函数和析构函数<br>构造函数:用于创建对象的函数 class foo(obj)<br>析构函数:用于销毁对象的函数 del obj()</p></li><li><p>重写和重载的区别<br>重载:函数名相同而参数类型,个数,返回值不同<br>重写:在子类中,对父类的方法重写</p></li><li><p>抽象类和抽象方法<br>java :<br>抽象类中可以有抽象方法也可以有普通方法。<br>抽象类被继承后需要实现抽象方法</p></li></ol>]]></content>
<summary type="html"><h3 id="唯一的URL-x2F-重定向行为"><a href="#唯一的URL-x2F-重定向行为" class="headerlink" title="唯一的URL&#x2F;重定向行为"></a>唯一的URL&#x2F;重定向行为</h3><hr>
<p>route内的</summary>
<category term="Flask Study" scheme="https://intyouss.github.io/tags/Flask-Study/"/>
</entry>
<entry>
<title>3月5日 学习记录</title>
<link href="https://intyouss.github.io/2023/03/05/3%E6%9C%885%E6%97%A5%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
<id>https://intyouss.github.io/2023/03/05/3%E6%9C%885%E6%97%A5%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</id>
<published>2023-03-05T09:34:24.000Z</published>
<updated>2023-03-09T03:33:54.088Z</updated>
<content type="html"><![CDATA[<h4 id="flask中函数app-route-嵌套装饰器可能错误"><a href="#flask中函数app-route-嵌套装饰器可能错误" class="headerlink" title="flask中函数app.route()嵌套装饰器可能错误"></a>flask中函数app.route()嵌套装饰器可能错误</h4><hr><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">atuh1</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta"> @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">inner</span>(<span class="params">*arg, **kwarg</span>):</span><br><span class="line"> <span class="keyword">return</span> func(*arg, **kwarg)</span><br><span class="line"> <span class="keyword">return</span> inner</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">atuh2</span>(<span class="params">func</span>):</span><br><span class="line"><span class="meta"> @functools.wraps(<span class="params">func</span>)</span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">inner</span>(<span class="params">*arg, **kwarg</span>):</span><br><span class="line"> <span class="keyword">return</span> func(*arg, **kwarg)</span><br><span class="line"> <span class="keyword">return</span> inner </span><br><span class="line"></span><br><span class="line"><span class="meta">@auth1</span></span><br><span class="line"><span class="meta">@auth2</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>():</span><br><span class="line"> <span class="built_in">print</span>(<span class="string">"index"</span>)</span><br></pre></td></tr></table></figure></div><p>单装饰器装饰: 装饰器在装饰一个函数时,此函数将被装饰器内部函数替代,如果想暴露原本函数,可以使用装饰器@functools.wraps(func)</p><p>多装饰器装饰: 优先使用内部装饰器,逐层向上</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask</span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/index"</span></span>)</span></span><br><span class="line"><span class="meta">@auth</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">index</span>():</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"h"</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">"/login"</span></span>)</span></span><br><span class="line"><span class="meta">@auth</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">login</span>():</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"g"</span></span><br></pre></td></tr></table></figure></div><p>@app.route() endpoint默认为函数名 </p><p>两个函数使用相同函数装饰auth, 在不使用@functools.warps(func)的情况下,两个初始函数被装饰后,都将变为inner的属性,形成别名重复错误:<br><code>AssertionError: View function mapping is ovwewriting an existing endpoint function: inner</code></p><h4 id="flask蓝图"><a href="#flask蓝图" class="headerlink" title="flask蓝图"></a>flask蓝图</h4><hr><p>flask蓝图主要功能就是分类(Blueprint),不同功能函数放在不同文件中实现: </p><ol><li><p>创建蓝图</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xxx = Blueprint(<span class="string">'xxx'</span>, __name__)</span><br></pre></td></tr></table></figure></div></li><li><p>在主进程中引用蓝图</p><div class="highlight-container" data-rel="Python"><figure class="iseeu highlight python"><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"><span class="keyword">from</span> <span class="string">'/path'</span> <span class="keyword">import</span> xxx</span><br><span class="line">app.register_blueprint(xxx, url_prefix=<span class="string">'/x'</span>)</span><br></pre></td></tr></table></figure></div><p>url_prefix 添加前缀 使用: /x/xxx</p></li></ol><h4 id="Django的路由映射和Flask的蓝图有什么区别"><a href="#Django的路由映射和Flask的蓝图有什么区别" class="headerlink" title="Django的路由映射和Flask的蓝图有什么区别"></a>Django的路由映射和Flask的蓝图有什么区别</h4><hr><p>flask:</p><ol><li><p>flask是利用蓝图进行模块划分的,通过app装饰器注册模块蓝图,在主模块统一调用;</p></li><li><p>在flask中,路由是通过装饰器给每个视图函数提供的, 而且根据请求方式的不同可以一个url用于不同的作用。功能相似,但是底层实现不一样</p></li></ol><p>Django:</p><ol><li>Django路由映射是通过路由正则匹配映射到每一个模块的</li><li>在django中,路由是浏览器访问服务器时,先访问的项目中的url,再由项目中的url找到应用中url,这些url是放在一个列表里,遵从从前往后匹配的规则。</li></ol>]]></content>
<summary type="html"><h4 id="flask中函数app-route-嵌套装饰器可能错误"><a href="#flask中函数app-route-嵌套装饰器可能错误" class="headerlink" title="flask中函数app.route()嵌套装饰器可能错误"></a>flas</summary>
<category term="Flask Study" scheme="https://intyouss.github.io/tags/Flask-Study/"/>
</entry>
</feed>