-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlocal-search.xml
311 lines (149 loc) · 380 KB
/
local-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
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>LSM-Tree 缓解写放大 论文阅读笔记</title>
<link href="/2024/10/22/lsm-tree-schedule/"/>
<url>/2024/10/22/lsm-tree-schedule/</url>
<content type="html"><![CDATA[<p>缓解 LSM-Tree write amplification / write stall 的论文列表:</p><p>(7 篇文章)</p><ul><li>bLSM: A General Purpose Log Structured Merge Tree(SIGMOD 2012)</li></ul><blockquote><p>第一次提出针对 Write Stall 的优化调度方法,提出弹簧-齿轮调度器;</p><p>核心思想是允许在每一层增加额外的调度控制组件来并发调度不同层的flush/compaction 过程;</p></blockquote><ul><li>A Priority and Fairness Mixed Compaction Scheduling Mechanism for LSM-tree Based KV-Stores(ICA3PP 2018)体系结构会议</li></ul><blockquote><p>提出了新的压缩调度方案:将以分数为中心的优先级调度与以时间片为中心的公平性调度相结合,减少每层(each component)SST 文件的数量,进而减少写放大;</p></blockquote><ul><li>LSM-based Storage Techniques: A Survey(VLDB Journel 2020)</li></ul><blockquote><p>总结了到 2019 年为止所有的 LSM-Tree 的优化方向和优化策略,其中在 write stall 方面对 blsm 进行了评价:设计上仅适用于未分区的分层合并策略,没有考虑更复杂的分区管理策略;仅优化了写入内存组件的最大延迟,而忽略了写入请求的排队延迟,这可能导致系统在高负载情况下性能不稳定。</p><p>bLSM is the only effort that attempts to address this problem(since 2019)</p><p>未分区的 lsm-tree 系统:leveldb、rocksdb</p><p>分区管理的 lsm-tree 系统:Cassandra、HBase</p></blockquote><ul><li>MatrixKV: Reducing Write Stalls and Write Amplification in LSM-tree Based KV Stores with Matrix Container in NVM(ATC 2020)华科</li></ul><blockquote><p>首次提出将 L0 层数据放入 NVM,利用细粒度列压缩,减少树深度等方式降低 LSM-Tree 的写放大和写停滞;</p></blockquote><ul><li>PM-Blade: A Persistent Memory Augmented LSM-tree Storage for Database(ICDE 2023)华东师大</li></ul><blockquote><p>用了PM+协程,优化的是压缩,减少写放大,但并没有针对 write stall 进行优化</p><p>解决方案:在基于 PM 的 L0 层进行内部压缩,将 L0 层转化为 sorted;</p><p>提出 cost-based compaction:基于代价的压缩策略提高读效率,减少写放大;</p><p>同时第一次提出基于协程的压缩方法,减少 CPU 等待时间和 IO 争用;</p></blockquote><ul><li>On Performance Stability in LSM-based Storage Systems(VLDB 20)</li></ul><blockquote><p>一种两阶段实验方法评测 LSM-Tree 的 write stall 问题</p><p>对 write stall 的优化方法提出一些参考性建议</p></blockquote><ul><li>A GPU-accelerated Compaction Strategy for LSM-based Key-Value Store System(MSST 2024)南开 体系结构会议</li></ul><blockquote><p>重点研究当 KV 对的大小为 中小型 时,利用 GPU 加速合并;</p><p>提出两种 NVMe SSD + GPU 数据传输机制:pipeline、P2P,最小化压缩过程的数据传输开销;</p><p>基于 Leveldb 实现,能够尽可能减少 write stall,使吞吐量趋于稳定;</p></blockquote><h3 id="bLSM-A-General-Purpose-Log-Structured-Merge-Tree"><a href="#bLSM-A-General-Purpose-Log-Structured-Merge-Tree" class="headerlink" title="bLSM: A General Purpose Log Structured Merge Tree"></a>bLSM: A General Purpose Log Structured Merge Tree</h3><h5 id="论文重点概述:"><a href="#论文重点概述:" class="headerlink" title="论文重点概述:"></a>论文重点概述:</h5><h5 id="1、每层数据-compaction-的代价计算:"><a href="#1、每层数据-compaction-的代价计算:" class="headerlink" title="1、每层数据 compaction 的代价计算:"></a>1、每层数据 compaction 的代价计算:</h5><p><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.17ex;" xmlns="http://www.w3.org/2000/svg" width="12.129ex" height="5.471ex" role="img" focusable="false" viewBox="0 -1459 5361.1 2418"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(1363.7,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mfrac" transform="translate(2419.5,0)"><g data-mml-node="mrow" transform="translate(220,709.5)"><g data-mml-node="mo" transform="translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="msub" transform="translate(278,0)"><g data-mml-node="mi"><path data-c="1D436" d="M50 252Q50 367 117 473T286 641T490 704Q580 704 633 653Q642 643 648 636T656 626L657 623Q660 623 684 649Q691 655 699 663T715 679T725 690L740 705H746Q760 705 760 698Q760 694 728 561Q692 422 692 421Q690 416 687 415T669 413H653Q647 419 647 422Q647 423 648 429T650 449T651 481Q651 552 619 605T510 659Q484 659 454 652T382 628T299 572T226 479Q194 422 175 346T156 222Q156 108 232 58Q280 24 350 24Q441 24 512 92T606 240Q610 253 612 255T628 257Q648 257 648 248Q648 243 647 239Q618 132 523 55T319 -22Q206 -22 128 53T50 252Z"></path></g><g data-mml-node="TeXAtom" transform="translate(748,-150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(345,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mn" transform="translate(1123,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g><g data-mml-node="mo" transform="translate(2223.6,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g></g><g data-mml-node="mrow" transform="translate(671.8,-709.5)"><g data-mml-node="mo" transform="translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="msub" transform="translate(278,0)"><g data-mml-node="mi"><path data-c="1D436" d="M50 252Q50 367 117 473T286 641T490 704Q580 704 633 653Q642 643 648 636T656 626L657 623Q660 623 684 649Q691 655 699 663T715 679T725 690L740 705H746Q760 705 760 698Q760 694 728 561Q692 422 692 421Q690 416 687 415T669 413H653Q647 419 647 422Q647 423 648 429T650 449T651 481Q651 552 619 605T510 659Q484 659 454 652T382 628T299 572T226 479Q194 422 175 346T156 222Q156 108 232 58Q280 24 350 24Q441 24 512 92T606 240Q610 253 612 255T628 257Q648 257 648 248Q648 243 647 239Q618 132 523 55T319 -22Q206 -22 128 53T50 252Z"></path></g><g data-mml-node="mi" transform="translate(748,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(1320,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g></g><rect width="2701.6" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></p><p>完整数据总量:<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.819ex;" xmlns="http://www.w3.org/2000/svg" width="20.5ex" height="6.712ex" role="img" focusable="false" viewBox="0 -1720.9 9061.1 2966.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo" transform="translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mi" transform="translate(278,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path></g><g data-mml-node="mi" transform="translate(798,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mi" transform="translate(1327,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mi" transform="translate(1688,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mo" transform="translate(2217,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mo" transform="translate(2772.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mo" transform="translate(3828.6,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="msub" transform="translate(4106.6,0)"><g data-mml-node="mi"><path data-c="1D436" d="M50 252Q50 367 117 473T286 641T490 704Q580 704 633 653Q642 643 648 636T656 626L657 623Q660 623 684 649Q691 655 699 663T715 679T725 690L740 705H746Q760 705 760 698Q760 694 728 561Q692 422 692 421Q690 416 687 415T669 413H653Q647 419 647 422Q647 423 648 429T650 449T651 481Q651 552 619 605T510 659Q484 659 454 652T382 628T299 572T226 479Q194 422 175 346T156 222Q156 108 232 58Q280 24 350 24Q441 24 512 92T606 240Q610 253 612 255T628 257Q648 257 648 248Q648 243 647 239Q618 132 523 55T319 -22Q206 -22 128 53T50 252Z"></path></g><g data-mml-node="mn" transform="translate(748,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g><g data-mml-node="mo" transform="translate(5258.1,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mo" transform="translate(5758.3,0)"><path data-c="2217" d="M229 286Q216 420 216 436Q216 454 240 464Q241 464 245 464T251 465Q263 464 273 456T283 436Q283 419 277 356T270 286L328 328Q384 369 389 372T399 375Q412 375 423 365T435 338Q435 325 425 315Q420 312 357 282T289 250L355 219L425 184Q434 175 434 161Q434 146 425 136T401 125Q393 125 383 131T328 171L270 213Q283 79 283 63Q283 53 276 44T250 35Q231 35 224 44T216 63Q216 80 222 143T229 213L171 171Q115 130 110 127Q106 124 100 124Q87 124 76 134T64 161Q64 166 64 169T67 175T72 181T81 188T94 195T113 204T138 215T170 230T210 250L74 315Q65 324 65 338Q65 353 74 363T98 374Q106 374 116 368T171 328L229 286Z"></path></g><g data-mml-node="munderover" transform="translate(6480.6,0)"><g data-mml-node="mo" transform="translate(25,0)"><path data-c="220F" d="M220 812Q220 813 218 819T214 829T208 840T199 853T185 866T166 878T140 887T107 893T66 896H56V950H1221V896H1211Q1080 896 1058 812V-311Q1076 -396 1211 -396H1221V-450H725V-396H735Q864 -396 888 -314Q889 -312 889 -311V896H388V292L389 -311Q405 -396 542 -396H552V-450H56V-396H66Q195 -396 219 -314Q220 -312 220 -311V812Z"></path></g><g data-mml-node="TeXAtom" transform="translate(90.2,-1087.9) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(345,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mn" transform="translate(1123,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g><g data-mml-node="TeXAtom" transform="translate(0,1150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(600,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mn" transform="translate(1378,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g><g data-mml-node="msub" transform="translate(7975.2,0)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container></p><p>代价总和:total cost = <mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.819ex;" xmlns="http://www.w3.org/2000/svg" width="6.101ex" height="6.712ex" role="img" focusable="false" viewBox="0 -1720.9 2696.6 2966.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="munderover"><g data-mml-node="mo"><path data-c="2211" d="M60 948Q63 950 665 950H1267L1325 815Q1384 677 1388 669H1348L1341 683Q1320 724 1285 761Q1235 809 1174 838T1033 881T882 898T699 902H574H543H251L259 891Q722 258 724 252Q725 250 724 246Q721 243 460 -56L196 -356Q196 -357 407 -357Q459 -357 548 -357T676 -358Q812 -358 896 -353T1063 -332T1204 -283T1307 -196Q1328 -170 1348 -124H1388Q1388 -125 1381 -145T1356 -210T1325 -294L1267 -449L666 -450Q64 -450 61 -448Q55 -446 55 -439Q55 -437 57 -433L590 177Q590 178 557 222T452 366T322 544L56 909L55 924Q55 945 60 948Z"></path></g><g data-mml-node="TeXAtom" transform="translate(148.2,-1087.9) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(345,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mn" transform="translate(1123,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g><g data-mml-node="TeXAtom" transform="translate(58,1150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(600,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mn" transform="translate(1378,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g><g data-mml-node="msub" transform="translate(1610.7,0)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container></p><p>现在目标是让代价总和最小,即最优化问题 <mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="9.839ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 4349 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D45A" d="M21 287Q22 293 24 303T36 341T56 388T88 425T132 442T175 435T205 417T221 395T229 376L231 369Q231 367 232 367L243 378Q303 442 384 442Q401 442 415 440T441 433T460 423T475 411T485 398T493 385T497 373T500 364T502 357L510 367Q573 442 659 442Q713 442 746 415T780 336Q780 285 742 178T704 50Q705 36 709 31T724 26Q752 26 776 56T815 138Q818 149 821 151T837 153Q857 153 857 145Q857 144 853 130Q845 101 831 73T785 17T716 -10Q669 -10 648 17T627 73Q627 92 663 193T700 345Q700 404 656 404H651Q565 404 506 303L499 291L466 157Q433 26 428 16Q415 -11 385 -11Q372 -11 364 -4T353 8T350 18Q350 29 384 161L420 307Q423 322 423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 181Q151 335 151 342Q154 357 154 369Q154 405 129 405Q107 405 92 377T69 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(878,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(1223,0)"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(1823,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(2212,0)"><path data-c="1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path></g><g data-mml-node="mi" transform="translate(2645,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mi" transform="translate(3130,0)"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"></path></g><g data-mml-node="mi" transform="translate(3599,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mo" transform="translate(3960,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g></g></g></svg></mjx-container></p><p>由拉格朗日:<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.819ex;" xmlns="http://www.w3.org/2000/svg" width="38.889ex" height="6.712ex" role="img" focusable="false" viewBox="0 -1720.9 17189.1 2966.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D43F" d="M228 637Q194 637 192 641Q191 643 191 649Q191 673 202 682Q204 683 217 683Q271 680 344 680Q485 680 506 683H518Q524 677 524 674T522 656Q517 641 513 637H475Q406 636 394 628Q387 624 380 600T313 336Q297 271 279 198T252 88L243 52Q243 48 252 48T311 46H328Q360 46 379 47T428 54T478 72T522 106T564 161Q580 191 594 228T611 270Q616 273 628 273H641Q647 264 647 262T627 203T583 83T557 9Q555 4 553 3T537 0T494 -1Q483 -1 418 -1T294 0H116Q32 0 32 10Q32 17 34 24Q39 43 44 45Q48 46 59 46H65Q92 46 125 49Q139 52 144 61Q147 65 216 339T285 628Q285 635 228 637Z"></path></g><g data-mml-node="mo" transform="translate(958.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="munderover" transform="translate(2014.6,0)"><g data-mml-node="mo"><path data-c="2211" d="M60 948Q63 950 665 950H1267L1325 815Q1384 677 1388 669H1348L1341 683Q1320 724 1285 761Q1235 809 1174 838T1033 881T882 898T699 902H574H543H251L259 891Q722 258 724 252Q725 250 724 246Q721 243 460 -56L196 -356Q196 -357 407 -357Q459 -357 548 -357T676 -358Q812 -358 896 -353T1063 -332T1204 -283T1307 -196Q1328 -170 1348 -124H1388Q1388 -125 1381 -145T1356 -210T1325 -294L1267 -449L666 -450Q64 -450 61 -448Q55 -446 55 -439Q55 -437 57 -433L590 177Q590 178 557 222T452 366T322 544L56 909L55 924Q55 945 60 948Z"></path></g><g data-mml-node="TeXAtom" transform="translate(148.2,-1087.9) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(345,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mn" transform="translate(1123,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g><g data-mml-node="TeXAtom" transform="translate(58,1150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(600,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mn" transform="translate(1378,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g><g data-mml-node="msub" transform="translate(3625.2,0)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(4933.4,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mi" transform="translate(5933.6,0)"><path data-c="1D706" d="M166 673Q166 685 183 694H202Q292 691 316 644Q322 629 373 486T474 207T524 67Q531 47 537 34T546 15T551 6T555 2T556 -2T550 -11H482Q457 3 450 18T399 152L354 277L340 262Q327 246 293 207T236 141Q211 112 174 69Q123 9 111 -1T83 -12Q47 -12 47 20Q47 37 61 52T199 187Q229 216 266 252T321 306L338 322Q338 323 288 462T234 612Q214 657 183 657Q166 657 166 673Z"></path></g><g data-mml-node="mo" transform="translate(6738.8,0)"><path data-c="2217" d="M229 286Q216 420 216 436Q216 454 240 464Q241 464 245 464T251 465Q263 464 273 456T283 436Q283 419 277 356T270 286L328 328Q384 369 389 372T399 375Q412 375 423 365T435 338Q435 325 425 315Q420 312 357 282T289 250L355 219L425 184Q434 175 434 161Q434 146 425 136T401 125Q393 125 383 131T328 171L270 213Q283 79 283 63Q283 53 276 44T250 35Q231 35 224 44T216 63Q216 80 222 143T229 213L171 171Q115 130 110 127Q106 124 100 124Q87 124 76 134T64 161Q64 166 64 169T67 175T72 181T81 188T94 195T113 204T138 215T170 230T210 250L74 315Q65 324 65 338Q65 353 74 363T98 374Q106 374 116 368T171 328L229 286Z"></path></g><g data-mml-node="mo" transform="translate(7461.1,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mo" transform="translate(7850.1,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="msub" transform="translate(8128.1,0)"><g data-mml-node="mi"><path data-c="1D436" d="M50 252Q50 367 117 473T286 641T490 704Q580 704 633 653Q642 643 648 636T656 626L657 623Q660 623 684 649Q691 655 699 663T715 679T725 690L740 705H746Q760 705 760 698Q760 694 728 561Q692 422 692 421Q690 416 687 415T669 413H653Q647 419 647 422Q647 423 648 429T650 449T651 481Q651 552 619 605T510 659Q484 659 454 652T382 628T299 572T226 479Q194 422 175 346T156 222Q156 108 232 58Q280 24 350 24Q441 24 512 92T606 240Q610 253 612 255T628 257Q648 257 648 248Q648 243 647 239Q618 132 523 55T319 -22Q206 -22 128 53T50 252Z"></path></g><g data-mml-node="mn" transform="translate(748,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g><g data-mml-node="mo" transform="translate(9279.6,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mo" transform="translate(9779.8,0)"><path data-c="2217" d="M229 286Q216 420 216 436Q216 454 240 464Q241 464 245 464T251 465Q263 464 273 456T283 436Q283 419 277 356T270 286L328 328Q384 369 389 372T399 375Q412 375 423 365T435 338Q435 325 425 315Q420 312 357 282T289 250L355 219L425 184Q434 175 434 161Q434 146 425 136T401 125Q393 125 383 131T328 171L270 213Q283 79 283 63Q283 53 276 44T250 35Q231 35 224 44T216 63Q216 80 222 143T229 213L171 171Q115 130 110 127Q106 124 100 124Q87 124 76 134T64 161Q64 166 64 169T67 175T72 181T81 188T94 195T113 204T138 215T170 230T210 250L74 315Q65 324 65 338Q65 353 74 363T98 374Q106 374 116 368T171 328L229 286Z"></path></g><g data-mml-node="munderover" transform="translate(10502.1,0)"><g data-mml-node="mo" transform="translate(25,0)"><path data-c="220F" d="M220 812Q220 813 218 819T214 829T208 840T199 853T185 866T166 878T140 887T107 893T66 896H56V950H1221V896H1211Q1080 896 1058 812V-311Q1076 -396 1211 -396H1221V-450H725V-396H735Q864 -396 888 -314Q889 -312 889 -311V896H388V292L389 -311Q405 -396 542 -396H552V-450H56V-396H66Q195 -396 219 -314Q220 -312 220 -311V812Z"></path></g><g data-mml-node="TeXAtom" transform="translate(90.2,-1087.9) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(345,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mn" transform="translate(1123,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g><g data-mml-node="TeXAtom" transform="translate(0,1150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(600,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mn" transform="translate(1378,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g><g data-mml-node="msub" transform="translate(11996.7,0)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(13304.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mo" transform="translate(14305.1,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mi" transform="translate(14583.1,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path></g><g data-mml-node="mi" transform="translate(15103.1,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mi" transform="translate(15632.1,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mi" transform="translate(15993.1,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mo" transform="translate(16522.1,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mo" transform="translate(16800.1,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g></g></g></svg></mjx-container></p><p>即求偏导:<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.218ex;" xmlns="http://www.w3.org/2000/svg" width="34.516ex" height="6.18ex" role="img" focusable="false" viewBox="0 -1751.3 15255.9 2731.6"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mrow" transform="translate(446.2,676)"><g data-mml-node="mi"><path data-c="1D715" d="M202 508Q179 508 169 520T158 547Q158 557 164 577T185 624T230 675T301 710L333 715H345Q378 715 384 714Q447 703 489 661T549 568T566 457Q566 362 519 240T402 53Q321 -22 223 -22Q123 -22 73 56Q42 102 42 148V159Q42 276 129 370T322 465Q383 465 414 434T455 367L458 378Q478 461 478 515Q478 603 437 639T344 676Q266 676 223 612Q264 606 264 572Q264 547 246 528T202 508ZM430 306Q430 372 401 400T333 428Q270 428 222 382Q197 354 183 323T150 221Q132 149 132 116Q132 21 232 21Q244 21 250 22Q327 35 374 112Q389 137 409 196T430 306Z"></path></g><g data-mml-node="mi" transform="translate(566,0)"><path data-c="1D43F" d="M228 637Q194 637 192 641Q191 643 191 649Q191 673 202 682Q204 683 217 683Q271 680 344 680Q485 680 506 683H518Q524 677 524 674T522 656Q517 641 513 637H475Q406 636 394 628Q387 624 380 600T313 336Q297 271 279 198T252 88L243 52Q243 48 252 48T311 46H328Q360 46 379 47T428 54T478 72T522 106T564 161Q580 191 594 228T611 270Q616 273 628 273H641Q647 264 647 262T627 203T583 83T557 9Q555 4 553 3T537 0T494 -1Q483 -1 418 -1T294 0H116Q32 0 32 10Q32 17 34 24Q39 43 44 45Q48 46 59 46H65Q92 46 125 49Q139 52 144 61Q147 65 216 339T285 628Q285 635 228 637Z"></path></g></g><g data-mml-node="mrow" transform="translate(220,-686)"><g data-mml-node="mi"><path data-c="1D715" d="M202 508Q179 508 169 520T158 547Q158 557 164 577T185 624T230 675T301 710L333 715H345Q378 715 384 714Q447 703 489 661T549 568T566 457Q566 362 519 240T402 53Q321 -22 223 -22Q123 -22 73 56Q42 102 42 148V159Q42 276 129 370T322 465Q383 465 414 434T455 367L458 378Q478 461 478 515Q478 603 437 639T344 676Q266 676 223 612Q264 606 264 572Q264 547 246 528T202 508ZM430 306Q430 372 401 400T333 428Q270 428 222 382Q197 354 183 323T150 221Q132 149 132 116Q132 21 232 21Q244 21 250 22Q327 35 374 112Q389 137 409 196T430 306Z"></path></g><g data-mml-node="msub" transform="translate(566,0)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D457" d="M297 596Q297 627 318 644T361 661Q378 661 389 651T403 623Q403 595 384 576T340 557Q322 557 310 567T297 596ZM288 376Q288 405 262 405Q240 405 220 393T185 362T161 325T144 293L137 279Q135 278 121 278H107Q101 284 101 286T105 299Q126 348 164 391T252 441Q253 441 260 441T272 442Q296 441 316 432Q341 418 354 401T367 348V332L318 133Q267 -67 264 -75Q246 -125 194 -164T75 -204Q25 -204 7 -183T-12 -137Q-12 -110 7 -91T53 -71Q70 -71 82 -81T95 -112Q95 -148 63 -167Q69 -168 77 -168Q111 -168 139 -140T182 -74L193 -32Q204 11 219 72T251 197T278 308T289 365Q289 372 288 376Z"></path></g></g></g><rect width="1899.3" height="60" x="120" y="220"></rect></g><g data-mml-node="mo" transform="translate(2417.1,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mn" transform="translate(3472.9,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mo" transform="translate(4195.1,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mi" transform="translate(5195.3,0)"><path data-c="1D706" d="M166 673Q166 685 183 694H202Q292 691 316 644Q322 629 373 486T474 207T524 67Q531 47 537 34T546 15T551 6T555 2T556 -2T550 -11H482Q457 3 450 18T399 152L354 277L340 262Q327 246 293 207T236 141Q211 112 174 69Q123 9 111 -1T83 -12Q47 -12 47 20Q47 37 61 52T199 187Q229 216 266 252T321 306L338 322Q338 323 288 462T234 612Q214 657 183 657Q166 657 166 673Z"></path></g><g data-mml-node="mo" transform="translate(6000.6,0)"><path data-c="2217" d="M229 286Q216 420 216 436Q216 454 240 464Q241 464 245 464T251 465Q263 464 273 456T283 436Q283 419 277 356T270 286L328 328Q384 369 389 372T399 375Q412 375 423 365T435 338Q435 325 425 315Q420 312 357 282T289 250L355 219L425 184Q434 175 434 161Q434 146 425 136T401 125Q393 125 383 131T328 171L270 213Q283 79 283 63Q283 53 276 44T250 35Q231 35 224 44T216 63Q216 80 222 143T229 213L171 171Q115 130 110 127Q106 124 100 124Q87 124 76 134T64 161Q64 166 64 169T67 175T72 181T81 188T94 195T113 204T138 215T170 230T210 250L74 315Q65 324 65 338Q65 353 74 363T98 374Q106 374 116 368T171 328L229 286Z"></path></g><g data-mml-node="mo" transform="translate(6722.8,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="msub" transform="translate(7000.8,0)"><g data-mml-node="mi"><path data-c="1D436" d="M50 252Q50 367 117 473T286 641T490 704Q580 704 633 653Q642 643 648 636T656 626L657 623Q660 623 684 649Q691 655 699 663T715 679T725 690L740 705H746Q760 705 760 698Q760 694 728 561Q692 422 692 421Q690 416 687 415T669 413H653Q647 419 647 422Q647 423 648 429T650 449T651 481Q651 552 619 605T510 659Q484 659 454 652T382 628T299 572T226 479Q194 422 175 346T156 222Q156 108 232 58Q280 24 350 24Q441 24 512 92T606 240Q610 253 612 255T628 257Q648 257 648 248Q648 243 647 239Q618 132 523 55T319 -22Q206 -22 128 53T50 252Z"></path></g><g data-mml-node="mn" transform="translate(748,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g><g data-mml-node="mo" transform="translate(8152.3,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mo" transform="translate(8652.5,0)"><path data-c="2217" d="M229 286Q216 420 216 436Q216 454 240 464Q241 464 245 464T251 465Q263 464 273 456T283 436Q283 419 277 356T270 286L328 328Q384 369 389 372T399 375Q412 375 423 365T435 338Q435 325 425 315Q420 312 357 282T289 250L355 219L425 184Q434 175 434 161Q434 146 425 136T401 125Q393 125 383 131T328 171L270 213Q283 79 283 63Q283 53 276 44T250 35Q231 35 224 44T216 63Q216 80 222 143T229 213L171 171Q115 130 110 127Q106 124 100 124Q87 124 76 134T64 161Q64 166 64 169T67 175T72 181T81 188T94 195T113 204T138 215T170 230T210 250L74 315Q65 324 65 338Q65 353 74 363T98 374Q106 374 116 368T171 328L229 286Z"></path></g><g data-mml-node="mfrac" transform="translate(9374.8,0)"><g data-mml-node="mrow" transform="translate(220,803.3)"><g data-mml-node="munderover"><g data-mml-node="mo"><path data-c="220F" d="M158 656Q147 684 131 694Q110 707 69 710H55V750H888V710H874Q840 708 820 698T795 678T786 656V-155Q798 -206 874 -210H888V-250H570V-210H584Q618 -208 638 -197T663 -178T673 -155V710H270V277L271 -155Q283 -206 359 -210H373V-250H55V-210H69Q103 -208 123 -197T148 -178T158 -155V656Z"></path></g><g data-mml-node="TeXAtom" transform="translate(977,477.1) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(600,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mn" transform="translate(1378,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g><g data-mml-node="TeXAtom" transform="translate(977,-285.4) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mo" transform="translate(345,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mn" transform="translate(1123,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g></g><g data-mml-node="msub" transform="translate(2521.6,0)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g><g data-mml-node="msub" transform="translate(1457.1,-686)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D457" d="M297 596Q297 627 318 644T361 661Q378 661 389 651T403 623Q403 595 384 576T340 557Q322 557 310 567T297 596ZM288 376Q288 405 262 405Q240 405 220 393T185 362T161 325T144 293L137 279Q135 278 121 278H107Q101 284 101 286T105 299Q126 348 164 391T252 441Q253 441 260 441T272 442Q296 441 316 432Q341 418 354 401T367 348V332L318 133Q267 -67 264 -75Q246 -125 194 -164T75 -204Q25 -204 7 -183T-12 -137Q-12 -110 7 -91T53 -71Q70 -71 82 -81T95 -112Q95 -148 63 -167Q69 -168 77 -168Q111 -168 139 -140T182 -74L193 -32Q204 11 219 72T251 197T278 308T289 365Q289 372 288 376Z"></path></g></g><rect width="3807.6" height="60" x="120" y="220"></rect></g><g data-mml-node="mo" transform="translate(13700.1,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mn" transform="translate(14755.9,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g></g></svg></mjx-container></p><p>得到只有 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.666ex;" xmlns="http://www.w3.org/2000/svg" width="13.794ex" height="2.211ex" role="img" focusable="false" viewBox="0 -683 6096.8 977.2"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(1363.7,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="msub" transform="translate(2419.5,0)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D457" d="M297 596Q297 627 318 644T361 661Q378 661 389 651T403 623Q403 595 384 576T340 557Q322 557 310 567T297 596ZM288 376Q288 405 262 405Q240 405 220 393T185 362T161 325T144 293L137 279Q135 278 121 278H107Q101 284 101 286T105 299Q126 348 164 391T252 441Q253 441 260 441T272 442Q296 441 316 432Q341 418 354 401T367 348V332L318 133Q267 -67 264 -75Q246 -125 194 -164T75 -204Q25 -204 7 -183T-12 -137Q-12 -110 7 -91T53 -71Q70 -71 82 -81T95 -112Q95 -148 63 -167Q69 -168 77 -168Q111 -168 139 -140T182 -74L193 -32Q204 11 219 72T251 197T278 308T289 365Q289 372 288 376Z"></path></g></g><g data-mml-node="mo" transform="translate(3830.6,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="msub" transform="translate(4886.4,0)"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D458" d="M121 647Q121 657 125 670T137 683Q138 683 209 688T282 694Q294 694 294 686Q294 679 244 477Q194 279 194 272Q213 282 223 291Q247 309 292 354T362 415Q402 442 438 442Q468 442 485 423T503 369Q503 344 496 327T477 302T456 291T438 288Q418 288 406 299T394 328Q394 353 410 369T442 390L458 393Q446 405 434 405H430Q398 402 367 380T294 316T228 255Q230 254 243 252T267 246T293 238T320 224T342 206T359 180T365 147Q365 130 360 106T354 66Q354 26 381 26Q429 26 459 145Q461 153 479 153H483Q499 153 499 144Q499 139 496 130Q455 -11 378 -11Q333 -11 305 15T277 90Q277 108 280 121T283 145Q283 167 269 183T234 206T200 217T182 220H180Q168 178 159 139T145 81T136 44T129 20T122 7T111 -2Q98 -11 83 -11Q66 -11 57 -1T48 16Q48 26 85 176T158 471L195 616Q196 629 188 632T149 637H144Q134 637 131 637T124 640T121 647Z"></path></g></g></g></g></svg></mjx-container>,每层之间的大小倍数相同时,total cost 有极小值;</p><p>带回得到:两层之间最佳的比率为 <mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.567ex;" xmlns="http://www.w3.org/2000/svg" width="15.488ex" height="6.923ex" role="img" focusable="false" viewBox="0 -1925.3 6845.5 3060"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(1363.7,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"></path></g><g data-mml-node="mroot" transform="translate(2890.5,0)"><g><g data-mml-node="mfrac" transform="translate(1491,0)"><g data-mml-node="mrow" transform="translate(220,709.5)"><g data-mml-node="mo" transform="translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mi" transform="translate(278,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path></g><g data-mml-node="mi" transform="translate(798,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mi" transform="translate(1327,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mi" transform="translate(1688,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mo" transform="translate(2217,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g></g><g data-mml-node="mrow" transform="translate(613.7,-709.5)"><g data-mml-node="mo" transform="translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="msub" transform="translate(278,0)"><g data-mml-node="mi"><path data-c="1D436" d="M50 252Q50 367 117 473T286 641T490 704Q580 704 633 653Q642 643 648 636T656 626L657 623Q660 623 684 649Q691 655 699 663T715 679T725 690L740 705H746Q760 705 760 698Q760 694 728 561Q692 422 692 421Q690 416 687 415T669 413H653Q647 419 647 422Q647 423 648 429T650 449T651 481Q651 552 619 605T510 659Q484 659 454 652T382 628T299 572T226 479Q194 422 175 346T156 222Q156 108 232 58Q280 24 350 24Q441 24 512 92T606 240Q610 253 612 255T628 257Q648 257 648 248Q648 243 647 239Q618 132 523 55T319 -22Q206 -22 128 53T50 252Z"></path></g><g data-mml-node="mn" transform="translate(748,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g><g data-mml-node="mo" transform="translate(1429.6,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g></g><rect width="2695" height="60" x="120" y="220"></rect></g></g><g data-mml-node="mrow" transform="translate(0,556.3) scale(0.5)"><g data-mml-node="mi"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"></path></g><g data-mml-node="mo" transform="translate(888,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mn" transform="translate(1666,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g><g data-mml-node="mo" transform="translate(471,115.3)"><path data-c="221A" d="M983 1739Q988 1750 1001 1750Q1008 1750 1013 1745T1020 1733Q1020 1726 742 244T460 -1241Q458 -1250 439 -1250H436Q424 -1250 424 -1248L410 -1166Q395 -1083 367 -920T312 -601L201 44L137 -83L111 -57L187 96L264 247Q265 246 369 -357Q470 -958 473 -963L727 384Q979 1729 983 1739Z"></path></g><rect width="2935" height="60" x="1491" y="1805.3"></rect></g></g></g></svg></mjx-container> </p><p>但由于实际情况下要插入的数据总量 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.564ex;" xmlns="http://www.w3.org/2000/svg" width="5.645ex" height="2.26ex" role="img" focusable="false" viewBox="0 -749.5 2495 999"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo" transform="translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g><g data-mml-node="mi" transform="translate(278,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path></g><g data-mml-node="mi" transform="translate(798,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mi" transform="translate(1327,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mi" transform="translate(1688,0)"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mo" transform="translate(2217,0) translate(0 -0.5)"><path data-c="7C" d="M139 -249H137Q125 -249 119 -235V251L120 737Q130 750 139 750Q152 750 159 735V-235Q151 -249 141 -249H139Z"></path></g></g></g></svg></mjx-container>未知,最佳的 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="2.457ex" height="1.902ex" role="img" focusable="false" viewBox="0 -683 1086 840.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D445" d="M230 637Q203 637 198 638T193 649Q193 676 204 682Q206 683 378 683Q550 682 564 680Q620 672 658 652T712 606T733 563T739 529Q739 484 710 445T643 385T576 351T538 338L545 333Q612 295 612 223Q612 212 607 162T602 80V71Q602 53 603 43T614 25T640 16Q668 16 686 38T712 85Q717 99 720 102T735 105Q755 105 755 93Q755 75 731 36Q693 -21 641 -21H632Q571 -21 531 4T487 82Q487 109 502 166T517 239Q517 290 474 313Q459 320 449 321T378 323H309L277 193Q244 61 244 59Q244 55 245 54T252 50T269 48T302 46H333Q339 38 339 37T336 19Q332 6 326 0H311Q275 2 180 2Q146 2 117 2T71 2T50 1Q33 1 33 10Q33 12 36 24Q41 43 46 45Q50 46 61 46H67Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628Q287 635 230 637ZM630 554Q630 586 609 608T523 636Q521 636 500 636T462 637H440Q393 637 386 627Q385 624 352 494T319 361Q319 360 388 360Q466 361 492 367Q556 377 592 426Q608 449 619 486T630 554Z"></path></g><g data-mml-node="mi" transform="translate(792,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container> 只存在于理想情况;</p><p>最小化总的压缩代价,只能减少写放大,但对 write stall 没有很好的效果;</p><h5 id="2、分区调度器与层调度器对比"><a href="#2、分区调度器与层调度器对比" class="headerlink" title="2、分区调度器与层调度器对比"></a>2、分区调度器与层调度器对比</h5><p>分区调度器 Partition schedulers 在特定倾斜分布的负载场景下能尽可能减少 IO,减少写停滞,如 Cassandra、HBase 系统应用了这个优化,但缺点也很明显,当数据分布不倾斜时,这种调度方案没有显著效果,因此 leveldb、rocksdb 没有采用,作者接下来提出为不分区的 LSM-Tree 系统的层调度器方案(spring-and-gear scheduler)</p><img src="/img/image-20240708105840631.png" alt="image-20240708105840631" style="zoom: 67%;"> <h5 id="3、弹簧-齿轮调度器的设计"><a href="#3、弹簧-齿轮调度器的设计" class="headerlink" title="3、弹簧-齿轮调度器的设计"></a>3、弹簧-齿轮调度器的设计</h5><h5 id="3-1-首先是作者最初的想法:齿轮调度器的建模"><a href="#3-1-首先是作者最初的想法:齿轮调度器的建模" class="headerlink" title="3.1 首先是作者最初的想法:齿轮调度器的建模"></a>3.1 首先是作者最初的想法:齿轮调度器的建模</h5><p>核心思想:</p><ul><li><p>每层合并过程维护两个进度指示器,来控制合并线程的执行;</p></li><li><p>只要上下游合并能够保持一个相对的进度,write stall 问题就会尽可能避免发生</p></li></ul><p>(相对进度可以形象理解为多个齿轮之间的啮合运转,不会说某一层合并过慢导致前台停止接收写请求)</p><p><img src="/img/image-20240708111216590.png" alt="image-20240708111216590"></p><p>入合并操作的进度:分子为当前层的合并已读取的字节数</p><p><img src="/img/image-20240708111311755.png" alt="image-20240708111311755"></p><p>出合并操作的进度:<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.059ex;" xmlns="http://www.w3.org/2000/svg" width="39.961ex" height="5.362ex" role="img" focusable="false" viewBox="0 -1460 17662.9 2370"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mrow" transform="translate(220,710)"><g data-mml-node="mi"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">当</text></g><g data-mml-node="mi" transform="translate(1000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">前</text></g><g data-mml-node="mi" transform="translate(2000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">合</text></g><g data-mml-node="mi" transform="translate(3000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">并</text></g><g data-mml-node="mi" transform="translate(4000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">的</text></g><g data-mml-node="mi" transform="translate(5000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">进</text></g><g data-mml-node="mi" transform="translate(6000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">度</text></g><g data-mml-node="mo" transform="translate(7000,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mn" transform="translate(7389,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g><g data-mml-node="mo" transform="translate(8111.2,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"></path></g><g data-mml-node="mn" transform="translate(9111.4,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g><g data-mml-node="mo" transform="translate(9611.4,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(10222.7,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mi" transform="translate(11222.9,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">已</text></g><g data-mml-node="mi" transform="translate(12222.9,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">合</text></g><g data-mml-node="mi" transform="translate(13222.9,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">并</text></g><g data-mml-node="mi" transform="translate(14222.9,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">的</text></g><g data-mml-node="mi" transform="translate(15222.9,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">次</text></g><g data-mml-node="mi" transform="translate(16222.9,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">数</text></g></g><g data-mml-node="mrow" transform="translate(3831.4,-710)"><g data-mml-node="mi"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">合</text></g><g data-mml-node="mi" transform="translate(1000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">并</text></g><g data-mml-node="mi" transform="translate(2000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">次</text></g><g data-mml-node="mi" transform="translate(3000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">数</text></g><g data-mml-node="mi" transform="translate(4000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">预</text></g><g data-mml-node="mi" transform="translate(5000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">估</text></g><g data-mml-node="mi" transform="translate(6000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">的</text></g><g data-mml-node="mi" transform="translate(7000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">上</text></g><g data-mml-node="mi" transform="translate(8000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">限</text></g><g data-mml-node="mi" transform="translate(9000,0)"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">值</text></g></g><rect width="17422.9" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container></p><p>根据这两个进度指示器:要么减少上层写吞吐、要么停止合并让出CPU资源</p><p>就比如现在数据积压特别多,那就让给压缩线程,减少上层写吞吐,不至于整个系统直接 write stall</p><p>3.2 其次,在压缩合并过程中,比如 L0~L1,涉及到外排,作者提到外排用的是替换选择外排(扫雪机模型),而不是传统的多路归并外排;</p><p>优点是:替换选择外排优化的是外排的分割阶段,尽可能让分割后的每个顺串(SST file) 长度最大化;</p><p>从而减少初始读写 I/O,并且用小根堆实现,能够与最终提出的水位线方法相契合;</p><p>3.3 弹簧-齿轮调度器</p><p>为什么需要改进:齿轮调度器太脆弱,合并与插入操作绑定得太紧,同时与外排铲雪机模型交互性太差;</p><p>改进方法:在齿轮调度器的基础上,施加 low,high 水位线,比如:L0快满时反向施压,系统内部降低对写请求的接收频率,L0快空时尽量减少合并,停止压缩线程,让出 CPU 资源,给前台接收用户的写请求。</p><p>本质:控制合并的速率,在线程级别上进行调度的调控</p><h5 id="性能表现:"><a href="#性能表现:" class="headerlink" title="性能表现:"></a>性能表现:</h5><p>OPS 在 2W 左右</p><p><img src="/img/image-20240708151919955.png" alt="image-20240708151919955"></p><h5 id="本篇文章复现的缺点:"><a href="#本篇文章复现的缺点:" class="headerlink" title="本篇文章复现的缺点:"></a>本篇文章复现的缺点:</h5><p>虽然论文中给出了 github 代码地址,但由于长期不维护并且是基于06年伯克利的一个系统的优化型插件,跑不出来,19年有人提 issue 但都没人理,如果往后做对比实验可能只能照着论文思路自己实现个简单的版本。</p><h3 id="MatrixKV-Reducing-Write-Stalls-and-Write-Amplification-in-LSM-tree-Based-KV-Stores-with-Matrix-Container-in-NVM"><a href="#MatrixKV-Reducing-Write-Stalls-and-Write-Amplification-in-LSM-tree-Based-KV-Stores-with-Matrix-Container-in-NVM" class="headerlink" title="MatrixKV: Reducing Write Stalls and Write Amplification in LSM-tree Based KV Stores with Matrix Container in NVM"></a>MatrixKV: Reducing Write Stalls and Write Amplification in LSM-tree Based KV Stores with Matrix Container in NVM</h3><p>论文主要贡献点:</p><h5 id="1、关于-write-stall,写放大等原因与来源分析:"><a href="#1、关于-write-stall,写放大等原因与来源分析:" class="headerlink" title="1、关于 write stall,写放大等原因与来源分析:"></a>1、关于 write stall,写放大等原因与来源分析:</h5><ul><li>主要来源于 L0 ~ L1 的压缩合并</li><li>写放大随着 LSM-Tree 深度的增加而增加</li></ul><blockquote><p>LSM-Tree 的 immemtable 是跳表结构,整体有序,每当跳表满了就直接刷入磁盘上的 L0 层:</p><p>也就是说,L0 层多个 SST File 之间是无序的,SST 内部是有序的;</p><p>而 L1~Ln SST File 整体是完全有序的;</p><p>正因为 L0 可能存在多个 SST File 有针对相同 key 的增删改操作,每次合并几乎都是全量 merge;</p><p>而 L1~Ln merge 只需根据 key 的范围,选边界相同的几个 SST File 进行合并,I/O少很多。</p></blockquote><h5 id="2、提出的几个贡献点:"><a href="#2、提出的几个贡献点:" class="headerlink" title="2、提出的几个贡献点:"></a>2、提出的几个贡献点:</h5><ul><li>将 L0 层所有数据放入 PM</li><li>对 L0 层进行细粒度列压缩策略,减少 write stall</li><li>减少 level 数量方式减少写放大</li><li>Cross-Row hint search 方法提高读性能</li></ul><h5 id="3、具体实现"><a href="#3、具体实现" class="headerlink" title="3、具体实现"></a>3、具体实现</h5><p>对于 L0 层,每行 RowTable 存储 immemtable 一次性 flush 的数据,即每行数据有序,可理解为每行就是原先的一个 SST File;</p><p>内部压缩阶段,进行一个分区合并处理,按照 key 范围分区,多个行合并成一个大的行;</p><p>之后按照分区重新组成一个个 SST File</p><p>本质思想:让原本无序的 L0 层在 PM 快速存取特性下变成多个完全有序的 SST,进而让 L0 到 L1 的合并尽可能减少 I/O;</p><p><img src="/img/image-20240708164416389.png" alt="image-20240708164416389"></p><p>4、论文的实现效果</p><p><img src="/img/image-20240708165105034.png" alt="image-20240708165105034"></p><h3 id="PM-Blade-A-Persistent-Memory-Augmented-LSM-tree-Storage-for-Database"><a href="#PM-Blade-A-Persistent-Memory-Augmented-LSM-tree-Storage-for-Database" class="headerlink" title="PM-Blade: A Persistent Memory Augmented LSM-tree Storage for Database"></a>PM-Blade: A Persistent Memory Augmented LSM-tree Storage for Database</h3><h5 id="PM-Blade-贡献点"><a href="#PM-Blade-贡献点" class="headerlink" title="PM-Blade 贡献点"></a>PM-Blade 贡献点</h5><ul><li>利用 PM 优化读性能,同时减少写放大;</li><li>在基于 PM 的 L0 层进行内部压缩,将 L0 层转化为 sorted;</li><li>提出 cost-based compaction:在 L0 层基于代价的压缩策略提高读效率,减少写放大;</li><li>第一次提出在 LSM-Tree 中使用基于协程的压缩方法,减少 CPU 等待时间和 IO 争用;</li></ul><h5 id="1、整体架构"><a href="#1、整体架构" class="headerlink" title="1、整体架构"></a>1、整体架构</h5><p><img src="/img/image-20240709100834409.png" alt="image-20240709100834409"></p><h5 id="2、L0层的数据存储格式"><a href="#2、L0层的数据存储格式" class="headerlink" title="2、L0层的数据存储格式"></a>2、L0层的数据存储格式</h5><p><img src="/img/image-20240709102855728.png" alt="image-20240709102855728"></p><p>每个压缩后的键值对存储为:[共享键长度,额外长度,值大小,额外键,具体值]</p><p>Example:[ {apple, 10}, {applet, 20}, {application, 30} ]</p><p>After store:[ [0, 5, 2, ‘apple’, ‘10’], [5, 1, 2, ‘t’, ‘20’], [4, 7, 2, ‘ication’, ‘30’] ]</p><h5 id="3、L0-层的-Internel-compaction"><a href="#3、L0-层的-Internel-compaction" class="headerlink" title="3、L0 层的 Internel compaction"></a>3、L0 层的 Internel compaction</h5><p>unsorted tables:由 immemtable 直接 flush 下来的,每个块内部有序;</p><p><img src="/img/image-20240709103407001.png" alt="image-20240709103407001"></p><h5 id="4、压缩模型-compaction-model-指导压缩线程的调度"><a href="#4、压缩模型-compaction-model-指导压缩线程的调度" class="headerlink" title="4、压缩模型 compaction model 指导压缩线程的调度"></a>4、压缩模型 compaction model 指导压缩线程的调度</h5><p>压缩模型的三个目标:</p><ul><li>随着 L0 层 PM table 的增加,缓解读放大的问题</li><li>充分利用 PM 空间,减少下层合并的写放大问题</li><li>尽可能让 warm 数据留在 L0 层的 PM table,减少读数据的访问 IO</li></ul><p>算法1:给出了压缩策略的调度流程</p><p><img src="/img/image-20240709105816675.png" alt="image-20240709105816675"></p><p>1、当一个新的 PM 表被刷新到 L0 时,用公式1 rf 读放大的代价来判定是否使用内部压缩对读操作进行加速</p><p>2、判断 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="2.192ex" height="1.902ex" role="img" focusable="false" viewBox="0 -683 969 840.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D443" d="M287 628Q287 635 230 637Q206 637 199 638T192 648Q192 649 194 659Q200 679 203 681T397 683Q587 682 600 680Q664 669 707 631T751 530Q751 453 685 389Q616 321 507 303Q500 302 402 301H307L277 182Q247 66 247 59Q247 55 248 54T255 50T272 48T305 46H336Q342 37 342 35Q342 19 335 5Q330 0 319 0Q316 0 282 1T182 2Q120 2 87 2T51 1Q33 1 33 11Q33 13 36 25Q40 41 44 43T67 46Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628ZM645 554Q645 567 643 575T634 597T609 619T560 635Q553 636 480 637Q463 637 445 637T416 636T404 636Q391 635 386 627Q384 621 367 550T332 412T314 344Q314 342 395 342H407H430Q542 342 590 392Q617 419 631 471T645 554Z"></path></g><g data-mml-node="mi" transform="translate(675,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container> 表的容量是否超过阈值 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="2.322ex" height="1.332ex" role="img" focusable="false" viewBox="0 -431 1026.3 588.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D70F" d="M39 284Q18 284 18 294Q18 301 45 338T99 398Q134 425 164 429Q170 431 332 431Q492 431 497 429Q517 424 517 402Q517 388 508 376T485 360Q479 358 389 358T299 356Q298 355 283 274T251 109T233 20Q228 5 215 -4T186 -13Q153 -13 153 20V30L203 192Q214 228 227 272T248 336L254 357Q254 358 208 358Q206 358 197 358T183 359Q105 359 61 295Q56 287 53 286T39 284Z"></path></g><g data-mml-node="mi" transform="translate(470,-150) scale(0.707)"><path data-c="1D464" d="M580 385Q580 406 599 424T641 443Q659 443 674 425T690 368Q690 339 671 253Q656 197 644 161T609 80T554 12T482 -11Q438 -11 404 5T355 48Q354 47 352 44Q311 -11 252 -11Q226 -11 202 -5T155 14T118 53T104 116Q104 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Q21 293 29 315T52 366T96 418T161 441Q204 441 227 416T250 358Q250 340 217 250T184 111Q184 65 205 46T258 26Q301 26 334 87L339 96V119Q339 122 339 128T340 136T341 143T342 152T345 165T348 182T354 206T362 238T373 281Q402 395 406 404Q419 431 449 431Q468 431 475 421T483 402Q483 389 454 274T422 142Q420 131 420 107V100Q420 85 423 71T442 42T487 26Q558 26 600 148Q609 171 620 213T632 273Q632 306 619 325T593 357T580 385Z"></path></g></g></g></g></svg></mjx-container>,并判断 wf 写放大的代价来判定是否使用内部压缩</p><p>3、最后判断整个 L0 层的容量是否超过阈值 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="2.581ex" height="1.332ex" role="img" focusable="false" viewBox="0 -431 1140.8 588.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D70F" d="M39 284Q18 284 18 294Q18 301 45 338T99 398Q134 425 164 429Q170 431 332 431Q492 431 497 429Q517 424 517 402Q517 388 508 376T485 360Q479 358 389 358T299 356Q298 355 283 274T251 109T233 20Q228 5 215 -4T186 -13Q153 -13 153 20V30L203 192Q214 228 227 272T248 336L254 357Q254 358 208 358Q206 358 197 358T183 359Q105 359 61 295Q56 287 53 286T39 284Z"></path></g><g data-mml-node="mi" transform="translate(470,-150) scale(0.707)"><path data-c="1D45A" d="M21 287Q22 293 24 303T36 341T56 388T88 425T132 442T175 435T205 417T221 395T229 376L231 369Q231 367 232 367L243 378Q303 442 384 442Q401 442 415 440T441 433T460 423T475 411T485 398T493 385T497 373T500 364T502 357L510 367Q573 442 659 442Q713 442 746 415T780 336Q780 285 742 178T704 50Q705 36 709 31T724 26Q752 26 776 56T815 138Q818 149 821 151T837 153Q857 153 857 145Q857 144 853 130Q845 101 831 73T785 17T716 -10Q669 -10 648 17T627 73Q627 92 663 193T700 345Q700 404 656 404H651Q565 404 506 303L499 291L466 157Q433 26 428 16Q415 -11 385 -11Q372 -11 364 -4T353 8T350 18Q350 29 384 161L420 307Q423 322 423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 181Q151 335 151 342Q154 357 154 369Q154 405 129 405Q107 405 92 377T69 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container>,并选择合适的划分集进行向下层的压缩</p><p>符号解释:</p><p><img src="/img/image-20240709111823763.png" alt="image-20240709111823763"></p><h5 id="公式1理解"><a href="#公式1理解" class="headerlink" title="公式1理解"></a>公式1理解</h5><p>内部压缩能够提高读性能,但在不恰当的时间段进行内部压缩会产生过多的额外开销,比如当一个区块 partition 只包含少量 unsorted table 并且读的很少,对这个区块进行内部压缩是不需要的。</p><p>推导:读数据代价 = 压缩前读的代价 - 压缩后读的代价 - 压缩过程在单位时间内的成本代价</p><p><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.679ex;" xmlns="http://www.w3.org/2000/svg" width="2.267ex" height="2.207ex" role="img" focusable="false" viewBox="0 -675.5 1001.9 975.5"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msubsup"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(633,363) scale(0.707)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(633,-292.2) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container>:单位时间内在区块 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.439ex;" xmlns="http://www.w3.org/2000/svg" width="1.878ex" height="1.439ex" role="img" focusable="false" viewBox="0 -442 830 636"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45D" d="M23 287Q24 290 25 295T30 317T40 348T55 381T75 411T101 433T134 442Q209 442 230 378L240 387Q302 442 358 442Q423 442 460 395T497 281Q497 173 421 82T249 -10Q227 -10 210 -4Q199 1 187 11T168 28L161 36Q160 35 139 -51T118 -138Q118 -144 126 -145T163 -148H188Q194 -155 194 -157T191 -175Q188 -187 185 -190T172 -194Q170 -194 161 -194T127 -193T65 -192Q-5 -192 -24 -194H-32Q-39 -187 -39 -183Q-37 -156 -26 -148H-6Q28 -147 33 -136Q36 -130 94 103T155 350Q156 355 156 364Q156 405 131 405Q109 405 94 377T71 316T59 280Q57 278 43 278H29Q23 284 23 287ZM178 102Q200 26 252 26Q282 26 310 49T356 107Q374 141 392 215T411 325V331Q411 405 350 405Q339 405 328 402T306 393T286 380T269 365T254 350T243 336T235 326L232 322Q232 321 229 308T218 264T204 212Q178 106 178 102Z"></path></g><g data-mml-node="mi" transform="translate(536,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container> 的读操作数</p><p>压缩前存在 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="2.097ex" height="1.357ex" role="img" focusable="false" viewBox="0 -442 927 599.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(633,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container> 个未排序的 PM table,最多需要读取 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="5.994ex" height="1.864ex" role="img" focusable="false" viewBox="0 -666 2649.4 823.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(633,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(1149.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mn" transform="translate(2149.4,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path></g></g></g></svg></mjx-container> 个,最少需要读取 1 个,平均为<mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.781ex;" xmlns="http://www.w3.org/2000/svg" width="4.523ex" height="2.868ex" role="img" focusable="false" viewBox="0 -922.5 1999.1 1267.5"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mrow" transform="translate(220,451.6) scale(0.707)"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(633,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(927,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"></path></g><g data-mml-node="mn" transform="translate(1705,0)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path></g></g><g data-mml-node="mn" transform="translate(822.8,-345) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"></path></g><rect width="1759.1" height="60" x="120" y="220"></rect></g></g></g></svg></mjx-container> 个</p><p>压缩后,仅需1个,<mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="1.87ex" height="1.902ex" role="img" focusable="false" viewBox="0 -683 826.3 840.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D43C" d="M43 1Q26 1 26 10Q26 12 29 24Q34 43 39 45Q42 46 54 46H60Q120 46 136 53Q137 53 138 54Q143 56 149 77T198 273Q210 318 216 344Q286 624 286 626Q284 630 284 631Q274 637 213 637H193Q184 643 189 662Q193 677 195 680T209 683H213Q285 681 359 681Q481 681 487 683H497Q504 676 504 672T501 655T494 639Q491 637 471 637Q440 637 407 634Q393 631 388 623Q381 609 337 432Q326 385 315 341Q245 65 245 59Q245 52 255 50T307 46H339Q345 38 345 37T342 19Q338 6 332 0H316Q279 2 179 2Q143 2 113 2T65 2T43 1Z"></path></g><g data-mml-node="mi" transform="translate(473,-150) scale(0.707)"><path data-c="1D44F" d="M73 647Q73 657 77 670T89 683Q90 683 161 688T234 694Q246 694 246 685T212 542Q204 508 195 472T180 418L176 399Q176 396 182 402Q231 442 283 442Q345 442 383 396T422 280Q422 169 343 79T173 -11Q123 -11 82 27T40 150V159Q40 180 48 217T97 414Q147 611 147 623T109 637Q104 637 101 637H96Q86 637 83 637T76 640T73 647ZM336 325V331Q336 405 275 405Q258 405 240 397T207 376T181 352T163 330L157 322L136 236Q114 150 114 114Q114 66 138 42Q154 26 178 26Q211 26 245 58Q270 81 285 114T318 219Q336 291 336 325Z"></path></g></g></g></g></svg></mjx-container> 为 PM table 内部二分的代价</p><p>因此,<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="14.752ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 6520.5 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(833,0)"><path data-c="1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path></g><g data-mml-node="mi" transform="translate(1266,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mi" transform="translate(1751,0)"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"></path></g><g data-mml-node="msub" transform="translate(2220,0)"><g data-mml-node="mi"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mi" transform="translate(394,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(2908,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(3297,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(3748,0)"><path data-c="1D453" d="M118 -162Q120 -162 124 -164T135 -167T147 -168Q160 -168 171 -155T187 -126Q197 -99 221 27T267 267T289 382V385H242Q195 385 192 387Q188 390 188 397L195 425Q197 430 203 430T250 431Q298 431 298 432Q298 434 307 482T319 540Q356 705 465 705Q502 703 526 683T550 630Q550 594 529 578T487 561Q443 561 443 603Q443 622 454 636T478 657L487 662Q471 668 457 668Q445 668 434 658T419 630Q412 601 403 552T387 469T380 433Q380 431 435 431Q480 431 487 430T498 424Q499 420 496 407T491 391Q489 386 482 386T428 385H372L349 263Q301 15 282 -47Q255 -132 212 -173Q175 -205 139 -205Q107 -205 81 -186T55 -132Q55 -95 76 -78T118 -61Q162 -61 162 -103Q162 -122 151 -136T127 -157L118 -162Z"></path></g><g data-mml-node="mo" transform="translate(4298,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(4964.7,0)"><path data-c="3E" d="M84 520Q84 528 88 533T96 539L99 540Q106 540 253 471T544 334L687 265Q694 260 694 250T687 235Q685 233 395 96L107 -40H101Q83 -38 83 -20Q83 -19 83 -17Q82 -10 98 -1Q117 9 248 71Q326 108 378 132L626 250L378 368Q90 504 86 509Q84 513 84 520Z"></path></g><g data-mml-node="mn" transform="translate(6020.5,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g></g></svg></mjx-container> 意味着经过 L0 层内部压缩后读的代价是比压缩前整体都要小的,压缩是值得的 </p><p><img src="/img/image-20240709111849289.png" alt="image-20240709111849289"></p><h5 id="公式2:尽可能减少写放大"><a href="#公式2:尽可能减少写放大" class="headerlink" title="公式2:尽可能减少写放大"></a>公式2:尽可能减少写放大</h5><p>当区块 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="2.192ex" height="1.902ex" role="img" focusable="false" viewBox="0 -683 969 840.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D443" d="M287 628Q287 635 230 637Q206 637 199 638T192 648Q192 649 194 659Q200 679 203 681T397 683Q587 682 600 680Q664 669 707 631T751 530Q751 453 685 389Q616 321 507 303Q500 302 402 301H307L277 182Q247 66 247 59Q247 55 248 54T255 50T272 48T305 46H336Q342 37 342 35Q342 19 335 5Q330 0 319 0Q316 0 282 1T182 2Q120 2 87 2T51 1Q33 1 33 11Q33 13 36 25Q40 41 44 43T67 46Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628ZM645 554Q645 567 643 575T634 597T609 619T560 635Q553 636 480 637Q463 637 445 637T416 636T404 636Q391 635 386 627Q384 621 367 550T332 412T314 344Q314 342 395 342H407H430Q542 342 590 392Q617 419 631 471T645 554Z"></path></g><g data-mml-node="mi" transform="translate(675,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container> 的 table 数量超过阈值,并且每个 table 之间存在足够多重复数据时,将这些 table 进行 L0 层的内部压缩最为划算,这可以缓解向 L1 层的写放大问题</p><p><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.667ex;" xmlns="http://www.w3.org/2000/svg" width="3.857ex" height="1.667ex" role="img" focusable="false" viewBox="0 -442 1704.8 737"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="TeXAtom" transform="translate(633,-150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D44F" d="M73 647Q73 657 77 670T89 683Q90 683 161 688T234 694Q246 694 246 685T212 542Q204 508 195 472T180 418L176 399Q176 396 182 402Q231 442 283 442Q345 442 383 396T422 280Q422 169 343 79T173 -11Q123 -11 82 27T40 150V159Q40 180 48 217T97 414Q147 611 147 623T109 637Q104 637 101 637H96Q86 637 83 637T76 640T73 647ZM336 325V331Q336 405 275 405Q258 405 240 397T207 376T181 352T163 330L157 322L136 236Q114 150 114 114Q114 66 138 42Q154 26 178 26Q211 26 245 58Q270 81 285 114T318 219Q336 291 336 325Z"></path></g><g data-mml-node="mi" transform="translate(429,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"></path></g><g data-mml-node="mi" transform="translate(895,0)"><path data-c="1D453" d="M118 -162Q120 -162 124 -164T135 -167T147 -168Q160 -168 171 -155T187 -126Q197 -99 221 27T267 267T289 382V385H242Q195 385 192 387Q188 390 188 397L195 425Q197 430 203 430T250 431Q298 431 298 432Q298 434 307 482T319 540Q356 705 465 705Q502 703 526 683T550 630Q550 594 529 578T487 561Q443 561 443 603Q443 622 454 636T478 657L487 662Q471 668 457 668Q445 668 434 658T419 630Q412 601 403 552T387 469T380 433Q380 431 435 431Q480 431 487 430T498 424Q499 420 496 407T491 391Q489 386 482 386T428 385H372L349 263Q301 15 282 -47Q255 -132 212 -173Q175 -205 139 -205Q107 -205 81 -186T55 -132Q55 -95 76 -78T118 -61Q162 -61 162 -103Q162 -122 151 -136T127 -157L118 -162Z"></path></g></g></g></g></g></svg></mjx-container>:内部压缩前的区块总记录数,约等于写入数量</p><p><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.667ex;" xmlns="http://www.w3.org/2000/svg" width="3.849ex" height="1.667ex" role="img" focusable="false" viewBox="0 -442 1701.2 737"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="TeXAtom" transform="translate(633,-150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D44E" d="M33 157Q33 258 109 349T280 441Q331 441 370 392Q386 422 416 422Q429 422 439 414T449 394Q449 381 412 234T374 68Q374 43 381 35T402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487Q506 153 506 144Q506 138 501 117T481 63T449 13Q436 0 417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157ZM351 328Q351 334 346 350T323 385T277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q217 26 254 59T298 110Q300 114 325 217T351 328Z"></path></g><g data-mml-node="mi" transform="translate(529,0)"><path data-c="1D453" d="M118 -162Q120 -162 124 -164T135 -167T147 -168Q160 -168 171 -155T187 -126Q197 -99 221 27T267 267T289 382V385H242Q195 385 192 387Q188 390 188 397L195 425Q197 430 203 430T250 431Q298 431 298 432Q298 434 307 482T319 540Q356 705 465 705Q502 703 526 683T550 630Q550 594 529 578T487 561Q443 561 443 603Q443 622 454 636T478 657L487 662Q471 668 457 668Q445 668 434 658T419 630Q412 601 403 552T387 469T380 433Q380 431 435 431Q480 431 487 430T498 424Q499 420 496 407T491 391Q489 386 482 386T428 385H372L349 263Q301 15 282 -47Q255 -132 212 -173Q175 -205 139 -205Q107 -205 81 -186T55 -132Q55 -95 76 -78T118 -61Q162 -61 162 -103Q162 -122 151 -136T127 -157L118 -162Z"></path></g><g data-mml-node="mi" transform="translate(1079,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></g></g></svg></mjx-container>:内部压缩后的区块总记录数,约等于更新数量</p><p>若 <mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="15.352ex" height="2.262ex" role="img" focusable="false" viewBox="0 -750 6785.5 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="394" d="M51 0Q46 4 46 7Q46 9 215 357T388 709Q391 716 416 716Q439 716 444 709Q447 705 616 357T786 7Q786 4 781 0H51ZM507 344L384 596L137 92L383 91H630Q630 93 507 344Z"></path></g><g data-mml-node="mi" transform="translate(833,0)"><path data-c="1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path></g><g data-mml-node="mi" transform="translate(1266,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mi" transform="translate(1751,0)"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"></path></g><g data-mml-node="msub" transform="translate(2220,0)"><g data-mml-node="mi"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g><g data-mml-node="mi" transform="translate(394,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g><g data-mml-node="mo" transform="translate(2908,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path></g><g data-mml-node="mi" transform="translate(3297,0)"><path data-c="1D464" d="M580 385Q580 406 599 424T641 443Q659 443 674 425T690 368Q690 339 671 253Q656 197 644 161T609 80T554 12T482 -11Q438 -11 404 5T355 48Q354 47 352 44Q311 -11 252 -11Q226 -11 202 -5T155 14T118 53T104 116Q104 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Q21 293 29 315T52 366T96 418T161 441Q204 441 227 416T250 358Q250 340 217 250T184 111Q184 65 205 46T258 26Q301 26 334 87L339 96V119Q339 122 339 128T340 136T341 143T342 152T345 165T348 182T354 206T362 238T373 281Q402 395 406 404Q419 431 449 431Q468 431 475 421T483 402Q483 389 454 274T422 142Q420 131 420 107V100Q420 85 423 71T442 42T487 26Q558 26 600 148Q609 171 620 213T632 273Q632 306 619 325T593 357T580 385Z"></path></g><g data-mml-node="mi" transform="translate(4013,0)"><path data-c="1D453" d="M118 -162Q120 -162 124 -164T135 -167T147 -168Q160 -168 171 -155T187 -126Q197 -99 221 27T267 267T289 382V385H242Q195 385 192 387Q188 390 188 397L195 425Q197 430 203 430T250 431Q298 431 298 432Q298 434 307 482T319 540Q356 705 465 705Q502 703 526 683T550 630Q550 594 529 578T487 561Q443 561 443 603Q443 622 454 636T478 657L487 662Q471 668 457 668Q445 668 434 658T419 630Q412 601 403 552T387 469T380 433Q380 431 435 431Q480 431 487 430T498 424Q499 420 496 407T491 391Q489 386 482 386T428 385H372L349 263Q301 15 282 -47Q255 -132 212 -173Q175 -205 139 -205Q107 -205 81 -186T55 -132Q55 -95 76 -78T118 -61Q162 -61 162 -103Q162 -122 151 -136T127 -157L118 -162Z"></path></g><g data-mml-node="mo" transform="translate(4563,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path></g><g data-mml-node="mo" transform="translate(5229.7,0)"><path data-c="3E" d="M84 520Q84 528 88 533T96 539L99 540Q106 540 253 471T544 334L687 265Q694 260 694 250T687 235Q685 233 395 96L107 -40H101Q83 -38 83 -20Q83 -19 83 -17Q82 -10 98 -1Q117 9 248 71Q326 108 378 132L626 250L378 368Q90 504 86 509Q84 513 84 520Z"></path></g><g data-mml-node="mn" transform="translate(6285.5,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path></g></g></g></svg></mjx-container></p><p>意味着 内部压缩消失的数据若直接参与下层的合并代价 高于 压缩前所有数据参与内部压缩的代价,此时,内部压缩比直接进行下层合并要划算得多,进而减少写放大。</p><p><img src="/img/image-20240709133834332.png" alt="image-20240709133834332"></p><h5 id="公式3:尽可能将-warm-数据留在-L0-层"><a href="#公式3:尽可能将-warm-数据留在-L0-层" class="headerlink" title="公式3:尽可能将 warm 数据留在 L0 层"></a>公式3:尽可能将 warm 数据留在 L0 层</h5><p>由于 L0 各个 SST File 之间在内部压缩后形成有序,向下层合并过程不需要所有 SST File 参与;</p><p>我们可以决定哪些 SST File 需要合并到 L1</p><p>公式 3 将它抽象成一个背包问题,选取哪几个 SST File 合并能够让效益最大化</p><p>即选取的几个分区,它们受到用户侧读取的数据频率最多,这几个分区可以留在 L0 层,</p><p>同时它们的累加大小总和小于暂不向下合并的阈值 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="1.754ex" height="1.332ex" role="img" focusable="false" viewBox="0 -431 775.3 588.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D70F" d="M39 284Q18 284 18 294Q18 301 45 338T99 398Q134 425 164 429Q170 431 332 431Q492 431 497 429Q517 424 517 402Q517 388 508 376T485 360Q479 358 389 358T299 356Q298 355 283 274T251 109T233 20Q228 5 215 -4T186 -13Q153 -13 153 20V30L203 192Q214 228 227 272T248 336L254 357Q254 358 208 358Q206 358 197 358T183 359Q105 359 61 295Q56 287 53 286T39 284Z"></path></g><g data-mml-node="mi" transform="translate(470,-150) scale(0.707)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"></path></g></g></g></g></svg></mjx-container></p><p><img src="/img/image-20240709135152692.png" alt="image-20240709135152692"></p><p>以上就是 cost-based compaction model</p><h5 id="5、基于协程的压缩方法"><a href="#5、基于协程的压缩方法" class="headerlink" title="5、基于协程的压缩方法"></a>5、基于协程的压缩方法</h5><ul><li><p>以下的压缩仅用于 major compaction:上下层之间的压缩</p></li><li><p>表3是在单核 CPU 上运行多个 compaction 任务,每个任务由一个线程负责,即使如此,无论是 CPU 还是 IO 设备,都没有充分利用好,大部分时间都处在空闲。</p></li></ul><p><img src="/img/image-20240709142743718-17205064641031.png" alt="image-20240709142743718"></p><p>合并过程可以分为三个阶段:</p><ul><li>read block:将 <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.357ex;" xmlns="http://www.w3.org/2000/svg" width="2.28ex" height="1.902ex" role="img" focusable="false" viewBox="0 -683 1008 840.8"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D43F" d="M228 637Q194 637 192 641Q191 643 191 649Q191 673 202 682Q204 683 217 683Q271 680 344 680Q485 680 506 683H518Q524 677 524 674T522 656Q517 641 513 637H475Q406 636 394 628Q387 624 380 600T313 336Q297 271 279 198T252 88L243 52Q243 48 252 48T311 46H328Q360 46 379 47T428 54T478 72T522 106T564 161Q580 191 594 228T611 270Q616 273 628 273H641Q647 264 647 262T627 203T583 83T557 9Q555 4 553 3T537 0T494 -1Q483 -1 418 -1T294 0H116Q32 0 32 10Q32 17 34 24Q39 43 44 45Q48 46 59 46H65Q92 46 125 49Q139 52 144 61Q147 65 216 339T285 628Q285 635 228 637Z"></path></g><g data-mml-node="mi" transform="translate(714,-150) scale(0.707)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></svg></mjx-container> 层数据读入内存 read buffer</li><li>sort:在 read buffer 中排好序,并放入内存中另外一个区域 write buffer</li><li>write block:一旦 write buffer 填充满了,立刻刷盘</li></ul><p>S1, S3 都是 IO 操作,S2 为 CPU 操作,因此一个理想的合并过程应该是图4(a)</p><p>但在实际测试中发现,图4(b) 才是一般情况,即出现极短的 S2 ( fragment time )</p><p><img src="/img/image-20240709144557816.png" alt="image-20240709144557816"></p><p>产生的原因本质上是内存缓冲区的大小是有限的,因此转移缓冲区操作会产生碎片时间,以图5为例</p><p>第一个 S2 将 13 放入 write buffer 后,缓冲区满了,立刻执行 S3 刷入磁盘,而此时 read buffer 并不算太空,没有必要执行 read buffer 这个 IO 操作,因此 S3 结束后,依然是 S2,全部转移走后才执行S1读取数据</p><p><img src="/img/image-20240709144609405.png" alt="image-20240709144609405"></p><p>针对这个实际情况,如果直接上协程,即每个协程分配一个压缩操作,会无法充分利用 CPU 和 IO</p><p>图4(c) 的 CPU,IO 利用率分析,比如 S2 阶段切换任务后,C2协程一下就做完了,此时虽然 C1的S3 和 C2的S2 同时结束,但由于是随机性调度,调度给到 C2的S3 执行,导致让 CPU 处于闲置。 </p><p><img src="/img/image-20240709145442054.png" alt="image-20240709145442054"></p><h5 id="coroutine-based-method"><a href="#coroutine-based-method" class="headerlink" title="coroutine-based method"></a>coroutine-based method</h5><p>优化方面:</p><ul><li>将 S2 产生的碎片时间消除</li><li>调度协程防止突发的 IO 争用</li></ul><p>具体做法:</p><ul><li>将 S3 阶段分配一个专用的 flush 协程</li></ul><blockquote><p>原先对 write buffer 的处理策略是只有缓冲池满了,立刻刷入磁盘,这就导致 S2 执行时 write buffer 可能已经存在部分数据,进而存在碎片时间</p><p>现在由一个专门的 flush coroutine 进行刷盘,无需等待缓冲池满了的情况</p><p>并且这个协程可以由接下来的协程调度器进行调度。</p></blockquote><ul><li>提出一个协程调度策略,并将一个完整的 compaction 任务划分为多个协程级子任务</li></ul><blockquote><p>调度器的核心思想:当IO压力较低时执行IO操作,并在压力变高时限制IO操作数量;</p><p>具体方法:对 IO 数量进行监控,建立 flush 协程数量的数学模型</p></blockquote><p><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.667ex;" xmlns="http://www.w3.org/2000/svg" width="5.14ex" height="1.667ex" role="img" focusable="false" viewBox="0 -442 2272 737"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45E" d="M33 157Q33 258 109 349T280 441Q340 441 372 389Q373 390 377 395T388 406T404 418Q438 442 450 442Q454 442 457 439T460 434Q460 425 391 149Q320 -135 320 -139Q320 -147 365 -148H390Q396 -156 396 -157T393 -175Q389 -188 383 -194H370Q339 -192 262 -192Q234 -192 211 -192T174 -192T157 -193Q143 -193 143 -185Q143 -182 145 -170Q149 -154 152 -151T172 -148Q220 -148 230 -141Q238 -136 258 -53T279 32Q279 33 272 29Q224 -10 172 -10Q117 -10 75 30T33 157ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path></g><g data-mml-node="TeXAtom" transform="translate(479,-150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D453" d="M118 -162Q120 -162 124 -164T135 -167T147 -168Q160 -168 171 -155T187 -126Q197 -99 221 27T267 267T289 382V385H242Q195 385 192 387Q188 390 188 397L195 425Q197 430 203 430T250 431Q298 431 298 432Q298 434 307 482T319 540Q356 705 465 705Q502 703 526 683T550 630Q550 594 529 578T487 561Q443 561 443 603Q443 622 454 636T478 657L487 662Q471 668 457 668Q445 668 434 658T419 630Q412 601 403 552T387 469T380 433Q380 431 435 431Q480 431 487 430T498 424Q499 420 496 407T491 391Q489 386 482 386T428 385H372L349 263Q301 15 282 -47Q255 -132 212 -173Q175 -205 139 -205Q107 -205 81 -186T55 -132Q55 -95 76 -78T118 -61Q162 -61 162 -103Q162 -122 151 -136T127 -157L118 -162Z"></path></g><g data-mml-node="mi" transform="translate(550,0)"><path data-c="1D459" d="M117 59Q117 26 142 26Q179 26 205 131Q211 151 215 152Q217 153 225 153H229Q238 153 241 153T246 151T248 144Q247 138 245 128T234 90T214 43T183 6T137 -11Q101 -11 70 11T38 85Q38 97 39 102L104 360Q167 615 167 623Q167 626 166 628T162 632T157 634T149 635T141 636T132 637T122 637Q112 637 109 637T101 638T95 641T94 647Q94 649 96 661Q101 680 107 682T179 688Q194 689 213 690T243 693T254 694Q266 694 266 686Q266 675 193 386T118 83Q118 81 118 75T117 65V59Z"></path></g><g data-mml-node="mi" transform="translate(848,0)"><path data-c="1D462" d="M21 287Q21 295 30 318T55 370T99 420T158 442Q204 442 227 417T250 358Q250 340 216 246T182 105Q182 62 196 45T238 27T291 44T328 78L339 95Q341 99 377 247Q407 367 413 387T427 416Q444 431 463 431Q480 431 488 421T496 402L420 84Q419 79 419 68Q419 43 426 35T447 26Q469 29 482 57T512 145Q514 153 532 153Q551 153 551 144Q550 139 549 130T540 98T523 55T498 17T462 -8Q454 -10 438 -10Q372 -10 347 46Q345 45 336 36T318 21T296 6T267 -6T233 -11Q189 -11 155 7Q103 38 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(1420,0)"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"></path></g><g data-mml-node="mi" transform="translate(1889,0)"><path data-c="210E" d="M137 683Q138 683 209 688T282 694Q294 694 294 685Q294 674 258 534Q220 386 220 383Q220 381 227 388Q288 442 357 442Q411 442 444 415T478 336Q478 285 440 178T402 50Q403 36 407 31T422 26Q450 26 474 56T513 138Q516 149 519 151T535 153Q555 153 555 145Q555 144 551 130Q535 71 500 33Q466 -10 419 -10H414Q367 -10 346 17T325 74Q325 90 361 192T398 345Q398 404 354 404H349Q266 404 205 306L198 293L164 158Q132 28 127 16Q114 -11 83 -11Q69 -11 59 -2T48 16Q48 30 121 320L195 616Q195 629 188 632T149 637H128Q122 643 122 645T124 664Q129 683 137 683Z"></path></g></g></g></g></g></svg></mjx-container>:当前允许 flush 协程运行的 IO 数量</p><p>q:系统最大值,一般由用户设定</p><p><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.65ex;" xmlns="http://www.w3.org/2000/svg" width="4.875ex" height="1.65ex" role="img" focusable="false" viewBox="0 -442 2154.6 729.2"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45E" d="M33 157Q33 258 109 349T280 441Q340 441 372 389Q373 390 377 395T388 406T404 418Q438 442 450 442Q454 442 457 439T460 434Q460 425 391 149Q320 -135 320 -139Q320 -147 365 -148H390Q396 -156 396 -157T393 -175Q389 -188 383 -194H370Q339 -192 262 -192Q234 -192 211 -192T174 -192T157 -193Q143 -193 143 -185Q143 -182 145 -170Q149 -154 152 -151T172 -148Q220 -148 230 -141Q238 -136 258 -53T279 32Q279 33 272 29Q224 -10 172 -10Q117 -10 75 30T33 157ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path></g><g data-mml-node="TeXAtom" transform="translate(479,-150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path></g><g data-mml-node="mi" transform="translate(433,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"></path></g><g data-mml-node="mi" transform="translate(918,0)"><path data-c="1D45A" d="M21 287Q22 293 24 303T36 341T56 388T88 425T132 442T175 435T205 417T221 395T229 376L231 369Q231 367 232 367L243 378Q303 442 384 442Q401 442 415 440T441 433T460 423T475 411T485 398T493 385T497 373T500 364T502 357L510 367Q573 442 659 442Q713 442 746 415T780 336Q780 285 742 178T704 50Q705 36 709 31T724 26Q752 26 776 56T815 138Q818 149 821 151T837 153Q857 153 857 145Q857 144 853 130Q845 101 831 73T785 17T716 -10Q669 -10 648 17T627 73Q627 92 663 193T700 345Q700 404 656 404H651Q565 404 506 303L499 291L466 157Q433 26 428 16Q415 -11 385 -11Q372 -11 364 -4T353 8T350 18Q350 29 384 161L420 307Q423 322 423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 181Q151 335 151 342Q154 357 154 369Q154 405 129 405Q107 405 92 377T69 316T57 280Q55 278 41 278H27Q21 284 21 287Z"></path></g><g data-mml-node="mi" transform="translate(1796,0)"><path data-c="1D45D" d="M23 287Q24 290 25 295T30 317T40 348T55 381T75 411T101 433T134 442Q209 442 230 378L240 387Q302 442 358 442Q423 442 460 395T497 281Q497 173 421 82T249 -10Q227 -10 210 -4Q199 1 187 11T168 28L161 36Q160 35 139 -51T118 -138Q118 -144 126 -145T163 -148H188Q194 -155 194 -157T191 -175Q188 -187 185 -190T172 -194Q170 -194 161 -194T127 -193T65 -192Q-5 -192 -24 -194H-32Q-39 -187 -39 -183Q-37 -156 -26 -148H-6Q28 -147 33 -136Q36 -130 94 103T155 350Q156 355 156 364Q156 405 131 405Q109 405 94 377T71 316T59 280Q57 278 43 278H29Q23 284 23 287ZM178 102Q200 26 252 26Q282 26 310 49T356 107Q374 141 392 215T411 325V331Q411 405 350 405Q339 405 328 402T306 393T286 380T269 365T254 350T243 336T235 326L232 322Q232 321 229 308T218 264T204 212Q178 106 178 102Z"></path></g></g></g></g></g></svg></mjx-container>:实时压缩协程的 IO 数量,即 read block 协程数</p><p><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.439ex;" xmlns="http://www.w3.org/2000/svg" width="2.918ex" height="1.439ex" role="img" focusable="false" viewBox="0 -442 1289.8 636"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D45E" d="M33 157Q33 258 109 349T280 441Q340 441 372 389Q373 390 377 395T388 406T404 418Q438 442 450 442Q454 442 457 439T460 434Q460 425 391 149Q320 -135 320 -139Q320 -147 365 -148H390Q396 -156 396 -157T393 -175Q389 -188 383 -194H370Q339 -192 262 -192Q234 -192 211 -192T174 -192T157 -193Q143 -193 143 -185Q143 -182 145 -170Q149 -154 152 -151T172 -148Q220 -148 230 -141Q238 -136 258 -53T279 32Q279 33 272 29Q224 -10 172 -10Q117 -10 75 30T33 157ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"></path></g><g data-mml-node="TeXAtom" transform="translate(479,-150) scale(0.707)" data-mjx-texclass="ORD"><g data-mml-node="mi"><path data-c="1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"></path></g><g data-mml-node="mi" transform="translate(433,0)"><path data-c="1D459" d="M117 59Q117 26 142 26Q179 26 205 131Q211 151 215 152Q217 153 225 153H229Q238 153 241 153T246 151T248 144Q247 138 245 128T234 90T214 43T183 6T137 -11Q101 -11 70 11T38 85Q38 97 39 102L104 360Q167 615 167 623Q167 626 166 628T162 632T157 634T149 635T141 636T132 637T122 637Q112 637 109 637T101 638T95 641T94 647Q94 649 96 661Q101 680 107 682T179 688Q194 689 213 690T243 693T254 694Q266 694 266 686Q266 675 193 386T118 83Q118 81 118 75T117 65V59Z"></path></g><g data-mml-node="mi" transform="translate(731,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"></path></g></g></g></g></g></svg></mjx-container>:客户端侧读访问请求中需要到 SSD 访问的 IO 数量</p><p><img src="/img/image-20240709151449272.png" alt="image-20240709151449272"></p><p>在实现上,关于线程绑定物理核:</p><blockquote><p>To take full advantage of multicore, we assign c worker threads to c physical cores.</p></blockquote><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-comment">// every worker thread(int i)</span><br><span class="hljs-comment">// init: all bitmap set to zero</span><br><span class="hljs-type">cpu_set_t</span> cpuset;<br><span class="hljs-built_in">CPU_ZERO</span>(&cpuset);<br><br><span class="hljs-comment">// set location of bitmap, i = 1 设置要绑定的 CPU 核心编号</span><br><span class="hljs-built_in">CPU_SET</span>(i, &cpuset);<br><br><span class="hljs-comment">// set thread affinity bind to cpu</span><br><span class="hljs-built_in">pthread_setaffinity_np</span>(<span class="hljs-built_in">pthread_self</span>(), <span class="hljs-built_in">sizeof</span>(<span class="hljs-type">cpu_set_t</span>), &cpuset);<br></code></pre></td></tr></table></figure><h5 id="论文实验结果"><a href="#论文实验结果" class="headerlink" title="论文实验结果"></a>论文实验结果</h5><p>benchmark:db_bench, ycsb</p><ul><li>用了 PM 后,写放大显著降低</li></ul><p><img src="/img/image-20240709152244370.png" alt="image-20240709152244370"></p><ul><li>PM 对比 SSD,在读延迟方面的降低</li></ul><img src="/img/image-20240709152355625.png" alt="image-20240709152355625" style="zoom: 80%;"><ul><li>线程调度策略、朴素的协程调度策略、PMBlade调度策略对比</li></ul><p><img src="/img/image-20240709152451263.png" alt="image-20240709152451263"></p><ul><li>ycsb不同负载下的多个系统间的吞吐对比</li></ul><p><img src="/img/image-20240709152642316.png" alt="image-20240709152642316"></p><h3 id="On-Performance-Stability-in-LSM-based-Storage-Systems"><a href="#On-Performance-Stability-in-LSM-based-Storage-Systems" class="headerlink" title="On Performance Stability in LSM-based Storage Systems"></a>On Performance Stability in LSM-based Storage Systems</h3><h5 id="贡献点:"><a href="#贡献点:" class="headerlink" title="贡献点:"></a>贡献点:</h5><ul><li>两阶段实验方法来评估各种 LSM-Tree 的写延迟</li><li>探索 LSM 合并调度器的设计</li></ul><h5 id="分析-write-stall-出现的原因"><a href="#分析-write-stall-出现的原因" class="headerlink" title="分析 write stall 出现的原因"></a>分析 write stall 出现的原因</h5><ul><li>快速的内存写操作和较慢的背景 IO 操作之间的不匹配,如果异步的 IO flush / merge 跟不上,前台的内存写操作就会减慢或停止;</li><li>数据到达率 data arrival rate 直接影响 LSM-Tree 的写延迟,如果数据到达率较低,写入停滞发生的可能就会更低</li></ul><p><img src="/img/image-20240606094228539.png" alt="image-20240606094228539"></p><p>作者提出两个问题作为方向指导:</p><ul><li>如果数据到达率为最大写吞吐的 95%,是否还会导致写停滞?</li><li>LSM-Tree 是否能设计成高写吞吐、低写延迟?</li></ul><p>两阶段评估:</p><ul><li>测试阶段,尽可能多的写测量最大写吞吐</li><li>运行阶段,以接近实测最大写吞吐的数据达到率,评估写停滞</li></ul><p>实现上面临的挑战:</p><ul><li>如何准确测量出 LSM-Tree 的最大写入吞吐?</li><li>如何对 LSM-Tree 的 IO 操作进行最优调度,从而在运行时最小化写停滞?</li></ul><p>实验结果:</p><ul><li><p>LSM-Tree 的合并调度器的策略确实会对写停滞带来很大的影响</p></li><li><p>作者听出一种新的合并调度器来解决挑战</p></li><li><p>通过他们的方法和设置,LSM-Tree 能够同时保证较高的吞吐和较低的写停滞</p></li></ul><h5 id="Merge-Policy"><a href="#Merge-Policy" class="headerlink" title="Merge Policy"></a>Merge Policy</h5><ul><li>Leveling Merge Policy 分级合并(适用于读密集型)<strong>Leveldb</strong></li></ul><blockquote><p>每一层的文件大小相对均匀,level i+1 比 level i 固定大 T 倍</p><p>减少读放大,查询快延迟低:每层文件大小均匀,需要访问的文件数少</p><p>空间利用率高</p><p>增加写放大:合并频繁,IO 写操作过多</p></blockquote><ul><li>Tiering Merge Policy 分层合并(适用于写密集型)</li></ul><blockquote><p>每一层包含一组文件,当每层文件数或总大小达到阈值时,触发合并</p><p>高效写入:批量合并,降低前一种频繁合并的写放大</p><p>空间利用率低</p><p>增加读放大:读取操作需要扫描更多的文件</p></blockquote><p><img src="/img/image-20240606102336204.png" alt="image-20240606102336204"></p><p>评测部分 性能度量的三个指标:</p><ul><li>Arrival Rate:客户端提交写操作的速率</li><li>Processing Rate:LSM-Tree 可以处理写操作的速率</li><li>Write Throughput:LSM-Tree 单位时间内处理的写次数</li></ul><h5 id="LSM-Tree-的写入代价"><a href="#LSM-Tree-的写入代价" class="headerlink" title="LSM-Tree 的写入代价"></a>LSM-Tree 的写入代价</h5><p>由LSM-Tree设计和工作负载决定,而不是由如何执行合并确定;</p><p>但不同的调度选择会影响 LSM-Tree 的写延迟,通过精心设计合并调度能最小化写延迟;</p><p>提出一种 greedy scheduler:</p><ul><li><p>优先将 IO 带宽分配给当前 k 个最小的合并操作,即使大的合并会 starve</p></li><li><p>这k个合并由于在不同的层级,可以并发执行</p></li><li><p>目的:greedy 策略能够在任意时刻最小化磁盘上的组件数量,组件数量越少,写放大越低</p></li></ul><p>但这种策略在不同的 LSM-Tree 合并模式下性能表现不同</p><p>Tiering 显著好于 leveling,在 leveldb 下的性能表现不太行</p><p>single:单线程一个接一个层合并,是当前 leveldb 的默认策略;</p><p>fair:公平调度策略,当前 Rocksdb 的默认策略;</p><p><img src="/img/image-20240708170922889.png" alt="image-20240708170922889"></p>]]></content>
<categories>
<category>数据库</category>
</categories>
<tags>
<tag>LSM-Tree</tag>
</tags>
</entry>
<entry>
<title>I/O Passthru (FAST 24) 论文笔记</title>
<link href="/2024/05/30/io-passthru/"/>
<url>/2024/05/30/io-passthru/</url>
<content type="html"><![CDATA[<p>I/O Passthru: Upstreaming a flexible and efficient I/O Path in Linux (FAST 24)</p><p>I/O Passthru: 一种在Linux上灵活高效的 IO 路径</p><h2 id="1-Motivation"><a href="#1-Motivation" class="headerlink" title="1 Motivation"></a>1 Motivation</h2><h5 id="Problem-1:User-Interface-Scarcity-用户接口-系统调用的缺乏"><a href="#Problem-1:User-Interface-Scarcity-用户接口-系统调用的缺乏" class="headerlink" title="Problem 1:User Interface Scarcity 用户接口/系统调用的缺乏"></a>Problem 1:User Interface Scarcity 用户接口/系统调用的缺乏</h5><p>Kernel perfers protocol-agnostic abstractions 内核喜欢与协议无关的抽象</p><ul><li>Deal with a variety of devices in a similar fashion. 可扩展性,到多种设备</li><li>Coining a new syscall requires a more generic use case than the NVMe-specific one. 必须具有通用性</li><li>Once added, syscall has to be supported indefinitely. 一旦加入内核,必须长期支持</li></ul><h5 id="Problem-2:Poor-scalability"><a href="#Problem-2:Poor-scalability" class="headerlink" title="Problem 2:Poor scalability"></a>Problem 2:Poor scalability</h5><p>various I/O paths in the Linux kernel</p><p>路径1:speak file -> FS abstraction 通过文件系统访问存储设备,存取数据</p><p>路径2:speak block -> Block abstraction 使用较低级别的接口,与设备文件符交互进行访问</p><p>路径3:speak NVMe -> ioctl -> /dev/nvme0n1 NVMe设备专用路径,通过ioctl系统调用,同时附带特定的控制命令直接到 NVMe 驱动</p><p>路径1、2性能劣势:多了 FS / Block 抽象层,系统调用开销特别多,频繁上下文切换的开销</p><p>路径3性能劣势:ioctl 是同步阻塞式的系统调用,无法利用好 NVMe SSD 高并发吞吐的性能</p><p>当前 NVMe passthrough 的限制</p><p>通常使用 ioctl 系统调用,包含两种操作码 opcode</p><figure class="highlight c"><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><code class="hljs C">NVME_IOCTL_IO64_CMD<span class="hljs-comment">// 用于发送 I/O 命令到 NVMe 设备</span><br>NVME_IOCTL_ADMIN64_CMD <span class="hljs-comment">// 发送管理命令到 NVMe 设备</span><br> <br>ioctl(fd, NVME_IOCTL_IO64_CMD, &nvme_user_io_st);<br></code></pre></td></tr></table></figure><p>several limitations:</p><ul><li>与块设备高度耦合,只兼容部分nvme命令</li><li>ioctl系统调用是阻塞式的,处理大量并发请求时,线程需要等待IO操作的完成,无法充分利用多核和并发能力</li><li>用户空间和内核空间的复制开销</li><li>接口仅限有 root 权限用户使用</li></ul><p><img src="/img/image-20240528181712232.png" alt="image-20240528181712232"></p><p>路径3其他劣势:Poor accessibility 可用性限制,必须要有 root 权限,上云或多租户场景影响很大 </p><p>Lock to root user</p><img src="/img/image-20240528153001606.png" alt="image-20240528153001606" style="zoom:50%;" /><h5 id="Problem-3:NVMe-is-not-longer-tied-to-block-storage"><a href="#Problem-3:NVMe-is-not-longer-tied-to-block-storage" class="headerlink" title="Problem 3:NVMe is not longer tied to block storage"></a>Problem 3:NVMe is not longer tied to block storage</h5><h5 id="对于-Linux-内核来说,NVMe的新标准、非块语义的新指令功能如何进行适配?"><a href="#对于-Linux-内核来说,NVMe的新标准、非块语义的新指令功能如何进行适配?" class="headerlink" title="对于 Linux 内核来说,NVMe的新标准、非块语义的新指令功能如何进行适配?"></a>对于 Linux 内核来说,NVMe的新标准、非块语义的新指令功能如何进行适配?</h5><ul><li>Multiple command-sets</li></ul><p>块语义Block semantics: NVM, ZNS(将存储空间划分为多个区块的存储方式)</p><p>非块语义non block semantics: key-value(直接以键值对方式存储和访问数据), computational programs(在存储设备上直接运行计算任务)</p><p><img src="/img/image-20240528154456376.png" alt="image-20240528154456376"></p><ul><li>There are newer ways to interact with the storage,NVMe引入了以下几种交互方式</li></ul><p>zone append:允许数据顺序写入特定区域,避免传统块存储中的写入冲突,提高写入效率和设备寿命。</p><p>FDP write:根据数据的不同特性优化存储位置,减少写入放大</p><p>copy command:在存储设备内部进行数据复制</p><p>metadata transfer:支持元数据的独立传输和处理</p><p><img src="/img/image-20240528154548481.png" alt="image-20240528154548481"></p><ul><li>Many new NVMe commands do not fit into the existing user interface.</li></ul><p>Zone-append 和 Copy-offload 等功能虽然在内核层面支持,但如果没有用户空间 API,没有封装好的库函数与系统调用,这些功能对用户程序来说是不可用的。</p><h2 id="2-io-uring-特性的介绍"><a href="#2-io-uring-特性的介绍" class="headerlink" title="2 io_uring 特性的介绍"></a>2 io_uring 特性的介绍</h2><p>两种模式:SQPoll,IOPoll</p><ul><li>SQPoll 模式(syscall-free submission)</li></ul><blockquote><p>在开启SQPoll模式后,应用程序提交的I/O请求操作将卸载到一个内核线程,从而实现无系统调用的提交</p><p>好处:减少了用户空间和内核空间的上下文切换,提高了性能,io每次提交不再需要调用 io_uring_enter</p><p>适用于高频率I/O提交场景,相当于有一个专门的工作人员不断检查并处理任务,无需老板亲自下达命令(日志频繁写入)</p></blockquote><ul><li>IOPoll 模式(Interrupt-free IO)</li></ul><blockquote><p>在未设置IOPoll模式时,对于每个IO完成事件的通知都采用硬件中断,但当 IOPS 较高的场景下,硬件中断会产生较多的延迟和CPU开销</p><p>好处:启用IOPoll模式后,应用程序通过轮询完成队列 CQ 来检查IO是否完成,而不是依赖内核中断的通知</p><p>适用于高性能场景,相当于各个员工自己检查任务完成情况(数据库处理大量读请求)</p></blockquote><p><img src="/img/image-20240528183228641.png" alt="image-20240528183228641"></p><p>另外两个特性:</p><ul><li>batching,一次提交多个IO请求</li><li>chaining,让IO操作自定义有序,比如读后写,可规定依赖顺序</li></ul><h2 id="3-设计思想"><a href="#3-设计思想" class="headerlink" title="3 设计思想"></a>3 设计思想</h2><h5 id="本文设计目标"><a href="#本文设计目标" class="headerlink" title="本文设计目标"></a>本文设计目标</h5><ul><li>不依赖与块接口,新接口需兼容所有nvme命令</li><li>通用用户接口,每次 NVMe 引入新命令时增加一个新的系统调用是不现实的。因此,需要一个能够处理所有现有和未来 NVMe 命令的用户接口,而无需为每个命令单独设计新的系统调用。</li><li>高效且可扩展,新接口至少与原先的 direct block I/O path 性能相当或更高</li><li>通用可访问,不仅限于root用户使用</li><li>最终能合并进linux内核上游仓库</li></ul><h2 id="4-IO-Passthru-直通技术在内核的架构和实现"><a href="#4-IO-Passthru-直通技术在内核的架构和实现" class="headerlink" title="4 IO Passthru 直通技术在内核的架构和实现"></a>4 IO Passthru 直通技术在内核的架构和实现</h2><p>三个方面讲述 io passthru 的设计和实现:可用性,效率和可扩展性,可访问性</p><h5 id="4-1-可用性"><a href="#4-1-可用性" class="headerlink" title="4.1 可用性"></a>4.1 可用性</h5><p>块设备接口的局限性:NVMe 新特性,新命令不支持</p><p>解决方案:设计了通用字符设备接口:NVMe generic char interface</p><p>为每个 NVMe 命名空间(namespace)创建一个字符设备节点,即使引入了新的命令集,字符接口也会自动支持,无需更改 NVMe 驱动程序代码。</p><h5 id="4-2-效率和可扩展性"><a href="#4-2-效率和可扩展性" class="headerlink" title="4.2 效率和可扩展性"></a>4.2 效率和可扩展性</h5><p>创建一个异步的 ioctl 替代方案,包括设计 io_uring 命令,big SQE, big CQE</p><p>同时引入了两个优化点:fixed-buffer and completion-polling</p><p>异步 ioctl 实现的两种方式:</p><ul><li>defer-to-worker:传统异步实现,基于工作线程的异步方法,交给专门负责IO的线程,有性能瓶颈</li><li>true-async:完全消除线程阻塞和上下文切换带来的开销,通过 polling 机制实现高效IO</li></ul><p>User/Kernel interface</p><ul><li>Communication backbone: shared ring-buffers (SQ and CQ)</li><li>Reduce syscalls & copies</li><li>Programming model<ul><li>Prepare IO: Get SQE from SQ ring, and fill it up (fill more to make a batch)</li><li>Submit IO: By calling io_uring_enter</li><li>Complete IO: Reap CQE from CQ ring</li></ul></li><li>Submission can be offloaded (no syscall)</li><li>Completion can be polled (interrupt-free IO)</li></ul><img src="/img/image-20240528183318433.png" alt="image-20240528183318433" style="zoom:50%;" /><figure class="highlight c"><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><code class="hljs c"><span class="hljs-type">int</span> <span class="hljs-title function_">ioctl</span><span class="hljs-params">(fd, opX_cmd, arg)</span>;<br><span class="hljs-comment">// fd:设备文件描述符 opX_cmd:控制设备的命令,比如:_IOW('a', 1, char *)</span><br><span class="hljs-comment">// arg:如果是读文件,这里可以传进来一个 buffer</span><br></code></pre></td></tr></table></figure><blockquote><p>ioctl </p></blockquote><img src="/img/image-20240529092350857.png" alt="image-20240529092350857" style="zoom:50%;" /><p><img src="/img/image-20240528182840406.png" alt="image-20240528182840406"></p><p>这种设计带来的优点:</p><ul><li>零拷贝</li></ul><blockquote><p>在用户空间和内核空间同时共享两个队列:提交队列SQE,完成队列CQE</p><p>在用户空间中创建 SQE 结构体,无需通过 copy_from_user 进行数据拷贝。结果通过内核创建的 CQE 返回,避免了 put_user 操作。因此,通过在控制路径中实现了零拷贝,减少了数据传输的额外开销。</p></blockquote><ul><li>减少线程上下文切换带来的开销</li></ul><blockquote><p>传统的 defer-to-worker 在每次提交IO操作都需要线程之间的切换,有较多开销</p><p>true-async I/O 操作的提交和完成都由内核直接处理,用户空间程序无需涉及线程的创建、调度和切换操作,同时也避免了线程的同步开销</p></blockquote><p>两个实现过程中的优化点</p><ul><li>Fixed-buffer</li></ul><blockquote><p>IO操作结束后数据放置在内存缓冲区,原先的设计在分配缓冲区时会先尝试申请内存区域、锁定、释放,而 io_uring 通过复用先前已锁定的缓冲区减少了申请、锁定、释放的开销</p></blockquote><ul><li>Completion Polling 完成队列轮询</li></ul><blockquote><p>应用程序通过轮询完成队列 CQ 来检查IO是否完成,而不是依赖IO完成后硬件中断的通知</p></blockquote><h5 id="4-3-可访问性(访问控制)"><a href="#4-3-可访问性(访问控制)" class="headerlink" title="4.3 可访问性(访问控制)"></a>4.3 可访问性(访问控制)</h5><p>文件模式表示指定了文件的访问权限,当用户程序想直接与硬件设备进行交互时,NVMe驱动程序会进行二级检查,然而,NVMe 驱动程序通过一个粗粒度的 CAP_SYS_ADMIN 检查来保护所有的直通操作,完全忽略文件模式,导致直通接口受限于 root 用户。</p><p>解决方案:修改 NVMe 驱动程序,实现了一个细粒度的访问控制策略</p><p>根据命令类型,管理命令/ io命令进行了权限划分,让一般用户也能直接操作硬件;</p><p>同时,作者也引入了强制访问控制(MAC)的机制,进一步加强了对这些操作的管控。</p><h2 id="6-IO-passthru-可扩展性和灵活性:NVMe-新接口、新特性的适配"><a href="#6-IO-passthru-可扩展性和灵活性:NVMe-新接口、新特性的适配" class="headerlink" title="6 IO passthru 可扩展性和灵活性:NVMe 新接口、新特性的适配"></a>6 IO passthru 可扩展性和灵活性:NVMe 新接口、新特性的适配</h2><h5 id="6-1-FDP命令"><a href="#6-1-FDP命令" class="headerlink" title="6.1 FDP命令"></a>6.1 FDP命令</h5><p>NVMe FDP tackles this problem of high WAF by segregating I/Os of different longevity types in the physical NAND media.</p><p>FDP:Flexible data placement 灵活数据放置指令,最新的一种引导数据放置的标准</p><p>Linux内核先前开发了基于写提示的基础设施允许应用端在写操作时同时发送放置提示,但现在这个基础设施已经被 Linux 主线移除;</p><p>IO passthru 作用:在应用端利用异步IO时可以同时发送放置提示,而不必担心FDP垂直集成到内核存储堆栈的各个部分。</p><p>FDP的优势:通过在物理 NAND 介质中分离不同寿命类型的 IO 来解决写放大的问题;</p><p>SSD写放大问题出现的根源:在 SSD 上,数据的写入并不是简单的覆盖旧数据,而是需要将旧数据和新数据混合在一起管理。混合不同寿命(写入频率和保留时间)类型的数据会导致<strong>更多的内部数据迁移和垃圾回收</strong>,从而增加 WAF。</p><p>FDP的实际效果:</p><ul><li><strong>降低 WAF</strong>:由于减少了不同寿命数据的混合,内部数据迁移和垃圾回收的效率提高,写入放大系数显著降低。</li><li><strong>提高 SSD 性能和寿命</strong>:较低的 WAF 意味着 NAND 媒体的磨损减少,SSD 的寿命延长,性能也更稳定。</li></ul><h5 id="6-2-计算存储架构"><a href="#6-2-计算存储架构" class="headerlink" title="6.2 计算存储架构"></a>6.2 计算存储架构</h5><p>将计算能力直接集成到存储设备中,减少数据传输,提升性能。</p><p>NVMe标准新提出的两个命名空间,通过这两种命名空间管理内存资源和计算资源:</p><ul><li>Memory Namespace</li><li>Compute Namespace</li></ul><p>所有新的NVMe命令都可以通过I / O Passthru接口高效地下发,这使得用户空间能够在不改变内核的情况下利用计算存储。</p><h2 id="7-实验"><a href="#7-实验" class="headerlink" title="7 实验"></a>7 实验</h2><p>实验配置:</p><p><img src="/img/image-20240528191823797.png" alt="image-20240528191823797"></p><h5 id="实验1:对比-passthrough-IO-path-和-block-IO-path-的效率,确定两者的峰值性能"><a href="#实验1:对比-passthrough-IO-path-和-block-IO-path-的效率,确定两者的峰值性能" class="headerlink" title="实验1:对比 passthrough IO path 和 block IO path 的效率,确定两者的峰值性能"></a>实验1:对比 passthrough IO path 和 block IO path 的效率,确定两者的峰值性能</h5><p>图a:general kernel config 默认配置</p><p>图b:optimized kernel config:禁用CONFIG_RETPOLINE 和 CONFIG_PAGE_TABLE_ISOLATION 选项,内核之所以默认开启这两个选项的原因:缓解 spectre 和 meltdown 硬件缺陷</p><p>SSD 硬件规格:最大读性能:5M IOPS,只有 BASE + FB + POll + optimized kernel config 做到了</p><p>FB:fixed-buffer 优化 (<strong>申请、锁定、释放缓冲区的开销减少)</strong></p><p>Poll:降低了中断和上下文切换的开销</p><p>原因:io passthru 比 block io_uring 涉及的处理更少,其跳过了 split,merge,io scheduling</p><p><img src="/img/image-20240528092220809.png" alt="image-20240528092220809"></p><p>图9:</p><p>a图:CPU利用率、提交延迟和页面大小的关系图</p><ul><li>对比了不同页面大小 4KB, 16KB, 64KB 的提交延迟,4KB 利用率最高</li><li>fixed-buffer 减少了 IO 的提交延迟和 CPU 利用率</li></ul><p>b图:对比队列深度与可扩展性</p><ul><li>随着队列深度的增加,可接受的请求数量增加,IOPS持续增加</li></ul><p><img src="/img/image-20240528094252419.png" alt="image-20240528094252419"></p><p>图10:对比 SQPoll 和 批处理 对吞吐量的影响</p><p>SQPoll 减少了系统调用的开销,对 block 和 passthrough path 都能提升吞吐量</p><p><img src="/img/image-20240528103808973.png" alt="image-20240528103808973"></p><h5 id="实验2:"><a href="#实验2:" class="headerlink" title="实验2:"></a>实验2:</h5><p>将 IO passthru 接口与上层应用 Cachelib 相结合,并对比是否启用 FDP 对写放大的影响</p><p>使用 write-only KVcache 工作负载进行了实验,在没有放置提示的情况下,SSD的写放大上升到2.4倍之多,而在使用 io passthru 接口并启用 FDP 后,写放大趋近于1</p><p><img src="/img/image-20240528112728897.png" alt="image-20240528112728897"></p><h5 id="实验3:IO-passthru-与-用户态的-SPDK-做对比"><a href="#实验3:IO-passthru-与-用户态的-SPDK-做对比" class="headerlink" title="实验3:IO passthru 与 用户态的 SPDK 做对比"></a>实验3:IO passthru 与 用户态的 SPDK 做对比</h5><p>a图:两个CPU核连接两个 SSD设备,IO passthru 减少了与 SPDK 的性能差距</p><p>b图:单个CPU核连接多个 SSD设备,性能上仍落后于 SPDK,原因如下:</p><ul><li>SPDK在用户态,对 NVMe 设备独占拥有权</li><li>I/O Passthru 需要使用块层的特性,例如硬件队列抽象、标签管理、超时/中止支持等。这些特性增加了 I/O 路径中的额外处理负担</li><li>IO passthru 经过内核,还是免不了系统调用的开销</li></ul><p><img src="/img/image-20240528192755408.png" alt="image-20240528192755408"></p><h2 id="8-总结"><a href="#8-总结" class="headerlink" title="8 总结"></a>8 总结</h2><p>随着新的存储特性、存储规范的增加,Linux 内核缺乏充足的系统调用与接口,面临适应性的挑战;</p><p>作者提出了一个新的选择,通过在内核中添加新的 IO Passthru 来解决这个问题。</p><p>这个路径通过使用新的 NVMe 字符接口并与 io_uring 相结合,扩展 io_uring 并提出同步阻塞式系统调用 ioctl 的替代方案,为当前/未来的 NVMe 新特性提供了支持。</p>]]></content>
<categories>
<category>论文阅读</category>
</categories>
<tags>
<tag>io_uring</tag>
<tag>linux kernel</tag>
</tags>
</entry>
<entry>
<title>充分利用NVMe SSD特性提升OLTP存储引擎的性能 (VLDB23)</title>
<link href="/2024/05/09/nvme-storage/"/>
<url>/2024/05/09/nvme-storage/</url>
<content type="html"><![CDATA[<p>What Modern NVMe Storage Can Do, And How To Exploit It: High-Performance I/O for High-Performance Storage Engines (VLDB 23)</p><p>现代NVMe存储可以做些什么,我们在存储引擎上如何更好利用高性能的 IO</p><h2 id="摘要"><a href="#摘要" class="headerlink" title="摘要"></a>摘要</h2><p>基于闪存的 NVMe SSD 不仅便宜还具有高额的吞吐量,在将多个SSD集成到一个服务器上后,可以实现每秒千万级别的IO操作。</p><p>我们的实验表明,现存的 out-of-memory 数据库 和 存储系统并没有充分利用这种高性能的优势,在本篇工作中,我们通过优化IO存储引擎缩小了软硬件之间性能上的差距,在大于内存容量10倍的数据集中,我们系统达到了每秒百万的 TPC-C 事务。</p><h2 id="1-介绍"><a href="#1-介绍" class="headerlink" title="1 介绍"></a>1 介绍</h2><p>关于闪存,近年来闪存SSD替代传统磁盘成为默认的存储介质</p><ul><li><p>传输协议方面,传统的SATA接口逐渐被 PCIe/NVMe 接口所取代;</p></li><li><p>存储吞吐量方面,单个SSD可以实现百万级的随机IO操作;</p></li><li><p>现代服务器逐渐支持每个插槽128个PCIe通道,可以轻松在全带宽下承载8个SSD;</p></li><li><p>鉴于傲腾NVM商业上的停产,作者认为闪存是支持大型数据集经济且高效的方式;</p></li></ul><p>现有数据库系统的存储性能差距</p><p><img src="/img/image-20240504095403757.png" alt="image-20240504095403757"></p><p>图1是对几个 out-of-memory 数据库系统查找性能进行的实验,这些数据库分别通过内存缓冲池cache,LSM-Tree,B+树等数据结构进行索引查找,实验中采用了8块闪存SSD,每块支持每秒1.5M随机4KB读,因此服务器最高支持 12.5M IOPS。</p><p>但从实验结果来看,对闪存专门做了优化的 Leanstore 数据库也仅有每秒3.6M随机读,与系统最大支持的 IOPS 相去甚远。</p><p>本篇文章研究目标是如何缩短这一存储的性能差距,可以分解为以下几个研究问题:</p><p>1、NVMe SSD 阵列能否在实际工作中达到硬件规格中承诺的性能?</p><p>2、哪一种 IO API 在存储系统中最应该被使用:POSIX API,libaio,io_uring?</p><p> 是否有必要上 SPDK,基于内核旁路的 IO?</p><p>3、为了降低IO放大,存储引擎维护的页面大小选多少合适?</p><p>4、如何确保SSD吞吐量所需的并行性,同时多少IO并发请求能使系统性能最大化?</p><p>5、如何设计存储引擎,使得它支持千万级别的 IOPS?</p><p>6、IO操作应该交给专门的IO线程处理,还是每个工作线程自己处理性能更好?</p><h2 id="2-What-Modern-NVMe-Storage-Can-Do"><a href="#2-What-Modern-NVMe-Storage-Can-Do" class="headerlink" title="2 What Modern NVMe Storage Can Do"></a>2 What Modern NVMe Storage Can Do</h2><h5 id="2-1-SSD的可扩展性,根据第一个问题进行实验"><a href="#2-1-SSD的可扩展性,根据第一个问题进行实验" class="headerlink" title="2.1 SSD的可扩展性,根据第一个问题进行实验"></a>2.1 SSD的可扩展性,根据第一个问题进行实验</h5><p><img src="/img/image-20240504102519142.png" alt="image-20240504102519142"></p><p>从图2可以看出,在接了8块SSD的服务器上测试后,总吞吐量能够达到预期的12.5M IOPS;</p><p>事务型工作负载通常是写密集型的,右图通过改变读数据的占比,得到服务器最佳的硬件性能。</p><h5 id="2-2-页面大小的权衡"><a href="#2-2-页面大小的权衡" class="headerlink" title="2.2 页面大小的权衡"></a>2.2 页面大小的权衡</h5><p>大多数数据库尽可能选择大的页面,比如:8 KB (PostgreSQL, SQL Server), 16 KB (MySQL, LeanStore), 32 KB (WiredTiger-MongoDB)</p><p>大页面带来的好处:</p><ul><li>性能提升,更多的数据可以在一次访存中被读写,减少了访存的次数</li><li>减少了缓冲池需要管理的页面数量,减少了缓冲池管理的开销(元数据,索引)</li><li>充分利用带宽</li></ul><p>大页面带来的坏处:</p><ul><li>过大的page size可能会导致IO写放大:</li></ul><p>1、对于16KB页面,写入100B数据需要磁盘向内存完整传入整个页面,造成多余的160倍的额外IO读写<br>2、对于4KB页面,写入100B数据需要磁盘向内存完整传入整个页面,造成多余的40倍的额外IO读写</p><p>因此,用4KB页面比16KB页面要减少4倍的写放大。</p><p><img src="/img/image-20240504134517506.png" alt="image-20240504134517506"></p><p>图3做了对比工作,当页面大小为4KB时,吞吐量最高,带宽充分利用,延迟最低。</p><p>另外,对于更小的页面,IOPS和延迟都差的原因在于,闪存翻译层的开销较高。</p><h5 id="2-3-SSD的并行"><a href="#2-3-SSD的并行" class="headerlink" title="2.3 SSD的并行"></a>2.3 SSD的并行</h5><p>SSD是可高度并行的设备,闪存的随机读延迟约为100us,比磁盘快100倍,比内存慢1000倍;</p><p>如果使用同步访问请求,将只有 10K IOPS,或 40MB/s</p><p>根据IO并发量的不同,做了图4的实验:</p><p><img src="/img/image-20240504135311421.png" alt="image-20240504135311421"></p><p>想要获得良好的性能,至少同时1000个IO并发请求,每个SSD125个左右;</p><p>想要系统IO达到吞吐量的饱和,至少同时3000个IO并发请求。</p><h5 id="2-4-IO接口"><a href="#2-4-IO接口" class="headerlink" title="2.4 IO接口"></a>2.4 IO接口</h5><p><img src="/img/image-20240504135905548.png" alt="image-20240504135905548"></p><ul><li>POSIX API 阻塞式同步IO</li></ul><blockquote><p>pread,pwrite,用户线程发出IO请求,将请求交给内核,此时用户线程进入阻塞状态,被CPU移入阻塞队列,内核线程将判断数据是否从磁盘传输到内存,直到数据完成传输后,将数据传入用户空间,此时拷贝完成,唤醒用户线程。</p></blockquote><ul><li>libaio</li></ul><blockquote><p>允许一个系统调用提交多个 IO请求,允许应用程序发起异步I/O请求,然后继续执行其他任务,而不必等待I/O操作完成。异步I/O操作通过io_submit()函数提交I/O请求,然后通过io_getevents()函数获取完成的I/O事件。</p></blockquote><ul><li>io_uring</li></ul><blockquote><p>在用户空间和内核空间分别设置了两个队列:提交队列SQE,完成队列CQE。</p><p>SQE用于用户线程向内核提交异步IO请求,CQE用于用户线程从内核接收完成的异步IO操作</p><p>io_uring与aio的区别在于:</p><p>1、libaio 仅支持 O_DIRECT 模式下的访存,io_uring 支持所有模式,同时也支持网络通信;</p><p>2、libaio一些内部实现仍采用阻塞式进行,比如元数据访存,设备不足时阻塞式等待;</p><p>3、libaio开销较高,需要一些额外的拷贝;</p><p>4、io_uring能够解决上面所有问题的同时,具有更强的性能和可扩展性。</p></blockquote><ul><li>SPDK 绕过内核的存取方式</li></ul><blockquote><p>SPDK库可以让用户空间线程直接将IO请求写入 NVMe请求队列,完全绕过系统内核</p></blockquote><h5 id="2-6-确定-leanstore-IO性能下降的来源"><a href="#2-6-确定-leanstore-IO性能下降的来源" class="headerlink" title="2.6 确定 leanstore IO性能下降的来源"></a>2.6 确定 leanstore IO性能下降的来源</h5><p>1、并行度不够</p><p>leanstore使用的是传统的 POSIX API (O_DIRECT),工作线程使用同步IO操作处理缓冲区出现的页错误,每个请求都需要进行至少一次与内核的上下文切换,并在IO完成之前有阻塞。</p><p>2、过度订阅问题</p><p>当有大量的工作线程争夺剩余的CPU核心时,会导致非常高的上下文切换开销;</p><p>工作线程数量 > 系统最大支持的线程数时,调度延迟、资源竞争、上下文切换都会导致性能下降</p><h2 id="3-如何设计-IO-优化的存储引擎"><a href="#3-如何设计-IO-优化的存储引擎" class="headerlink" title="3 如何设计 IO 优化的存储引擎"></a>3 如何设计 IO 优化的存储引擎</h2><h5 id="1、让系统每个模块充分利用并行性"><a href="#1、让系统每个模块充分利用并行性" class="headerlink" title="1、让系统每个模块充分利用并行性"></a>1、让系统每个模块充分利用并行性</h5><p>总览</p><ul><li>每个工作线程可以同时接收数千级的并发请求,每个请求由工作线程内部的轻量级用户协程处理</li><li>当请求涉及到I/O存取时,User Task 将换页请求发往I/O后端,当前协程被挂起 (await)</li></ul><p><img src="/img/image-20240505160152527.png" alt="image-20240505160152527"></p><h5 id="3-2-如何在不引起线程超额订阅情况下,合理管理大量并发请求"><a href="#3-2-如何在不引起线程超额订阅情况下,合理管理大量并发请求" class="headerlink" title="3.2 如何在不引起线程超额订阅情况下,合理管理大量并发请求"></a>3.2 如何在不引起线程超额订阅情况下,合理管理大量并发请求</h5><ul><li>引入协程:如何在不引起线程超额订阅情况下,合理管理大量并发请求</li></ul><p><img src="/img/image-20240506144340920.png" alt="image-20240506144340920"></p><p>1、为避免超额订阅,工作线程的最大数量 = CPU物理核心数</p><p>但由于并发请求的数量是数以千计的,这里用到了新的思想,每个工作线程调度多个协程。</p><p>2、每个工作线程内部维护一个调度器,用于调度轻量级协程 User Task 的进行;</p><p>这里用的轻量级协程来自 Boost.Context 库,通过作者的测试,一个协程任务的切换,只需要20个CPU周期,而一个内核上下文切换需要几千个CPU周期。</p><p>3、这种非抢占式的协程调度意味着不能再使用同步阻塞式IO,因为它会阻塞整个工作线程。</p><p>因此,工作线程将使用非阻塞式异步IO接口,比如:libaio,io_uring,SPDK</p><p>当发生缺页时,IO请求将被提交到IO后端,并将当前协程的控制权交还给调度器,调度器会轮询并执行就绪的协程。</p><h5 id="3-3-具体的执行流程例子"><a href="#3-3-具体的执行流程例子" class="headerlink" title="3.3 具体的执行流程例子"></a>3.3 具体的执行流程例子</h5><p><img src="/img/image-20240506154956824.png" alt="image-20240506154956824"></p><p>在原先设计的 Leanstore 存储引擎中,缺页时的页面替换,异步IO任务都交由后台线程完成,但缺点在于当工作负载发生变化的时候,我们很难知道后台线程的数量,难免会发生过度订阅,花更多的时间在切换上下文。</p><p>作者这里提出的观点是,由于协程和异步IO的加入,我们可以将这类后台线程放到前台,作为协程交给调度器调度,整个执行过程以图9为例:</p><ul><li>工作线程收到一个查询请求,需要通过遍历索引查找某项数据,于是创建协程的上下文,运行 user task1</li><li>但在 task1 运行的过程中发现,这个页面不在内存中,这触发了缺页,这将生成一个IO请求,并将提交这个IO请求到IO后端,yield关键字会将当前协程挂起,并将控制权返还给调度器。</li><li>调度器会轮询自己的任务队列,触发向SSD提交IO请求,页面替换任务,并交由IO控制器</li><li>当有新的请求 user task2 到来时,若数据都已存在内存中,task2 将继续执行直到完成</li><li>当 task1 的IO页面调度完成后,IO后端将调用 callback 函数通知调度器可以继续执行 task1</li></ul><h5 id="3-4-I-O模型的对比与选择"><a href="#3-4-I-O模型的对比与选择" class="headerlink" title="3.4 I/O模型的对比与选择"></a>3.4 I/O模型的对比与选择</h5><ul><li>Dedicated I/O Threads Model</li></ul><blockquote><p>实现简单,在这种模型中,工作线程无法直接访问 SSD,而必须与处理 I/O 的专用 I/O 线程进行通信,这种专用I/O线程作为内核工作线程,处理大量I/O请求时需频繁切换上下文。</p></blockquote><ul><li>SSD Assignment Model</li></ul><blockquote><p>每个SSD分配给对应的工作线程,目的是改进缓存的局部性和减少轮询;</p><p>但系统在实际的运行过程中,每个工作线程需要与其他工作线程进行通信,以访问其所分配的 SSD,带来同步的开销。</p></blockquote><ul><li>All-to-All Model</li></ul><blockquote><p>每个工作线程都可以访问所有的SSD,线程之间不再需要传递I/O消息。</p></blockquote><p>Leanstore 存储引擎原本的实现基于 Dedicated I/O Threads 模型,用的是传统的同步阻塞I/O</p><p>但为了更好的利用SSD并行性和异步I/O带来的优势,本文选择了 All-to-All 模型;</p><p>为了能更好反映 tradeoff,作者基于后两种I/O模型在两个异步I/O库上分别做了以下实验:</p><p><img src="/img/image-20240506193448614.png" alt="image-20240506193448614"></p><p>由于本文基于多个SSD设备,需要一个RAID的软件解决方案</p><p>Linux md raid(Multiple Device RAID)是 Linux 内核提供的软件 RAID 解决方案,但它存在一个硬限制:the Linux md raid seems to have a hard limit at around 15 GB/s.</p><p>因此,本文实现了一个 RAID0 级别的数据条带化抽象:</p><p>在图13的消融实验中,可以看到一个较高的吞吐量的提升。</p><p>RAID0:无容错设计的条带硬盘阵列,以条带形式将RAID组的数据均匀分布在各个SSD中 </p><p><img src="/img/image-20240507103929648.png" alt="image-20240507103929648"></p><h5 id="3-5-CPU部分的小优化"><a href="#3-5-CPU部分的小优化" class="headerlink" title="3.5 CPU部分的小优化"></a>3.5 CPU部分的小优化</h5><p>主要是页面替换方面,通过引入乐观父节点指针减少树的遍历次数节约CPU时间</p><p>leanstore 管理页面的方式:用B+树同时管理内存和磁盘中的页面,将页面分为 hot, cooling, cold 三类</p><p>在原本的leanstore baseline中,页面驱逐分为两个阶段:</p><ul><li>第一阶段,随机页面被选取,并放入 cooling 队列</li><li>第二阶段,从 cooling 队列取出页面进行替换,替换后的页面标记为 cold</li></ul><p>本文的优化在于,当某个内部节点的所有叶节点被 unswizzled 后,它应立即被放入 cooling 队列,但由于原先的实现B+树没有从叶节点指向父节点的指针,只能从根节点遍历,浪费大量的CPU周期(测出大约占10%CPU时间),而如果盲目对每个节点加入指向其父节点指针,会带来一笔空间上的开销。作者用了乐观父节点指针方法减少了树的遍历,但这里我个人认为算是小优化。</p><p>对于内部节点,只有它所有的叶节点都被 unswizzled 后,它才允许被替换</p><p><img src="/img/image-20240509185950088.png" alt="image-20240509185950088"></p><h2 id="4-实验部分"><a href="#4-实验部分" class="headerlink" title="4 实验部分"></a>4 实验部分</h2><ul><li>实验1:比较系统之间的性能,其中 leanstore 的存储引擎采取了本文提到的若干 I/O 优化</li></ul><p><img src="/img/image-20240507110859632.png" alt="image-20240507110859632"></p><ul><li>Ablation Study. 对比各项优化对系统整体性能提升的影响</li><li>baseline 在相同负载情况下,事务吞吐量和随机查找性能较低,但带宽很高,这是由于 baseline 默认页面大小为 16KB</li></ul><p>实验2,消融实验,对比各个优化对系统整体性能提升的影响</p><p>baseline在相同负载情况下,事务吞吐量和随机查找性能较低,但带宽很高,这是由于baseline默认页面大小为 16KB</p><p>(可以理解为在相同并发请求下,带宽是有限制的)</p><ul><li>4KB在TPCC负载下有比较大的TPS提升,是由于减少了写放大</li><li>CPU的小优化,提升不算大,页面替换时占用的CPU周期缩短约5%~10%</li><li>作者自己设计的RAID0解决方案,没有了linux md raid的硬限制,有一定的提升</li><li>协程级别的task,避免了过度订阅,减少了内核上下文切换所需的CPU周期</li><li>SPDK,异步IO,绕过内核直接在用户空间与SSD进行读写</li></ul><p><img src="/img/image-20240507110939127.png" alt="image-20240507110939127"></p><ul><li>实验3:性能测试,随着数据集(仓库数量)的增加,事务吞吐量和带宽的变化</li></ul><p><img src="/img/image-20240507113705657.png" alt="image-20240507113705657"></p><ul><li>实验4:实现了多种不同异步I/O接口,比较它们在CPU核数增加时的性能影响</li></ul><p><img src="/img/image-20240507113739751.png" alt="image-20240507113739751"></p><ul><li>实验5:对比不同异步I/O接口的 CPU 利用率</li></ul><p><img src="/img/image-20240509095706091.png" alt="image-20240509095706091"></p><ul><li>实验6:测量事务延迟,在 TPC-C 负载下,由于每个事务平均需要4个同步读操作和3个页面写入,这导致了更高的读尾延迟,因为它们会被写操作暂停</li></ul><p><img src="/img/image-20240509100312393.png" alt="image-20240509100312393"></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本篇文章最大的贡献在于做了很多对比实验,对TP数据库的存储引擎设计与优化提供了很多参考性的建议</p><p>1、一些重要概念:</p><ul><li>闪存SSD, 普通SSD</li><li>PCIe/NVMe接口,SATA接口</li></ul><p>2、关于 O_direct,直接io,即非缓冲io</p><p>为什么有的系统需要内核缓冲io,有的系统需要直接io?</p><ul><li>文件系统,网络服务等需要内核缓冲io(不加io_direct),由于内核缓冲区可以减少磁盘IO的次数,相当于利用了数据的局部性;</li><li>数据库等应用需要 io_direct,数据库需要自己的内存缓冲区管理数据,不经过内核缓冲区可以减少数据复制的次数,提高性能。</li></ul><p>leanstore 运行 spdk</p><figure class="highlight routeros"><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><code class="hljs routeros">sudo ./frontend/tpcc <span class="hljs-attribute">--ssd_path</span>=<span class="hljs-string">"traddr=0000:03:00.0"</span> <span class="hljs-attribute">--ioengine</span>=spdk <span class="hljs-attribute">--nopp</span>=<span class="hljs-literal">true</span> <span class="hljs-attribute">--partition_bits</span>=12 <span class="hljs-attribute">--tpcc_warehouse_count</span>=5 <span class="hljs-attribute">--run_for_seconds</span>=10 <span class="hljs-attribute">--dram_gib</span>=1 <span class="hljs-attribute">--worker_tasks</span>=32 <span class="hljs-attribute">--async_batch_size</span>=32 <span class="hljs-attribute">--optimistic_parent_pointer</span>=1 <span class="hljs-attribute">--xmerge</span>=1 <span class="hljs-attribute">--contention_split</span>=1 <span class="hljs-attribute">--worker_threads</span>=1 <span class="hljs-attribute">--pp_threads</span>=1<br><br><br>sudo ./frontend/tpcc <span class="hljs-attribute">--ssd_path</span>=<span class="hljs-string">"traddr=0000:03:00.0"</span> <span class="hljs-attribute">--ioengine</span>=spdk <span class="hljs-attribute">--nopp</span>=<span class="hljs-literal">true</span> <span class="hljs-attribute">--partition_bits</span>=12 <span class="hljs-attribute">--tpcc_warehouse_count</span>=10 <span class="hljs-attribute">--run_for_seconds</span>=20 <span class="hljs-attribute">--dram_gib</span>=2 <span class="hljs-attribute">--worker_tasks</span>=32 <span class="hljs-attribute">--async_batch_size</span>=32 <span class="hljs-attribute">--optimistic_parent_pointer</span>=1 <span class="hljs-attribute">--xmerge</span>=1 <span class="hljs-attribute">--contention_split</span>=1 <span class="hljs-attribute">--worker_threads</span>=1 <span class="hljs-attribute">--pp_threads</span>=1<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>论文阅读</category>
</categories>
<tags>
<tag>database</tag>
<tag>storage</tag>
</tags>
</entry>
<entry>
<title>喜欢听的歌</title>
<link href="/2024/04/27/music/"/>
<url>/2024/04/27/music/</url>
<content type="html"><![CDATA[<p>记录一下自己比较喜欢听的歌~</p><h2 id="华语歌坛"><a href="#华语歌坛" class="headerlink" title="华语歌坛"></a>华语歌坛</h2><style>table th:first-of-type { width: 10%;}table th:nth-of-type(2) { width: 80%;}</style><table><thead><tr><th>歌手</th><th align="center">歌曲</th></tr></thead><tbody><tr><td>许嵩</td><td align="center">断桥残雪,清明雨上,山水之间,半城烟沙,庐州月,乌鸦,素颜,雅俗共赏,千百度,玫瑰花的葬礼,有何不可,城府,白马非马,雨幕,如果当时,弹指一挥间,幻听</td></tr><tr><td>毛不易</td><td align="center">消愁,牧马城市,借,像我这样的人,一荤一素</td></tr><tr><td>周传雄</td><td align="center">寂寞沙洲冷,黄昏,青花,冬天的秘密</td></tr><tr><td>林俊杰</td><td align="center">黑夜问白天,曹操,修炼爱情,起风了,江南,不为谁而作的歌</td></tr><tr><td>周杰伦</td><td align="center">烟花易冷,夜曲,兰亭序,发如雪,稻香,枫,青花瓷,一路向北,夜的第七章</td></tr><tr><td>汪苏泷</td><td align="center">三国杀,不分手的恋爱,后会无期,小星星,有点甜,巴赫旧约</td></tr><tr><td>郑智化</td><td align="center">水手,星星点灯,堕落天使</td></tr><tr><td>王强</td><td align="center">秋天不回来</td></tr><tr><td>伍佰</td><td align="center">挪威的森林,突然的自我</td></tr><tr><td>林志炫</td><td align="center">你的样子,凤凰花开的路口</td></tr><tr><td>张宇</td><td align="center">雨一直下</td></tr><tr><td>屠洪刚</td><td align="center">精忠报国,中国功夫,霸王别姬</td></tr><tr><td>朴树</td><td align="center">平凡之路</td></tr><tr><td>许巍</td><td align="center">故乡,曾经的你</td></tr><tr><td>五月天</td><td align="center">干杯,突然好想你</td></tr><tr><td>凤凰传奇</td><td align="center">奢香夫人,荷塘月色,绿旋风,自由飞翔</td></tr><tr><td>王菲</td><td align="center">如愿</td></tr><tr><td>赵雷</td><td align="center">成都</td></tr></tbody></table><h2 id="粤语歌坛"><a href="#粤语歌坛" class="headerlink" title="粤语歌坛"></a>粤语歌坛</h2><style>table th:first-of-type { width: 15%;}table th:nth-of-type(2) { width: 80%;}</style><table><thead><tr><th>歌手(乐队)</th><th align="center">歌曲</th></tr></thead><tbody><tr><td>Beyond</td><td align="center">光辉岁月,大地,灰色轨迹,不再犹豫,再见理想,谁伴我闯荡,真的爱你,曾是拥有,海阔天空,岁月无声,情人,喜欢你,俾面派对,冲开一切</td></tr><tr><td>陈百强</td><td align="center">偏偏喜欢你,眼泪为你流,一生何求</td></tr><tr><td>陈奕迅</td><td align="center">K歌之王,浮夸,富士山下,白玫瑰,明年今日(十年),单车,人来人往,无条件</td></tr><tr><td>邓丽君</td><td align="center">漫步人生路</td></tr><tr><td>李克勤</td><td align="center">月半小夜曲,红日</td></tr><tr><td>罗文&甄妮</td><td align="center">铁血丹心,世间始终你好,一生有意义</td></tr><tr><td>郑伊健</td><td align="center">友情岁月</td></tr><tr><td>张国荣</td><td align="center">沉默是金,风继续吹,倩女幽魂,Monica,浮生若梦,当年情,(当爱已成往事)</td></tr><tr><td>张学友</td><td align="center">相思风雨中</td></tr><tr><td>郑少秋</td><td align="center">笑看风云</td></tr><tr><td>钟镇涛</td><td align="center">让一切随风</td></tr><tr><td>许冠杰</td><td align="center">沧海一声笑</td></tr><tr><td>卢冠廷</td><td align="center">一生所爱</td></tr><tr><td>周华健</td><td align="center">难念的经,(刀剑如梦)</td></tr><tr><td>林子祥</td><td align="center">长路漫漫伴你闯,敢爱敢做,男儿当自强</td></tr><tr><td>陈慧娴</td><td align="center">千千阙歌,人生何处不相逢</td></tr></tbody></table><blockquote><p> <del>皇后大道东</del>(有一说一,曲调是真好听,逃)</p></blockquote>]]></content>
<categories>
<category>好听的歌</category>
</categories>
<tags>
<tag>华语歌坛</tag>
<tag>粤语歌坛</tag>
</tags>
</entry>
<entry>
<title>NOC-NOC 面向性能最优的分布式事务 (sigmod 24) 论文阅读</title>
<link href="/2024/04/16/NOC-NOC/"/>
<url>/2024/04/16/NOC-NOC/</url>
<content type="html"><![CDATA[<p>NOC-NOC:面向性能最优的分布式事务(sigmod 24)</p><h4 id="1-摘要"><a href="#1-摘要" class="headerlink" title="1 摘要"></a>1 摘要</h4><p>以前的分布式事务性能最优的工作都致力于研究优化事务的读操作,但忽略了影响写整体性能的关键因素–写操作,本文通过优化写操作进一步提高分布式事务的性能;</p><p>提出了一个新的设计目标:NOC-NOC</p><p>利用 双视图 + 版本向量技术 实现了两个满足这一设计目标的新事务算法</p><h4 id="2-介绍"><a href="#2-介绍" class="headerlink" title="2 介绍"></a>2 介绍</h4><p>只读事务优化文章:</p><h5 id="SNOW-不可能定理:只读事务优化的4个属性无法同时满足,只能同时满足其中3个:"><a href="#SNOW-不可能定理:只读事务优化的4个属性无法同时满足,只能同时满足其中3个:" class="headerlink" title="SNOW 不可能定理:只读事务优化的4个属性无法同时满足,只能同时满足其中3个:"></a>SNOW 不可能定理:只读事务优化的4个属性无法同时满足,只能同时满足其中3个:</h5><ul><li>S 严格可串行化 strict serializability,要求事务之间按开始时间严格有序,不存在内部操作的并发执行,比如查询余额一定在转账之后;</li><li>N 操作非阻塞 Non-Blocking Operation,要求读操作能立刻被响应,不会因为等待其他节点或锁占用而延迟;</li><li>O 每个读操作仅有单个回复 One Response Per Read,要求读操作只读一个版本;</li><li>W 允许同时存在冲突写事务 Write Transactions that Conflict,要求允许对同一个数据项更改的写事务在并发进行‘</li></ul><p>满足 N+O 的算法:延迟最低的只读事务算法</p><p>满足 S+W 的算法:功能最强的只读事务算法,因为其简化了上层应用的开发</p><p><img src="/img/image-20240402165323594.png" alt="image-20240402165323594"></p><h5 id="NOCS-定理:只读事务的最优性能为:在一致性视图的情况下,同时满足-NOC-三个属性:"><a href="#NOCS-定理:只读事务的最优性能为:在一致性视图的情况下,同时满足-NOC-三个属性:" class="headerlink" title="NOCS 定理:只读事务的最优性能为:在一致性视图的情况下,同时满足 NOC 三个属性:"></a>NOCS 定理:只读事务的最优性能为:在一致性视图的情况下,同时满足 NOC 三个属性:</h5><ul><li>N 非阻塞 non-blocking</li><li>O 单轮通信 one round communication</li><li>C 常量元数据 constant amount of data,需要满足跨分区数据的一致性读的视图</li></ul><p>元数据用于计算只读事务所需的一致性视图,比如事务ID,时间戳;</p><p>作者进行的实验:</p><p><img src="/img/image-20240402165909291.png" alt="image-20240402165909291"></p><p>LORA 和 RA-NOC 在事务数量小的情况下,或写比例小的情况下,延迟较低,吞吐量较高;</p><p>但是当事务数量增加并且写事务增多时,整体的延迟在变大,吞吐量在降低;</p><p>由于 NOC 仅优化了只读事务,在写事务占比较多时,锁竞争会导致整体的吞吐量降低;</p><p>左右两幅图都是既有只读事务,又有写的事务;</p><p>近几年提出的几个隔离级别:</p><p><img src="/img/image-20240402170909363.png" alt="image-20240402170909363"></p><p>RC:读已提交</p><p>RA:读原子性:在RC的基础上,避免了断裂读取异常 fractured reads</p><p>关于断裂读取异常:read committed 可能会导致下面的情景</p><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>begin ( x = 1, y = 1 )</td><td>begin ( x = 1, y = 1 )</td></tr><tr><td></td><td>read x = 1</td></tr><tr><td>write x = 2</td><td></td></tr><tr><td>write y = 3</td><td></td></tr><tr><td>commit</td><td></td></tr><tr><td></td><td>read y = 3</td></tr><tr><td></td><td>commit</td></tr></tbody></table><p>RA:T2 要么对 T1 所有更新操作感知,要么都不感知,这里之前读了 x = 1,那么后面对 y 的读取只能是1;</p><p>RA+:在RA隔离级别的基础上,同时能够保证 read your writes</p><blockquote><p>read your write (Session Guarantees for Weakly Consistent Replicated Data 94)</p><p>没有 read your write 的情景:通常出现在弱一致性系统中</p><p>一个用户改了个新的密码,在重新登录的过程中,由于密码副本是分布式存储,有一个副本异步更新的较慢,用户输入新更改的密码去登录,结果查询到的是那个未来得及更新的副本,导致收到密码无效。而 read your write 能够保证客户端在登录过程中始终读取最新的密码</p></blockquote><p>TCC:事务之间执行能够保证因果一致性</p><blockquote><p>能在并发情况下,满足下面两个事务的顺序关系,即先执行T1,后执行T2</p><p>T1:用户A浏览了商品X并将其添加到购物车</p><p>T2:用户B查看了用户A的购物车,并在此基础上给用户A推荐了相关商品Y</p></blockquote><p>TCCv:TCC基础上使其收敛</p><p>NOC-NOC标准:</p><p>只读事务需要满足前面三个原则(第一个NOC)</p><p>并且由SNOW定理证明的,在存在冲突写事务的情况下,只读事务算法只能满足 N+O+W</p><p>本文在SNOW基础上,写事务算法如果满足写的 NOC,能够最大化提升系统性能;</p><p>定理3.1 在SI隔离级别下,没有事务算法能够同时满足 NOC-NOC 这6个准则</p><p>定理3.2 RC隔离级别下,没有事务算法能够同时满足 NOC-NOC 这6个准则 + 全局可见性</p><p>图3是在RC隔离级别下,T2读取到的数据应该是一样的,都是最开始的数据;</p><p>在满足NOC-NOC + 全局可见性下,T2会读到T1未提交的数据,违背了RC;</p><p>否则,如果T2读到的是原始数据,在T1先开始T2后开始的情况下,在a中T2读不到T1更新的数据,也违背了NOC-NOC里的 $$O_R$$</p><p><img src="/img/image-20240403101615412.png" alt="image-20240403101615412"></p><p>本文设计了两个算法,分别在 RA+ 和 TCCv 隔离级别下满足 NOC-NOC 6个准则的算法</p><p><img src="/img/image-20240403102717806.png" alt="image-20240403102717806"></p><h4 id="RA-NOC2-算法"><a href="#RA-NOC2-算法" class="headerlink" title="RA-NOC2 算法"></a>RA-NOC2 算法</h4><p>LSV:局部安全视图</p><p>GSV:全局安全视图</p><p>疑问:(GPT)</p><p>1、事务算法指的是?并发控制算法</p><p>2、RA会出现不可重复读吗?会,仅比RC多个避免 fracture read</p><p>3、它们跟2PL,OCC有什么联系和区别?</p><blockquote><p>RAMP-family 和 Eiger-family 算法主要用于分布式环境下的事务处理,以解决分区数据一致性和可靠性的问题;(数据分布在不同的分区)</p><p>而2PL 和 OCC 算法通常用于单机或分布式环境下的本地事务处理,以解决并发控制和一致性问题;</p></blockquote><p>4、如何理解TCCv的收敛?</p><p>RAMP算法提出的背景:</p><blockquote><p>传统的隔离级别是针对单节点环境下的异常定义的,而分布式数据库会出现新的异常</p><p>主要原因在于多个节点上同一个事务的更新可能不是原子可见的</p><p>受网络等影响,某些节点未完成更新,使得同一个读操作读分布在不同副本上的数据时,读取的版本不一致;</p><p>RAMP算法保证了多节点上读取结果的一致性;</p></blockquote>]]></content>
<categories>
<category>论文阅读</category>
</categories>
<tags>
<tag>transaction</tag>
</tags>
</entry>
<entry>
<title>HackWrench 细粒度分布式事务提交 (VLDB 23) 论文阅读</title>
<link href="/2024/04/16/Hackwrench/"/>
<url>/2024/04/16/Hackwrench/</url>
<content type="html"><![CDATA[<p>Fine-Grained Re-Execution for Efficient Batched Commit of Distributed Transactions (VLDB 23)</p><h3 id="通过细粒度重新执行的方式实现分布式事务的高效提交"><a href="#通过细粒度重新执行的方式实现分布式事务的高效提交" class="headerlink" title="通过细粒度重新执行的方式实现分布式事务的高效提交"></a>通过细粒度重新执行的方式实现分布式事务的高效提交</h3><h2 id="1-介绍"><a href="#1-介绍" class="headerlink" title="1 介绍"></a>1 介绍</h2><p>分布式OLTP系统通常需要横跨多个节点来提交一个可串行化的事务;</p><p>近10年来有很多分布式的OLTP被提出,包括 Spanner,CockroachDB,FoundationDB,虽然它们可以横向扩展到多个节点,但性能始终没有得到很好的优化。</p><p>分布式事务执行缓慢的原因:</p><ul><li>数据是跨节点分布的,在事务执行过程中,系统需要远程抓取数据;</li><li>为了提交事务,系统还必须在多个节点之间协调,来确保可串行化;</li><li>执行和提交事务都需要节点间大量的往返阻塞式通信,这导致了吞吐量的降低和性能的下降;</li></ul><h5 id="Aurora-的优化"><a href="#Aurora-的优化" class="headerlink" title="Aurora 的优化"></a>Aurora 的优化</h5><p>为了有效减少远程通信的数量,云数据库Aurora使用 caching 和 batching 显著提高了性能;</p><p>在计算节点上的缓存:在执行事务操作的时候可以减少对存储节点的远程读操作;</p><p>分批验证和提交:可以摊销多个事务之间的通信,从而减少每个事务的通信次数。打个比方,A事务涉及3个数据节点的通信,B,C事务也涉及这3个数据节点的通信,如果分别执行A,B,C事务,将产生至少9轮往返通信,但若这三个事务以一个批次执行,只需要3轮通信的往返,能够极大提高吞吐量;</p><p>但这两种方法仍存在缺陷,当事务之间存在的更多的关联性时,缓存和批处理会显著增加事务冲突的概率,从而引入大量事务回滚的性能开销:</p><blockquote><p>若某个计算节点缓存了数据,当需要执行操作时,首先会从缓存中读取数据;</p><p>但如果其他节点同时提交了对相同数据的写操作时,那么该计算节点将会错过最新的数据;</p><p>在分布式事务的验证阶段,当存在一个事务对过时数据进行操作,将会因数据不一致而中止提交</p></blockquote><p>同理,当多个事务被一起进行批处理验证时,如果发现存在一个事务是无效的,那么整个批处理的事务都得 rollback;</p><h5 id="COCO-的优化"><a href="#COCO-的优化" class="headerlink" title="COCO 的优化"></a>COCO 的优化</h5><p>COCO是mit在vldb21提出来的一种事务提交策略,它以epoch为单位提交事务。通过对事务进行单独验证,并且只对验证通过的事务进行跨节点复制的方式来解决批处理性能较差的问题。这意味着每个事务都会单独进行验证,而不是一起批处理验证。只有验证通过的事务才会被收集起来,成批进行跨节点复制。这样做的好处是可以避免因为批处理中存在验证失败的事务而导致其他事务被迫中止的情况。</p><p>后面实验有本文作者提出的 Hackwrench 与 COCO 对比的实验</p><h5 id="Hackwrench"><a href="#Hackwrench" class="headerlink" title="Hackwrench"></a>Hackwrench</h5><p>本文提出的 Hackwrench,创新性地使用事务修复机制降低了缓存和批处理带来的分布式事务回滚开销。</p><p>核心思想:</p><ul><li><p>通过应用最小修复措施,而非中止整个事务批次来提升缓存和批处理的性能;</p></li><li><p>事务的细粒度修复:通过重新执行被陈旧和无效读操作影响的操作修复;</p></li></ul><p>为了支持细粒度修复的功能,Hackwrench 使用数据流图 dataflow graph 来清晰地表示事务操作之间的关联;</p><p>在提交阶段,Hackwrench 使用了分层提交协议,事务提交分为了本地提交和全局提交</p><ul><li>本地提交使用2PL处理计算节点内部事务之间的冲突,并且节点内部使用的是RU隔离级别,事务可以读取本地提交事务的未全局提交的数据;</li><li>全局提交用于验证和提交一批本地节点已提交的事务组,类似2PC的工作原理,具体操作为:<ul><li>准备阶段,计算节点与所有存储节点通信,验证事务的读操作,并在存储节点上持久化批处理中的写操作;</li><li>提交阶段,计算节点通知存储节点这批次事务的提交状态,各存储节点提交或回滚事务;</li></ul></li></ul><p>与传统2PC不同的是,Hackwrench 引入了两个变体:</p><p>1、当事务批次验证失败时候,计算节点将更新缓存,并利用更新后的缓存与事务之间的依赖关系进行事务的细粒度修复;(传统的2PC是直接该批次所有事务回滚)</p><p>2、Hackwrench 使用一个时间戳服务器来确定一批事务的提交顺序,修复事务时可以通过时间戳的顺序依次对事务进行修复;</p><h5 id="one-shot-事务类型"><a href="#one-shot-事务类型" class="headerlink" title="one-shot 事务类型"></a>one-shot 事务类型</h5><ul><li>普遍认为的分布式事务</li></ul><blockquote><p>事务里面的语句比如select,需要从多个不同的存储节点拿到数据;</p><p>即数据是分散存储的,一个select可能需要跨多个节点访问;</p></blockquote><ul><li>one-shot 事务</li></ul><p>来源于 VLDB08 的 H-Store,虽然一个事务不能在同一个节点上完成所有的执行,但事务里面的每条语句都能在一个存储节点上完成。</p><blockquote><p>One-shot Transactions: A transaction is one-shot if it cannot execute on a single-site, but each of its individual queries will execute on just one site (not necessarily all the same site). </p><p>From H-Store —VLDB 08</p></blockquote><p>对于 one-shot 事务的优化:</p><p>one-shot transaction:事务里面的每条语句都能在一个存储节点上完成,如果仍然通过分层提交协议提交将造成不必要的性能损失,Hackwrench 使用 fast-path optimization 方法将修复和提交的任务由存储节点直接完成,不依赖计算节点。</p><h5 id="本文主要的贡献点"><a href="#本文主要的贡献点" class="headerlink" title="本文主要的贡献点"></a>本文主要的贡献点</h5><p>1、利用缓存和批处理降低远程节点通信次数,提高吞吐量;</p><p>2、提出分布式事务的分层提交协议,保证了事务先在本地节点可串行化,再在全局保证可串行化;</p><p>3、为了缓解过时缓存和批量提交冲突带来的事务回滚,Hackwrench 使用事务操作间的依赖和细粒度修改来修复无效或过时的读操作,降低了事务回滚的成本;</p><p>4、对一次性事务,提出了快速转发优化,直接在存储节点提交和修复,而不是计算节点,降低了整个系统远程节点之间的通信往返次数和2PC带来的开销;</p><p>5、在对比实验中,显著好于 FoundationDB , COCO 等系统。</p><h2 id="2-背景和动机"><a href="#2-背景和动机" class="headerlink" title="2 背景和动机"></a>2 背景和动机</h2><h5 id="2-1-影响分布式OLTP事务吞吐量和性能的两个重要原因:"><a href="#2-1-影响分布式OLTP事务吞吐量和性能的两个重要原因:" class="headerlink" title="2.1 影响分布式OLTP事务吞吐量和性能的两个重要原因:"></a>2.1 影响分布式OLTP事务吞吐量和性能的两个重要原因:</h5><ul><li>跨节点通信</li><li>节点之间的远程同步</li></ul><h5 id="2-2-缓存与批处理在存算分离系统中的优点与挑战"><a href="#2-2-缓存与批处理在存算分离系统中的优点与挑战" class="headerlink" title="2.2 缓存与批处理在存算分离系统中的优点与挑战"></a>2.2 缓存与批处理在存算分离系统中的优点与挑战</h5><p>在一般的存算分离系统中,开始执行事务时,计算节点首先会读取存储节点的数据,并将修改保存在自己的本地缓存;在提交事务的时候,计算节点首先需要与存储节点通信以验证本地的事务提交是没有冲突的。</p><p><strong>Caching 机制</strong>:计算节点维护一个本地的缓存用于存储最近读取到的数据项,当只存在一个计算节点时,比如 single-master Aurora,这个缓存永远是一致的和最新的;但当存在多个计算节点时,每个计算节点的事务都可能对同一个存储节点的数据进行更改,这就会造成缓存的过时和无效。为了保证这种情况下事务的正确性,在事务的提交验证阶段就需要对数据项所在的存储节点进行检查,如果存在冲突,事务就需要回滚,比如 Sinfonia 系统,这就导致了事务处理的性能下降。</p><p><strong>Batching 机制</strong>:事务的批量提交可以让存算分离系统尽可能地减少跨界点通信,但计算节点需要在存储节点上验证事务的读操作的正确性,如果某一个事务失败,将导致整一批事务的回滚,性能也会下降。</p><p>表1描述了缓存机制和批处理机制在分布式事务的性能表现:</p><p>p表示 分布式新订单事务的占比,它的百分比可以代表跨多节点事务的冲突程度;</p><p>在只有其他事务时,缓存和批处理策略能让吞吐量有近乎几倍的提升,而在单计算节点的多个不同批次间读未提交的数据时,吞吐量是巨大的;</p><p>但随着多仓库新订单事务的占比增加,所有的方法吞吐量都呈现下滑的趋势,下滑最明显的是batching策略,由于冲突的增加,无效的单个事务在批处理中增加,导致整一批事务都需要回滚,性能也在下滑</p><img src="/img/image-20240220123457334.png" alt="image-20240220123457334" style="zoom: 67%;" /><p>作者从这个实验得出的结论:</p><blockquote><p>在低竞争程度时,尽可能保持有 RU 的性能;</p><p>在中或高竞争程度时,使用缓存和批处理方式外加一定的细粒度修复机制可以让分布式事务系统有更高的吞吐量。</p></blockquote><h5 id="2-3-提出的方法"><a href="#2-3-提出的方法" class="headerlink" title="2.3 提出的方法"></a>2.3 提出的方法</h5><ul><li><p>分层提交策略</p></li><li><p>细粒度重新执行读操作修复无效的事务</p></li></ul><p>图1用来说明细粒度修复的主要思想:</p><p><img src="/img/image-20240220125347139.png" alt="image-20240220125347139"></p><p>在批处理执行事务的过程中,系统会生成 dataflow graph;</p><p>事务操作之间是有隐藏的版本依赖关系,比如图中T1,T2的两个依赖关系:</p><p>W3 -> R6,W4 -> R7</p><p>在全局提交的阶段中,批处理的事务会首先在所有存储节点进行验证,当发现因为冲突或过时的缓存导致某些读操作读取的版本是过时的,此时借助数据流图中的依赖关系,可以精确定位需要重新执行的操作子集,将它们重新执行即可得到修复后的事务操作;</p><p>再以图1举例,假定在全局验证阶段发现原本 T0版本的数据C 已经被 T4事务修改成最新的,此时,只需要重做 R2, W4, R7, W10 即可进行细粒度修复,而无需对完整的一批事务(T1,T2)所有操作进行回滚重试。</p><h2 id="3-Hackwrench-设计"><a href="#3-Hackwrench-设计" class="headerlink" title="3 Hackwrench 设计"></a>3 Hackwrench 设计</h2><h5 id="3-0-系统架构图"><a href="#3-0-系统架构图" class="headerlink" title="3.0 系统架构图"></a>3.0 系统架构图</h5><p>Hackwrench 由多个计算节点 DatabaseNode 和多个存储节点 StorageNode 组成,计算节点负责执行事务和协调分层提交,存储节点负责存储数据和验证分布式事务,</p><p>同时维护一个时间戳服务器,用于确定全局事务批次的提交顺序:</p><p><img src="/img/image-20240220130640796.png" alt="image-20240220130640796"></p><p>Hackwrench通过一个基于 paxos 的设置来维护系统各个节点的配置信息(视图),这个视图包括时间戳服务器的标识和数据段与逻辑存储节点的映射关系。</p><h5 id="3-1-数据的组织方式"><a href="#3-1-数据的组织方式" class="headerlink" title="3.1 数据的组织方式"></a>3.1 数据的组织方式</h5><p>Hackwrench 将所有数据划分为数据段,每个数据段包含一批版本化的键值对元组;</p><p>每个数据段存储在一个逻辑节点上,配置服务器维护数据段与逻辑存储节点的映射关系;</p><p>每个元组的版本:<63位唯一标识符id,修复位></p><p>其中,唯一标识符表示最后修改该元组的事务id,修复位用于确保重新执行的写与原来执行的不同;</p><p>每个计算节点维护一个元组级别的缓存,当发生 cache miss 时,将从远程存储节点读取元组缓存至本地,缓存每隔一定时间进行异步更新。</p><h5 id="3-2-事务的执行和本地提交"><a href="#3-2-事务的执行和本地提交" class="headerlink" title="3.2 事务的执行和本地提交"></a>3.2 事务的执行和本地提交</h5><ul><li>关于事务的执行</li></ul><blockquote><p>在 Hackwrench 中,事务被描述成存储过程。在一般的OLTP系统中,存储过程通常用来进行性能的加速。</p><p>但在这里,Hackwrench 采用了基于数据流的编程抽象来描述存储过程;</p><p>基于设计的 dataflow API,用户提交的每个事务都能够清晰描述成一个数据流图;</p><p>并且我们为每个节点都复制一份事务的数据流图;</p></blockquote><ul><li>本地事务的提交</li></ul><p>计算节点通过2PL的并发控制方法执行本地的事务批次,执行完成后,事务中的写操作会被应用到当前节点的缓存中,当前节点的其他事务都可见。 </p><p>本地提交的事务将会追加到节点的本地队列中,等待批处理的全局提交。为了保证事务维持本地提交的顺序性,直到提交的事务被放入本地队列后,才释放2PL的锁。</p><h5 id="3-3-全局提交"><a href="#3-3-全局提交" class="headerlink" title="3.3 全局提交"></a>3.3 全局提交</h5><p>全局提交的算法</p><p><img src="/img/image-20240220155227896.png" alt="image-20240220155227896"></p><figure class="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><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br></pre></td><td class="code"><pre><code class="hljs go">Transaction Batch {<br> rset, wset; <span class="hljs-comment">// read, write set</span><br> cts;<span class="hljs-comment">// segment-level timestamp list, each segment belongs to a logical sn</span><br> fast_opt;<span class="hljs-comment">// whether to use fast-path optimization (only one-shot trx)</span><br> input;<span class="hljs-comment">// operations of trx batch</span><br> final_wset;<span class="hljs-comment">// after trx execute and repair, the final value of tuple</span><br> delta_wset;<span class="hljs-comment">// value before and after repair</span><br>}<br><br><span class="hljs-comment">// 计算节点对本地提交的事务进行全局提交 (compute node)</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">GlobalCommit</span><span class="hljs-params">(local_queue, batch_size)</span></span> {<br> <span class="hljs-comment">// 1.get transation batch from local queue</span><br> batch := BatchLocalTxns(local_queue, batch_size) <br> <span class="hljs-comment">// 2.fetch timestamp to each data segment of rset and wset</span><br> foreach segment seg in batch.rset and batch.wset do:<br> batch.cts[seg.id] := FetchTimestamp(seg.id, seg.op_type)<br> <span class="hljs-comment">// 3.check whether the batch could use fast path optimization</span><br> batch.fast_opt := AnalyzeFastPathFeasibilityOf(batch)<br> <span class="hljs-comment">// 4.find storage node and send prepare msg to each physical storage node </span><br> sn_locations := FindSnLocations(batch)<br> foreach sn in sn_locations do:<br> send Prepare(batch) to all physical replicas of sn<br>}<br><br><span class="hljs-comment">// 存储节点处理收到的 prepare request (storage node)</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">HandlePrepare</span><span class="hljs-params">(batch)</span></span> {<br> <span class="hljs-comment">// 1.wait for other batches finish segments locking,</span><br> <span class="hljs-comment">// and acquire lock for all access tuples.</span><br> foreach seg in batch do:<br> local_ts := GetLocalTimeStamp(seg.id) <br> WaitForPreviousBatch(local_ts, batch.cts[seg.id])<br> EnqueueTupleLockRequests(batch.rset, batch.wset) <br> foreach seg in batch do: <br> UpdateLocalTimeStamp(seg.id, seg.op_type)<br> WaitForAllTupleLocksAcquired(batch.rset, batch.wset)<br> <span class="hljs-comment">// 2.after all access tuples locked, sn start to validate</span><br> validation := success <br> foreach tuple in batch.rset do:<br> <span class="hljs-keyword">if</span> tuple.version != GetCurrentVersion(tuple):<br> fresh_tuples.add(tuple)<br> validation := fail<br> <span class="hljs-keyword">if</span> batch.fast_opt is <span class="hljs-literal">false</span>: <br> Persist(batch.cts,batch.input, batch.wset, batch.rset.keys) <br> <span class="hljs-keyword">if</span> validation is success:<br> <span class="hljs-comment">// send ok to compute node </span><br> reply <PrepareOK> to DB<br> <span class="hljs-keyword">else</span>:<br><span class="hljs-comment">// send fresh tuple to update local cache of compute node</span><br> reply <PrepareNotOK, fresh_tuples> to DB<br> <span class="hljs-keyword">else</span>: <br> Use the fast path optimization<br>}<br><br><span class="hljs-comment">// 计算节点收到存储节点在 prepare 阶段发来的请求</span><br><span class="hljs-keyword">if</span> at least one PrepareNotOK reply was received:<br><span class="hljs-comment">// 1.repair and re-execute rset/wset by fresh_tuples and dependency graph</span><br>Repair(batch.input, batch.rset, batch.wset, fresh_tuples)<br><span class="hljs-comment">// 2.refresh the local cache in compute node</span><br> RefreshDbCache(fresh_tuples)<br> ApplyToDbCache(batch.delta_wset)<br><span class="hljs-comment">// 3.repair finish and start the second stage of 2PC</span><br>foreach sn in sn_locations do:<br>send Commit(batch) to all physical replicas of sn<br><br><span class="hljs-comment">// 存储节点收到修复后执行的数据和commit指令</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">HandleCommit</span><span class="hljs-params">(batch)</span></span> {<br> <span class="hljs-comment">// 修复成功后,存储节点将 final_wset 应用到本地的数据上</span><br><span class="hljs-keyword">if</span> batch was repaired: <br> Persist(batch.deleta_wset) <span class="hljs-comment">// 持久化增量修改,用于容错</span><br> ReformFinalWriteSetOf(batch.wset, batch.delta_wset)<br> ApplyToSnStorage(batch.final_wset)<br> ReleaseTupleLocks(batch.rset, batch.wset) <span class="hljs-comment">// unlock all tuples</span><br>}<br><br>=========================================================================<br><span class="hljs-comment">// Fast-path Optimization</span><br><span class="hljs-comment">// storage node </span><br>Persist(batch.cts, batch.input)<br><span class="hljs-keyword">if</span> validation is fail:<br>Repair(batch.input, batch.rset, batch.wset)<br>ApplyToSnStorage(batch.final_wset)<br>ReleaseTupleLocks(batch.rset, batch.wset)<br>reply <FastPathOK, fresh_tuples,batch.delta_wset> to DB<br><br><span class="hljs-comment">// compute node receive from storage node</span><br><span class="hljs-keyword">if</span> batch was repaired:<br> RefreshDbCache(fresh_tuples)<br> ApplyToDbCache(batch.delta_wset)<br></code></pre></td></tr></table></figure><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs go">Repair(batch.input, batch.rset, batch.wset, fresh_tuples)<br></code></pre></td></tr></table></figure><blockquote><p>计算节点从存储节点获取到最新的元组版本(fresh_tuple),依据时间戳顺序依次对遍历事务</p><p>首先是 T1,由 rset 确定首先受影响的读操作(R2)</p><p>根据 dataflow graph 重新执行与它相关的写操作(W4)</p><p>接着根据时间戳的顺序重新执行 R7, W10,最终生成 batch.delta_wset,发送给存储节点;</p><p>计算节点和存储节点都可以根据 delta_wset 生成 final_wset,将本地缓存或数据项持久化</p></blockquote><p>全局提交和快速路径优化的区别在于:</p><ul><li>前者需要完成2PC的两个阶段,由计算节点对事务进行修复,修复后发往各个存储节点执行;</li><li>后者直接由存储节点对事务修复,执行并完成提交后,返回通知计算节点</li></ul><h2 id="4-Hackwrench-实现细节"><a href="#4-Hackwrench-实现细节" class="headerlink" title="4 Hackwrench 实现细节"></a>4 Hackwrench 实现细节</h2><h5 id="Batch-splitting"><a href="#Batch-splitting" class="headerlink" title="Batch splitting"></a>Batch splitting</h5><p>本地节点需要对自己的所有事务划分批次,将所有事务划分成多个非冲突的子批次。</p><p>方法:构建一个无向图,各个事务为图的顶点,如果两个事务之间有相同数据项的访问,这两个顶点之间将会有一条无向边进行连接;每个连通分量所包含的各个事务都是可能产生冲突的,它们将形成一个子批次,进入分层提交协议的本地提交阶段。</p><h5 id="Fine-grained-re-execution"><a href="#Fine-grained-re-execution" class="headerlink" title="Fine-grained re-execution."></a>Fine-grained re-execution.</h5><blockquote><p>To mitigate the abort cost, we need a more efficient solution than aborting and retrying a batch of transactions. Our insight is that it is cheaper to repair the transactions batch by selectively re-executing operations affected by stale or invalid reads.</p></blockquote><p>一个直观方法是根据事务的依赖图来进行,如果每一批次使用一个大的依赖图囊括所有的事务操作将导致构建和遍历复杂度过高。</p><p>论文中采用的方法:</p><p>由于同一个批次中的事务操作顺序是能按照时间戳顺序确定的,修复可以按照事务的顺序进行修复;</p><p>每个事务会生成一个静态的数据流图,Hackwrench可以根据数据流图修复事务;</p><p>对于读操作,计算节点会根据 fresh_tuple 重新读取,然后依据 dataflow graph 对所有与读操作有直接联系的写操作进行重新执行。</p><h2 id="5-实验"><a href="#5-实验" class="headerlink" title="5 实验"></a>5 实验</h2><h5 id="BenchMark"><a href="#BenchMark" class="headerlink" title="BenchMark"></a>BenchMark</h5><ul><li>TPC-C</li><li>FDB-Micro</li></ul><blockquote><p>FDB-Micro是一个kv benchmark,元组分布在多个不同的存储节点,包含 80% 的只读事务和 20% 的读写事务(select+update),参数d代表分布式事务的占比</p><p>TPC-C主要以 multi-wareHouse NewOrder 分布式事务的占比对比系统的吞吐量和延迟;</p></blockquote><h5 id="实验1:对比在分布式事务占比不同的情况下各个系统的性能比较"><a href="#实验1:对比在分布式事务占比不同的情况下各个系统的性能比较" class="headerlink" title="实验1:对比在分布式事务占比不同的情况下各个系统的性能比较"></a>实验1:对比在分布式事务占比不同的情况下各个系统的性能比较</h5><p>分析:Hackwrench吞吐量相比其他两个系统有将近10倍的提升,主要体现在</p><ul><li>事务是批处理形式提交</li><li>在节点本地的事务并发过程中,允许读取本地已提交但全局未提交的数据</li><li>Hackwrench 通过修复事务(部分操作重做),而不是abort整个事务得到了吞吐量的提升</li></ul><p>Hackwrench 与 nofast 版本的对比,前者在处理 one-shot 事务的时候将事务的提交和修复权给到了存储节点,避免了多一次的2PC通信往返,使得吞吐量得到提升;</p><p>FDB性能表现较差的原因在于其计算节点并没有缓存数据,导致每次事务执行需要读取数据都得进行一次与存储节点的通信往返;但有意思的是,FDB在分布式事务占比数量不同的情况下仍然有一个稳定的吞吐,是因为FDB不使用缓存也就不会出现缓存陈旧而读取存储节点来增加通信次数;</p><p><img src="/img/image-20240220212227692.png" alt="image-20240220212227692"></p><h5 id="实验2:对比分析几种系统的延迟"><a href="#实验2:对比分析几种系统的延迟" class="headerlink" title="实验2:对比分析几种系统的延迟"></a>实验2:对比分析几种系统的延迟</h5><p>分析:</p><p>相比于 OCC+Caching 系统,当 batch size 增加时,Hackwrench 系统的延迟会增加,这是由于时间都花在了本地节点形成批次的场景上,若BS=1,单个事务执行完直接全局提交,并不需要考虑前面事务在队列中的阻塞;</p><p>(虽然BS增加会增加延迟,但它会减少节点之间的通信次数,进而提高吞吐量)</p><p>OCC 比 FDB 快的原因在于每次通信会更新多个tuple到本地缓存,下次执行时会减少通信次数,降低延迟</p><p><img src="/img/image-20240220213746517.png" alt="image-20240220213746517"></p><h5 id="实验3:对比分析5种优化里面,哪些在OCC基础上的优化最大"><a href="#实验3:对比分析5种优化里面,哪些在OCC基础上的优化最大" class="headerlink" title="实验3:对比分析5种优化里面,哪些在OCC基础上的优化最大"></a>实验3:对比分析5种优化里面,哪些在OCC基础上的优化最大</h5><p>r 代表 TPCC 中分布式事务 multi-warehouse NewOrder 的占比</p><p>caching 和 batching 都有一定的吞吐量提升;</p><p>+RU 指本地节点能读取本地已提交但全局未提交事务修改的数据,在低竞争条件下时(几乎没有分布式事务时,吞吐量特别高)</p><p>+Repair 指采用细粒度修复事务的方法,吞吐量提升明显;</p><p>+Fast 指在 one-shot 事务上直接让存储节点提交和修改,吞吐量提升显著;</p><p><img src="/img/image-20240220214930906.png" alt="image-20240220214930906"></p><h5 id="实验4:对比分析各个系统的CPU利用率"><a href="#实验4:对比分析各个系统的CPU利用率" class="headerlink" title="实验4:对比分析各个系统的CPU利用率"></a>实验4:对比分析各个系统的CPU利用率</h5><p>随着与远程节点访问的增加,网络通信开销的增加,吞吐量在减少</p><p>但 Hackwrench 仍然能够保持一个比较高的吞吐量;</p><p>右图是对比各个系统的CPU时间分布:</p><ul><li>COCO由于每个事务需要单独进行全局提交的验证,计算节点大部分时间处于阻塞的状态</li><li>Calvin系统是规定每个事务在所有计算节点都需要进行提交,导致CPU大部分时间花在了多节点同步的过程</li><li>Sundial 是不支持复制的,在10(a)中,当事务基本都是单点事务时,吞吐量非常高;但在右图中,与COCO一样,每个计算线程只允许每次验证单个事务,并且在事务重试前进行了sleep,因此CPU的利用程度不足</li><li>与上面三个相比,Hackwrench 充分利用了CPU,大部分时间花在了事务的执行,剩余时间大部分用在了全局提交阶段的 prepare 和 commit 阶段。</li></ul><p><img src="/img/image-20240221104010142.png" alt="image-20240221104010142"></p><h5 id="实验5-以-FDB-micro-benchmark-为基准的在不同分布式事务占比系统的吞吐量"><a href="#实验5-以-FDB-micro-benchmark-为基准的在不同分布式事务占比系统的吞吐量" class="headerlink" title="实验5 以 FDB-micro benchmark 为基准的在不同分布式事务占比系统的吞吐量"></a>实验5 以 FDB-micro benchmark 为基准的在不同分布式事务占比系统的吞吐量</h5><p>图11表明,COCO系统在分布式事务少的时候吞吐量表现优异,但分布式事务增加会导致吞吐量的下滑,而 Hackwrench 保持了较高的吞吐量;</p><p>并且在11(b)与11(a)的比较中,在高竞争场景下,Hackwrench 在 one-shot 事务的 Fast-path opt 优化在吞吐量上有显著的提升;</p><p>图13是说明 batch size 的不同对吞吐量和延迟的影响,吞吐量越大,延迟越高,是因为本地节点将事务成批的过程耗费了整体的时间;</p><p>同时,随着 batch size 的增加,由于引入了细粒度修复事务的策略,能让延迟增长地更加缓慢一点 </p><p><img src="/img/image-20240221111353349.png" alt="image-20240221111353349"></p><h2 id="6-分布式OLTP系统的两种分类"><a href="#6-分布式OLTP系统的两种分类" class="headerlink" title="6 分布式OLTP系统的两种分类"></a>6 分布式OLTP系统的两种分类</h2><h5 id="1、remote-storage-system"><a href="#1、remote-storage-system" class="headerlink" title="1、remote-storage system"></a>1、remote-storage system</h5><p>存算分离,spanner,FoundationDB,事务中的所有读都需要与远程的存储节点通信;</p><h5 id="2、co-located-system"><a href="#2、co-located-system" class="headerlink" title="2、co-located system"></a>2、co-located system</h5><p>将事务的执行(计算)与 存储节点的工作线程 放在一起,可以最大限度减少网络通信开销;</p><p>但涉及到跨分区的操作,仍然需要与远程节点通信,来确保事务的正确执行。</p><p>代表的三个系统</p><ul><li>Calvin (SIGMOD 12)</li></ul><blockquote><p>在执行之前,为整批事务分配了总的顺序再进行执行和提交,并且避免了2PC的开销;</p><p>规定每个事务在所有计算节点都需要进行提交</p><p>与 Hackwrench 的对比:后者是在整批事务执行完成后,再为事务分配提交顺序;</p></blockquote><ul><li>COCO (VLDB 21)</li></ul><blockquote><p>准备提交事务批时,对每个事务单独进行验证,再进行批量复制;</p><p>与 Hackwrench 的对比:后者由于使用细粒度重新执行,能够批量验证和批量复制</p></blockquote><ul><li>Sundial (VLDB 18)</li></ul><blockquote><p>使用逻辑租约的方法进行缓存管理,以降低OCC验证失败的概率;</p><p>与 Hackwrench 的对比:后者允许OCC验证失败,但通过细粒度修复来保证性能</p></blockquote>]]></content>
<categories>
<category>论文阅读</category>
</categories>
<tags>
<tag>transaction</tag>
</tags>
</entry>
<entry>
<title>LSM-Tree 笔记</title>
<link href="/2024/04/16/lsm-tree/"/>
<url>/2024/04/16/lsm-tree/</url>
<content type="html"><![CDATA[<p>LSM-Tree ( log structured merged tree )</p><p><img src="/img/image-20240303162140140.png" alt="image-20240303162140140"></p><h4 id="1-在-OLTP-里数据存储分为两种:"><a href="#1-在-OLTP-里数据存储分为两种:" class="headerlink" title="1 在 OLTP 里数据存储分为两种:"></a>1 在 OLTP 里数据存储分为两种:</h4><ul><li>面向页的存储引擎:innodb(B+ tree)</li><li>面向日志结构的存储引擎:leveldb(lsm tree)</li></ul><p><img src="/img/image-20240303154558590.png" alt="image-20240303154558590"></p><h4 id="2-LSM-Tree-用于解决什么问题?用在什么场景?"><a href="#2-LSM-Tree-用于解决什么问题?用在什么场景?" class="headerlink" title="2 LSM-Tree 用于解决什么问题?用在什么场景?"></a>2 LSM-Tree 用于解决什么问题?用在什么场景?</h4><p><strong>写多读少</strong>的场景</p><blockquote><p>海量数据存储</p><p>日志系统</p><p>推荐系统</p></blockquote><ul><li>数据需要大量写入</li><li>存在少部分读数据的场景,但大部分情况<strong>对读的实时性要求较低</strong></li></ul><h4 id="3-LSM-Tree-如何解决大量写?"><a href="#3-LSM-Tree-如何解决大量写?" class="headerlink" title="3 LSM-Tree 如何解决大量写?"></a>3 LSM-Tree 如何解决大量写?</h4><p>利用<strong>顺序IO,即追加写的方式</strong>,将用户的所有修改操作(insert,update,delete)采用追加的方式记录在磁盘中,类似于打印日志的方式。</p><blockquote><p>优点:顺序IO,使写性能提高;</p><p>缺点:空间放大,同一份数据的各种修改操作分在了不同的块中,存在过时的数据修改记录;</p><p>解决方法:<strong>后台定期压缩和合并数据</strong>,消除无效数据;</p></blockquote><h5 id="3-1-LSM-Tree-如何提升合并的效率?"><a href="#3-1-LSM-Tree-如何提升合并的效率?" class="headerlink" title="3.1 LSM-Tree 如何提升合并的效率?"></a>3.1 LSM-Tree 如何提升合并的效率?</h5><ul><li><p>当每个文件<strong>写入的数据有序</strong>时,可以利用<strong>多路归并</strong>的思想合并;</p></li><li><p>合并的方式:</p><ul><li>分级合并:leveling</li><li>分层合并:tiring</li></ul></li><li><p>如何保证每个文件写入的数据有序:</p><ul><li>先在内存中缓存数据,并将数据排好序,异步刷入磁盘,保证了每个磁盘文件内部数据有序</li></ul></li><li><p>需要首先保证内存中的数据有序:</p><ul><li>跳表,B+树,红黑树</li></ul></li><li><p>如何保证在内存中缓存的数据不丢失,进程挂掉了,数据丢了咋办?(容错机制)</p><ul><li>WAL 写前记录的日志(redo 日志同理)</li><li>在所有写操作应用于状态机之前,写入redo log,顺序IO刷入磁盘,出故障后修复</li></ul></li></ul><h5 id="3-2-内存中数据结构的选型"><a href="#3-2-内存中数据结构的选型" class="headerlink" title="3.2 内存中数据结构的选型"></a>3.2 内存中数据结构的选型</h5><p>如何选择一个在各方面 trade-off 的数据结构,能高效维护内存数据的有序性,并发性?</p><p>来自 rocksdb 的解答:<a href="https://github.com/facebook/rocksdb/wiki/memtable">https://github.com/facebook/rocksdb/wiki/memtable</a></p><p><img src="/img/image-20240303160512171.png" alt="image-20240303160512171"></p><p>跳表、hash跳表、hash链表、数组</p><p>跳表能很好地支持并发插入,非常适用于大量写场景</p><h5 id="3-3-大量写的总结"><a href="#3-3-大量写的总结" class="headerlink" title="3.3 大量写的总结"></a>3.3 大量写的总结</h5><p>系统内部存在三种数据:</p><ul><li>内存数据(memtable)需要满足有序性,比如:skiplist</li><li>磁盘数据(SSTable)</li><li>日志数据(redo log)也就是 mysql 里面的 WAL</li></ul><p>数据压缩、合并等处理方式:</p><ul><li>分层压缩</li><li>分级压缩</li><li>后台定时触发</li><li>后台达到阈值时触发</li></ul><h4 id="4-LSM-Tree-合并的策略"><a href="#4-LSM-Tree-合并的策略" class="headerlink" title="4 LSM-Tree 合并的策略"></a>4 LSM-Tree 合并的策略</h4><p><img src="/img/image-20240303162301659.png" alt="image-20240303162301659"></p><h4 id="5-LSM-Tree-如何解决读数据?"><a href="#5-LSM-Tree-如何解决读数据?" class="headerlink" title="5 LSM-Tree 如何解决读数据?"></a>5 LSM-Tree 如何解决读数据?</h4><p><img src="/img/image-20240303161414887.png" alt="image-20240303161414887"></p><h4 id="6-LSM-Tree-的三大问题"><a href="#6-LSM-Tree-的三大问题" class="headerlink" title="6 LSM-Tree 的三大问题"></a>6 LSM-Tree 的三大问题</h4><p><img src="/img/image-20240303161504694.png" alt="image-20240303161504694"></p><p>读放大</p><blockquote><p>从最新数据往前读,一次读不止一次IO</p></blockquote><p>空间放大</p><blockquote><p>一个数据项可能涉及多个日志记录的操作,过期或删除的数据项不会立刻清除,造成空间浪费</p></blockquote><p>写放大</p><blockquote><p>在插入新数据时,频繁的合并和压缩操作导致磁盘的写入量非常大</p><p>合并操作:涉及大量的读数据、写数据</p><p>压缩操作:读取数据,重新组织,写入新数据</p><p>写放大不仅限制了LSM树的写入性能,而且会由于频繁的磁盘写入缩短 SSD 的寿命</p></blockquote><h4 id="详细解读-lsm-tree-的两个合并策略"><a href="#详细解读-lsm-tree-的两个合并策略" class="headerlink" title="详细解读 lsm-tree 的两个合并策略"></a>详细解读 lsm-tree 的两个合并策略</h4><h5 id="1、level-compaction-分层合并"><a href="#1、level-compaction-分层合并" class="headerlink" title="1、level compaction 分层合并"></a>1、level compaction 分层合并</h5><ul><li>每个 level 维护多个基本相同大小的 SST</li><li>每个 SST 之间都是顺序有序的<ul><li>比如第 i 层 SST1: 0<del>59,SST2:60</del>89,SST3:90~99</li></ul></li><li>合并可以并行,第3层第3个合并到第4层2<del>4组件,同时第3层第7个合并到第4层6</del>8组件</li></ul><p><img src="/img/image-20240617201337876.png" alt="image-20240617201337876"></p><h5 id="2、Tier-compaction-分级合并"><a href="#2、Tier-compaction-分级合并" class="headerlink" title="2、Tier compaction 分级合并"></a>2、Tier compaction 分级合并</h5><ul><li>每层中,一个 SST 内部键值对有序,SST 之间键值对无序</li><li>若干个(4个)SST 合并到 i+1 层的一个新的 SST,新的 SST 与本层其他 SST 可能会有键值对重复</li></ul><p><img src="/img/image-20240617201358299.png" alt="image-20240617201358299"></p>]]></content>
<categories>
<category>数据库</category>
</categories>
<tags>
<tag>LSM-Tree</tag>
</tags>
</entry>
<entry>
<title>A Critique of ANSI SQL Isolation Levels 论文阅读</title>
<link href="/2024/04/16/transaction-isolation/"/>
<url>/2024/04/16/transaction-isolation/</url>
<content type="html"><![CDATA[<h4 id="A-Critique-of-ANSI-SQL-Isolation-Levels-经典论文"><a href="#A-Critique-of-ANSI-SQL-Isolation-Levels-经典论文" class="headerlink" title="A Critique of ANSI SQL Isolation Levels 经典论文"></a>A Critique of ANSI SQL Isolation Levels 经典论文</h4><h5 id="2-1、基本定义"><a href="#2-1、基本定义" class="headerlink" title="2.1、基本定义"></a>2.1、基本定义</h5><p>history:将多个事务的多个操作交替执行的序列建模为一个线性的执行顺序,如 R1,W2,R2,W1</p><p>conflict:两个事务操作同一个data item,且其中一个是写操作</p><p>data item:可能是表的一个元组,一个逻辑页面,或一个完整的关系表,或队列中的消息等</p><p>dependency graph:依赖图,图中每个节点代表事务的一个操作,若 T1 事务的 op1 在 T2 事务的 op2 之前发生,并且这两个发生了冲突,那么在依赖图中可以用 <op1,op2> 代表 op1 和 op2 节点之间的边;</p><p>两个history是等价的情况:有相同的已提交的事务集合,并且依赖图相同;</p><p>定义 history 是否可以串行化:若一个普通的 history(事务操作相互重叠,如 R1,W2,R2,W1) 能和另一个串行执行事务的 history (事务顺序执行,如:R1,W1,W2,R2 先事务1后事务2)等价,说明该 history 可串行化;</p><h5 id="2-2-ANSI-SQL-的隔离级别"><a href="#2-2-ANSI-SQL-的隔离级别" class="headerlink" title="2.2 ANSI SQL 的隔离级别"></a>2.2 ANSI SQL 的隔离级别</h5><p><strong>P0</strong> 脏写(Dirty Write):有写锁,但写完立刻释放</p><p>导致的问题:T2 事务覆盖了另一个在运行中、尚未提交事务 T1 写入的值</p><p>比如约束条件: x == y,init:x = y = 0</p><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>write x = 1</td><td></td></tr><tr><td></td><td>write x =2</td></tr><tr><td></td><td>write y = 2</td></tr><tr><td>write x= 1</td><td></td></tr></tbody></table><p>但最终结果,x = 1,y = 2 违反了一开始的一致性约束</p><p><strong>P1</strong> 脏读(Dirty Read):读到了一个尚未提交的事务修改的值,那个事务在修改后发生了rollback,导致本事务脏读 </p><p>狭义脏读:w1[x]…r2[x]…(a1 and c2 in either order) </p><table><thead><tr><th>事务 T1</th><th>事务 T2</th></tr></thead><tbody><tr><td>begin (a=1)</td><td>begin (a=1)</td></tr><tr><td>write a=2</td><td></td></tr><tr><td></td><td>read a==2</td></tr><tr><td>rollback</td><td></td></tr><tr><td></td><td>read a==1 (前后两次 read 不一致)</td></tr></tbody></table><p>广义脏读:w1[x]…r2[x]…((c1 or a1) and (c2 or a2) in any order)</p><table><thead><tr><th>事务 T1</th><th>事务 T2</th></tr></thead><tbody><tr><td>read x=50</td><td></td></tr><tr><td>write x=10</td><td></td></tr><tr><td></td><td>read x=10</td></tr><tr><td></td><td>read y=50</td></tr><tr><td></td><td>commit</td></tr><tr><td>read y=50</td><td></td></tr><tr><td>write y=90</td><td></td></tr><tr><td>commit</td><td></td></tr></tbody></table><p>事务 T1 将 x 转账 40 到 y 账户上,T2 也会造成脏读</p><p><strong>P2</strong> 不可重复读(Non-repeatable):针对数据的修改,本事务分别两次读到了其他事务修改前和修改后的值</p><table><thead><tr><th>事务 T1</th><th>事务 T2</th></tr></thead><tbody><tr><td>begin (a=1)</td><td>begin (a=1)</td></tr><tr><td>read a=1</td><td></td></tr><tr><td></td><td>modify a=2</td></tr><tr><td></td><td>commit</td></tr><tr><td>read a=2(前后两次 read 不一致)</td><td></td></tr></tbody></table><p><strong>P3</strong> 幻读(Phantom):针对数据的插入或删除,本事务分别两次读到了其他事务修改前后值的数量不同</p><table><thead><tr><th>事务 T1</th><th>事务 T2</th></tr></thead><tbody><tr><td>begin</td><td>begin</td></tr><tr><td>select count(*) from student (50)</td><td></td></tr><tr><td></td><td>insert into student a tuple</td></tr><tr><td>select count(*) from student (51)</td><td></td></tr></tbody></table><p><strong>P4</strong> 丢失修改(lost update):两个事务同时对一个数据项进行更改,一个先提交,一个后提交,后提交的事务修改将覆盖前一个提交事务的修改</p><table><thead><tr><th>事务 T1</th><th>事务 T2</th></tr></thead><tbody><tr><td>read x=10</td><td></td></tr><tr><td></td><td>read x=10</td></tr><tr><td></td><td>write x=12</td></tr><tr><td></td><td>commit</td></tr><tr><td>write x=13</td><td></td></tr><tr><td>commit</td><td>(T2 的修改无效了)</td></tr></tbody></table><p>Cursor Stability:可以防止丢失修改异常</p><p>在RC(read commited)基础上,在游标打开后进行加锁,其他事务可以读取打开的游标的数据,但不能修改这些在游标控制范围内的数据项</p><p><strong>P5</strong> 读偏斜(Read Skew)与写偏斜(Write Skew)</p><p><strong>读偏斜:读到了数据一致性被破坏了的数据</strong>,比如数据的约束条件是 X + Y = 100,但事务并发过程中,其中某个阶段一个事务读到了 X + Y =120,就会破坏了事务的一致性</p><table><thead><tr><th>事务 T1</th><th>事务 T2</th><th>X</th><th>Y</th></tr></thead><tbody><tr><td>Read X=50</td><td>Read X=50</td><td>50</td><td>50</td></tr><tr><td>write X=30</td><td></td><td>30</td><td>50</td></tr><tr><td>write Y=70</td><td></td><td>30</td><td>70</td></tr><tr><td></td><td>Read Y=70</td><td>30</td><td>70</td></tr></tbody></table><p> 在事务 T2 看来,X + Y = 120,违反了事务的一致性</p><p><strong>写偏斜:两个并发事务首先都读到了相同的数据集,然后各自分别修改了不同的部分,造成最后数据的一致性约束被破坏</strong></p><p>比如数据的一致性约束是 X + Y <= 100</p><table><thead><tr><th>事务 T1</th><th>事务 T2</th><th>X</th><th>Y</th></tr></thead><tbody><tr><td>Read X=10 Y=20</td><td>Read X=10 Y=20</td><td>10</td><td>20</td></tr><tr><td>write X=70</td><td></td><td>70</td><td>20</td></tr><tr><td>commit</td><td></td><td>70</td><td>20</td></tr><tr><td></td><td>Write Y=50</td><td>70</td><td>50</td></tr><tr><td></td><td>commit</td><td>70</td><td>50</td></tr></tbody></table><p>在最后的数据结果中,X + Y > 100,违法了一致性约束</p><p>一些缩写:</p><p>w1[x]:事务1写数据项x</p><p>r2[x]:事务2读数据项x</p><p>r1[P]:事务1读符合条件 P 的所有数据项</p><p>w1[P]:事务1写符合条件 P 的所有数据项</p><p>c1:事务1进行 commit</p><p>a1:事务1进行 abort 或 rollback</p><p>根据上面的三种现象,可以分别进行形式化表达:(严格定义)</p><p>A1脏读:w1[x] . . . r2[x] . . . (c1 or a1)</p><p>A2 不可重复读:r1[x]…w2[x]…c2…r1[x]…c1</p><p>A3 幻读:r1[P]…w2[y in P]…c2…r1[P]…c1</p><p>在 ANSI 中定义了四种隔离级别和与之对应的三种情形:(打勾代表会出现的问题)</p><ul><li>RU:读未提交(读不上锁;写的时候上锁,并只在事务提交或回滚才释放)</li><li>RC:读已提交(读上锁,读完立刻释放;写的时候上锁,并只在事务提交或回滚才释放)</li><li>RR:可重复读</li><li>Serializable:可串行化</li></ul><p><img src="/img/image-20230916215857936.png"></p><h5 id="4-2-Snapshot-Isolation"><a href="#4-2-Snapshot-Isolation" class="headerlink" title="4.2 Snapshot Isolation"></a>4.2 Snapshot Isolation</h5><p>快照隔离:一种多版本并发控制隔离</p><p>1、每个事务在 begin 时创建一个开始时间戳,该事务自此以后所有的操作(读取写入)都基于这个快照版本,其他事务在这期间对相同数据项的修改,此事务不可见</p><p>2、当事务 T1 准备提交,它会获得一个 commit 时间戳,当前 T1 能够成功提交的充要条件:不存在其他事务 T2 的 commit 时间戳在 T1 的执行时间内(即 [ start-timestamp, commit-timestamp ] ),否则 T1 rollback,这是为了防止丢失修改错误</p><p>(谁先 commit,谁先成功)</p><p>如果一个事务在另一个事务进行了更新并提交后才试图读取相同的数据,那么它将看到更新后的数据,而不是原始数据。这可以防止脏读(Dirty Read)和不可重复读(Non-Repeatable Read)等并发问题</p><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>begin</td><td>begin</td></tr><tr><td>read x0=50</td><td></td></tr><tr><td>write x1=10</td><td></td></tr><tr><td></td><td>read x0=50(读到的是begin的快照)</td></tr><tr><td></td><td>read y0=50</td></tr><tr><td></td><td>commit</td></tr><tr><td>read y0=50</td><td></td></tr><tr><td>write y1=90</td><td></td></tr><tr><td>commit</td><td></td></tr></tbody></table><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>begin</td><td>begin</td></tr><tr><td>read x=50</td><td></td></tr><tr><td>read y=50</td><td></td></tr><tr><td></td><td>read x=50</td></tr><tr><td></td><td>read y=50</td></tr><tr><td>write y=-40</td><td></td></tr><tr><td></td><td>write x=-40</td></tr><tr><td>commit</td><td></td></tr><tr><td></td><td>commit</td></tr></tbody></table><p>狭义上的幻读 A3</p><p><strong>A3:</strong> r1[P]…w2[y in P]…c2…r1[P]…c1 </p><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>begin</td><td>begin</td></tr><tr><td>select count(*) from student (50)</td><td></td></tr><tr><td></td><td>insert into student a tuple</td></tr><tr><td></td><td>commit</td></tr><tr><td>select count(*) from student (51)</td><td></td></tr><tr><td>commit</td><td></td></tr></tbody></table><p>广义上的幻读 P3</p><p><strong>P3:</strong> r1[P]…w2[y in P]…((c1 or a1) and (c2 or a2) any order)</p><h4 id="隔离级别的总结"><a href="#隔离级别的总结" class="headerlink" title="隔离级别的总结"></a>隔离级别的总结</h4><p>读未提交(RU):顾名思义,其他事务读到了当前事务没提交的修改</p><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>begin (a=1)</td><td>begin (a=1)</td></tr><tr><td>write a=2</td><td></td></tr><tr><td></td><td>read a==2</td></tr></tbody></table><p>读已提交(RC):会读到其他事务已提交的数据,造成不可重复读</p><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>begin</td><td>begin</td></tr><tr><td>select * from user;(1,a)</td><td>select * from user;(1,a)</td></tr><tr><td>update user set name =’b’ where id=1;</td><td></td></tr><tr><td>select * from user;(1,b)</td><td></td></tr><tr><td></td><td>select * from user;(1,a)</td></tr><tr><td>commit</td><td></td></tr><tr><td></td><td>select * from user;(1,b)</td></tr><tr><td></td><td>commit</td></tr></tbody></table><p>可重复读:读不到其他事务已提交的数据</p><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>begin</td><td>begin</td></tr><tr><td>select * from user;(1,a)</td><td>select * from user;(1,a)</td></tr><tr><td>update user set name =’b’ where id=1;</td><td></td></tr><tr><td>select * from user;(1,b)</td><td></td></tr><tr><td></td><td>select * from user;(1,a)</td></tr><tr><td>commit</td><td></td></tr><tr><td></td><td>select * from user;(1,a)</td></tr><tr><td></td><td>commit</td></tr></tbody></table><p>特殊:并发写的例子:</p><table><thead><tr><th>T1</th><th>T2</th></tr></thead><tbody><tr><td>begin</td><td>begin</td></tr><tr><td>select * from user;(1,a)</td><td>select * from user;(1,a)</td></tr><tr><td>update user set name =’b’ where id=1;</td><td></td></tr><tr><td>select * from user;(1,b)</td><td></td></tr><tr><td></td><td>select * from user;(1,a)</td></tr><tr><td></td><td>update user set name =’c’ where id=1;</td></tr><tr><td></td><td>等待.. T1commit后这条update语句才执行</td></tr><tr><td>commit</td><td>wait</td></tr><tr><td></td><td>update执行成功(1,c)</td></tr><tr><td></td><td>commit</td></tr></tbody></table><p>表2 根据读锁、写锁定义一致性的程度和隔离级别</p><p>degree 0:只需要保证每个操作是原子性的,读不上锁,写操作加锁写完立刻释放;</p><p>degree 1:读未提交,读不上锁,写锁仅在事务 commit 或 rollback 释放,可以防止上一级的 dirty write 异常,但可能会读到其他未提交事务的更改,即脏读(dirty read);</p><p>degree 2:读已提交,读上读锁,读完立即释放,写锁仅在事务 commit 或 rollback 释放,可以避免读到其他事务未提交的数据,但当别的事务提交后,可以读到新的数据,由于与之前读到的不一致,可能造成不可重复读(Fuzzy Read)和丢失修改(lost update);</p><p>Cursor Stability 级别:用于在RC基础上防止丢失修改(后一个事务对相同数据的更新提交覆盖前一个事务的提交),方法是读数据时在游标打开后进行加读锁,其他事务可以读取打开的游标的数据,但不能修改这些在游标控制范围内的数据项;</p><p>可重复读:在当前事务过程中,读不到其他事务提交的更改</p><p>快照隔离:每个事务开始时获取一个快照,之后的读写都从这个私有快照中获取数据</p><blockquote><p>可重复读和快照隔离的区别:</p><p>可重复读通过对数据项加长时间读锁来防止丢失修改和不可重复读</p><p>快照隔离通过采用多版本数据项形式防止不可重复读</p></blockquote><p>degree 3 串行化:事务可串行化</p><p><img src="/img/image-20230922103832028.png" alt="image-20230922103832028"></p><p><img src="/img/image-20230922111033020.png" alt="image-20230922111033020"></p>]]></content>
<categories>
<category>论文阅读</category>
</categories>
<tags>
<tag>transaction</tag>
</tags>
</entry>
<entry>
<title>6.824-raft-笔记</title>
<link href="/2024/04/16/6-824-raft-%E7%AC%94%E8%AE%B0/"/>
<url>/2024/04/16/6-824-raft-%E7%AC%94%E8%AE%B0/</url>
<content type="html"><![CDATA[<p>Raft算法中服务器的三种角色</p><ol><li>Follower</li><li>Candidate</li><li>Leader</li></ol><p>每台服务器需要维护的变量</p><p><strong>每个节点的持久状态:</strong></p><ol><li>currentTerm: 当前节点位于的最大的Term任期,初始化为0,单调递增</li><li>log []Entry : 日志条目(每条日志条目包含命令和任期)</li><li>votedFor: 可以理解为当前任期 支持的候选者 或 当前整个系统的leader</li></ol><blockquote><p>votedFor 一开始为 -1,以下情况将改变 votedFor 的值:</p><ul><li>选举超时(相当于心跳超时),follower 转变为 candidate,votedFor = me 更改</li><li>收到 leader 的心跳,votedFor = leaderId 更改</li><li>收到任意 server 的 term > currentTerm,votedFor = candidateId 更改</li><li>竞选超时,votedFor = me 不变</li><li>成为 leader,votedFor = me 不变</li></ul></blockquote><p><strong>每个节点的易失状态:</strong></p><ol><li>commitIndex: 当前节点最高的被提交的日志的索引, 初始化为0并单调递增</li><li>lastApplied: 当前节点最高的被应用于状态机的日志的索引, 初始化为0并单调递增</li></ol><h5 id="Leader-竞选成功后需要维护的变量:"><a href="#Leader-竞选成功后需要维护的变量:" class="headerlink" title="Leader 竞选成功后需要维护的变量:"></a>Leader 竞选成功后需要维护的变量:</h5><p>1、nextIndex[n]: 下次与各个follower进行日志同步时,发送的日志起始序号</p><blockquote><p>初始化为leader最后一个日志索引+1</p><p>即下回发送 [ nextIndex,leader_all ]</p></blockquote><p>2、matchIndex[n]: 与各follower节点同步到的日志序号位置,init = 0</p><p>在日志复制环节:</p><h5 id="为什么有了-nextIndex-还要有-matchIndex,matchIndex为什么不等于-nextIndex-1?"><a href="#为什么有了-nextIndex-还要有-matchIndex,matchIndex为什么不等于-nextIndex-1?" class="headerlink" title="为什么有了 nextIndex 还要有 matchIndex,matchIndex为什么不等于 nextIndex - 1?"></a>为什么有了 nextIndex 还要有 matchIndex,matchIndex为什么不等于 nextIndex - 1?</h5><blockquote><p>由于领导者在同步follower日志的时候,需要考虑容错,即发生丢包等情况</p><ul><li>如果follower更新成功:</li></ul><p>matchIndex[ me ] = 最后一条日志 + 1 (防止客户端可能在并行发送)</p><p>matchIndex[ follower ] = 领导者已发送的最后一条日志 </p><p>nextIndex[ follower ] = matchIndex[ follower ] + 1</p><ul><li>如果follower更新失败:</li></ul><p>nextIndex[ follower ] –,matchIndex不变</p><p>注意 nextIndex 不能小于0</p><p>比如:领导者:A B C D E,在某时刻,follower 有 A B F;</p><p>第一轮:nextIndex=6,matchIndex=0,leader发送空日志,</p><ul><li>匹配失败 matchIndex = 0,nextIndex = 5</li></ul><p>第二轮:leader 发送 [ E ],</p><ul><li>匹配失败 matchIndex = 0,nextIndex = 4</li></ul><p>第三轮:leader 发送日志 [ D E ],</p><ul><li>匹配失败 matchIndex = 0,nextIndex = 3</li></ul><p>第四轮:leader 发送日志 [ C D E ]</p><ul><li>匹配失败 matchIndex = 0,nextIndex = 2</li></ul><p>第五轮:leader 发送日志 [ B C D E ]</p><ul><li>匹配成功,matchIndex = 2,follower 删除 F,添加后为 [ A B C D E ]</li></ul></blockquote><p><strong>一般变量:</strong></p><ol><li>role 处在的角色:领导者、跟随者、候选者</li><li>electionTimeout:选举超时</li><li>totalVotes:当节点为 candidate 时,收到的选票数</li><li>timer 定时器,不同状态下的超时不一样(选举超时,竞选超时,心跳超时)</li><li>heartBeatChannel chan bool 心跳包通道,用于follower状态下接收来自leader空的心跳包</li><li>isLeaderChannel chan bool 一旦选上成为领导,通道里就有一个true,仅用于在make中的candidate分支判断</li></ol><p>4、RPC 请求和响应</p><p>(1)领导者选举:RequestVote RPC</p><blockquote><p>当节点成为 candidate 后,将调用所有其他节点的 RequestVote 函数</p></blockquote><p>请求参数:</p><ol><li>term: 当前 candidate 节点的 term 值</li><li>candidateId: 当前 candidate 节点的编号</li><li>lastLogIndex: 当前 candidate 节点最后一个日志的下标(从1开始)</li><li>lastLogTerm: 当前 candidate 节点最后一个日志的 term 值</li></ol><p>返回值:</p><ol><li>term: 其他节点的当前任期, 如果比自己大,则需要更新 candidate 自己的 term,转化为跟随者</li><li>voteGranted: 是否给该申请节点投票,true:给当前节点的选票 + 1</li></ol><h5 id="选举RPC如何保证安全性,为什么需要-lastLogIndex-等参数?"><a href="#选举RPC如何保证安全性,为什么需要-lastLogIndex-等参数?" class="headerlink" title="选举RPC如何保证安全性,为什么需要 lastLogIndex 等参数?"></a>选举RPC如何保证安全性,为什么需要 lastLogIndex 等参数?</h5><p>可能存在的不安全性:</p><blockquote><p>一个follower在进入宕机后它的leader提交了若干日志条目,当follower重新上线后,leader宕机了,此时如果重新选举,在没有安全限制的情况下,这个follower可能当选为领导者,并且覆盖leader已提交的条目,导致每个状态机执行的序列不一致。</p><p>A:1 2 3,leader时候将123均复制到大多数节点,123均为committed</p><p>B:1 ,宕机 | 恢复 4</p><p>此时A宕机了,B又恢复了,如果B被当选了,将会覆盖掉23,正确的是应该由当前集群中日志最新的节点当选,这样能保证每个节点的状态机执行的日志序列相同</p></blockquote><p>解决方法:</p><blockquote><p>1、原领导者的日志达成 commit 的条件:超过半数的 follower 成功复制了该日志条目</p><p>2、候选者在投票给其他节点时,follower将自己最后一个日志的编号和任期号和传入的candidate的日志编号和任期号做比较,</p><ul><li>如果与candidate相同或candidate更新一点,则允许给它投票</li><li>否则不变</li></ul><p>(在集群中没有选出领导者时,客户端发送的指令将阻塞)</p></blockquote><h5 id="Raft-保证日志安全性的措施:Leader-在commit日志时,只能提交当前任期的日志"><a href="#Raft-保证日志安全性的措施:Leader-在commit日志时,只能提交当前任期的日志" class="headerlink" title="Raft 保证日志安全性的措施:Leader 在commit日志时,只能提交当前任期的日志"></a>Raft 保证日志安全性的措施:Leader 在commit日志时,只能提交当前任期的日志</h5><p>场景:(Corner Case)</p><blockquote><p>A: 1 1 2 3</p><p>B: 1 1 2</p><p>C: 1 1 2 3 3 | 4</p><p>C在任期3是leader,1123都是已提交的,但在最后一个3提交之前宕机了,之后重新上线,超时重新选举,C由于最新在任期4又成为了leader,此时第二个命令3如何处理?</p><p>丢弃?还是将其改为任期4,然后复制给其他节点?</p></blockquote><p>raft论文里的方法:每个领导者刚当选时,提交一个 no-op 无操作日志</p><p>一种可能实现的方法:</p><p>宕机后通过 readPersist() 方法读取获得 commitIndex,</p><p>log 直接截至到 commitIndex,丢掉后面未提交的之前任期的日志;</p><p>但这样客户端在超时后可能需要重新向集群发送被丢弃的日志(需要在客户端处理)</p><p>或者将所有commitIndex ~ leader_log_end 的所有日志的任期改为当前任期(能保证容错)</p><h5 id="Corner-Case-2"><a href="#Corner-Case-2" class="headerlink" title="Corner Case 2"></a>Corner Case 2</h5><p>3个节点012,1一开始被选为leader,客户端传入第一条日志{100}</p><p>1维持着心跳,但1与0两个节点之间网络不顺,直到超时0号节点依然没能收到1的心跳</p><p>此时0超时后成为candidate,term+1,通过了2号节点的选票</p><p>这样一来,当网络正常后,1和0又能连接上了,此时0的任期比1大,会让1变更为follower</p><p>之后,假定网络一直顺畅,0一直是leader,则那条日志永远不会复制给除1以外的节点了</p><p>(网络分区)</p><p>解决方法:在RequestVote()</p><h4 id="Raft-中的超时时间"><a href="#Raft-中的超时时间" class="headerlink" title="Raft 中的超时时间"></a>Raft 中的超时时间</h4><p>超时时间:建议随机生成 150ms ~ 300ms,论文中提到的是 50ms~500ms</p><h5 id="1、选举超时时间(Election-Timeout)"><a href="#1、选举超时时间(Election-Timeout)" class="headerlink" title="1、选举超时时间(Election Timeout)"></a>1、选举超时时间(Election Timeout)</h5><p>每个 follower 维护一个随机的超时时间,在这个时间范围内,只要没有收到领导者的心跳,即可转变为候选者。包括:</p><blockquote><p>第一次选举的超时时间</p><p>没收到领导者心跳包的心跳超时</p></blockquote><h5 id="2、竞选超时"><a href="#2、竞选超时" class="headerlink" title="2、竞选超时"></a>2、竞选超时</h5><p>当前节点状态为 candidate,在规定时间内没有获取到足够多的票数,则当前轮次的选举竞选失败,当前节点重新回到 candidate,对应状态机中的自环。</p><blockquote><p>此时,需要重置超时时间、得到的票数重置为0</p></blockquote><p>关注时间的设置,raft若要稳定工作,必须维持一个稳定的leader:</p><p>广播时间 < 选举超时 < 平均故障间隔时间</p><blockquote><p>broadcastTime < electionTimeout < MTBF</p><p>广播时间:一个服务器并行发送 RPC 给其他服务器,接收,响应的平均时间;</p><p>由于选举超时 == 心跳超时,要想维持领导者稳定,心跳超时必须远大于广播时间</p></blockquote><h4 id="5-3-Leader-复制日志-发送心跳包"><a href="#5-3-Leader-复制日志-发送心跳包" class="headerlink" title="5.3 Leader 复制日志 / 发送心跳包"></a>5.3 Leader 复制日志 / 发送心跳包</h4><h5 id="1、整体日志复制流程:"><a href="#1、整体日志复制流程:" class="headerlink" title="1、整体日志复制流程:"></a>1、整体日志复制流程:</h5><ul><li>客户端发来的每条消息里面包含一条待执行的指令</li><li>leader 会将指令追加到自己的 log,然后通过 AppendEntries RPC 并发复制给其他节点</li><li>复制成功后(commited),leader 将这条日志交给自己的状态机执行,然后将结果返回给客户端</li><li>如果 follower 挂掉了或很慢,或者发生了丢包,leader 会无限重试 AppendEntries 请求,直到所有 follower 最终都存储了所有的 log entries</li></ul><h5 id="2、提交的定义"><a href="#2、提交的定义" class="headerlink" title="2、提交的定义"></a>2、提交的定义</h5><p>Commited:leader 将它成功复制到大多数节点(大于n/2),这个 entry 就算提交了</p><h5 id="3、Raft-日志的两个基本定理"><a href="#3、Raft-日志的两个基本定理" class="headerlink" title="3、Raft 日志的两个基本定理"></a>3、Raft 日志的两个基本定理</h5><ul><li>如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。</li><li>如果在不同的日志中的两个条目拥有相同的索引和任期号,那么他们之前的所有日志条目也全部相同。</li></ul><p>在正常情况下,即每个任期的领导者均不发生宕机的情况下,所有 follower 的日志都是和领导者保持相同序列,即使 follower 宕机回来后也能正常从当前末尾依次复制缺失的日志。</p><h5 id="4、日志不一致的场景"><a href="#4、日志不一致的场景" class="headerlink" title="4、日志不一致的场景"></a>4、日志不一致的场景</h5><p>当领导者节点发生宕机,后面的未 commit 的日志将会在该领导者节点上形成冗余</p><blockquote><p>比如下图 (c) 是任期6的领导者,前三个日志都持久化了,但最后一个未发送到其他节点,自己就宕机了</p><p>(框框里的数字代表每个日志创建时的任期)</p></blockquote><p><img src="/img/image-20230916103407914.png" alt="image-20230916103407914"></p><h5 id="5、解决日志不一致的办法"><a href="#5、解决日志不一致的办法" class="headerlink" title="5、解决日志不一致的办法"></a>5、解决日志不一致的办法</h5><p>(1)领导者需寻找 leader 和 follower 的最后一个完全相同的条目,该日志编号为 a</p><p>(2)删除 follower 那个条目之后的所有项 ( delete a+1 ~ all )</p><p>(3)leader 发送自己的日志给 follower (send a+1 ~ leader_last_log )</p><p>但如何将上述三个操作合并为一个 RPC 进行处理呢?</p><blockquote><p>领导者针对每个 follower 维护一个 nextIndex,</p><p>当领导者刚获得权力的时候,初始化所有 nextIndex = 最后一条commit的日志编号 + 1</p><p>调用 RPC 第一次可能失败,此时更新 nextIndex = follower匹配的最后一条 + 1</p><p>间隔数ms,第二次调用RPC,发送日志 [ nextIndex, last_log ]</p></blockquote><p>详细描述:</p><p>1、Leader节点维护的变量<code>nextIndex[followerID]</code>:其他各个节点下一个需要给到的日志</p><p>2、节点选举Leader成功后,首先将所有 nextIndex 初始化为 log 的下一个编号(图7的11)</p><p>3、发送 AppendEntries,对于 follower <code>i</code>, 根据下列日志匹配原则进行判断</p><p><strong>日志匹配原则:如果两个日志条目包含的 index 和 term 完全相同,那从这个条目往前的所有条目也都是完全相同的。</strong></p><p>比较请求参数中的上一个日志编号的任期 <code>prevLogTerm</code> 与 follower 节点中的对应日志编号的任期 <code>log[ prevLogIndex ].Term</code></p><p>(其中<code>prevLogTerm</code> 等于 leader 中的 <code>nextIndex[followerId] - 1</code>,即当前最末尾的日志编号)</p><p>如果不一致,则 follower 直接拒绝请求,leader 收到拒绝后,将减小 nextIndex[followerId] 重试,直到有日志是两边匹配的;</p><p>当匹配成功后,follower 将会删除 index 之后的所有日志,并将 leader 中从这个 index 往后的所有日志复制到自己的 log 里面;</p><p>follower 需要注意的几点:</p><p>(1)如果 args.term < currentTerm,return false</p><p>(2)如果 prevLogTerm 与节点的 log[ prevLogIndex ].Term 不匹配,return false</p><p>(3)args.leaderCommit 用于更新节点的 commitIndex (在匹配完日志后)</p><p>如果存在日志条目的序号 N,且领导者的 log[N].term == currentTerm,在满足 </p><ul><li>N > 节点的 commitIndex</li><li>matchIndex[节点id] >= N</li></ul><p>两个条件对大部分节点都成立时,设置领导者的 commitIndex = N</p><p>(4)如果 leader 的 commitIndex 大于 节点的 commitIndex,在更新了节点的日志后</p><blockquote><p>跟随者节点的 rf.commitIndex = min( leaderCommit, len(rf.log) )</p></blockquote><p>确保能跟 leader 一样保持正确的 commit</p><h5 id="5、AppendEntries-RPC-参数"><a href="#5、AppendEntries-RPC-参数" class="headerlink" title="5、AppendEntries RPC 参数"></a>5、AppendEntries RPC 参数</h5><p>请求参数:</p><ol><li>term: 当前leader节点的term值</li><li>leaderId: 当前leader节点的编号</li><li>prevLogIndex: 当前发送的日志的前面一个日志的索引 (nextIndex[节点编号] - 1) 索引是自增的</li><li>prevLogTerm: 当前发送的日志的前面一个日志的term值 log[prevLogIndex].term</li><li>entries[]: 需要各个节点存储的日志条目(用作心跳包时为空, 可能会出于效率发送超过一个日志条目)</li><li>leaderCommit: 当前leader节点最高的被提交的日志的索引 (就是leader节点的commitIndex)</li></ol><p>返回值:</p><ol><li>term: 接收日志节点的term值, 主要用来更新当前leader节点的term值</li><li>success: 如果接收日志节点的log[]结构中prevLogIndex索引处含有日志并且该日志的term等于prevLogTerm则返回true, 否则返回false</li></ol><h5 id="figure-8-证明-requestVote需要考虑最后一个索引的原因"><a href="#figure-8-证明-requestVote需要考虑最后一个索引的原因" class="headerlink" title="figure 8 证明 + requestVote需要考虑最后一个索引的原因:"></a>figure 8 证明 + requestVote需要考虑最后一个索引的原因:</h5><p><strong>每个 candidate 必须在 RequestVote RPC 中携带自己本地日志的最新 (term, index),如果 follower 发现这个 candidate 的日志还没有自己的新,则拒绝投票给该 candidate。</strong></p><p><img src="/img/image-20230916103549329.png" alt="image-20230916103549329"></p><p><img src="/img/image-20230913230808374.png" alt="image-20230913230808374"></p><p>疑问1:</p><p>在raft论文里面,提到了三个超时,一个是选举超时(follower转变为candidate),一个是心跳超时,一个竞选超时(就是规定时间内没有获取到足够多的票数,则当前 Leader 选举竞选失败)</p><p>但是这幅图里面candidate超时立刻发起一轮新的选举,会不会造成两个节点同时开始选举,同时发生超时这样的循环,我想的是超时后能不能先转换成follower,通过 election timeout 再转换成candidate</p><p><img src="/img/image-20230916103638545.png" alt="image-20230916103638545"></p><p><strong>解决办法</strong>:用同一个定时器timer在不同状态下完成不一样的定时效果,比如:</p><ul><li>votedfor=-1且为follower时,定时的是选举超时</li><li>votedfor不为-1且为follower时,定时的是心跳超时</li><li>角色为candidate时,定时的是竞选超时</li></ul><h5 id="每当节点的状态发生转变时,timer重新生成一个超时时间,以下几种状态的转变:"><a href="#每当节点的状态发生转变时,timer重新生成一个超时时间,以下几种状态的转变:" class="headerlink" title="每当节点的状态发生转变时,timer重新生成一个超时时间,以下几种状态的转变:"></a>每当节点的状态发生转变时,timer重新生成一个超时时间,以下几种状态的转变:</h5><ul><li>转变为 candidate 时,立刻重置超时时间</li><li>转变为 follower 时,立刻重置超时时间</li><li>当前角色为 follower,但 votedFor 参数改变时,立刻重置超时时间</li></ul><p>疑问2:</p><p>节点重新连接后,从哪里开始执行代码?</p><blockquote><p>1、开启新节点的执行顺序:</p><p>make_config() -> cfg.start1() 分配一个全新的raft节点-> Make() 创建一个协程判断自己的状态,主程序返回</p><p>这里当一个节点 disconnected:在Make里面的协程依旧是执行的,但与其他各个节点无法通信 </p><p>因此,reconnect 的含义是,Make里面的协程重新可以与其他节点进行交流,从协程的首部继续执行</p></blockquote><p>疑问3:当绝大部分机器挂了,最多只能收到的选票数量小于(n/2+1),怎么办?leader 没法选出来</p><p>疑问4:节点重新连接后,若原先就是leader,接下来如何操作?</p><ul><li>是接着发送心跳包,并在接收可能已成为 leader 的节点比较 Term 大小,进而选择继续当领导者或转变为跟随者(正确)</li><li>还是直接变成 follower,等待超时时间结束,开启新的一轮选举?(错误)</li></ul><p>疑问5:一直过不了2A的根本原因:</p><p>3个节点,当他们都成为 candidate 后,此时 votedfor 都只为自己,造成投票分裂,节点会在竞选超时后,再等待一定时间?,重新转换为 candidate</p><p>结果就是所有节点几乎一直都是 candidate,直到 5s 后选举失败</p><p>解决办法:只能根据谁的 term 大,另外的在比较完 currentTerm 后转变为 follower</p><p>同时,随机数种子必须是<code>time.Now().UnixNano()</code>,否则时间靠太近选不出来</p><p>疑问6:Make中的 applyCh chan ApplyMsg 有什么作用?</p><p>这里是对节点日志提交的一种假定,每个节点都有一个 applych 的通道,当某项日志实现commited后,就会将这条日志打包成 ApplyMsg 发送到通道中,在测试文件中,goRoutine会遍历每个节点的这个通道,若一个日志已经被大多数节点commited,则视为提交成功的测试判断依据</p><p>疑问7:正确的 commit、apply 顺序?(并发条件下的正确顺序)</p><p>1、leader 收到指令,存到 log 数组,用n个并发协程向其他节点复制这条日志</p><p>2、这 n 个协程中,当有 n/2+1 个协程返回成功时,说明已经将日志复制到大多数节点上了</p><p>此时,</p><ul><li>n/2+1 个节点:LeaderCommit 仍然在0,但日志长度和 leader 相同;</li><li>leader:LeaderCommit 更新为1,将这条日志 apply 到状态机上;</li><li>其余节点:无变化</li></ul><p>3、剩下协程执行完 RPC 准备返回,此时:</p><ul><li>n/2+1 个节点:无变化,保持 LeaderCommit 仍然在0</li><li>其余节点:LeaderCommit 更新为1,将这条日志 apply 到状态机上;</li></ul><p>4、下一轮 leader 进行 AppendEntries RPC 时,或发送心跳包时</p><ul><li>n/2+1 个节点:LeaderCommit 更新为1,将这条日志 apply 到状态机上;</li></ul><p>疑问8:2B最简单的过不了的最大可能性:</p><p>测试文件要求 raft 所有的索引,比如 nextIndex 必须从1开始</p><p>因此 log[] 一般都需要 index - 1</p><h4 id="为什么-cfg-rafts-i-log-与-cfg-logs-i-开始序列不同?"><a href="#为什么-cfg-rafts-i-log-与-cfg-logs-i-开始序列不同?" class="headerlink" title="为什么 $cfg.rafts[i].log$ 与 $cfg.logs[i]$ 开始序列不同?"></a>为什么 $cfg.rafts[i].log$ 与 $cfg.logs[i]$ 开始序列不同?</h4><p>前者从0开始(用户编写),后者从1开始(按照论文)</p><blockquote><p>前者在start的append中加入到当前节点的日志 raft[i].log,但这个日志实际上并没有被状态机执行,并不是commited,所以后者此时并没保存这条日志;</p><p>后者发生在 ApplyLogToStateMachine,在节点认为该日志已经commited情况下,将这条日志放入了 msg,</p><p>而这个msg通过 applych 通道传给 config.go中的协程 applier(i, applyCh),这个协程将通道里面的每个传进来的日志赋值给 cfg.logs[i]</p><p>config.go: 152</p></blockquote><p>解决方案:</p><p>将日志编号和存放的索引位置解耦,论文中也是 Index 从1开始</p><figure class="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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">type</span> Entry <span class="hljs-keyword">struct</span> {<br> Index <span class="hljs-type">int</span><br>Term <span class="hljs-type">int</span> <br>Command <span class="hljs-keyword">interface</span>{} <br>}<br></code></pre></td></tr></table></figure><p>疑问9:客户端刚submit后领导者宕机的情景</p><blockquote><p>场景:假设A成为了leader,A在任期1内收到客户端陆续submit的命令1,2,3,在某个时刻,A成功复制命令1,2到了大多数节点,1,2视为已提交,但突然A宕机了,此时集群重新选出C来担任领导者,那客户端原本提交的命令3去哪了,如何解决?</p></blockquote><p>这种情况需要客户端使用请求等待的容错策略,由于选主和日志复制的耗时在毫秒级,因此客户端实际上可以等待直到当前submit的请求被commit,并应用于状态机,可以确保消息不会因选主而丢失。</p><h3 id="集群成员变更"><a href="#集群成员变更" class="headerlink" title="集群成员变更"></a>集群成员变更</h3><p>为了让 raft 更有弹性,需要对外提供集群更改的接口/RPC,管理员可以随时拓展或删除集群中的机器:</p><p><img src="/img/image-20240118163024563.png" alt="image-20240118163024563"></p><p></p><p>在安全性方面,如果同时添加若干个机器,将可能导致问题:</p><blockquote><p>下面的例子里,集群从3个服务器增长到5个服务器</p><p>在某个时刻点,两个不同的领导者可以在同一个任期内被选举出来;</p><p>一个拥有旧集群的多数票,一个拥有新集群的多数票</p></blockquote><p><img src="/img/image-20240118163106922.png" alt="image-20240118163106922"></p><p>因此,Raft 限制了允许的更改类型:一次只能从集群中添加或删除一个服务器。</p><h4 id="golang-定时器的使用"><a href="#golang-定时器的使用" class="headerlink" title="golang 定时器的使用"></a>golang 定时器的使用</h4><figure class="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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-comment">// Timer是time包中的一次性计时器,作用是定时触发事件,外界只能调用C channel </span><br><span class="hljs-keyword">import</span> <span class="hljs-string">"time"</span><br><span class="hljs-keyword">type</span> Timer <span class="hljs-keyword">struct</span> {<br> C <-<span class="hljs-keyword">chan</span> Time <span class="hljs-comment">// 只能读取的通道</span><br> r runtimeTimer<br>}<br><br><span class="hljs-comment">// 创建一个定时器任务:3秒后输出</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> timer := <span class="hljs-built_in">new</span> time.newTimer(<span class="hljs-number">3</span> * time.Second)<span class="hljs-comment">// 创建定时器</span><br> <span class="hljs-keyword">select</span> {<br> <span class="hljs-keyword">case</span> <- timer.C:<br> fmt.Println(<span class="hljs-string">"已过3秒"</span>)<span class="hljs-comment">// 可以执行定时事件了</span><br> <span class="hljs-keyword">default</span>:<br> }<br> timer.Stop() <span class="hljs-comment">// 中止</span><br> timer.Reset(<span class="hljs-number">3</span> * time.Second) <span class="hljs-comment">// 重置计时器</span><br>}<br></code></pre></td></tr></table></figure><h5 id="Duration-类型"><a href="#Duration-类型" class="headerlink" title="Duration 类型"></a>Duration 类型</h5><p><code>Duration</code> 类型用于表示两个时刻 ( <code>Time</code> ) 之间经过的时间,以 <strong>纳秒</strong> ( ns ) 为单位。</p><figure class="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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewTimer</span><span class="hljs-params">(d Duration)</span></span> *Timer<br><br><span class="hljs-comment">// Duration 类型填的是时间,需要带单位</span><br>time.NewTimer(<span class="hljs-number">2</span>*time.Second)<br></code></pre></td></tr></table></figure><h4 id="channel"><a href="#channel" class="headerlink" title="channel"></a>channel</h4><p>在并发条件下,函数与函数之间交换数据的两种方式:</p><ul><li>用锁实现的共享内存</li><li>类似通信的方式用通道传递信息</li></ul><p>go语言的并发模型是CSP,用通信传递信息(Communicating Sequential Processes)</p><p>channel 是可以让一个 go routine 发送特定值到另一个 go routine 的通信机制</p><figure class="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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-comment">// 通道的声明</span><br><span class="hljs-keyword">var</span> t1 <span class="hljs-keyword">chan</span> <span class="hljs-type">bool</span><br><span class="hljs-keyword">var</span> t2 <span class="hljs-keyword">chan</span> []<span class="hljs-type">int</span><span class="hljs-comment">// 声明一个传递int切片的通道</span><br><br><span class="hljs-comment">// 创建通道实例</span><br>t1 = <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">bool</span>)<br>t2 = <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> []<span class="hljs-type">int</span>)<br><br><span class="hljs-comment">// 通道的使用</span><br>ch := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>)<br>ch <- <span class="hljs-number">10</span><span class="hljs-comment">// 1.发送到通道内</span><br>x := <- ch<span class="hljs-comment">// 2.从通道接受</span><br><span class="hljs-built_in">close</span>(ch)<span class="hljs-comment">// 3.关闭通道</span><br></code></pre></td></tr></table></figure><h5 id="无缓冲通道与有缓冲通道"><a href="#无缓冲通道与有缓冲通道" class="headerlink" title="无缓冲通道与有缓冲通道"></a>无缓冲通道与有缓冲通道</h5><figure class="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><code class="hljs go">t1 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>)<span class="hljs-comment">// 无缓冲通道</span><br>t2 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>, <span class="hljs-number">3</span>)<span class="hljs-comment">// 有缓冲通道</span><br><br><span class="hljs-comment">// 无缓冲通道有发送必须有接收,否则报错</span><br><span class="hljs-comment">// 正确使用的例子</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> t1 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>)<br> <span class="hljs-keyword">go</span> f2(t1)<span class="hljs-comment">// 创建协程</span><br> t1 <- <span class="hljs-number">10</span><span class="hljs-comment">// 发送</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">f2</span><span class="hljs-params">(c <span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>)</span></span> {<br> ret := <- c<span class="hljs-comment">// 另一个协程在接收</span><br>}<br><br><span class="hljs-comment">// 有缓冲通道则可以直接下面这样</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> t2 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>, <span class="hljs-number">3</span>)<br> t2 <- <span class="hljs-number">10</span><span class="hljs-comment">// 发送</span><br>}<br></code></pre></td></tr></table></figure><p>无缓冲通道上:发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值</p><h5 id="单向通道"><a href="#单向通道" class="headerlink" title="单向通道"></a>单向通道</h5><blockquote><p>只能往这个 channel 中写入数据,或者只能从这个 channel 读取数据</p></blockquote><p>单向通道的定义一般用在<strong>形参</strong>里,防止一些逻辑上的错误</p><figure class="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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sendData</span><span class="hljs-params">(ch <span class="hljs-keyword">chan</span><- <span class="hljs-type">int</span>)</span></span> {<span class="hljs-comment">// 只定义发送单向管道</span><br> <span class="hljs-keyword">for</span> i:=<span class="hljs-number">0</span>; i<=<span class="hljs-number">100</span>; i++ {<br> ch <- i<br> }<br> <span class="hljs-built_in">close</span>(ch)<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">receiveData</span><span class="hljs-params">(ch <-<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>)</span></span> {<span class="hljs-comment">// 只定义接收单向管道</span><br> <span class="hljs-keyword">for</span> {<br> data, ok := <-ch<br> <span class="hljs-keyword">if</span> ok {<br> fmt.Println(data)<br> }<br> }<br>}<br><br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> <span class="hljs-comment">// define a common channel</span><br> c1 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-type">int</span>)<br> <br> <span class="hljs-keyword">go</span> sendData(c1)<br> <span class="hljs-keyword">go</span> receiveData(c1)<br> <br> c1 <- <span class="hljs-number">123</span><br>}<br></code></pre></td></tr></table></figure><h5 id="通道的关闭(只在发送端关)"><a href="#通道的关闭(只在发送端关)" class="headerlink" title="通道的关闭(只在发送端关)"></a>通道的关闭(只在发送端关)</h5><p>close() 管道的意义:通过管道发送有限的数据时,可以通过close告知管道另一边的协程停止等待,数据已全部发送完毕。</p><p>上例第5行,发送端在发完所有需要发送的数据后,执行关闭通道;</p><p><strong>关闭通道:表示发送端不再向通道里发送数据,但如果通道里还有数据,接收方仍会把数据接收完毕。</strong></p><h4 id="golang-中的-select-机制"><a href="#golang-中的-select-机制" class="headerlink" title="golang 中的 select 机制"></a>golang 中的 select 机制</h4><p>1、select 的特点:每个case都必须是一个通信</p><p>2、不同于C++,go中每个case不需要break,它不会顺着下去执行</p><p>3、关于 default</p><p>两种写法:</p><figure class="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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-comment">// 1. for + select + default:会一直遍历并打印下面那句话</span><br><span class="hljs-keyword">for</span> {<br> <span class="hljs-keyword">select</span> {<br> <span class="hljs-keyword">case</span> <-ch1:<br> <span class="hljs-comment">// 这里不会执行,因为 ch1 为空</span><br> <span class="hljs-keyword">case</span> <-ch2:<br> <span class="hljs-comment">// 这里也不会执行,因为 ch2 为空</span><br> <span class="hljs-keyword">case</span> <span class="hljs-keyword">default</span>:<br> <span class="hljs-comment">// 执行这里</span><br> }<br><br> fmt.Println(<span class="hljs-string">"This line will be reached in each iteration."</span>)<br> time.Sleep(<span class="hljs-number">1</span> * time.Second)<br>}<br><br><br><span class="hljs-comment">// 2. select + 没有default:整个主线程会阻塞住,直到满足其中一个case条件</span><br><span class="hljs-keyword">select</span> {<br> <span class="hljs-keyword">case</span> <-ch1:<br> <span class="hljs-comment">// 这里不会执行,因为 ch1 为空</span><br> <span class="hljs-keyword">case</span> <-ch2:<br> <span class="hljs-comment">// 这里也不会执行,因为 ch2 为空</span><br>}<br><br><span class="hljs-comment">// 主线程被阻塞住了,除非满足其中一个 case 条件</span><br>fmt.Println(<span class="hljs-string">"This line will not be reached."</span>)<br></code></pre></td></tr></table></figure><p>3、go 中的 case 语法</p><p>无论是 switch 还是 select,只执行 case i 里面的内容,不需要 break</p><p>如果要顺下去执行多个 case,使用 fallthrough 关键字</p><p><strong>复制状态机</strong>的两个部分:</p><ul><li>共识算法将主节点日志复制到其他所有节点,若在某一个节点中,如果已正确复制了日志,即命令和它的顺序,则视为已提交:commited</li><li>已 commit 的服务器将使用状态机执行这些命令</li></ul><p>关于拜占庭故障:</p><p>拜占庭故障:故障节点可能存在各种形式的错误,如传递错误信息,完全违反系统规则,可能由恶意攻击,硬件故障导致;</p><blockquote><p>9个将军率领的部队在各个山头,各个之间通讯只能由信使传达;</p><p>每个将军须向其余所有将军传达自己想要 进攻 或者 撤退,</p><p>当进攻或撤退票数超过一半时,所有人将达成统一意见,开始执行任务。</p><p>假设8个忠诚将军,1个叛徒。其中4个忠诚将军选择进攻,另外4个忠诚将军选择撤退;</p><p>但此时,叛徒给4个选进攻的将军传达进攻,给4个选撤退的将军传达撤退;</p><p>4个选进攻的将军获得5个进攻票数,发动进攻,同理另外4个撤退;</p><p>导致了队伍的最终一致性发生了破坏。</p></blockquote><p>拜占庭故障的解决方案:数字签名密码学、PBFT</p><p>Raft 并不保证拜占庭容错,但可保证除了拜占庭条件下的其他错误,比如 网络延迟、分区、数据包丢失、数据包重复等异常。</p><blockquote><p>结论1:Raft 可以容忍小于等于一半节点出现故障,系统仍能正常对外服务</p><p>比如5台服务器,任意两个发生了故障,系统仍能正常运行,更多故障只能重启。</p></blockquote><p>lab2A</p><p><img src="/img/image-20240119195453357.png" alt="image-20240119195453357"></p><p>TestCount2B 测试点:(需要优化RPC次数)</p><p>要测RPC的次数:比如初始选举一个leader的RPC<30</p><p>TestBackUp2B</p><blockquote><p>5个节点的集群,首先选出节点0成为 leader,发送日志 {1, 1, 100},</p><p>所有成员均复制并已提交</p><p>此时,断连 2,3,4节点,客户端向集群添加了50条日志,节点0和1复制完成后但由于复制成功的节点数量小于大多数节点,commitIndex 依然等于 1</p><p>此时,断连0,1节点,重连2,3,4节点,集群选出了2号节点作为新的 leader;</p><p>客户端向集群又新添加了50条日志,并等待三个节点将所有日志均提交,</p><p>2,3,4节点的 commitIndex = 51</p><p>此时,断连3号节点,客户端向集群又新添加了50条日志,但集群内只剩两个节点,无法达成多数节点复制成功,这50条日志并没有提交;</p><p>一段时间后,2,4节点失去连接,0,1,3节点重新连接,集群此时剩下了3个能互相连接的节点,3号节点当选为 leader,将已提交的全部更新到0,1节点,此后客户端向集群又新添加了50条日志,这些日志都被提交;</p><p>0,1,3节点的 commitIndex = 101</p><p>又过来一段时间后,所有节点都能够连接,2,4节点成为follower,将0,1,3节点的所有已提交的更新到自己的日志上;</p><p>最后,节点0依然是leader,客户端提交最后1条日志</p><p>所有节点的 commitIndex = 102,结束</p></blockquote><h3 id="AppendEntries-RPC-优化:"><a href="#AppendEntries-RPC-优化:" class="headerlink" title="AppendEntries RPC 优化:"></a>AppendEntries RPC 优化:</h3><p>from MIT6.824 Lecture 7</p><p>当网络分区造成的影响太长时,Leader 需要与刚恢复的 follower 节点进行日志同步,如果每发一个 RPC 递减一次 nextIndex,则同步一个 follower 最大需要发送 len(rf.log) 次RPC;</p><p>优化目标:减少 RPC 次数,并且尽可能快达到日志同步</p><figure class="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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">type</span> AppendEntriesReply <span class="hljs-keyword">struct</span> {<span class="hljs-comment">// RPC Return</span><br>Term <span class="hljs-type">int</span> <br>Success <span class="hljs-type">bool</span><br> XTerm <span class="hljs-type">int</span><br> XIndex<span class="hljs-type">int</span><br> XLen<span class="hljs-type">int</span><br>}<br></code></pre></td></tr></table></figure><p>上面的三个参数仅用于匹配失败的情况</p><p>对比的两个条目:follower 在 prevLogIndex 的日志的索引和任期与 leader 传进来的比较</p><blockquote><p>XTerm:</p><ul><li>检查 follower 在 prevLogIndex 的任期号和 leader 传进来的 prevLogTerm 不同, 将自己的任期号放入 XTerm;</li><li>若 follower 在这个位置上没有日志条目,XTerm = -1,leader 将会根据 XLen 决定下一个 nextIndex;</li></ul><p>XIndex:返回任期号为 XTerm 的第一个日志索引的编号;</p><p>XLen:</p><ul><li>如果 follower 在这个位置上有日志条目,返回任期号为 XTerm 的日志的数量</li><li>没有日志条目,返回空白日志的数量:preLogIndex - len(rf.log),leader 将会回退到 nextIndex - BlankLogNum</li></ul></blockquote><p>这种方法相当于一个一个 Term 进行同步</p><p>分别可以用下列3种情景分类讨论:</p><figure class="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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">case</span> <span class="hljs-number">1</span><br>follower s1 = <span class="hljs-number">4</span> <span class="hljs-number">5</span> <span class="hljs-number">5</span> <span class="hljs-number">5</span><br>leader s2 = <span class="hljs-number">4</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span><br>init:leader:nextIndex=<span class="hljs-number">5</span>, preLogIndex=<span class="hljs-number">4</span><br>after: XTerm=<span class="hljs-number">5</span>,XLen=<span class="hljs-number">3</span>,XIndex=<span class="hljs-number">2</span> => nextIndex=<span class="hljs-number">2</span>,preLogIndex=<span class="hljs-number">1</span><br><br><br><span class="hljs-keyword">case</span> <span class="hljs-number">2</span><br>follower s1 = <span class="hljs-number">4</span> <span class="hljs-number">4</span> <span class="hljs-number">4</span><br>leader s2 = <span class="hljs-number">4</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span><br>init:leader:nextIndex=<span class="hljs-number">5</span>, preLogIndex=<span class="hljs-number">4</span><br>after <span class="hljs-number">1</span>RPC: XTerm=<span class="hljs-number">-1</span>,XLen=<span class="hljs-number">1</span> => nextIndex=<span class="hljs-number">4</span>,preLogIndex=<span class="hljs-number">3</span><br>after <span class="hljs-number">2</span>RPC: XTerm=<span class="hljs-number">4</span>,XLen=<span class="hljs-number">3</span>,XIndex=<span class="hljs-number">1</span> => nextIndex=<span class="hljs-number">2</span>,preLogIndex=<span class="hljs-number">1</span><br><br><br><span class="hljs-keyword">case</span> <span class="hljs-number">3</span><br>follower s1 = <span class="hljs-number">4</span> <span class="hljs-number">4</span><br>leader s2 = <span class="hljs-number">4</span> <span class="hljs-number">4</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span><br>init:leader:nextIndex=<span class="hljs-number">7</span>, preLogIndex=<span class="hljs-number">6</span><br>after: XTerm=<span class="hljs-number">-1</span>,XLen=<span class="hljs-number">4</span>,XIndex=<span class="hljs-number">3</span> => nextIndex=<span class="hljs-number">3</span>,preLogIndex=<span class="hljs-number">2</span><br><br><br><span class="hljs-keyword">case</span> <span class="hljs-number">4</span><br>follower s1 = <span class="hljs-number">4</span> <span class="hljs-number">4</span> <span class="hljs-number">5</span> <span class="hljs-number">5</span> <span class="hljs-number">5</span> <span class="hljs-number">5</span> <span class="hljs-number">5</span> <span class="hljs-number">5</span><br>leader s2 = <span class="hljs-number">4</span> <span class="hljs-number">4</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span> <span class="hljs-number">6</span><br>init:leader:nextIndex=<span class="hljs-number">7</span>, preLogIndex=<span class="hljs-number">6</span><br>after: XTerm=<span class="hljs-number">5</span>,XLen=<span class="hljs-number">4</span>,XIndex=<span class="hljs-number">3</span> => nextIndex=<span class="hljs-number">3</span>,preLogIndex=<span class="hljs-number">2</span><br></code></pre></td></tr></table></figure><p>继续优化,为了能尽快日志同步,当匹配不上时,不必再等待心跳超时,直接发送下一个 AppendEntries RPC</p><p><img src="/img/image-20240125151211750.png" alt="image-20240125151211750"></p><h3 id="lab2D-Log-compaction"><a href="#lab2D-Log-compaction" class="headerlink" title="lab2D Log compaction"></a>lab2D Log compaction</h3><h5 id="1、日志压缩的原因和本质:"><a href="#1、日志压缩的原因和本质:" class="headerlink" title="1、日志压缩的原因和本质:"></a>1、日志压缩的原因和本质:</h5><p>当日志的数量太多,会造成的影响:</p><ul><li>空间占用过高</li><li>状态机完整执行一次时间过长,且会存在很多过时的日志</li><li>AppendEntries RPC 让 follower 跟上 leader 的时间也会变长</li></ul><p>每当进行一次日志压缩后,将会剔除过时的日志,比如图12中的 x=3 将会被 x=0 取代</p><p>日志压缩最简单高效的方法:Snapshotting 日志快照</p><h5 id="2、日志快照需维护三个变量:"><a href="#2、日志快照需维护三个变量:" class="headerlink" title="2、日志快照需维护三个变量:"></a>2、日志快照需维护三个变量:</h5><ul><li>上一次快照的最后一个日志的索引:lastIncludedIndex</li><li>上一次快照的最后一个日志的任期:lastIncludedTerm</li><li>压缩后状态机保留的日志:State</li></ul><p><img src="/img/image-20240205164135118.png" alt="image-20240205164135118"></p><p>每个节点独立进行快照,lab里是每当节点的日志数量达到某个阈值时,进行一次快照;</p><p>为了保证快照的最终一致性,leader 需要周期性发送快照 RPC 到其他节点,当跟随者存在与快照中的日志不一致时,删除自己原先的所有日志,并用传入的快照替代;</p><h5 id="3、要点分析"><a href="#3、要点分析" class="headerlink" title="3、要点分析"></a>3、要点分析</h5><p>为了维护快照的状态,需要对 raft 结构体新增两个持久化的变量</p><ul><li>上一次快照的最后一个日志的索引:lastIncludedIndex</li><li>上一次快照的最后一个日志的任期:lastIncludedTerm</li></ul><p>这能够确保:当前准备进行的快照不是过时的,不会重复多次同一个快照</p><h5 id="4、实现思路与分析"><a href="#4、实现思路与分析" class="headerlink" title="4、实现思路与分析"></a>4、实现思路与分析</h5><p>1、Snapshot()</p><p>当日志数量达到阈值时,测试文件将当前的 commitIndex 和 apply 到状态机的所有日志序列化成二进制编码,作为形参传入 snapshot()</p><figure class="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></pre></td><td class="code"><pre><code class="hljs go">raft.Snapshot(idx <span class="hljs-type">int</span>, snapshot []<span class="hljs-type">byte</span>) <br><span class="hljs-comment">/*</span><br><span class="hljs-comment">1.检查当前即将创建的快照是否过时,若传入的 idx < rf.lastIncludedIndex,说明当前内存中索引为idx的日志已经被压缩过了,立即返回;</span><br><span class="hljs-comment">2.删除rf.log在idx及其之前的日志,更新raft结构体中的变量</span><br><span class="hljs-comment">3.使用 persister.SaveStateAndSnapshot() 对传入的snapshot进行持久化</span><br><span class="hljs-comment">*/</span><br></code></pre></td></tr></table></figure><p>注意:</p><p>cfg.log:保存所有的 log</p><p>rf.log:保存压缩后的 log</p>]]></content>
<categories>
<category>6.824</category>
</categories>
<tags>
<tag>raft</tag>
<tag>6.824</tag>
</tags>
</entry>
<entry>
<title>Enabling Erasure Coding Tiering for LSM-tree-based Storage (Fast 24) 论文阅读</title>
<link href="/2024/03/16/ELECT/"/>
<url>/2024/03/16/ELECT/</url>
<content type="html"><![CDATA[<p>ELECT: Enabling Erasure Coding Tiering for LSM-tree-based Storage (Fast 24)</p><p>存储分层(Storage tiering)</p><p>热层:频繁访问的数据,有限的存储空间,较高的访问性能;</p><p>冷层:不频繁访问的数据,存储空间充足,但性能较低;</p><p>典型的应用场景:边缘云存储(edge-cloud storage)</p><p>边缘云存储主要建立在 分布式KV 系统上</p><p>motivation:物联网应用的数据量将达到 79.4 ZB(1ZB = 10^9TB)</p><blockquote><p>1、云服务通常位于远程的数据中心,而访问云服务需要通过互联网连接。由于互联网带宽有限,因此在大量 IoT 设备同时访问云服务时,可能会造成性能瓶颈和延迟增加;</p><p>2、边缘计算将计算和存储资源部署在靠近数据产生源的地方,例如物联网设备附近的边缘节点。这些边缘节点通常是轻量级的实例,例如微型数据中心,它们具有有限的计算和存储资源,但可以提供更快的响应时间和更低的延迟。</p><p>3、从存储的角度考虑,我们可以构建一个分布式 KV 系统在热层,云作为冷层持久化数据;</p></blockquote><p>边缘存储设备通常要考虑容错,一般操作是将副本放在多个边缘节点上,而无需从云上获取数据;</p><p>复制使得存储空间的成本增加,这对资源有限的高性能热点层是非常不利的;</p><p>Erasure Coding:纠删码,通过矩阵的逆运算和冗余的方式提供了更高效的存储副本的方式;</p><p>优点:</p><ul><li>各个节点有相同大小的冗余副本,可以做到各个节点之间读数据的负载均衡;</li><li>n = k + m,n个节点中允许有m个节点发生故障而不会丢失数据;</li></ul><p>缺点:</p><ul><li>故障发生时,重建数据会导致 I/O 和带宽的增加;</li></ul><p>ELECT:实现纠删码分层的一个分布式 KV 存储系统,采用混合冗余的方式扩展 LSM-Tree</p><p>通过在热层存储有限的频繁访问的kv提高访问性能,同时在热层存储大量带有纠删码的冷KV来实现显著的存储节省;</p><p>ELECT 的新颖之处:将基于LSM树存储的副本和纠删码和几种新的设计技术巧妙的结合在一起。</p>]]></content>
<categories>
<category>论文阅读</category>
</categories>
<tags>
<tag>Erasure Code</tag>
</tags>
</entry>
<entry>
<title>Hexo + Fluid 搭建博客</title>
<link href="/2024/01/16/hexo-fluid-build/"/>
<url>/2024/01/16/hexo-fluid-build/</url>
<content type="html"><![CDATA[<p>1、安装Hexo</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install -g hexo-cli<br></code></pre></td></tr></table></figure><p>新建 hexo 目录</p><figure class="highlight shell"><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><code class="hljs shell">cd Hexo<br>hexo init <br>npm install<br></code></pre></td></tr></table></figure><p>本地查看 hexo 服务</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs shell">hexo generate<br>hexo server<br></code></pre></td></tr></table></figure><p>2、安装 Fluid 主题</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">npm install --save hexo-theme-fluid<br></code></pre></td></tr></table></figure><p>然后随便搞搞 _config.yaml, _config_fluid.yaml</p><p>3、发布</p><figure class="highlight yml"><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><code class="hljs yml"><span class="hljs-attr">deploy:</span><br> <span class="hljs-attr">type:</span> <span class="hljs-string">git</span><br> <span class="hljs-attr">repo:</span> <span class="hljs-string">https://github.com/Yourname/Yourname.github.io.git</span><br> <span class="hljs-attr">branch:</span> <span class="hljs-string">main</span><br></code></pre></td></tr></table></figure><p>正式发布</p><figure class="highlight shell"><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><code class="hljs shell">hexo clean<br>hexo generate<br>hexo deploy<br></code></pre></td></tr></table></figure><p>4、关于新建文章</p><p>在 hexo 目录下执行:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs shell">hexo new "my-article-title"<br></code></pre></td></tr></table></figure>]]></content>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title>从线程池模型看 Oceanbase 线程池的应用和实现</title>
<link href="/2024/01/16/thread-pool-learning/"/>
<url>/2024/01/16/thread-pool-learning/</url>
<content type="html"><![CDATA[<div class="markdown-body"><h3 id="1-线程池的基本原理和组件"><a href="#1-线程池的基本原理和组件" class="headerlink" title="1. 线程池的基本原理和组件"></a>1. 线程池的基本原理和组件</h3><p>线程池的定义:A thread pool maintains multiple threads waiting for tasks to be allocated for concurrent execution by the supervising program. Client can send “work” to the pool and somehow this work gets done without blocking the main thread. It’s efficient because threads are not initialized each time we want the work to be done. Threads are initialized once and remain inactive until some work has to be done. </p><p>三个基本要点:</p><ul><li><p>任务队列</p></li><li><p>线程池调度</p></li><li><p>所有任务结束后唤醒主线程</p></li></ul><p><img src="/img/image-20240113191409986.png" alt="image-20240113191409986"></p><p>一个高质量的C实现线程池:<a href="https://github.com/Pithikos/C-Thread-Pool">https://github.com/Pithikos/C-Thread-Pool</a></p><h5 id="1-1-线程池需要向外提供的必要接口"><a href="#1-1-线程池需要向外提供的必要接口" class="headerlink" title="1.1 线程池需要向外提供的必要接口"></a>1.1 线程池需要向外提供的必要接口</h5><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-comment">// 1.初始化</span><br><span class="hljs-function">threadpool <span class="hljs-title">thpool_init</span><span class="hljs-params">(<span class="hljs-type">int</span> num_threads)</span></span>;<br><br><span class="hljs-comment">// 2.构造job/task,封装成函数调用,压入任务队列</span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">thpool_add_work</span><span class="hljs-params">(threadpool, <span class="hljs-type">void</span> (*function_p)(<span class="hljs-type">void</span>*), <span class="hljs-type">void</span>* arg_p)</span></span>;<br><br><span class="hljs-comment">// 3.等待所有任务执行完成,both in queue and currently running</span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">thpool_wait</span><span class="hljs-params">(threadpool)</span></span>;<br><br><span class="hljs-comment">// 4.暂停所有线程,无论它们处在空闲还是工作</span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">thpool_pause</span><span class="hljs-params">(threadpool)</span></span>;<br><br><span class="hljs-comment">// 5.恢复:从暂停中恢复所以线程原本的工作</span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">thpool_resume</span><span class="hljs-params">(threadpool)</span></span>;<br><br><span class="hljs-comment">// 6.销毁线程池,如果有线程还在运行,等待它工作完毕</span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">thpool_destroy</span><span class="hljs-params">(threadpool)</span></span>;<br><br><span class="hljs-comment">// 7.返回正在工作的线程数量</span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">thpool_num_threads_working</span><span class="hljs-params">(threadpool)</span></span>;<br></code></pre></td></tr></table></figure><h5 id="1-2-用到的数据结构"><a href="#1-2-用到的数据结构" class="headerlink" title="1.2 用到的数据结构"></a>1.2 用到的数据结构</h5><figure class="highlight c++"><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></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-comment">// 任务:本质上是一个个由工作函数构成的链表结点</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">job</span>{<br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">job</span>* prev;<br><span class="hljs-built_in">void</span> (*function)(<span class="hljs-type">void</span>* arg);<br><span class="hljs-type">void</span>* arg; <br>} job;<br><br><span class="hljs-comment">// 任务队列:可以通过链表实现,在并发场景下,需要增加读写锁、信号量</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">jobqueue</span>{<br><span class="hljs-type">pthread_mutex_t</span> rwmutex; <br>job *front;<br>job *rear; <br>bsem *has_jobs;<span class="hljs-comment">// 这里设置一个布尔的信号量,用于各个线程等待还是执行</span><br><span class="hljs-type">int</span> len;<br>} jobqueue;<br><br><span class="hljs-comment">// 线程:基于pthread</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">thread</span>{<br><span class="hljs-type">int</span> id; <br><span class="hljs-type">pthread_t</span> pthread; <br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">thpool_</span>* thpool_p; <br>} thread;<br><br><span class="hljs-comment">// 线程池</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">thpool_</span>{<br>thread** threads; <span class="hljs-comment">// 由指针组成的数组</span><br><span class="hljs-keyword">volatile</span> <span class="hljs-type">int</span> num_threads_alive; <br><span class="hljs-keyword">volatile</span> <span class="hljs-type">int</span> num_threads_working;<br><span class="hljs-type">pthread_mutex_t</span> thcount_lock; <span class="hljs-comment">// 互斥锁,用于主线程计算线程数</span><br><span class="hljs-type">pthread_cond_t</span> threads_all_idle;<br>jobqueue jobqueue; <br>} thpool_;<br></code></pre></td></tr></table></figure><h5 id="1-3-重要接口的内部实现"><a href="#1-3-重要接口的内部实现" class="headerlink" title="1.3 重要接口的内部实现"></a>1.3 重要接口的内部实现</h5><p>线程池部分:</p><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-keyword">struct</span> <span class="hljs-title class_">thpool_</span>* <span class="hljs-built_in">thpool_init</span>(<span class="hljs-type">int</span> num_threads){<br> <span class="hljs-comment">// 1.Make new thread pool</span><br> <span class="hljs-comment">// 2.Initialise the job queue</span><br> <span class="hljs-comment">// 3.malloc threads in pool</span><br> <span class="hljs-comment">// 4.each thread init, 每个线程调用thread_init(..)开始执行</span><br>}<br><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">thpool_add_work</span><span class="hljs-params">(thpool_* thpool_p, <span class="hljs-type">void</span> (*function_p)(<span class="hljs-type">void</span>*), <span class="hljs-type">void</span>* arg_p)</span></span>{<br> <span class="hljs-comment">// 1.malloc new job</span><br> <span class="hljs-comment">// 2.jobqueue_push(job)</span><br>}<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">thpool_wait</span><span class="hljs-params">(thpool_* thpool_p)</span></span>{<br> <span class="hljs-comment">// 1.lock the thcount_lock </span><br> <span class="hljs-comment">// 2.若任务队列不为空或有线程正在执行任务,循环等待</span><br> <span class="hljs-comment">// 同时设置条件变量 threads_all_idle,由于每个线程都会跟踪线程池</span><br> <span class="hljs-comment">// 只要正在工作的线程数=0,将调用 pthread_cond_signal 通知 thpool_wait</span><br> <span class="hljs-comment">// 3.unlock the thcount_lock </span><br>}<br><br><br></code></pre></td></tr></table></figure><p>线程部分:</p><figure class="highlight c++"><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></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">int</span> <span class="hljs-title">thread_init</span> <span class="hljs-params">(...)</span></span>{<br><span class="hljs-comment">// 1.malloc new thread</span><br> <span class="hljs-comment">// 2.create the thread, use do_task(args) to finish the work</span><br> <span class="hljs-built_in">pthread_create</span>(.., .., do_task, args);<br> <span class="hljs-comment">// 3.detach the child thread and main thread</span><br> <span class="hljs-built_in">pthread_detach</span>(..)<br>}<br><br><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span>* <span class="hljs-title">do_task</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> thread* thread_p)</span></span>{<br> <span class="hljs-comment">// 1.Register signal handler</span><br> <span class="hljs-comment">// 2.Mark thread as alive (initialized)</span><br> lock the thcount_lock, <span class="hljs-keyword">and</span> let num_threads_alive++, unlock<br> <span class="hljs-comment">// 3.while loop, execute the task</span><br> lock the thcount_lock, <span class="hljs-keyword">and</span> let num_threads_runnning++, unlock<br> job* j = <span class="hljs-built_in">jobqueue_pull</span>();<br> j.<span class="hljs-built_in">func</span>(args);<br> lock the thcount_lock, <span class="hljs-keyword">and</span> let num_threads_runnning--, unlock<br> <span class="hljs-comment">// 4. if num_threads_runnning == 0, call the main thread</span><br> <span class="hljs-comment">// main thread will keep the thpool_wait()</span><br> <span class="hljs-built_in">pthread_cond_signal</span>(&thpool_p->threads_all_idle);<br> <span class="hljs-comment">// 5. Mark thread as finish</span><br> lock the thcount_lock, <span class="hljs-keyword">and</span> let num_threads_alive--, unlock <br>}<br></code></pre></td></tr></table></figure><h3 id="2-Oceanbase创建线程池并启动任务"><a href="#2-Oceanbase创建线程池并启动任务" class="headerlink" title="2. Oceanbase创建线程池并启动任务"></a>2. Oceanbase创建线程池并启动任务</h3><h4 id="1-使用线程组创建函数注册一个线程池"><a href="#1-使用线程组创建函数注册一个线程池" class="headerlink" title="1. 使用线程组创建函数注册一个线程池"></a>1. 使用线程组创建函数注册一个线程池</h4><p>线程池注册函数:TG_DEF(id, name, type, args…)</p><figure class="highlight c++"><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></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-comment">/*</span><br><span class="hljs-comment">lib::TGDefIDs::id 将参数id传入作用域,获取该id对应的函数编号</span><br><span class="hljs-comment">宏定义的一些语法:A##B:连接AB字符串,#取字符串</span><br><span class="hljs-comment">*/</span><br><span class="hljs-meta">#<span class="hljs-keyword">define</span> TG_DEF(id, name, type, args...) \</span><br><span class="hljs-meta">lib::create_funcs_[lib::TGDefIDs::id] = []() { \</span><br><span class="hljs-meta"> auto ret = OB_NEW(TG_##type, SET_USE_500(<span class="hljs-string">"tg"</span>), args); \</span><br> ret->attr_ = {<span class="hljs-meta">#name, TGType::type}; \ </span><br> <span class="hljs-keyword">return</span> ret; \<br>};<br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"share/ob_thread_define.h"</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">undef</span> TG_DEF</span><br><br><span class="hljs-comment">// type类别</span><br><span class="hljs-keyword">enum class</span> <span class="hljs-title class_">TGType</span><br>{<br> INVALID,<br> REENTRANT_THREAD_POOL,<br> THREAD_POOL,<br> TIMER,<br> QUEUE_THREAD,<br> DEDUP_QUEUE,<br> ASYNC_TASK_QUEUE,<br> MAP_QUEUE_THREAD<br>};<br><br><span class="hljs-comment">// 比如注册一个 ParallelDDLPool 线程池</span><br><span class="hljs-built_in">TG_DEF</span>(ParallelDDLPool, ParallelDDLPool, QUEUE_THREAD, <span class="hljs-number">16</span>, <span class="hljs-number">2000</span>)<br></code></pre></td></tr></table></figure><p>为每个注册了的线程池赋予一个系统tg_id: lib::TGDefIDs::{id}</p><figure class="highlight c++"><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></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-comment">// #define TG_DEF(id, ...) id, 映射整个TG_DEF() 取其中id</span><br><span class="hljs-comment">// 相当于给各个id赋予一个编号,比如:enum {OB_START=-1, ParallelDDLPool, OB_END}</span><br><span class="hljs-keyword">namespace</span> lib {<br> <span class="hljs-keyword">namespace</span> TGDefIDs {<br> <span class="hljs-keyword">enum</span> <span class="hljs-title class_">OBTGDefIDEnum</span><br> {<br> OB_START = TGDefIDs::LIB_END - <span class="hljs-number">1</span>,<br> <span class="hljs-meta">#<span class="hljs-keyword">define</span> TG_DEF(id, ...) id,</span><br> <span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">"share/ob_thread_define.h"</span></span><br> <span class="hljs-meta">#<span class="hljs-keyword">undef</span> TG_DEF</span><br> OB_END,<br> };<br> }<br>} <br></code></pre></td></tr></table></figure><h4 id="2-创建线程池类(基于-TGRunnable-或-TGTaskHandler-接口)"><a href="#2-创建线程池类(基于-TGRunnable-或-TGTaskHandler-接口)" class="headerlink" title="2. 创建线程池类(基于 TGRunnable 或 TGTaskHandler 接口)"></a>2. 创建线程池类(基于 <em>TGRunnable</em> 或 <em>TGTaskHandler</em> 接口)</h4><ul><li><em>TGRunnable</em>:适合处理无参数允许的任务,比如开启一个定时器等;<ul><li>纯虚函数:<code>virtual void run1() = 0;</code></li></ul></li><li><em>TGTaskHandler</em>:适合处理任何任务<ul><li>纯虚函数:<code>virtual void handle(void *task) = 0;</code></li></ul></li></ul><figure class="highlight c++"><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></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-keyword">class</span> <span class="hljs-title class_">ObParallelDDLOperator</span> : <span class="hljs-keyword">public</span> lib::TGTaskHandler {<br><span class="hljs-keyword">public</span>:<br> <span class="hljs-built_in">ObParallelDDLOperator</span>();<br> ~<span class="hljs-built_in">ObParallelDDLOperator</span>();<br><br><span class="hljs-keyword">public</span>:<br> <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">int</span> <span class="hljs-title">init</span><span class="hljs-params">()</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">void</span> <span class="hljs-title">handle</span><span class="hljs-params">(<span class="hljs-type">void</span> *task)</span> <span class="hljs-keyword">override</span></span>;<br> <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">int</span> <span class="hljs-title">push_task</span><span class="hljs-params">(<span class="hljs-type">void</span> *task)</span></span>;<br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span></span>;<br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">wait</span><span class="hljs-params">()</span></span>;<br> <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">destroy</span><span class="hljs-params">()</span></span>;<br> <span class="hljs-function"><span class="hljs-type">static</span> ObParallelDDLOperator &<span class="hljs-title">get_instance</span><span class="hljs-params">()</span> </span>{<span class="hljs-comment">// 单例模式</span><br> <span class="hljs-keyword">return</span> parallel_ddl_; <br> }<br> <span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-keyword">inline</span> ObArenaAllocator &<span class="hljs-title">get_allocator</span><span class="hljs-params">()</span> </span>{ <br> <span class="hljs-keyword">return</span> parallel_ddl_.allocator_; <br> } <br> <br><span class="hljs-keyword">private</span>:<br> <span class="hljs-type">int</span> tg_id_;<br> <span class="hljs-type">bool</span> is_inited_;<br> ObArenaAllocator allocator_;<br> <span class="hljs-type">static</span> ObParallelDDLOperator parallel_ddl_;<br>};<br></code></pre></td></tr></table></figure><h5 id="2-1-线程池的初始化"><a href="#2-1-线程池的初始化" class="headerlink" title="2.1 线程池的初始化"></a>2.1 线程池的初始化</h5><p>1、根据已注册线程池的id,生成系统 tg_id,同时映射上 <tg_id,tg_queue_thread*></p><p>2、将已实现的基于 TGTaskHandler 的 parallel_ddl_ 交给线程池,并调用 tg_queue_thread->start();</p><p>3、为方便后面调试线程数量,将线程数解耦放入 global context,可在配置文件中自定义。</p><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">ObParallelDDLOperator::init</span><span class="hljs-params">()</span> </span><br><span class="hljs-function"></span>{<br> <span class="hljs-built_in">TG_CREATE</span>(lib::TGDefIDs::ParallelDDLPool, parallel_ddl_.tg_id_);<br> <span class="hljs-built_in">TG_SET_HANDLER_AND_START</span>(parallel_ddl_.tg_id_, parallel_ddl_);<br> <span class="hljs-built_in">TG_SET_THREAD_CNT</span>(parallel_ddl_.tg_id_, GCTX.parallel_ddl_thread_num_);<br>}<br></code></pre></td></tr></table></figure><h5 id="2-2-线程池的任务队列接口"><a href="#2-2-线程池的任务队列接口" class="headerlink" title="2.2 线程池的任务队列接口"></a>2.2 线程池的任务队列接口</h5><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">ObParallelDDLOperator::push_task</span><span class="hljs-params">(<span class="hljs-type">void</span> *task)</span> </span><br><span class="hljs-function"></span>{<br> <span class="hljs-built_in">TG_PUSH_TASK</span>(parallel_ddl_.tg_id_, task);<br>}<br></code></pre></td></tr></table></figure><h5 id="2-3-每个线程完成任务的接口"><a href="#2-3-每个线程完成任务的接口" class="headerlink" title="2.3 每个线程完成任务的接口"></a>2.3 每个线程完成任务的接口</h5><p>具体的任务工作可由task对应类型的实现类的 do_task() 接口完成</p><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">ObParallelDDLOperator::handle</span><span class="hljs-params">(<span class="hljs-type">void</span> *task)</span> </span><br><span class="hljs-function"></span>{<br> ObIDDLOperatorTask *ddl_task = <span class="hljs-built_in">static_cast</span><ObIDDLOperatorTask*>(task);<br> <span class="hljs-keyword">if</span> (xxx == ddl_task-><span class="hljs-built_in">get_type</span>()) {<br> <span class="hljs-keyword">auto</span> *trans_task = <span class="hljs-built_in">static_cast</span><ObTransEndDDLOperatorTask*>(ddl_task);<br> trans_task-><span class="hljs-built_in">do_task</span>();<br> }<br> <span class="hljs-built_in">free</span>(ddl_task);<br>}<br></code></pre></td></tr></table></figure><h4 id="3-定义-Task-基类,实现各类型-Task-需要完成操作的接口"><a href="#3-定义-Task-基类,实现各类型-Task-需要完成操作的接口" class="headerlink" title="3. 定义 Task 基类,实现各类型 Task 需要完成操作的接口"></a>3. 定义 Task 基类,实现各类型 Task 需要完成操作的接口</h4><p>ob决赛中有这样一个小情景:在创建系统租户的过程中,需要完成 1650 张系统表的创建,</p><p>在原本的实现中采用的是一张一张表的创建,效率非常低,因此我们的思路是通过创建若干线程建表,</p><p>在实际的实现过程中,我们发现每张表都在使用事务进行创建,而事务创建和销毁也有开销;</p><p>因此,我们最终的方案是通过 batch create 来实现,每个 batch 由一个事务构成。</p><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-comment">// 基类</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ObIDDLOperatorTask</span> {<br><span class="hljs-keyword">public</span>:<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> ObDDLOperatorTaskType <span class="hljs-title">get_type</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;<br>};<br></code></pre></td></tr></table></figure><h5 id="3-1-实现类"><a href="#3-1-实现类" class="headerlink" title="3.1 实现类"></a>3.1 实现类</h5><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-comment">// 分批并行执行 create_table</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ObCreateTableBatchDDLOperatorTask</span> : <span class="hljs-keyword">public</span> ObDDLOperatorTask {<br><span class="hljs-keyword">public</span>:<br> <span class="hljs-built_in">ObCreateTableBatchDDLOperatorTask</span>(ObDDLService *ddl_service, <span class="hljs-type">uint64_t</span> tenant_id, CallbackFunc callback, ObIArray<ObTableSchema> &schemas, <span class="hljs-type">int64_t</span> begin, <span class="hljs-type">int64_t</span> end);<br> <span class="hljs-keyword">virtual</span> ~<span class="hljs-built_in">ObCreateTableBatchDDLOperatorTask</span>() <span class="hljs-keyword">override</span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">int</span> <span class="hljs-title">do_task</span><span class="hljs-params">()</span> <span class="hljs-keyword">override</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">int</span> <span class="hljs-title">do_task</span><span class="hljs-params">(ObDDLSQLTransaction *trans)</span> <span class="hljs-keyword">override</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> ObDDLOperatorTaskType <span class="hljs-title">get_type</span><span class="hljs-params">()</span> <span class="hljs-keyword">override</span></span>;<br><br><span class="hljs-keyword">private</span>:<br> CallbackFunc callback_;<br> ObSArray<ObTableSchema> schemas_;<br> <span class="hljs-type">int64_t</span> begin_;<br> <span class="hljs-type">int64_t</span> end_;<br>};<br></code></pre></td></tr></table></figure><p>构造函数:将任务所需的所有参数传入</p><p>回调函数:在这里调用的是下面的主线程原子操作:<em>ObBootstrap</em>::inc_finish_ddl_task_cnt(),目的是每创建一个任务,主线程记录的任务数量 + 1,最后主线程只需通过返回的 get_finish_ddl_task_cnt() 来判断是否继续进行下去。</p><blockquote><p>话说,这里其实可以使用条件变量来实现,不用这么麻烦<del>(之后有时间再说)</del></p></blockquote><p>do_task():综合各个参数,创建事务,执行建表指令</p><h5 id="3-2-在服务逻辑中构造任务队列,使用线程池"><a href="#3-2-在服务逻辑中构造任务队列,使用线程池" class="headerlink" title="3.2 在服务逻辑中构造任务队列,使用线程池"></a>3.2 在服务逻辑中构造任务队列,使用线程池</h5><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-comment">// 1.单例模式,先获取线程池</span><br>ObParallelDDLOperator &parallel_ddl_operator = ObParallelDDLOperator::<span class="hljs-built_in">get_instance</span>();<br><br><span class="hljs-comment">// 2.划分数据的区间,每个区间构造一个任务,push进线程池</span><br><span class="hljs-keyword">for</span> (<span class="hljs-type">int64_t</span> i = <span class="hljs-number">0</span>; i < batch_count; ++i) {<br> <span class="hljs-type">int64_t</span> left = i * batch_size;<br> <span class="hljs-type">int64_t</span> right = left + batch_size - <span class="hljs-number">1</span>; <br> <span class="hljs-keyword">if</span> (right >= table_schemas.<span class="hljs-built_in">count</span>()) {<br> right = table_schemas.<span class="hljs-built_in">count</span>()<span class="hljs-number">-1</span>;<br> }<br><br> <span class="hljs-comment">// push the task into thread pool</span><br> ObDDLOperatorTask *task = <span class="hljs-keyword">new</span> <span class="hljs-built_in">ObCreateTableBatchDDLOperatorTask</span>(&ddl_service, OB_SYS_TENANT_ID, &ObBootstrap::inc_finish_ddl_task_cnt, table_schemas, left, right + <span class="hljs-number">1</span>);<br> parallel_ddl_operator.<span class="hljs-built_in">push_task</span>(task);<br> ObBootstrap::<span class="hljs-built_in">inc_start_ddl_task_cnt</span>();<br>}<br></code></pre></td></tr></table></figure><p>使用原子操作等待所有任务的执行完毕,主线程才将继续</p><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-comment">// 等待所有任务执行完毕</span><br><span class="hljs-type">const</span> <span class="hljs-type">int64_t</span> start_time = ObTimeUtility::<span class="hljs-built_in">current_time</span>();<br><span class="hljs-keyword">while</span> (<span class="hljs-built_in">OB_SUCC</span>(ret) && ObBootstrap::<span class="hljs-built_in">get_finish_ddl_task_cnt</span>() < ObBootstrap::<span class="hljs-built_in">get_start_ddl_task_cnt</span>()) {<br> <span class="hljs-keyword">if</span> (ObTimeUtility::<span class="hljs-built_in">current_time</span>() - start_time > WAIT_CREATE_SCHEMAS_TIMEOUT_US) {<br> ret = OB_TIMEOUT;<br> <span class="hljs-keyword">break</span>;<br> }<br>}<br><br><span class="hljs-comment">// 几个主线程等待的原子操作</span><br><span class="hljs-type">int64_t</span> ObBootstrap::finish_ddl_task_cnt = <span class="hljs-number">0</span>;<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">ObBootstrap::inc_finish_ddl_task_cnt</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-built_in">ATOMIC_INC</span>(&finish_ddl_task_cnt);<br>}<br><span class="hljs-function"><span class="hljs-type">int64_t</span> <span class="hljs-title">ObBootstrap::get_finish_ddl_task_cnt</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">ATOMIC_LOAD</span>(&finish_ddl_task_cnt);<br>}<br></code></pre></td></tr></table></figure><h3 id="3-oceanbase线程池的实现原理"><a href="#3-oceanbase线程池的实现原理" class="headerlink" title="3. oceanbase线程池的实现原理"></a>3. oceanbase线程池的实现原理</h3><p>在前面的例子里, 以 <em>tg_queue_thread</em> 为例,从上层向底层探索:</p><p>上层的线程池类型实现:</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-comment">// 1. 使用ITG作为基类,设置基本的接口</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ITG</span> {<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">int</span> <span class="hljs-title">thread_cnt</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">int</span> <span class="hljs-title">set_thread_cnt</span><span class="hljs-params">(<span class="hljs-type">int64_t</span>)</span> </span>= <span class="hljs-number">0</span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">int</span> <span class="hljs-title">start</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">void</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">void</span> <span class="hljs-title">wait</span><span class="hljs-params">()</span> </span>= <span class="hljs-number">0</span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">int</span> <span class="hljs-title">push_task</span><span class="hljs-params">(<span class="hljs-type">void</span> *task)</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">int</span> <span class="hljs-title">set_handler</span><span class="hljs-params">(TGTaskHandler &handler)</span></span>;<br> <span class="hljs-function"><span class="hljs-keyword">virtual</span> <span class="hljs-type">int</span> <span class="hljs-title">wait_task</span><span class="hljs-params">(<span class="hljs-type">const</span> common::ObTimerTask &task)</span></span>;<br>}<br><br><span class="hljs-comment">// 2.作为一种线程池实现类型,为上层提供接口:set_handler(TGTaskHandler &handler)</span><br><span class="hljs-comment">// 与 TG_THREAD_POOL 的区别在于,后者接口主要服务于无参数的任务:TGRunnable </span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">TG_QUEUE_THREAD</span> : <span class="hljs-keyword">public</span> ITG<br>{<br><span class="hljs-keyword">public</span>:<br> <span class="hljs-built_in">TG_QUEUE_THREAD</span>(ThreadCountPair pair, <span class="hljs-type">const</span> <span class="hljs-type">int64_t</span> task_num_limit);<br> ~<span class="hljs-built_in">TG_QUEUE_THREAD</span>() { <span class="hljs-built_in">destroy</span>(); }<br> <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">thread_cnt</span><span class="hljs-params">()</span> <span class="hljs-keyword">override</span> </span>{ <span class="hljs-keyword">return</span> (<span class="hljs-type">int</span>)thread_num_; }<br> <br> <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">set_handler</span><span class="hljs-params">(TGTaskHandler &handler)</span></span><br><span class="hljs-function"> </span>{<br> <span class="hljs-type">int</span> ret = common::OB_SUCCESS;<br> <span class="hljs-type">uint64_t</span> tenant_id = <span class="hljs-built_in">get_tenant_id</span>();<br> <span class="hljs-keyword">if</span> (qth_ != <span class="hljs-literal">nullptr</span>) {<br> ret = common::OB_ERR_UNEXPECTED;<br> } <span class="hljs-keyword">else</span> {<br> qth_ = <span class="hljs-built_in">new</span> (buf_) <span class="hljs-built_in">MySimpleThreadPool</span>();<br> qth_->handler_ = &handler;<br> qth_-><span class="hljs-built_in">set_run_wrapper</span>(tg_helper_);<br> ret = qth_-><span class="hljs-built_in">init</span>(thread_num_, task_num_limit_, attr_.name_, tenant_id);<br> }<br> <span class="hljs-keyword">return</span> ret;<br> }<br> <br> <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">start</span><span class="hljs-params">()</span> <span class="hljs-keyword">override</span></span><br><span class="hljs-function"> </span>{<br> <span class="hljs-type">int</span> ret = common::OB_SUCCESS;<br> <span class="hljs-keyword">return</span> ret;<br> }<br> <br> <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">wait</span><span class="hljs-params">()</span> <span class="hljs-keyword">override</span></span><br><span class="hljs-function"> </span>{<br> <span class="hljs-keyword">if</span> (qth_ != <span class="hljs-literal">nullptr</span>) {<br> qth_-><span class="hljs-built_in">wait</span>();<br> <span class="hljs-built_in">destroy</span>();<br> }<br> }<br> <br> <span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">push_task</span><span class="hljs-params">(<span class="hljs-type">void</span> *task)</span> <span class="hljs-keyword">override</span></span><br><span class="hljs-function"> </span>{<br> <span class="hljs-type">int</span> ret = common::OB_SUCCESS;<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">OB_ISNULL</span>(qth_)) {<br> ret = common::OB_ERR_UNEXPECTED;<br> } <span class="hljs-keyword">else</span> {<br> ret = qth_-><span class="hljs-built_in">push</span>(task);<br> }<br> <span class="hljs-keyword">return</span> ret;<br> }<br> <br><span class="hljs-keyword">private</span>:<br> <span class="hljs-type">char</span> buf_[<span class="hljs-built_in">sizeof</span>(MySimpleThreadPool)];<br> MySimpleThreadPool *qth_ = <span class="hljs-literal">nullptr</span>;<span class="hljs-comment">// 继承自 ObSimpleThreadPool</span><br> <span class="hljs-type">int64_t</span> thread_num_;<br> <span class="hljs-type">int64_t</span> task_num_limit_;<br>};<br></code></pre></td></tr></table></figure><p>继续往下分析,ObSimpleThreadPool 是整个ob线程池实现模型的核心环节</p><figure class="highlight c++"><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></pre></td><td class="code"><pre><code class="hljs c++"><span class="hljs-comment">/*</span><br><span class="hljs-comment">init:初始化元数据,开启线程池;</span><br><span class="hljs-comment">*/</span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">ObSimpleThreadPool::init</span><span class="hljs-params">(...)</span></span><br><span class="hljs-function"></span>{<br> queue_.<span class="hljs-built_in">init</span>(task_num_limit, name, tenant_id);<br> <span class="hljs-built_in">set_thread_count</span>(thread_num);<br> lib::ThreadPool::<span class="hljs-built_in">start</span>();<br>}<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">ObSimpleThreadPool::run1</span><span class="hljs-params">()</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-comment">/*</span><br><span class="hljs-comment"> 使用 ObAdaptiveStrategy 策略,并使用 ATOMIC_STORE 原子存储 total_thread_num_ 和 active_thread_num_;</span><br><span class="hljs-comment"> 如果当前有线程空闲,对任务队列执行pop操作,用上层传入的handler执行这个任务;</span><br><span class="hljs-comment"> 比如:ObParallelDDLOperator有用户实现的自定义handler,执行具体的任务。</span><br><span class="hljs-comment"> */</span><br>}<br></code></pre></td></tr></table></figure><p>继续往下,分析线程池中线程的调度:</p><p>每个线程其实封装了worker,在当前线程的生命周期内,可以通过线程本地存储TLS获取到这个worker对象。</p><figure class="highlight c++"><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></pre></td><td class="code"><pre><code class="hljs C++"><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">Threads::start</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-comment">// 1.在检查租户上下文完备后,内存分配一个 threads_ 动态数组</span><br> <span class="hljs-comment">// 2.每一个thread使用 Threads::create_thread -> Thread::start()</span><br>}<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">Threads::run</span><span class="hljs-params">(<span class="hljs-type">int64_t</span> idx)</span></span><br><span class="hljs-function"></span>{<br> <span class="hljs-function">ObTLTaGuard <span class="hljs-title">ta_guard</span><span class="hljs-params">(GET_TENANT_ID() ?:OB_SERVER_TENANT_ID)</span></span>;<br> thread_idx_ = <span class="hljs-built_in">static_cast</span><<span class="hljs-type">uint64_t</span>>(idx);<br> Worker worker;<br> Worker::<span class="hljs-built_in">set_worker_to_thread_local</span>(&worker);<br> <span class="hljs-built_in">run1</span>();<br>}<br></code></pre></td></tr></table></figure><p>最底层线程的实现,本质就是调用 POSIX 中关于 pthread 的各类接口,并做一定的封装</p><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-comment">/*</span><br><span class="hljs-comment">pthread_create(</span><br><span class="hljs-comment">pthread_t pointer, </span><br><span class="hljs-comment">attr of new thread,</span><br><span class="hljs-comment">the main execute function pointer,</span><br><span class="hljs-comment">args which will send to main function)</span><br><span class="hljs-comment">*/</span><br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">Thread::start</span><span class="hljs-params">()</span> </span>{<br> pret = <span class="hljs-built_in">pthread_create</span>(&pth_, &attr, __th_start, <span class="hljs-keyword">this</span>);<br> <span class="hljs-built_in">ATOMIC_FAA</span>(&total_thread_count_, <span class="hljs-number">-1</span>);<br>}<br><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">Thread::dump_pth</span><span class="hljs-params">()</span> <span class="hljs-comment">// 可以用于 debug pthread join failed</span></span><br><span class="hljs-function"> </span><br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">Thread::wait</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-built_in">pthread_join</span>(pth_, <span class="hljs-literal">nullptr</span>);<br>}<br><br><span class="hljs-type">void</span>* Thread::__th_start(<span class="hljs-type">void</span> *arg) {<br> <span class="hljs-comment">// 主要工作:设置线程上下文、元数据,并调用 th->run(); 执行 Threads::run</span><br>}<br></code></pre></td></tr></table></figure><h3 id="4-复习:"><a href="#4-复习:" class="headerlink" title="4. 复习:"></a>4. 复习:</h3><h5 id="4-1-条件变量-condition-variable"><a href="#4-1-条件变量-condition-variable" class="headerlink" title="4.1 条件变量 condition variable"></a>4.1 条件变量 condition variable</h5><p>作用:条件变量通过将等待线程挂起,直到条件满足主线程才继续,而不是通过一直循环检查条件,从而可以有效避免忙等,节省 CPU 资源。</p><p>1、多线程中用于线程间同步与通信的一种机制;</p><p>2、允许一个线程A休眠,直到等待另一个线程B满足特定条件时,A才再继续执行。</p><p>3、在不用条件变量时,线程A等待线程B满足一个条件C时,若C迟迟不能满足,A将一直使用CPU资源进行轮询,直到看到满足条件C时,不再轮询,A继续执行:这导致空耗CPU资源。</p><h5 id="4-2-信号量"><a href="#4-2-信号量" class="headerlink" title="4.2 信号量"></a>4.2 信号量</h5><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-comment">/* Binary semaphore */</span><br><span class="hljs-keyword">typedef</span> <span class="hljs-keyword">struct</span> <span class="hljs-title class_">bsem</span> {<br><span class="hljs-type">pthread_mutex_t</span> mutex;<br><span class="hljs-type">pthread_cond_t</span> cond;<br><span class="hljs-type">int</span> v;<br>} bsem;<br></code></pre></td></tr></table></figure><p>基本操作:</p><figure class="highlight c++"><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><code class="hljs c++"><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">bsem_init</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> bsem *bsem_p, <span class="hljs-type">int</span> value)</span></span>;<br><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">bsem_reset</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> bsem *bsem_p)</span></span>;<br><br><span class="hljs-comment">// V操作:设置信号量为1,通知其他线程,有资源可用</span><br><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">bsem_post</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> bsem *bsem_p)</span></span>;<br><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">bsem_post_all</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> bsem *bsem_p)</span></span>;<br><br><span class="hljs-comment">// P操作:如果信号量为 1,表示资源可用,线程将 v=v-1,继续执行</span><br><span class="hljs-comment">// 如果为 0,表示资源不可用,线程阻塞等待条件变量</span><br><span class="hljs-function"><span class="hljs-type">static</span> <span class="hljs-type">void</span> <span class="hljs-title">bsem_wait</span><span class="hljs-params">(<span class="hljs-keyword">struct</span> bsem *bsem_p)</span></span>;<br></code></pre></td></tr></table></figure><p>Reference:</p><p>[1] <a href="https://github.com/mtrebi/thread-pool">https://github.com/mtrebi/thread-pool</a></p><p>[2] <a href="https://github.com/Pithikos/C-Thread-Pool">https://github.com/Pithikos/C-Thread-Pool</a></p><p>[3] <a href="https://open.oceanbase.com/train?questionId=600006">https://open.oceanbase.com/train?questionId=600006</a> </p></div>]]></content>
<categories>
<category>Oceanbase</category>
</categories>
<tags>
<tag>线程池</tag>
<tag>数据库</tag>
</tags>
</entry>
</search>