-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
226 lines (107 loc) · 107 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>docker部署redis-cluster集群</title>
<link href="/2022/04/02/docker-bu-shu-redis-cluster-ji-qun/"/>
<url>/2022/04/02/docker-bu-shu-redis-cluster-ji-qun/</url>
<content type="html"><![CDATA[<ol><li><p>拉取redis镜像,查看版本</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> pull redis<span class="token function">docker</span> inspect redis<span class="token operator">|</span><span class="token function">grep</span> -i version<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre></li><li><p>创建模版文件</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token comment"># redis端口</span>port <span class="token variable">${PORT}</span><span class="token comment"># 关闭保护模式</span>protected-mode no<span class="token comment"># 开启集群</span>cluster-enabled <span class="token function">yes</span><span class="token comment"># 集群节点配置</span>cluster-config-file nodes.conf<span class="token comment"># 超时</span>cluster-node-timeout <span class="token number">5000</span><span class="token comment"># 集群节点IP host模式为宿主机IP</span>cluster-announce-ip <span class="token number">192.168</span>.8.183<span class="token comment"># 集群节点端口 7001 - 7006</span>cluster-announce-port <span class="token variable">${PORT}</span>cluster-announce-bus-port <span class="token number">1</span><span class="token variable">${PORT}</span><span class="token comment"># 开启 appendonly 备份模式</span>appendonly <span class="token function">yes</span><span class="token comment"># 每秒钟备份</span>appendfsync everysec<span class="token comment"># 对aof文件进行压缩时,是否执行同步操作</span>no-appendfsync-on-rewrite no<span class="token comment"># 当目前aof文件大小超过上一次重写时的aof文件大小的100%时会再次进行重写</span>auto-aof-rewrite-percentage <span class="token number">100</span><span class="token comment"># 重写前AOF文件的大小最小值 默认 64mb</span>auto-aof-rewrite-min-size 64mb<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><p>执行脚本创建配置文件</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token builtin class-name">cd</span> redis-cluster<span class="token keyword">for</span> <span class="token for-or-select variable">port</span> <span class="token keyword">in</span> <span class="token variable"><span class="token variable">`</span><span class="token function">seq</span> <span class="token number">7001</span> <span class="token number">7006</span><span class="token variable">`</span></span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token punctuation">\</span> <span class="token function">mkdir</span> -p ./<span class="token variable">${port}</span>/conf <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token assign-left variable">PORT</span><span class="token operator">=</span><span class="token variable">${port}</span> envsubst <span class="token operator"><</span> ./redis-cluster.tmpl <span class="token operator">></span> ./<span class="token variable">${port}</span>/conf/redis.conf <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">mkdir</span> -p ./<span class="token variable">${port}</span>/data<span class="token punctuation">;</span> <span class="token punctuation">\</span><span class="token keyword">done</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><p>编写docker-compose文件</p><pre class="line-numbers language-none"><code class="language-none"># docker-compose 版本version: '3.7'services: redis7001: image: 'redis' container_name: redis7001 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./7001/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./7001/data:/data ports: - "7001:7001" - "17001:17001" environment: # 设置时区为上海,否则时间会有问题 - TZ=Asia/Shanghai redis7002: image: 'redis' container_name: redis7002 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./7002/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./7002/data:/data ports: - "7002:7002" - "17002:17002" environment: # 设置时区为上海,否则时间会有问题 - TZ=Asia/Shanghai redis7003: image: 'redis' container_name: redis7003 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./7003/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./7003/data:/data ports: - "7003:7003" - "17003:17003" environment: # 设置时区为上海,否则时间会有问题 - TZ=Asia/Shanghai redis7004: image: 'redis' container_name: redis7004 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./7004/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./7004/data:/data ports: - "7004:7004" - "17004:17004" environment: # 设置时区为上海,否则时间会有问题 - TZ=Asia/Shanghai redis7005: image: 'redis' container_name: redis7005 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./7005/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./7005/data:/data ports: - "7005:7005" - "17005:17005" environment: # 设置时区为上海,否则时间会有问题 - TZ=Asia/Shanghai redis7006: image: 'redis' container_name: redis7006 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./7006/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./7006/data:/data ports: - "7006:7006" - "17006:17006" environment: # 设置时区为上海,否则时间会有问题 - TZ=Asia/Shanghai<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><p>集群配置</p><pre class="line-numbers language-none"><code class="language-none">docker exec -it redis7001 redis-cli -p 7001 -a a123456 --cluster create 192.168.8.183:7001 192.168.8.183:7002 192.168.8.183:7003 192.168.8.183:7004 192.168.8.183:7005 192.168.8.183:7006 --cluster-replicas 1<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></li><li><p>使用测试</p><pre class="line-numbers language-none"><code class="language-none">docker exec -it redis7001 redis-cli -h 192.168.8.183 -p 7003 -a a123456<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></li></ol>]]></content>
<categories>
<category> devops </category>
</categories>
<tags>
<tag> redis </tag>
<tag> docker </tag>
</tags>
</entry>
<entry>
<title>消息队列</title>
<link href="/2022/03/29/xiao-xi-dui-lie/"/>
<url>/2022/03/29/xiao-xi-dui-lie/</url>
<content type="html"><![CDATA[<h3 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h3><p><img src="https://img.yanqic.cn/blog/202203291210225.png?imageslim" alt="image.png"></p><h2 id="1-消息队列应用场景"><a href="#1-消息队列应用场景" class="headerlink" title="1. 消息队列应用场景"></a>1. 消息队列应用场景</h2><h3 id="1-1-异步处理"><a href="#1-1-异步处理" class="headerlink" title="1.1 异步处理"></a>1.1 异步处理</h3><p><img src="https://img.yanqic.cn/blog/202203291207621.png?imageslim" alt="image.png"></p><h3 id="1-2-流量削峰"><a href="#1-2-流量削峰" class="headerlink" title="1.2 流量削峰"></a>1.2 流量削峰</h3><p><img src="https://img.yanqic.cn/blog/202203291208311.png?imageslim" alt="image.png"></p><p><img src="https://img.yanqic.cn/blog/202203291209562.png?imageslim" alt="image.png"><br>基于令牌桶实现限流</p><h3 id="1-3-服务解耦"><a href="#1-3-服务解耦" class="headerlink" title="1.3 服务解耦"></a>1.3 服务解耦</h3><p>我们知道订单是电商系统中比较核心的数据,当一个新订单创建时:<br>支付系统需要发起支付流程;<br>风控系统需要审核订单的合法性;<br>客服系统需要给用户发短信告知用户;<br>经营分析系统需要更新统计数据;<br>……<br>这些订单下游的系统都需要实时获得订单数据。随着业务不断发展,这些订单下游系统不断的增加,不断变化,并且每个系统可能只需要订单数据的一个子集,负责订单服务的开发团队不得不花费很大的精力,应对不断增加变化的下游系统,不停地修改调试订单系统与这些下游系统的接口。任何一个下游系统接口变更,都需要订单模块重新进行一次上线,对于一个电商的核心服务来说,这几乎是不可接受的。<br>所有的电商都选择用消息队列来解决类似的系统耦合过于紧密的问题。引入消息队列后,订单服务在订单变化时发送一条消息到消息队列的一个主题 Order 中,所有下游系统都订阅主题 Order,这样每个下游系统都可以获得一份实时完整的订单数据。</p><h3 id="1-4-其它应用场景"><a href="#1-4-其它应用场景" class="headerlink" title="1.4 其它应用场景"></a>1.4 其它应用场景</h3><ul><li>作为发布 / 订阅系统实现一个微服务级系统间的观察者模式;</li><li>连接流计算任务和数据;</li><li>用于将消息广播给大量接收者。</li></ul><h3 id="1-5-队列功能维度分类"><a href="#1-5-队列功能维度分类" class="headerlink" title="1.5 队列功能维度分类"></a>1.5 队列功能维度分类</h3><p>• 优先级队列:<br>优先级队列不同于先进先出队列,优先级高的消息具备优先被消费的特权,这样可以为下游提供不同消息级别的保证。<br>应用:生产> 消费<br>• 延迟队列</p><ul><li>基于消息型:基于消息的延迟是指为每条消息设置不同的延迟时间,那么每当队列中有新消息进入的时候就会重新根据延迟时间排序(消耗性能);</li><li>基于列队(通用): 设置不同延迟级别的队列,比如 5s、10s、30s、1min、5mins、10mins 等,每个队列中消息的延迟时间都是相同的,这样免去了延迟排序所要承受的性能之苦,通过一定的扫描策略(比如定时)即可投递超时的消息。</li></ul><p>• 死信队列<br>可以为每个队列设置一个回退队列,它和死信队列都是为异常的处理提供的一种机制保障。实际情况下,回退队列的角色可以由死信队列和重试队列来扮演。<br>• 重试队列<br>重试越多次重新投递的时间就越久,为此需要设置一个上限,超过投递次数就入死信队列。重试队列与延迟队列有相同的地方,都是需要设置延迟级别,它们彼此的区别是:延迟队列动作由内部触发,重试队列动作由外部消费端触发;延迟队列作用一次,而重试队列的作用范围会向后传递。</p><h3 id="1-6-消费模式分类"><a href="#1-6-消费模式分类" class="headerlink" title="1.6 消费模式分类"></a>1.6 消费模式分类</h3><p>消费模式分为推(push)模式和拉(pull)模式。<br>• 广播消费<br>• 消息一般有两种传递模式:点对点(P2P,Point-to-Point)模式和发布/订阅(Pub/Sub)模式<br>RabbitMQ 是一种典型的点对点模式,而 Kafka 是一种典型的发布订阅模式。<br>RabbitMQ 中可以通过设置交换器类型来实现发布订阅模式而达到广播消费的效果,Kafka 中也能以点对点的形式消费,你完全可以把其消费组(consumer group)的概念看成是队列的概念。不过对比来说,Kafka 中因为有了消息<strong>回溯功能</strong>的存在(基于 offset),对于广播消费的力度支持比 RabbitMQ 的要强<br>• 消息回溯<br>• 消息堆积+持久化<br>消息堆积分内存式堆积和磁盘式堆积。RabbitMQ 是典型的内存式堆积,但这并非绝对,在某些条件触发后会有换页动作来将内存中的消息换页到磁盘(换页动作会影响吞吐),或者直接使用惰性队列来将消息直接持久化至磁盘中。Kafka 是一种典型的磁盘式堆积</p><h2 id="2-主流-MQ-介绍及对比"><a href="#2-主流-MQ-介绍及对比" class="headerlink" title="2. 主流 MQ 介绍及对比"></a>2. 主流 MQ 介绍及对比</h2><h3 id="2-1-Rabbitmq"><a href="#2-1-Rabbitmq" class="headerlink" title="2.1 Rabbitmq"></a>2.1 Rabbitmq</h3><p>优点:轻量,迅捷,容易部署和使用,拥有灵活的<strong>路由</strong>配置 amqp 协议,保证消息<strong>严格顺序性</strong>; 默认<strong>push 模式</strong><br>缺点:性能和吞吐量较差,消息堆积的支持不好,不易进行二次开发,消息不可回溯,队列模式,一对多消费支持不好;<br>**</p><h3 id="2-2-Rocketmq"><a href="#2-2-Rocketmq" class="headerlink" title="2.2 Rocketmq"></a>2.2 Rocketmq</h3><p>优点:性能好,稳定可靠,有活跃的中文社区,特点响应快,适合<strong>在线实时业务处理</strong><br>缺点:兼容性较差,但随意影响力的扩大,该问题会有改善,队列有序,topic 无序;<strong>(解决方案</strong>:用一致性哈希算法,计算出队列 ID,指定队列 ID 发送,这样可以保证相同的订单/用户的消息总被发送到同一个队列上,就可以确保严格顺序了。)<img src="https://img.yanqic.cn/blog/202203291212813.png?imageslim" alt="image.png"></p><h3 id="2-3-Kafka"><a href="#2-3-Kafka" class="headerlink" title="2.3 Kafka"></a>2.3 Kafka</h3><p>优点:拥有强大的<strong>性能及吞吐量</strong>,兼容性很好,适合大数据、日志处理;(顺序读写,zero copy 实现无内存交换, pagecahce 优化磁盘 io); pull 模式(基于长轮询);<br>缺点:由于“攒一波再处理”导致<strong>延迟比较高,可以通过配置修改批次数量;</strong></p><h3 id="2-4-Pulsar:"><a href="#2-4-Pulsar:" class="headerlink" title="2.4 Pulsar:"></a>2.4 Pulsar:</h3><p>采用存储和计算分离的设计,是消息队里产品中黑马,值得持续关注</p><h2 id="3-MQ-常见问题"><a href="#3-MQ-常见问题" class="headerlink" title="3. MQ 常见问题"></a>3. MQ 常见问题</h2><h3 id="3-1-消息队列事务一致性"><a href="#3-1-消息队列事务一致性" class="headerlink" title="3.1 消息队列事务一致性"></a>3.1 消息队列事务一致性</h3><p>RocketMQ 的事务反查机制</p><h3 id="3-2-如何确保消息不会丢失"><a href="#3-2-如何确保消息不会丢失" class="headerlink" title="3.2 如何确保消息不会丢失?"></a>3.2 如何确保消息不会丢失?</h3><p>生产阶段注意错误捕获重试机制<br>存储阶段注意多节点,配置 Broker 参数保证消息存储到磁盘(单节点)<br>消费节点注意业务逻辑处理完成后 ack</p><h3 id="3-3-如何处理消费过程中的重复消息?"><a href="#3-3-如何处理消费过程中的重复消息?" class="headerlink" title="3.3 如何处理消费过程中的重复消息?"></a>3.3 如何处理消费过程中的重复消息?</h3><p>通过业务实现幂等性;</p><h3 id="3-4-消息积压了该如何处理?"><a href="#3-4-消息积压了该如何处理?" class="headerlink" title="3.4 消息积压了该如何处理?"></a>3.4 消息积压了该如何处理?</h3><p>增加消费能力。在扩容 Consumer 的实例数量的同时,必须同步扩容主题中的分区(也叫队列)数量,确保 Consumer 的实例数和分区数量是相等的</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 后端 </tag>
<tag> queue </tag>
</tags>
</entry>
<entry>
<title>ES搜索匹配优化</title>
<link href="/2022/03/28/es-sou-suo-pi-pei-you-hua/"/>
<url>/2022/03/28/es-sou-suo-pi-pei-you-hua/</url>
<content type="html"><![CDATA[<h3 id="Elasticsearch-聚合原理"><a href="#Elasticsearch-聚合原理" class="headerlink" title="Elasticsearch 聚合原理"></a>Elasticsearch 聚合原理</h3><ul><li>倒排索引(Inverted Index) <ul><li>term dictionary (有序, log(n)) </li><li>term index (FST Trie, O(m))</li></ul></li></ul><p><img src="https://cdn.nlark.com/yuque/0/2021/png/299905/1629890263570-abec2392-3dba-4a67-af02-6edea38403fd.png#clientId=u7a616698-dc88-4&from=paste&id=u717a8731&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1069&originWidth=1920&originalType=url&ratio=1&size=203152&status=done&style=none&taskId=u3761f39b-e6d8-448b-acef-974cc10d1ca" alt="image.png"></p><ul><li>列式存储(doc_values) <ul><li>顺序磁盘读写 </li><li>序列化压缩 </li><li>analyzed strings 不支持(text)</li></ul></li><li>顺排索引(fielddata)(原理:完整加载这个字段所有 Segment 中的倒排索引到内存中) <ul><li>jvm内存读取,查询性能更高 </li><li>查询预热 </li><li>indices.fielddata.cache.size =20%(默认没有上限)</li></ul></li></ul><p>配置示例: </p><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> <span class="token property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"tags"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span><span class="token punctuation">,</span> <span class="token property">"doc_values"</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token comment">// 默认开启</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"content"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span> <span class="token property">"fielddata"</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token property">"eager_global_ordinals"</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token comment">// 预加载</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"extra"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token string">"keyword"</span><span class="token punctuation">,</span> <span class="token property">"index"</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token property">"title"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span> <span class="token property">"fields"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"keyword"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"type"</span><span class="token operator">:</span> <span class="token string">"keyword"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>Aggregations 分类及常用聚合 </p><ul><li><p>Bucket </p><ul><li>Terms</li></ul></li><li><p>Composite </p></li><li><p>Multi Terms </p></li><li><p>Histogram </p></li><li><p>Metrics<br>- Top-hits </p></li><li><p>Stats </p></li><li><p>Cardinality </p></li><li><p>Pipeline aggregations </p><ul><li>Bucket script <h3 id="工单聚合搜索优化过程"><a href="#工单聚合搜索优化过程" class="headerlink" title="工单聚合搜索优化过程"></a>工单聚合搜索优化过程</h3><h5 id="2-1-储存结构优化"><a href="#2-1-储存结构优化" class="headerlink" title="2.1 储存结构优化"></a>2.1 储存结构优化</h5></li></ul></li><li><p>关闭动态索引(避免无用字段和全文索引) </p></li><li><p>分词器调整 </p></li><li><p>通过fields 设置专门的聚合字段</p></li></ul><p>备注:新版本不支持动态查询boost, 可以使用function_score 查询 </p><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> mappings<span class="token operator">:</span> <span class="token punctuation">{</span> dynamic<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> properties<span class="token operator">:</span> <span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'keyword'<span class="token punctuation">,</span> boost<span class="token operator">:</span> <span class="token number">10</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> project<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik_max_word'<span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'ik_smart'<span class="token punctuation">,</span> fields<span class="token operator">:</span> <span class="token punctuation">{</span> english<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik_english'<span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'english' <span class="token punctuation">}</span><span class="token punctuation">,</span> pinyin<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik_pinyin_analyzer'<span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'standard' <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> approvalId<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'long' <span class="token punctuation">}</span><span class="token punctuation">,</span> reporter<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'object'<span class="token punctuation">,</span> dynamic<span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> subject<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik_max_word'<span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'ik_smart'<span class="token punctuation">,</span> boost<span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span> fields<span class="token operator">:</span> <span class="token punctuation">{</span> english<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik-english'<span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'english' <span class="token punctuation">}</span><span class="token punctuation">,</span> pinyin<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik_pinyin_analyzer'<span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'standard' <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> content<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik_strip_html'<span class="token punctuation">,</span> boost<span class="token operator">:</span> <span class="token number">1.5</span><span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'ik_smart'<span class="token punctuation">,</span> fields<span class="token operator">:</span> <span class="token punctuation">{</span> english<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik_english'<span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'english' <span class="token punctuation">}</span><span class="token punctuation">,</span> pinyin<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'text'<span class="token punctuation">,</span> analyzer<span class="token operator">:</span> 'ik_pinyin_analyzer'<span class="token punctuation">,</span> search_analyzer<span class="token operator">:</span> 'standard' <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> envDescription<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'flattened' <span class="token punctuation">}</span><span class="token punctuation">,</span> tags<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'keyword'<span class="token punctuation">,</span> fields<span class="token operator">:</span> <span class="token punctuation">{</span> query<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'keyword'<span class="token punctuation">,</span> normalizer<span class="token operator">:</span> 'lowercase' <span class="token comment">// search: case_insensitive: true 7.10</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> chatGroups<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'flattened' <span class="token punctuation">}</span><span class="token punctuation">,</span> deletedAt<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'date' <span class="token punctuation">}</span><span class="token punctuation">,</span> createdAt<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'date' <span class="token punctuation">}</span><span class="token punctuation">,</span> updatedAt<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'date' <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> settings<span class="token operator">:</span> <span class="token punctuation">{</span> number_of_shards<span class="token operator">:</span> <span class="token number">5</span><span class="token punctuation">,</span> number_of_replicas<span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> analysis<span class="token operator">:</span> <span class="token punctuation">{</span> analyzer<span class="token operator">:</span> <span class="token punctuation">{</span> ik_pinyin_analyzer<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'custom'<span class="token punctuation">,</span> tokenizer<span class="token operator">:</span> 'ik_max_word'<span class="token punctuation">,</span> filter<span class="token operator">:</span> 'pinyin_filter' <span class="token punctuation">}</span><span class="token punctuation">,</span> ik_strip_html<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'custom'<span class="token punctuation">,</span> tokenizer<span class="token operator">:</span> 'ik_smart'<span class="token punctuation">,</span> char_filter<span class="token operator">:</span> <span class="token punctuation">[</span>'html_strip'<span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> ik_english<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'custom'<span class="token punctuation">,</span> tokenizer<span class="token operator">:</span> 'ik_max_word'<span class="token punctuation">,</span> filter<span class="token operator">:</span> <span class="token punctuation">[</span>'stemmer'<span class="token punctuation">]</span><span class="token punctuation">,</span> stopwords<span class="token operator">:</span> '_english_' <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> filter<span class="token operator">:</span> <span class="token punctuation">{</span> pinyin_filter<span class="token operator">:</span> <span class="token punctuation">{</span> type<span class="token operator">:</span> 'pinyin'<span class="token punctuation">,</span> keep_first_letter<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> keep_separate_first_letter<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> limit_first_letter_length<span class="token operator">:</span> <span class="token number">16</span><span class="token punctuation">,</span> keep_full_pinyin<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> keep_joined_full_pinyin<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> keep_none_chinese<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> keep_none_chinese_together<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> keep_none_chinese_in_first_letter<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> keep_none_chinese_in_joined_full_pinyin<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> none_chinese_pinyin_tokenize<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> keep_original<span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> lowercase<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> trim_whitespace<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> remove_duplicated_term<span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h5 id="2-2-搜索查询优化"><a href="#2-2-搜索查询优化" class="headerlink" title="2.2 搜索查询优化"></a>2.2 搜索查询优化</h5><p>2.2.1 Tags 聚合优化 </p><pre class="line-numbers language-json" data-language="json"><code class="language-json"><span class="token punctuation">{</span> size<span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span> query<span class="token operator">:</span> <span class="token punctuation">{</span> bool<span class="token operator">:</span> <span class="token punctuation">{</span> filter<span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> prefix<span class="token operator">:</span> <span class="token punctuation">{</span> 'tags.query'<span class="token operator">:</span> <span class="token punctuation">{</span> value<span class="token operator">:</span> input weight<span class="token operator">:</span><span class="token number">10</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> script<span class="token operator">:</span> <span class="token punctuation">{</span> script<span class="token operator">:</span> <span class="token punctuation">{</span> source<span class="token operator">:</span> `return doc.tags.size() != <span class="token number">0</span> && doc.deletedAt.size() == <span class="token number">0</span> && doc.status.value != '$<span class="token punctuation">{</span>StatusEnum.DRAFT<span class="token punctuation">}</span>'` <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> aggs<span class="token operator">:</span> <span class="token punctuation">{</span> genres<span class="token operator">:</span> <span class="token punctuation">{</span> terms<span class="token operator">:</span> <span class="token punctuation">{</span> field<span class="token operator">:</span> 'tags' <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>2.2.2 工单搜索优化<br><a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/scoring-theory.html">Es评分机制</a></p><ul><li><strong>TF</strong>:TF(Term Frequency),即<strong>词频</strong>,表示词条在文本中出现的频率。 </li><li><strong>IDF</strong>:IDF(Inverse Document Frequency),即**逆文档频率。 **</li><li><strong>字段长度归一值:</strong>( norm )是字段中词数平方根的倒数。越短越好 </li><li><strong>向量空间模型</strong>(vector space model): 多词匹配</li></ul><p>自定义评分规则: function_score boost_mode </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">GET /_search<span class="token punctuation">{</span> <span class="token string">"query"</span><span class="token builtin class-name">:</span> <span class="token punctuation">{</span> <span class="token string">"dis_max"</span><span class="token builtin class-name">:</span> <span class="token punctuation">{</span> <span class="token string">"queries"</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token string">"term"</span><span class="token builtin class-name">:</span> <span class="token punctuation">{</span> <span class="token string">"subject"</span><span class="token builtin class-name">:</span> <span class="token string">"Quick pets"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>, <span class="token punctuation">{</span> <span class="token string">"term"</span><span class="token builtin class-name">:</span> <span class="token punctuation">{</span> <span class="token string">"content"</span><span class="token builtin class-name">:</span> <span class="token string">"Quick pets"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span>, <span class="token string">"tie_breaker"</span><span class="token builtin class-name">:</span> <span class="token number">0.2</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>2.2.3 常见的查询分析命令 </p><ol><li><p>查看不同分词器分词 </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> --location --request POST <span class="token string">'http://elastic:Tiger!23@172.28.49.74:9200/_analyze'</span> <span class="token punctuation">\</span>--data-raw <span class="token string">'{ "text": "特朗普", "analyzer": "ik_max_word"}'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><p>查看当前字段分词 </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> --location -g --request GET <span class="token string">'{{es}}/work-ticket_v7/_doc/2021080600001/_termvectors?fields=content.english'</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></li><li><p>查看当前搜索在文档中的评分情况 </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> --location -g --request GET <span class="token string">'{{es}}/work-ticket_v7/_doc/2021080600001/_termvectors?fields=content.english'</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre></li><li><p>Explain 优化搜索结果相关性 </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">GET /_validate/query?explain <span class="token punctuation">{</span> <span class="token string">"query"</span><span class="token builtin class-name">:</span> <span class="token punctuation">{</span> <span class="token string">"match"</span> <span class="token builtin class-name">:</span> <span class="token punctuation">{</span> <span class="token string">"tweet"</span> <span class="token builtin class-name">:</span> <span class="token string">"really powerful"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li><li><p>Profile 定位查询性能问题 </p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> XPOST http://localhost:9200/myindex/mytype/_search -d <span class="token string">'{ "profile": true, "query": { "match": { "brand": "Cotton Plus" } }}'</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h5 id="2-3-关于搜索推荐实现"><a href="#2-3-关于搜索推荐实现" class="headerlink" title="2.3 关于搜索推荐实现"></a>2.3 关于搜索推荐实现</h5></li></ol><ul><li><strong>词条建议器(term suggester)</strong>:对于给定文本的每个词条,该建议器从索引中抽取要建议的关键词,这对于短字段(如分类标签)很有效。 </li><li><strong>词组建议器(phrase suggester)</strong>:我们可以认为它是词条建议器的扩展,为整个文本(而不是单个词条)提供了替代方案,它考虑了各词条彼此临近出现的频率,使得该建议器更适合较长的字段,比如商品的描述。 </li><li><strong>完成建议器(completion suggester</strong>):该建议器根据词条的前缀,提供自动完成的功能(智能提示,有点最左前缀查询的意思),为了实现这种实时的建议功能,它得到了优化,工作在内存中。所以,速度要比之前说的match_phrase_prefix快的多! </li><li><strong>上下文建议器(context suggester)</strong>:它是完成建议器的扩展,允许我们根据词条或分类亦或是地理位置对结果进行过滤。</li></ul><p>其它实现方式: </p><ul><li><p>match_phrase_prefix </p></li><li><p>ngram 实现搜索推荐 </p><h3 id="其它优化调整"><a href="#其它优化调整" class="headerlink" title="其它优化调整"></a>其它优化调整</h3></li><li><p>项目启动自动更新索引、数据 </p></li><li><p>暴露接口维护旧索引、刷新数据 </p></li><li><p>配置动态词库(本地词库、tags、用户表用户名); (7.12以下版本IK分词注意关闭gzip) </p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3></li><li><p>设置合理的mapping字段类型、分词 </p></li><li><p>使用节点查询缓存(filter) </p></li><li><p>使用分片请求缓存(size = 0) </p></li><li><p>通过 msearch 拆解多个聚合为单个子语句 </p></li><li><p>低版本设置 “collect_mode” : “breadth_first”</p></li></ul><p>参考文档:<br><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html">Terms aggregation | Elasticsearch Guide [7.14]</a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html">| Elastic</a><br><a href="https://ningg.top/elastic-series-03-elastic-lucene-data-structure/">Elastic 系列:Lucene 的索引结构和查询效率 | NingG 个人博客</a><br><a href="https://cloud.tencent.com/developer/article/1195274">ElasticSearch Aggregations GroupBy 实现源码分析</a><br><a href="https://juejin.cn/post/6932265453125369869">通过Function Score Query优化Elasticsearch搜索结果</a></p>]]></content>
<tags>
<tag> elasticsearch </tag>
<tag> 后端 </tag>
</tags>
</entry>
<entry>
<title>后端缓存原理及应用</title>
<link href="/2022/03/25/hou-duan-huan-cun-yuan-li-ji-ying-yong/"/>
<url>/2022/03/25/hou-duan-huan-cun-yuan-li-ji-ying-yong/</url>
<content type="html"><![CDATA[<h2 id="1-避免进程内缓存,使用服务缓存"><a href="#1-避免进程内缓存,使用服务缓存" class="headerlink" title="1.避免进程内缓存,使用服务缓存"></a>1.避免进程内缓存,使用服务缓存</h2><p> <br><strong>为什么不能频繁使用进程内缓存?</strong><br><strong>答</strong>:<strong>分层架构设计,有一条准则</strong>:站点层、服务层要做到无数据无状态,这样才能任意的加节点水平扩展,数据和状态尽量存储到后端的数据存储服务,例如数据库服务或者缓存服务。 </p><h2 id="2-避免使用缓存进行通信"><a href="#2-避免使用缓存进行通信" class="headerlink" title="2.避免使用缓存进行通信"></a>2.避免使用缓存进行通信</h2><p> pull 模式延时;<br> 服务化的原则,数据是私有的(本质也是解耦):</p><ul><li>service层会向数据的需求方屏蔽下层存储引擎,分库,chace的复杂</li><li>任何需求方不能绕过service读写其后端的数据</li></ul><h2 id="3-缓存雪崩"><a href="#3-缓存雪崩" class="headerlink" title="3.缓存雪崩"></a>3.缓存雪崩</h2><p>什么时候会产生雪崩?<br>答:如果缓存挂掉,所有的请求会压到数据库,如果未提前做容量预估,可能会把数据库压垮(在缓存恢复之前,数据库可能一直都起不来),导致系统整体不可服务。<br> <br>解决方案:缓存集群,水平切分 </p><h2 id="4-避免调用方缓存"><a href="#4-避免调用方缓存" class="headerlink" title="4.避免调用方缓存"></a>4.避免调用方缓存</h2><p>调用方本地缓存,然后判断是否请求服务获取数据<br> <br>调用方需要关注数据获取的复杂性<br>更严重的,服务修改db里的数据,淘汰了服务cache之后,难以通知调用方淘汰其cache里的数据,从而导致数据不一致 </p><h2 id="5-多服务共用缓存实例"><a href="#5-多服务共用缓存实例" class="headerlink" title="5.多服务共用缓存实例"></a>5.多服务共用缓存实例</h2><p>可能导致key冲突,彼此冲掉对方的数据<br> <br>画外音:可能需要服务A和服务B提前约定好了key,以确保不冲突,常见的约定方式是使用namespace:key的方式来做key。<br>不同服务对应的数据量,吞吐量不一样,共用一个实例容易导致一个服务把另一个服务的热数据挤出去<br>共用一个实例,会导致服务之间的耦合,与微服务架构的“数据库,缓存私有”的设计原则是相悖的</p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 后端 </tag>
<tag> 存储 </tag>
</tags>
</entry>
<entry>
<title>mysql事务与锁</title>
<link href="/2022/03/24/mysql-shi-wu-yu-suo/"/>
<url>/2022/03/24/mysql-shi-wu-yu-suo/</url>
<content type="html"><![CDATA[<h3 id="事务特性"><a href="#事务特性" class="headerlink" title="事务特性"></a>事务特性</h3><ul><li>原子性</li><li>隔离性</li><li>持久性</li><li>一致性</li></ul><h3 id="并发事务存在问题"><a href="#并发事务存在问题" class="headerlink" title="并发事务存在问题"></a><strong>并发</strong>事务存在问题</h3><ul><li><strong>脏读(Dirty read):</strong> 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。</li><li><strong>丢失修改(Lost to modify):</strong> 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务 1 读取某表中的数据 A=20,事务 2 也读取 A=20,事务 1 修改 A=A-1,事务 2 也修改 A=A-1,最终结果 A=19,事务 1 的修改被丢失。</li><li><strong>不可重复读(Unrepeatableread):</strong> 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。</li><li><strong>幻读(Phantom read):</strong> 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。</li></ul><h3 id="事务隔离级别"><a href="#事务隔离级别" class="headerlink" title="事务隔离级别"></a>事务隔离级别</h3><ul><li><strong>READ-UNCOMMITTED(读取未提交):</strong> 最低的隔离级别,允许读取尚未提交的数据变更,<strong>可能会导致脏读、幻读或不可重复读</strong>。</li><li><strong>READ-COMMITTED(读取已提交):</strong> 允许读取并发事务已经提交的数据,<strong>可以阻止脏读,但是幻读或不可重复读仍有可能发生</strong>。</li><li><strong>REPEATABLE-READ(可重复读):</strong> 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,<strong>可以阻止脏读和不可重复读,但幻读仍有可能发生</strong>。</li></ul><p><strong>SERIALIZABLE(可串行化):</strong> 最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,<strong>该级别可以防止脏读、不可重复读以及幻读</strong>。</p><p>InnoDB 存储引擎的默认支持的隔离级别是 <strong>REPEATABLE-READ(可重读)</strong>,但是可以通过应用加锁读(例如 <code>select * from table for update</code> 语句)来保证不会产生幻读,而这个加锁度使用到的机制就是 Next-Key Lock 锁算法。从而达到了 SQL 标准的 <strong>SERIALIZABLE(可串行化)</strong> 隔离级别。</p><h2 id="锁的分类"><a href="#锁的分类" class="headerlink" title="锁的分类"></a>锁的分类</h2><h3 id="读写分类"><a href="#读写分类" class="headerlink" title="读写分类"></a>读写分类</h3><ul><li>LOCK_IS:读意向锁;</li><li>LOCK_IX:写意向锁;</li><li>LOCK_S:读锁;又称读锁,若事务 T 对数据对象 A 加上 S 锁,则事务 T 可以读 A 但不能修改 A,其他事务只能再对 A 加 S 锁,而不能加 X 锁,直到 T 释放 A 上的 S 锁。</li><li>LOCK_X:写锁;又称写锁。若事务 T 对数据对象 A 加上 X 锁,事务 T 可以读 A 也可以修改 A,其他事务不能再对 A 加任何锁,直到 T 释放 A 上的锁</li><li>LOCK_AUTO_INC:自增锁(AUTO_INC 表锁 0 )</li></ul><p>innodb_autoinc_lock_mode = 1 (consecutive lock mode) 自增锁分类设置<br>兼容性:</p><ul><li>意向锁之间互不冲突;</li><li>S 锁只和 S/IS 锁兼容,和其他锁都冲突;</li><li>X 锁和其他所有锁都冲突;</li><li>AI 锁只和意向锁兼容;</li></ul><h3 id="锁粒度"><a href="#锁粒度" class="headerlink" title="锁粒度"></a>锁粒度</h3><ul><li>表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。(DDL,无索引更新,事务级别为 RR,<code>Serializable)</code></li><li>行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。</li><li>Gap 锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。<blockquote><p><strong>InnoDB 行锁是通过给索引上的索引项加锁来实现的,这一点 MySQL 与 Oracle 不同,后者是通过在数据块中对相应数据行加锁来实现的。 InnoDB 这种行锁实现特点意味着:只有通过**<strong>索引*</strong>*条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用</strong>Next-Key Locks<strong>!(</strong><code>RU</code>和<code>RC</code>,无论条件列上是否有索引,都不会锁表,只锁行!**)**</p></blockquote></li></ul><p>应用:<br>select * from table where pId = 2 for update;<br>如果 pid 没有索引,会全表加行锁,然后扫码释放。 如果有一级索引,直接在该一级索引行加锁;如果是二级索引,在该二级索引加锁,查找该行一级索引,加锁</p><h3 id="悲观锁和乐观锁"><a href="#悲观锁和乐观锁" class="headerlink" title="悲观锁和乐观锁"></a>悲观锁和乐观锁</h3><p>悲观锁和乐观锁是抽象应用,不真实存在这个锁</p><ul><li>悲观锁</li></ul><p>正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据<strong>访问的排他性</strong>,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。<br>在悲观锁的情况下,为了保证事务的隔离性,就需要一致性锁定读。读取数据时给加锁,其它事务无法修改这些数据。修改删除数据时也要加锁,其它<strong>事务无法读取这些数据</strong>。</p><ul><li>乐观锁</li></ul><p>相对悲观锁而言,乐观锁机制采取了更加宽松的加锁机制。悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。<br>乐观锁,大多是基于<strong>数据版本</strong>( Version )记录机制实现。何谓数据版本?即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个 “version” 字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。</p><h3 id="MVCC-在-MySQL-的-InnoDB-中的实现-乐观锁"><a href="#MVCC-在-MySQL-的-InnoDB-中的实现-乐观锁" class="headerlink" title="MVCC 在 MySQL 的 InnoDB 中的实现(乐观锁)"></a>MVCC 在 MySQL 的 InnoDB 中的实现(乐观锁)</h3><p>在 InnoDB 中,会在每行数据后添加两个额外的隐藏的值来实现 MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。 在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。 在可重读 Repeatable reads 事务隔离级别下:</p><ul><li>SELECT 时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。</li><li>INSERT 时,保存当前事务版本号为行的创建版本号</li><li>DELETE 时,保存当前事务版本号为行的删除版本号</li><li>UPDATE 时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行</li></ul><p>通过 MVCC,虽然每行记录都需要额外的存储空间,更多的行检查工作以及一些额外的维护工作,但可以减少锁的使用,大多数读操作都不用加锁,读数据操作很简单,性能很好,并且也能保证只会读取到符合标准的行,也只锁住必要行。</p><h3 id="“读”与“读”的区别"><a href="#“读”与“读”的区别" class="headerlink" title="“读”与“读”的区别"></a>“读”与“读”的区别</h3><ul><li>快照读:就是 select<ul><li>select * from table ….;</li></ul></li><li>当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁。<ul><li>select * from table where ? lock in share mode; (<strong>当前读,s 锁</strong>)</li><li>select * from table where ? for update; (<strong>当前读,x 锁</strong>)</li><li>insert;</li><li>update ;</li><li>delete;</li></ul></li></ul><h3 id="Next-Key-锁"><a href="#Next-Key-锁" class="headerlink" title="Next-Key 锁"></a>Next-Key 锁</h3><p>Next-Key 锁是行锁和 GAP(间隙锁)的合并,行锁上文已经介绍了,接下来说下 GAP 间隙锁。<br>GAP 锁是避免别的事务插入数据造成问题。(隔离级别为<code>Repeatable Read</code>和<code>Serializable</code>时,就会存在间隙锁)<br>事务 A<br>update class_teacher set class_name=’初一一班’ where teacher_id=20; // 锁住 teacher_id 开始到结束区间段;<br>事务 B<br>insert into class_teacher values (null,’初三五班’,10); // 需要等待<br>事务 C<br>insert into class_teacher values (null,’初三五班’,40);</p><h3 id="意向锁"><a href="#意向锁" class="headerlink" title="意向锁"></a>意向锁</h3><ul><li><strong>意向共享锁</strong>(IS 锁):一个事务在获取(任何一行/或者全表)S 锁之前,一定会先在所在的表上加 IS 锁。</li><li><strong>意向排他锁</strong>(IX 锁):一个事务在获取(任何一行/或者全表)X 锁之前,一定会先在所在的表上加 IX 锁。</li></ul><h4 id="意向锁作用"><a href="#意向锁作用" class="headerlink" title="意向锁作用"></a>意向锁作用</h4><p>假设事务 T1,用 X 锁来锁住了表上的几条记录,那么此时表上存在 IX 锁,即意向排他锁。那么此时事务 T2 要进行<code>LOCK TABLE … WRITE</code>的表级别锁的请求,可以直接根据意向锁是否存在而判断是否有锁冲突。</p><p>锁优化:<br>在查询字段尽量加索引,索引值尽量差异读较大。<br><strong>mysql 默认是 RR 隔离级别,使用 next-key 锁;锁加在聚簇索引上</strong><br><strong>RR 解决幻读原理:</strong><br>查询:快照读,使用 mvcc 快照,基于 undolog, 事务版本号<br>更新:当前读,使用 next-key 锁;<br>避免升级表锁:</p><ul><li>alter table;</li><li>更新表格加 where 条件,避免 查询条件类型转化;</li><li>控制事务大小,减少锁定的资源量和锁定时间长度。</li><li>所有的数据检索都通过索引来完成,从而避免因为无法通过索引加锁而升级为表锁。</li><li>减少基于范围的数据检索过滤条件,避免因为间隙锁带来的负面影响而锁定了不该锁定的数据。</li></ul><p>参考文档:<br><a href="https://tech.meituan.com/2014/08/20/innodb-lock.html">https://tech.meituan.com/2014/08/20/innodb-lock.html</a><br><a href="https://www.cnblogs.com/rjzheng/p/9950951.html">https://www.cnblogs.com/rjzheng/p/9950951.html</a><br><a href="https://zhuanlan.zhihu.com/p/67793185">https://zhuanlan.zhihu.com/p/67793185</a></p>]]></content>
<categories>
<category> 技术 </category>
</categories>
<tags>
<tag> 后端 </tag>
<tag> mysql </tag>
</tags>
</entry>
<entry>
<title>数据结构入门</title>
<link href="/2022/03/23/shu-ju-jie-gou-ru-men/"/>
<url>/2022/03/23/shu-ju-jie-gou-ru-men/</url>
<content type="html"><![CDATA[<h2 id="1-逻辑结构和物理结构"><a href="#1-逻辑结构和物理结构" class="headerlink" title="1.逻辑结构和物理结构"></a>1.逻辑结构和物理结构</h2><h3 id="1-1逻辑结构:数据对象中数据元素之间的关系"><a href="#1-1逻辑结构:数据对象中数据元素之间的关系" class="headerlink" title="1.1逻辑结构:数据对象中数据元素之间的关系"></a>1.1逻辑结构:数据对象中数据元素之间的关系</h3><ul><li><p>集合结构:数据元素除了同属于一个集合之外,她们没有任何关系</p></li><li><p>线性结构:数据元素之间一对一关系</p></li><li><p>树形结构:数据元素之间存在一对一或一对多的层级关系</p></li><li><p>图形结构:数据元素是多对多的关系</p><h3 id="1-2物理结构-存储结构-:数据的逻辑结构在计算机中的存储形式"><a href="#1-2物理结构-存储结构-:数据的逻辑结构在计算机中的存储形式" class="headerlink" title="1.2物理结构(存储结构):数据的逻辑结构在计算机中的存储形式"></a>1.2物理结构(存储结构):数据的逻辑结构在计算机中的存储形式</h3><p>存储结构分四类:顺序存储、链接存储、索引存储 和 散列存储。<br>顺序结构和链接结构适用在内存结构中。<br>索引结构和散列结构适用在外存与内存交互结构。</p></li><li><p>顺序结构:在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构。</p></li><li><p>链式结构:把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的</p></li><li><p><strong>索引存储</strong>:除建立存储结点信息外,还建立附加的<strong>索引表</strong>来标识结点的地址。索引表由若干索引项组成。</p></li><li><p><strong>散列存储</strong>:散列存储,又称hash存储,是一种力图将数据元素的存储位置与关键码之间建立确定对应关系的查找技术。(本质: 一个数组和多个单链表的组合而成的复合数据结构)</p></li></ul><p><strong>哈希表(散列存储)<strong>:用散列法存储的线性表被称为哈希表,使用的函数被称为散列函数或者哈希函数,f(k)被称为散列地址或者哈希地址。通常情况下,散列表的存储空间是一个</strong>一维数组</strong>,而其哈希地址为数组的下标</p><h2 id="2-知识补充"><a href="#2-知识补充" class="headerlink" title="2. 知识补充"></a>2. 知识补充</h2><h4 id="2-1指针,地址,指针变量"><a href="#2-1指针,地址,指针变量" class="headerlink" title="2.1指针,地址,指针变量"></a>2.1指针,地址,指针变量</h4><p>指针: 指针就是内存地址,是变量的地址 (*p)<br>地址:地址也作为一种值,能被存储、比较、赋值,并称地址数据为指针类型 (&a)<br>指针变量:指针变量是存放地址的变量 a;</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token keyword">int</span> a<span class="token operator">=</span><span class="token number">3</span><span class="token punctuation">;</span><span class="token keyword">int</span> <span class="token operator">*</span>p<span class="token punctuation">;</span>p<span class="token operator">=</span><span class="token operator">&</span>a<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h4 id="2-2结构体"><a href="#2-2结构体" class="headerlink" title="2.2结构体"></a>2.2结构体</h4><p>结构体类型:包含不同类型的成员;<br>结构体指针,就是指向结构体变量的指针</p><h4 id="2-3链表"><a href="#2-3链表" class="headerlink" title="2.3链表"></a>2.3链表</h4><p>链表是一种数据结构,必须利用指针变量来实现;数据结构包括(number结构类型,Object类型,Array类型等等);链表是根据需要开辟内存单元,链表有一个头指针,存放一个地址,该地址指向一个元素(每个链表都有一个头指针,必不可少);链表中的每一个元素称为节点;节点包含两个部分,用户需要的实际数据,下一个节点的地址(next);链表中的地址是不连续的;要找某一元素,必须先找到上一个元素,根据他提供的下一个元素地址才能找到下一个元素<br>备注: 形参不占内存中的存储单元;如果函数不需要返回值则不需要return语句</p><h3 id="2-4-内存中的-堆与栈"><a href="#2-4-内存中的-堆与栈" class="headerlink" title="2.4 内存中的 堆与栈"></a>2.4 内存中的 堆与栈</h3><h4 id="2-4-1用户空间程序三个阶段"><a href="#2-4-1用户空间程序三个阶段" class="headerlink" title="2.4.1用户空间程序三个阶段"></a>2.4.1用户空间程序三个阶段</h4><ul><li><strong>.data</strong>段:包含了已经初始化了的数据项,这些数据在程序开始运行前就拥有自己的值,这些值是可执行文件的一部分,当可执行文件被加载到内存中用于执行时,这些数据也被加载到内存中。</li><li>.<strong>bss</strong>段:并不是所有数据项在程序开始之前都拥有值,例如你可以定义一个缓冲区来存在某些数据,这个缓冲区是.bss段中定义的。</li><li><strong>.text</strong>段:以上两个段都是源程序所需要的数据,而真正组成程序的机器指令则存放在.text段中。一般情况下,在.text段中不进行数据项的定义。.text段包含名为标号(label)的符号,这些符号用于标识跳转和调用程序代码位置。</li></ul><p>汇编中常说的<strong>堆栈</strong>,其实是栈,并不包含堆。</p><ul><li><strong>栈(操作系统)</strong>:由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈,栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放</li><li><strong>堆(操作系统)</strong>: 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些</li><li><strong>堆(数据结构)</strong>:堆可以被看成是一棵树,如:堆排序</li><li><strong>栈(数据结构)</strong>:一种后进先出的数据结构</li></ul><p><img src="https://cdn.nlark.com/yuque/0/2019/png/299905/1554040251860-cf6615e9-9b4a-4110-b3c0-7693e0a60882.png#align=left&display=inline&height=723&margin=%5Bobject%20Object%5D&name=image.png&originHeight=740&originWidth=492&size=54020&status=done&style=none&width=481" alt="image.png"></p><h2 id="3-线性表"><a href="#3-线性表" class="headerlink" title="3.线性表"></a>3.线性表</h2><p>线性表是零个或多个具有相同特性的<strong>数据元素</strong>组成的有限序列(特点:有序,有限,没有前驱元素只有后继元素),每个元素为<strong>单元素</strong><br>3.1 存储结构</p><ul><li>顺序存储: 优点:不需要额外引用地址空间 缺点:增删移动大量元素</li><li>链式存储: 节点包含数据域、指针域, 优缺点相反</li></ul><p>3.2 单链表<br>一个节点只包含数据域和<strong>一个</strong>指针域<br>3.3 静态链表<br>有人想出了用数组来代替指针,来描述单链表,让每个数组的元素都由两个数据域组成,数组的每个下标都对应两个数据域,一个用来存放数据元素,一个用来存放next指针<br>3.4 循环链表<br>将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表。<br>3.5 双向链表<br>在单链表的基础上,再在每个结点中设置一个指向其前驱结点的指针域,这样一个结点既可以指向它的前面又可以指向它的下一个,我们把这种链表称为双向链表。</p><h2 id="4-栈和队列"><a href="#4-栈和队列" class="headerlink" title="4. 栈和队列"></a>4. 栈和队列</h2><h3 id="4-1-栈"><a href="#4-1-栈" class="headerlink" title="4.1 栈"></a>4.1 栈</h3><p>栈是限定<strong>仅在表尾进行插入和删除操作</strong>的线性表</p><h4 id="4-1-1栈的存储结构"><a href="#4-1-1栈的存储结构" class="headerlink" title="4.1.1栈的存储结构"></a>4.1.1栈的存储结构</h4><p>栈是线性表的特例,所以栈的顺序存储结构其实就是线性表顺序存储结构的简称,我们简称为顺序栈。把<strong>栈顶放在单链表的头部</strong>,用链表来存储栈的的数据结构称为链栈。</p><h3 id="4-2-队列"><a href="#4-2-队列" class="headerlink" title="4.2 队列"></a>4.2 队列</h3><p>队列是只允许<strong>在一端进行插入操作,而在另一端进行删除操作</strong>的线性表。</p><p>4.2.1 循环队列<br>顺序表存储,顺序表成本高(移动指针),引入循环队列。为了避免假溢出,让队列的头尾进行相连,这种头尾相连的顺序存储结构称为循环队列。</p><pre class="line-numbers language-c" data-language="c"><code class="language-c"><span class="token comment">// 存储结构:</span><span class="token keyword">typedef</span> <span class="token keyword">struct</span><span class="token punctuation">{</span> <span class="token keyword">int</span> data<span class="token punctuation">[</span>maxsize<span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">//定义数组 </span> <span class="token keyword">int</span> front<span class="token punctuation">;</span> <span class="token comment">//队首指针 </span> <span class="token keyword">int</span> rear<span class="token punctuation">;</span> <span class="token comment">//队尾指针</span><span class="token punctuation">}</span>SqQuene<span class="token punctuation">;</span> <span class="token comment">//顺序队列定义</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>4.2.2 链列<br>链队就是采用链式存储结构存储队列。</p><pre class="line-numbers language-none"><code class="language-none">// 链队类型定义:typedef struct{ QNode *front; //队头指针 QNode *rear; //队尾指针}LiQuene; //链队类型定义// 节点定义typedef struct QNode{ int data; //数据域 struct QNode *next; //指针域}QNode; //队结点类型定义<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="5-串"><a href="#5-串" class="headerlink" title="5. 串"></a>5. 串</h2><p>由零个或多个字符串组成的有限序列,又叫字符串。(串中的元素都是字符),通常使用顺序存储。</p><h3 id="5-1-串的存储结构"><a href="#5-1-串的存储结构" class="headerlink" title="5.1 串的存储结构"></a>5.1 串的存储结构</h3><ul><li><strong>串的顺序存储结构</strong>:串值的存储空间可在程序执行过程中动态分配。在计算机中有个“堆”的自由存储区,可由C语言的动态分配函数malloc( )和free( )来管理</li><li><strong>串的链式存储结构</strong>:在考虑到空间浪费的问题上,所以一个结点可以存放一个字符,也可存放多个字符,最后一个结点若是未被占满时,用“#”或其他非串值字符补全。</li></ul><p>串和线性表的区别:线性表更关注的是单个元素的操作,比如查找一个元素,插入或删除一个元素,但串中更多的是查找子串位置,得到指定位置子串,替换子串等操作。<br>串的<strong>模式匹配算法</strong>:著名的模式匹配算法有两种:BF和KMP算法</p><h2 id="6-数组和广义表"><a href="#6-数组和广义表" class="headerlink" title="6. 数组和广义表"></a>6. 数组和广义表</h2><h3 id="6-1数组"><a href="#6-1数组" class="headerlink" title="6.1数组"></a>6.1数组</h3><h4 id="6-1-1-定义"><a href="#6-1-1-定义" class="headerlink" title="6.1.1 定义"></a>6.1.1 定义</h4><p>由类型相同的数据元素构成的有序集合。数组一般不做插入和删除操作,所以一般采用顺序存储结构。</p><ul><li><p>一维数组可以看成线性表</p></li><li><p>二维数组是数据元素为线性表的线性表;</p><h3 id="6-2-广义表"><a href="#6-2-广义表" class="headerlink" title="6.2 广义表"></a>6.2 广义表</h3><h4 id="6-2-1-定义"><a href="#6-2-1-定义" class="headerlink" title="6.2.1 定义"></a>6.2.1 定义</h4><p>广义表是线性表的推广,是一种多层次的数据结构,广义表的元素可以是单元素也可以是子表,而子表的元素还可以是子表。</p><h4 id="6-2广义表存储结构"><a href="#6-2广义表存储结构" class="headerlink" title="6.2广义表存储结构"></a>6.2广义表存储结构</h4><p>通常采用链式存储结构,两种结构的节点:一种是表节点,用以表示列表,一种是元素节点,用以表示单元素.<br>表节点:可由三个域组成:标志域,指示表头的指针域和指示表尾的指针域。<br>元素节点只需要两个域:标志域和值域</p><h2 id="7-树和二叉树"><a href="#7-树和二叉树" class="headerlink" title="7. 树和二叉树"></a>7. 树和二叉树</h2><p>是n(n>=0)个结点的有限集。线性表是一对一的结构,而树则是一对多的结构。</p><h3 id="7-1-树的相关概念"><a href="#7-1-树的相关概念" class="headerlink" title="7.1 树的相关概念"></a>7.1 树的相关概念</h3></li><li><p>高度: 节点到叶子节点最长边的总和</p></li><li><p>深度: 该节点到根节点最长边的总和</p></li><li><p>层级(level): 节点的Level是从1开始的,Level = Depth+1,根节点的Level=1</p><h3 id="7-2-二叉树"><a href="#7-2-二叉树" class="headerlink" title="7.2 二叉树"></a>7.2 二叉树</h3><h4 id="7-2-1-二叉树类型"><a href="#7-2-1-二叉树类型" class="headerlink" title="7.2.1 二叉树类型"></a>7.2.1 二叉树类型</h4><p>二叉树是一个每个最结最多只能有两个分支的树</p></li><li><p>完美/满二叉树:所有的子节点都包含两个子节点</p></li><li><p>完全二叉树: 除了最后一层都是满的(都有两个子节点),并且最后一层的节点是从左往右排列的。</p></li><li><p>完满二叉树:就是每个节点都有两个子节点。</p><h4 id="7-2-2-二叉树的遍历"><a href="#7-2-2-二叉树的遍历" class="headerlink" title="7.2.2 二叉树的遍历"></a>7.2.2 二叉树的遍历</h4></li><li><p>前序遍历:根结点 —> 左子树 —> 右子树</p></li><li><p>中序遍历:左子树 —> 根结点 —> 右子树</p></li><li><p>后序遍历:左子树 —> 右子树 —> 根结点</p></li><li><p>层次遍历:只需按层次遍历即可</p></li></ul><p>深度优先遍历<br>广度优先遍历:类似层次遍历<br>先序,后序,中序针对二叉树。深度、广度针对普通树。</p><h4 id="7-2-3-平衡二叉树"><a href="#7-2-3-平衡二叉树" class="headerlink" title="7.2.3 平衡二叉树"></a>7.2.3 平衡二叉树</h4><p>平衡二叉树是基于二分法的策略提高数据的查找速度的二叉树的数据结构;</p><h4 id="7-2-4-B-树和B-树"><a href="#7-2-4-B-树和B-树" class="headerlink" title="7.2.4 B_树和B+树"></a>7.2.4 B_树和B+树</h4><p>B树和平衡二叉树稍有不同的是B树属于多叉树又名平衡多路查找树<br>B+跟B树不同B+树的非叶子节点不保存关键字记录的指针,只进行数据索引,B+树叶子节点保存了父节点的所有关键字记录的指针,所有数据地址必须要到叶子节点才能获取到。所以每次数据查询的次数都一样;</p><h4 id=""><a href="#" class="headerlink" title=""></a></h4><h2 id="8-图"><a href="#8-图" class="headerlink" title="8. 图"></a>8. 图</h2><p>图是一种复杂的非线性结构。</p><h3 id="8-1术语"><a href="#8-1术语" class="headerlink" title="8.1术语"></a>8.1术语</h3><ul><li>顶点:图中的一个点</li><li>边:连接两个顶点的线段叫做边,edge</li><li>相邻的:一个边的两头的顶点称为是相邻的顶点</li><li>度数:由一个顶点出发,有几条边就称该顶点有几度,或者该顶点的度数是几,degree</li><li>路径:通过边来连接,按顺序的从一个顶点到另一个顶点中间经过的顶点集合</li><li>简单路径:没有重复顶点的路径</li><li>环:至少含有一条边,并且起点和终点都是同一个顶点的路径</li><li>简单环:不含有重复顶点和边的环</li><li>连通的:当从一个顶点出发可以通过至少一条边到达另一个顶点,我们就说这两个顶点是连通的</li><li>连通图:如果一个图中,从任意顶点均存在一条边可以到达另一个任意顶点,我们就说这个图是个连通图</li><li>无环图:是一种不包含环的图</li><li>稀疏图:图中每个顶点的度数都不是很高,看起来很稀疏</li><li>稠密图:图中的每个顶点的度数都很高,看起来很稠密</li><li>二分图:可以将图中所有顶点分为两部分的图</li></ul><p>相关知识点:<br>邻接表:表示了图中与每一个顶点相邻的边集的集合<br>背包:只进不出,内容无序。<br>有向无环图:如果一个有向图从任意顶点出发无法经过若干条边回到该点,则这个图是一个有向无环图(DAG图)</p><h2 id="9-哈希表"><a href="#9-哈希表" class="headerlink" title="9. 哈希表"></a>9. 哈希表</h2><h3 id="9-1-哈希函数"><a href="#9-1-哈希函数" class="headerlink" title="9.1 哈希函数"></a>9.1 哈希函数</h3><p>如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。</p><h3 id="9-2-哈希表定义"><a href="#9-2-哈希表定义" class="headerlink" title="9.2 哈希表定义"></a>9.2 哈希表定义</h3><p>哈希表就是一种以 键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其对应的值。<br>优缺点: 快速查找,方便增删。缺点: 不支持排序</p><h2 id="10-JavaScript中的数据结构"><a href="#10-JavaScript中的数据结构" class="headerlink" title="10. JavaScript中的数据结构"></a>10. JavaScript中的数据结构</h2><h3 id="10-1-JavaScript-数据类型"><a href="#10-1-JavaScript-数据类型" class="headerlink" title="10.1 JavaScript 数据类型"></a>10.1 JavaScript 数据类型</h3><table><thead><tr><th align="left"><strong>栈内存</strong></th><th align="left"><strong>堆内存</strong></th></tr></thead><tbody><tr><td align="left">存储基础数据类型</td><td align="left">存储引用数据类型</td></tr><tr><td align="left">按值访问</td><td align="left">按引用访问</td></tr><tr><td align="left">存储的值大小固定</td><td align="left">存储的值大小不定,可动态调整</td></tr><tr><td align="left">由系统自动分配内存空间</td><td align="left">由程序员通过代码进行分配</td></tr><tr><td align="left">主要用来执行程序</td><td align="left">主要用来存放对象</td></tr><tr><td align="left">空间小,运行效率高</td><td align="left">空间大,但是运行效率相对较低</td></tr><tr><td align="left">先进后出,后进先出</td><td align="left">无序存储,可根据引用直接获取</td></tr></tbody></table><p>V8中,Array 默认使用 顺序结构,数据到达一定量使用hash表结构;<br>参考文档:<br><a href="https://www.jianshu.com/p/52b5a1879aa1">内存中的堆和栈到底是什么</a><br><a href="https://blog.csdn.net/oohaha_123/article/details/27371859">四种数据存储结构—顺序存储 链接存储 索引存储 散列存储</a><br><a href="https://juejin.im/post/59fda59a51882546d71eb672">数据结构学习入门</a><br><a href="https://www.cnblogs.com/Evsward/p/dag.html">算法精解:DAG有向无环图</a><br><a href="http://www.cnblogs.com/Evsward/p/bag.html">基础大扫荡——背包,栈,队列,链表一口气全弄懂</a></p>]]></content>
<tags>
<tag> date_structure </tag>
</tags>
</entry>
<entry>
<title>这是一篇中文文章</title>
<link href="/2022/03/23/zhe-shi-yi-pian-zhong-wen-wen-zhang/"/>
<url>/2022/03/23/zhe-shi-yi-pian-zhong-wen-wen-zhang/</url>
<content type="html"><![CDATA[<p>今天我要给大家介绍一下今年做的一个项目:sablejs。项目的部分代码已经上传到了 Github,但 sablejs 1.x 版本的核心代码部分并没有对外公开,具体原因大家可以参考 Github Issues 的 sablejs 2.0 计划。大约明年 2.0 应该会产出稳定版本,到时候会完整放出对应的全部项目代码,但是目前 sablejs 1.x 版本是可用的。</p><p>这是我第二次作为讲师参加 GMTC,之前也是在 GMTC 深圳站有聊一些 WebAssembly 的内容,今天的议题其实和之前的议题是有一些关联性的,算是之前议题的进一步落地和思考。当然内容就和 WebAssembly 的关系不是很大了。在业余时间我有做过一些技术专栏文章的编写,同时也参与一些开源项目,大家如果有兴趣的话可以去看一看,然后帮忙点个 Star。</p><p>今天的分享我会先讲述一下项目的起因,其次是整个项目我们想达到的目标,接着就是我们在 sablejs 里应用的性能优化的思路和方案,这一块我觉得应该是对大家直接收益最高的部分,最后就是简要阐述一下 sablejs 2.0 的后期规划和开源计划。</p><p>为什么要做 sablejs?</p><p>我们先从项目背景说起。sablejs 的产生实际上是因为友验人机识别验证码这个产品,人机识别验证码这个产品大家应该都或多或少有相关的了解,如果我们简单的来理解的话,你完全可以把它理解为滑动验证码。目前非常多公司都有提供类似的产品和服务方案,包括且不限于极验、腾讯的防水墙、阿里人机验证、网易的易盾等。</p><p>在友验人机识别验证码这个产品里有三块核心的功能,分别是虚拟机保护,设备的特征识别以及 AI 的行为判断。对于设备的特征识别和 AI 的行为判断这两个功能而言,开源社区里有非常成熟的解决方案,例如 Tensorflow。因此使用类似的开源方案然后辅以数据,在我们的实践和落地当中是能够获取到比较精准的识别成功率的。</p><p>但对于虚拟机保护而言,特别是针对于 Web 的虚拟机保护,不管是开源还是商用方案都非常的不尽人意。说到这儿可能有人会疑惑,这个产品为什么还需要虚拟机保护这么复杂的东西?让我们看一看产品的执行逻辑,就应该能清晰的知道原因了。</p><p>从上图我们可以看到验证的整个流程包含了:获取初始化参数、收集客户端特征信息、获取验证挑战、提交特征信息及用户答案、后端特征识别和答案比对这五个关键步骤。其中最核心的,同时也是最脆弱的部分就是客户端的特征收集。之所以要收集客户端的特征信息,是因为我们要以此来产出一个唯一的设备指纹来定位到唯一用户,以便帮助我们的模型来确认当前用户是否存在行为的异常,及时对恶意请求进行阻断,减少企业的被攻击风险。</p><p>但我们也知道 Web 自身是非常开放和包容的,有非常多帮助开发者开发调试的工具,比如 Chrome 的开发者工具。但这也带来了许多问题,其中之一就是,不管我们对代码进行多高强度的混淆,借助开发者工具的帮助我们都可以非常容易的对关键代码进行调试。因此如果想隐藏客户端特征信息的收集逻辑,即使进行了非常复杂的源代码混淆,我们仍然可以较为容易的进行逻辑的调试并且复原原有的代码逻辑。大家有兴趣的话可以去百度或 Google 一下现有人机识别相关产品的破解文章,应该可以看到非常非常多关于此类问题的探讨。</p><p>为了解决这个问题,我们就势必需要自行实现虚拟机保护,依靠这种方式大大增加反编译的难度,以此有效达到防恶意调试的目的。</p><p>怎么去实现虚拟机保护?</p><p>对于 Web 端而言的话,要实现虚拟机保护最简单的方案实际上是通过 WebAssembly。由于 WebAssembly 它本身是完全独立于 JavaScript 引擎的,同时 WebAssembly 完全可以编译执行代码为二进制内容,因此在反调试上是有非常大的优势的。当然,考虑到大部分 Web 的同学更熟悉编写 JavaScript,我们便可以将 quickjs 和 WebAssembly 做一个结合,这样就能够得到一个非常完美的虚拟机保护方案了。</p><p>这个其实是我在之前 GMTC 上聊到过的思路,当然这个思路最后催生出了 SecurityWorker 并开源了出来。对于其他厂商而言,比如 Figma,他们也利用了同样的思路并将其用于用户 Web 插件的执行。尽管这两者用途不一样,但殊途同归,彼此的目的都是差不多的。</p><p>互调过程中并不安全</p><p>在整个实现落地的过程中,我们发现实际上 WebAssembly 这套方案也存在着一些问题,最明显的就是 WebAssembly 和 JavaScript 互相调用的安全性问题。究其原因在于 WebAssembly 和 JavaScript 是两套独立的执行环境,因此如果 WebAssembly 涉及到 DOM、BOM 的调用的时候,它务必需要跟 JavaScript 去做一些通信。那 WebAssembly 它是怎么去做通信的呢?答案是 WebAssembly 会直接使用 eval 来执行对应的 JavaScript 代码字符串。</p><p>eval 的使用是存在非常大的安全隐患的,因为我们完全有能力在 WebAssembly 执行前对 eval 进行复写拦截,从而获取到里面执行的 JavaScript 代码字符串。那我们怎么去解决这个问题呢?最简单的思路实际上就是我们让所有的相关执行都在同一个执行上下文环境中,这样就不存在通信的过程,也就不会存在此类的问题了。</p><p>根据这个思路,因为我们是需要访问 DOM、BOM 的 API 的,同时我们还需要限定在一个执行上下文环境中,那么我们可不可以尝试使用 JavaScript 编写一个 JavaScript 的解释器?参考了 Google Recaptcha 的相关文章后,我们认为这种思路是完全行得通的,同时开源社区也给出了非常多类似的实现,包括像 eval5、sval、sandboxjs……当然,他们也存在非常多的问题需要改进。</p><p>性能太差、VM 初始化时间过长</p><p>如果这么做,第一个我们就会面对的问题便是:解释器的性能。由于 JavaScript 自身是非常灵活,即使我们拥有了 V8 这样的底层执行引擎,从直觉上要基于 JavaScript 写出一个性能尚可的 JavaScript 解释器也会是个不小的挑战,这从各类开源实现的 benchmark 分数也可以得知。其次,目前所有能找到的相关开源实现都比较简陋,主要目的是供学习参考,自身并没有对 test262 的单测进行覆盖率的测试,用在实际生产中是存在比较大的风险的。</p><p>与此同时,我们也调研了一些商业公司的相关类似实现,其中包括腾讯以及字节。从调研结果上来看,他们的实现对于这块性能的优化也还是有很长的路需要走。除此之外,其 VM 实现的初始化耗时过长也是在实际生产中比较致命的一点。</p>]]></content>
</entry>
<entry>
<title>stock holding</title>
<link href="/2022/03/18/stock-holding/"/>
<url>/2022/03/18/stock-holding/</url>
<content type="html"><![CDATA[<p>What if the stock rebounds after I buy put?<br>Today, a friend asked me how to deal with the loss of positions. Is there any way to prevent the next possible drop.</p><p>I said yes, at the end of yesterday’s article, very simple operation. After reading it, he pondered for a moment and said something that surprised me: ‘What if the market rebounds after buying PUT?</p><p>There is really no denying that this happens. A bull market is throwing money away by buying put protection. But if the current bear market, as far as the eye can see, rebounds tomorrow, can share prices really go all the way up? Isn’t it more likely to rebound and then fall or sideways?</p><p>Consider strategy this year first to reverse their bullish thinking, overcome greed, correctly weigh the pros and cons.</p><p>Of course, I also understand that a lot of people have lost a lot of shares. Buying a PUT is like buying insurance. It’s an extra cost and you don’t want to lose more.</p><p>So I recommend the Collar Strategy. That is, if you own a stock, you use the proceeds from selling call to cover the cost of buying put.</p><p>For example, holding a losing stock is like a dilapidated house, and real insurance companies are not willing to insure you. But the stock market is very generous, not only smooth insurance, but also “rent” the “dilapidated” house. The premium is offset by rental fees, so the Collar Strategy is more cost-effective than simply buying a PUT, but there are a few details to be paid for.</p><p>Options Trading<br>the Collar Strategy mainly consists of three legs. The first leg is the holding stock, the second leg is the out-of-price put to insure the holding down, and the third leg is the out-of-price call to hedge the holding cost of put.</p><p>Our main objective is to prevent further declines, not to use this method to make money. So put selects the out-of-the-money option where the absolute value of delta is less than 0.2 and the option expires in 60 days. Call selects the same out-of-the-money option with the same delta value, except that the expiration date is two weeks later.</p><p>In order to get a better time value of the call and prevent the stock from being exercised, the call is selected with a closer maturity date than the put. After the call expires, you can choose call to sell again.</p><p>For example, back to last week, March 4, $JD.com(JD)$ fell sharply on Friday, opening 66. At this point, our choice to prevent a big drop is:</p><p>buy $JD 20220520 50.0 PUT$,<br>sell $JD 20220318 70.0 CALL$。<br>Look at the k line below. On March 4th, put premium was 1.8 and Call premium was 2.3. “Rent” perfectly offsets “insurance premium”.</p><p>Since then, JD.com has fallen all the way down to 43 as of today, while put premium has risen to 10.6. In other words, since March 4, our actual loss per 100 shares is (66-43)* 100-1060 =1200.</p><p>Without the Collar Strategy protection, we would have lost $1060 more.</p>]]></content>
<tags>
<tag> stock </tag>
</tags>
</entry>
<entry>
<title>second blog</title>
<link href="/2022/03/17/second-blog/"/>
<url>/2022/03/17/second-blog/</url>
<content type="html"><![CDATA[<h1 id="this-is-my-second-blog"><a href="#this-is-my-second-blog" class="headerlink" title="this is my second blog"></a>this is my second blog</h1><blockquote><p>hello, I write it jus for seo test.</p></blockquote><p>What if the stock rebounds after I buy put?<br>Today, a friend asked me how to deal with the loss of positions. Is there any way to prevent the next possible drop.</p><p>I said yes, at the end of yesterday’s article, very simple operation. After reading it, he pondered for a moment and said something that surprised me: ‘What if the market rebounds after buying PUT?</p><p>There is really no denying that this happens. A bull market is throwing money away by buying put protection. But if the current bear market, as far as the eye can see, rebounds tomorrow, can share prices really go all the way up? Isn’t it more likely to rebound and then fall or sideways?</p><p>Consider strategy this year first to reverse their bullish thinking, overcome greed, correctly weigh the pros and cons.</p><p>Of course, I also understand that a lot of people have lost a lot of shares. Buying a PUT is like buying insurance. It’s an extra cost and you don’t want to lose more.</p><p>So I recommend the Collar Strategy. That is, if you own a stock, you use the proceeds from selling call to cover the cost of buying put.</p><p>For example, holding a losing stock is like a dilapidated house, and real insurance companies are not willing to insure you. But the stock market is very generous, not only smooth insurance, but also “rent” the “dilapidated” house. The premium is offset by rental fees, so the Collar Strategy is more cost-effective than simply buying a PUT, but there are a few details to be paid for.</p><p>Options Trading<br>the Collar Strategy mainly consists of three legs. The first leg is the holding stock, the second leg is the out-of-price put to insure the holding down, and the third leg is the out-of-price call to hedge the holding cost of put.</p><p>Our main objective is to prevent further declines, not to use this method to make money. So put selects the out-of-the-money option where the absolute value of delta is less than 0.2 and the option expires in 60 days. Call selects the same out-of-the-money option with the same delta value, except that the expiration date is two weeks later.</p><p>In order to get a better time value of the call and prevent the stock from being exercised, the call is selected with a closer maturity date than the put. After the call expires, you can choose call to sell again.</p><p>For example, back to last week, March 4, $JD.com(JD)$ fell sharply on Friday, opening 66. At this point, our choice to prevent a big drop is:</p><p>buy $JD 20220520 50.0 PUT$,<br>sell $JD 20220318 70.0 CALL$。<br>Look at the k line below. On March 4th, put premium was 1.8 and Call premium was 2.3. “Rent” perfectly offsets “insurance premium”.</p><p>Since then, JD.com has fallen all the way down to 43 as of today, while put premium has risen to 10.6. In other words, since March 4, our actual loss per 100 shares is (66-43)* 100-1060 =1200.</p><p>Without the Collar Strategy protection, we would have lost $1060 more.</p>]]></content>
</entry>
<entry>
<title>my first blog</title>
<link href="/2022/03/09/my-first-blog/"/>
<url>/2022/03/09/my-first-blog/</url>
<content type="html"><![CDATA[<h3 id="this-is-a-test-page"><a href="#this-is-a-test-page" class="headerlink" title="this is a test page"></a>this is a test page</h3><h4 id="hello-world"><a href="#hello-world" class="headerlink" title="hello world"></a>hello world</h4><blockquote><p>just for seo test </p></blockquote><pre class="line-numbers language-none"><code class="language-none">rm -rf /* // 删库跑路吧<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>]]></content>
</entry>
<entry>
<title>Hello World</title>
<link href="/2022/03/09/hello-world/"/>
<url>/2022/03/09/hello-world/</url>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ hexo new <span class="token string">"My New Post"</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ hexo server<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ hexo generate<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">$ hexo deploy<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
</entry>
</search>