├── 10_Spanner_薛翔.md ├── 11_Low(Tail)+latency_李春淼.md ├── 12_Bugs_夏亦谦_刘宁 ├── 12_Bugs_夏亦谦_刘宁.md ├── equation.png ├── example.png ├── example2.png └── example3.png ├── 13_NFV_谭钧升_李宗垚_陈文康.md ├── 14-RDMA-吴强强.md ├── 2-F2FS-朱丽叶-刘同科-F2FS.md ├── 3_system_software_for_persistent_memory_黎哲明.md ├── 4-RDDspark_cuiyi.md ├── 5_ramcloud_李然.md ├── 6_Scalable-lock_李智.md ├── 7_PowerGraph_李宁_赵志勇.md ├── 8_TransactionalMemory_李阳德.md ├── 9_Bigtable_王英艺_张金石_姜德智.md ├── README.md └── img ├── 10_1.png ├── 10_2.png ├── 11-1.jpg ├── 11-2.jpg ├── 13_click_1.png ├── 13_click_2.png ├── 14-RDMA-hash-imp.png ├── 14-RDMA-hash.png ├── 4_code1.1.png ├── 4_code1.2.png ├── 4_code2.png ├── 4_code3.png ├── 4_system_1.jpg ├── 4_system_2.jpg ├── 4_system_3.jpg ├── 7-3-1.png ├── 7-3-2.png ├── 7-3-3.jpg ├── 7-3-4.png ├── 7-3-5.png ├── 8_1.png ├── 8_2.jpg ├── 9-1.png ├── 9-2.png ├── README.txt └── Thumbs.db /10_Spanner_薛翔.md: -------------------------------------------------------------------------------- 1 | # Spanner: Google’s Globally-Distributed Database, OSDI 2012 2 | 3 | ## 考点 4 | 1. SQL, noSQL, newSQL演化过程 5 | 2. Spanner技术细节 6 | 3. 课后题 7 | 8 | ## SQL -> noSQL -> newSQL 9 | - [为什么](http://dataconomy.com/sql-vs-nosql-vs-newsql-finding-the-right-solution/) 10 | 11 | SQL:使用广泛,标准统一,技术支持丰富,保证ACID;扩展性差,过于通用以至性能提升受限,调试复杂; 12 | 13 | noSQL:最终一致性使得availability很好,扩展性好,半结构化数据的设计适于动态调整schema;代价是ACID的弱化; 14 | 15 | newSQL:更容易实现强一致性,事务支持,支持SQL语义和工具,使用NoSQL风格的集群架构而提供传统的数据和查询模型;通用性还是没SQL好,并不支持所有传统SQL工具。 16 | 17 | ## Spanner 18 | ### **1. Overview** 19 | #### 现状 20 | 21 | 不适用于BigTable的应用:“complex, evolving schemas”,或要求所有副本保持强一致性。 22 | 23 | Megastore:poor write throughput 24 | 25 | #### 区别于BigTable 26 | 27 | - 从简单的key-value store加强到temporal multi-version database; 28 | - 数据以半关系型的table组织; 29 | - 支持txn语义; 30 | - 支持SQL查询。 31 | 32 | #### 两个特性 33 | - 其上的应用程序能够动态配置replication,达到负载均衡和降低延迟; 34 | - External Consistency:分布式事务系统中,txn1的commit先于txn2的start,那么txn1的提交时间应小于txn2的提交时间。即能看见txn2的时候一定要能看见txn1。 35 | 36 | ### **2. Implementation** 37 | #### 系统架构 38 | ![Figure 1 in paper](img/10_1.png "spannerServerOrganization") 39 | 40 | (从宏观到微观) 41 | 42 | Universe:Spanner的整个部署 43 | 44 | - universe master:单例,维护所有zones; 45 | - placement driver:单例,负责在zones之间迁移数据。 46 | - zone:等同于其下BigTable的部署节点,是管理配置的单位,也是物理隔离的单位; 47 | 48 | Zone内部: 49 | 50 | - zonemaster:每个zone都有一个,负责将数据分配给当前zone的spanserver; 51 | - location proxy:每个zone有多个,为client提供定位到需要的SpanServer的服务; 52 | - spanserver:每个zone有成百上千个,负责为client提供数据服务; 53 | 54 | 55 | 核心设计就在于span server。 56 | 57 | 58 | #### Spanserver架构 59 | 60 | 先不急着看图,顺着前文捋清楚层次架构关系。每个span server有如下组成部分: 61 | 62 | - tablets:存数据的最小单位,概念和BigTable的tablet相近(一个tablet往往有多个备份副本,会存在其他zone的span server上); 63 | - Paxos state machine:每个span server维护一个用来选举(使用场景:当需要修改本地某个tablet时,由于需要同时修改其他span server上的副本,此时用每个span server上的Paxos状态机来处理一致性的问题(选出leader等)),tablet副本的集合组成Paxos group;写请求由Paxos leader负责,读请求由任意足够up-to-date的tablet所在span server执行都行; 64 | - lock table:(单Paxos group中选为leader可用)标示该Paxos group中对应数据的上锁情况; 65 | - txn mngr:(多group leaders中选为coordinator leader可用)当要执行的txn跨多个Paxos group时,需要对这些groups leader进行再选举,选举出来coordinator leader & non-coordinator-participant leader。前者使用txn mngr来协调这个txn(协调方法:txn mngr对其下管理的Paxos leaders执行2PL,即“尝试拿锁-修改并释放”)。 66 | 67 | 然后我们就能看懂下图了: 68 | 69 | ![Figure 2 in paper](img/10_2.png "spannerSoftwareStack") 70 | 71 | 72 | #### Directories 73 | 74 | dir是数据放置的单元,其下所有数据有一致的备份设置 75 | 76 | 77 | #### Data Model 78 | 79 | 基于directory-bucketed key-value mappings,主键作为key,其他作为value 80 | 81 | 82 | ### **3. TrueTime** 83 | #### API 84 | 85 | TT.now():返回一个时间段[earliest, latest],保证被调用的一刻,所有spanserver的本地时间都处在这个范围内; 86 | 87 | TT.after(t), TT.before(t):检查是否所有spanserver都经历了t;或都还没有经历t。 88 | 89 | 90 | #### TrueTime实现方式 91 | 92 | 硬件设备:GPS和原子钟。 93 | 94 | 每个datacenter中有一些time master机器,每个都运行timeslave后台;大多数使用GPS其他用原子钟。特点如下: 95 | 96 | - GPS互相同步但易受干扰; 97 | - 原子钟相对稳定但一段时间不同步会导致TT.now()时间段变大(原子钟的频率会有微小差异)。 98 | 99 | 100 | ### **4. Concurrency Control** 101 | 102 | Clients通过location proxy来定位spanserver,将请求交由该spanserver处理。 103 | 104 | #### 主要的txn类别 105 | - read-write txn: 普通的读写txn; 106 | - read-only txn: 确定只读的txn。不拿锁,不block接下来的read-write txn,选择足够up-to-date的replica执行都行; 107 | - snapshot reads: 读历史数据(或者说,数据在某个时间的版本)的txn。不拿锁,选择足够up-to-date的replica执行都行(简单,没讲)。 108 | 109 | #### 4.1 Read-Write Txns 110 | 111 | 1. (**spanserver执行部分**)对要读的数据,向对应的group leader拿读锁(如果拿不到就放弃,重新拿,即wound-wait); 112 | 2. 执行本地读写(外部不可见); 113 | 3. 修改完成,开始2PC。选择coordinator group,将修改发送给coordinator leader和non-coordinator-participant leader; 114 | 4. (**每个non-coordinator-participant leader执行部分**)收到txn修改内容后,选择本地最新成功的txn commit timestamp作为"prepare timestamp"返回给coordinator leader; 115 | 5. (**coordinator leader执行部分**)获得每个leader相应的写锁; 116 | 6. 等待所有participant leader的"*prepare timestamps*",选择最大的"**s**"。再将**s**与TT.now().latest和本地最新成果的txn commit timestamp比较,取最大的作为commit timestamp,赋值给"**s**"; 117 | 7. 持续调用TrueTime获取interval,等待s < TT.now().earliest,即TT.after(s),确保所有在s之前的txn都全局生效; 118 | 8. 以s为commit timestamp提交当前txn,并反馈client; 119 | 9. 释放锁。 120 | 121 | 122 | 说明:第六步中,实际上,就是想拿到一个timestamp **s**作为当前txn的commit timestamp。由于txn的顺序是遵循他们的commit timestamp,这个**s**就要保证大于之前所有txn commit timestamp。所以第七步等待花的时间就是用来确保所有机器的时间都“经历”了这些“之前的”txn的commit timestamp时间点,这些“之前的”txn此刻确定全局可见。 123 | 124 | #### 4.2 Read-Only Txns 125 | 126 | 首先,提取所有会被读到的key作为scope,然后分类讨论: 127 | 128 | 129 | 1. 如果scope都落在一个Paxos group:将这个RO txn发送给group leader;leader调用LastTS()获取本spanserver最近一次的write commit timestamp作为RO txn的timestamp并执行; 130 | 2. 如果scope跨多个Paxos groups:读取TT.now().latest作为当前RO txn的timestamp并执行。 131 | 132 | 133 | 说明:以上两种处理都能保证这次读在所有已全局生效的写之后 134 | 135 | #### 4.3 Schema-Change Txns 136 | 137 | 通过TT,选取未来的timestamp作为该txn提交时间,记为s;所有在s之前的txn正常执行;在s之后的被blocked,直到TT.after(s)==true再执行。 138 | 139 | 140 | ## 课后题(仅供参考) 141 | 142 | 1. What is external consistency? What’s the difference between external consistency and serializability? 143 | 144 | Definition: if a transaction T1 commits before another transaction T2 starts, then T1’s commit timestamp is smaller than T2’s. 145 | 146 | E.C.强调的是,每个txn在系统中生效的时间点和他们的commit timestamp保持一致,即对于commit timestamp t1理解latency 在scale增大时更加严重(参见 课前题目参考答案 1) 5 | 6 | >哪些策略能在大规模下减轻 tail 的 latency (参见 下方 容延技巧) 7 | 8 | 9 | ### 概要和重点 10 | latency 存在在 CPU和cache间、网络上的客户端和服务器间、应用和disk间、任何系统工作的地方。想象client向server发送请求,迟迟无响应,表明解决latency的重要性。 11 | 12 | *降低latency的体现:cache(CPU 和 main memory)、In-memory computation* 13 | 14 | ####latency的出现场景 15 | 大数据(关系型->复杂,非结构化)中的查询:Online Search (OLS) 交互式查询和访问数据 16 | 17 | 数据横跨千万台服务器 18 | 19 | Latency critical 应用 ( 双十一购物online shopping; 20 | virtual reality instruments; 21 | self- driving car) 22 | 23 | 24 | ####latency出现原因(4 个 latency sources) 25 | 26 | ![](img/11-2.jpg) 27 | 28 | - 资源竞争(机器被多应用共享) 29 | - 倾斜的访问模式(一些纪录更容易被访问到)、 30 | - 排队延迟(队头慢影响队尾,队列延迟被放大)、 31 | - 后台活动(守护进程被周期性调度) 32 | 33 | ####降低组件变量延迟(从source的角度) 34 | 35 | - 区分服务类型和高级队列 36 | - 减少队首阻塞 37 | - 管理后台活动(同步多台机器上的后台活动) 38 | 39 | ###延迟被范围放大(Latency Amplified By Scale) 40 | Even rare performance hiccups affect a significant fraction of all requests in the large-scale settings. 41 | 42 | 具体参见课前题目参考答案1 43 | 44 | ### 容延技巧(Tail-tolerant techniques) 45 | 46 | - Within request short-term adaptations(几十毫秒级的请求内短期适应) 47 | 48 | - Cross request long-term adaptations(几十秒到分钟级的跨请求长期适应) 49 | 50 | (以上两种技巧的具体策略如下所示) 51 | 52 | #### **- Within request short-term adaptations(几十毫秒级的请求内短期适应)** 53 | 54 | 55 | **1.Hedged requests(对冲请求) 56 | ** 57 | 58 | 把同样的request发布给多台有data replica的servers,哪个server 响应最快,就使用这个响应结果。 59 | (We term such requests “hedged requests” because a client first sends one request to the replica 60 | believed to be the most appropriate, but then falls back on sending a secondary request after some 61 | brief delay. The client cancels remaining outstanding requests once the first result is received.) 62 | 63 | *改进版:延迟发送第二次请求(defer sending a secondary request until the first request has been outstanding 64 | for more than the 95th-percentile expected latency for this class of requests.)* 65 | 66 | 弱点:hedged-requests technique 中可能多台服务器可能不必要地执行相同请求。 67 | 68 | **2.Tied request(关联请求)(基于hedged request的优化)** 69 | 70 | 不是选择把request发送给哪台 server,而是直接把请求同时放置在多台服务器的服务队列上,允许 71 | 服务器彼此交流这个请求的服务更新状态。 把允许服务器执行跨服务器状态更新的请求称为“tied requests.”。 72 | 最简单的形式就是在发送一个request的同时,绑定发送给其他服务器的编号tag。 73 | 当一个请求开始执行的时候,就可通过tag告知其他服务器中止执行该请求,其他服务器的服务队列中 74 | 如果还有这个request,则直接将其移除或降低优先级。 75 | 76 | 弱点:request在servers的服务队列中延迟 77 | 78 | *改进版:先探查remote queues, 把请求发送给最低负载的server 79 | 弱点:load levels can change between probe and request time;请求服务时间难以估计;clients can create hot spots by 80 | all picking the same (least-loaded) server.* 81 | 82 | #### **- Cross request long-term adaptations(几十秒到分钟级的跨请求长期适应)** 83 | 84 | **1.Micro-partitions(微划分)** 85 | 86 | 87 | 产生远多于现存机器数的细小的分区,进行动态分配partitions,保证这些机器上的负载均衡,例如 88 | (Tablets in BigTable,typically each machine managing 20 ~ 1,000 tablets),也提升了容错恢复速度; 89 | virtual server partition, virtual processor partition) 90 | 91 | **2.Selective Replication(选择性复制)(微划分的增强版)** 92 | 93 | 预测可能的负载不均衡,创造额外的复制品(例如Google’s web search system will make additional 94 | copies of popular and important documents in multiple micro-partitions) 95 | 96 | **3.Latency-induced Probation(延迟引导的探查)** 97 | 98 | 观察不同机器,暂时排除特别慢的机器,对excluded servers继续发送shadow请求,一旦发现问题减缓, 99 | 把这些排除掉的机器再合并进来。 100 | 101 | 弱点(违反直觉的):在高负载的情况下,从一个live system中移除一个server实际上提高了延迟 102 | 103 | 104 | ### 大型信息检索系统 105 | 106 | - 速度是关键的质量度量标准(Returning good results quickly is better than returning the best results slowly) 107 | 108 | 109 | - 处理不精确结果的技巧 110 | 111 | — Good-enough schemes(够好就行):只要有足够数量的sub servers返回结果即可,跳过不必要的子系统提高响应能力(分布式环境下) 112 | 113 | ![](img/11-1.jpg) 114 | 115 | — Canary requests(金丝雀请求):在向所有子服务器发送请求之前,先找一两个测试一下,如成功才发送。(Provide additional safety;Slightly increase in latency:Wait for single or two servers to respond is much less variable than in the large fan-out settings) 116 | 117 | ### Hardware Trends and Their Effects 118 | 119 | - Trends that further **hurt the latency** 120 | 121 | — Variability at the hardware level is likely to be higher 122 | — Device heterogeneity 123 | — Increasing system scale 124 | 125 | - Trends that help **mitigate the latency** 126 | 127 | — Higher bisection bandwidth 128 | — Lower per-message overheads 129 | 130 | ### Conclusion 131 | 132 | - Even rare performance hiccups affect a significant fraction of all requests in large-scale distributed systems 133 | - Eliminating all sources of latency variability in large-scale systems is impractical, especially in shared environments 134 | - Using an approach analogous to fault-tolerant computing, tail-tolerant software techniques form a predictable whole out of less-predictable parts 135 | 136 | ### 课前题目参考答案 137 | 138 | * ***Why latency variability is amplified by scale?*** 139 | 140 | 当前在大型的在线服务中减少延迟的一个普遍的技巧就是分配子操作在多台不同的机器上,使其并行执行,并保证每个子操作和它使用的那部分数据会放置在一处。由root把请求fan out到众多的leaf servers,最后再通过request-distribution tree把来自leaf servers的响应merge起来。这些子操作必须都在一个严格的deadline之内完成,来保证service可响应。 141 | 142 | 然而,在大型分布式环境下,一个组件的延迟在service level就会被放大。如上所述,若每个单独的组件延迟很短时间,把他们整合起来的延迟时间就可能会很长。尤其是一些request的相应必须收集并行的、众多的、带有微小延迟的servers的response的时候,集合起来的性能延迟将是不可忽视的。 143 | 144 | * ***Please briefly outline some effective tail-tolerant techniques.*** 145 | 146 | 参见 上方 容延技巧 147 | 148 | * ***Why tolerating latency variability for write operations is easier?*** 149 | 150 | - The scale of latency-critical modifications in these services in generally small 151 | 152 | - Updates can often performed off the critical path, after responding to user 153 | 154 | - Many services can tolerate inconsistent update models 155 | 156 | - Services require the consistent updates usually go through Paxos, which is inherently tail-tolerant 157 | -------------------------------------------------------------------------------- /12_Bugs_夏亦谦_刘宁/12_Bugs_夏亦谦_刘宁.md: -------------------------------------------------------------------------------- 1 | #Bugs, Towards Optimization-Safe Systems: Analyzing the Impact of Undefined Behavior 2 | 3 | ##助教提供的考点 4 | 1. 主要看懂上课的slides 5 | 2. 什么是Undefined behavior? 6 | 3. 什么是Unstable code? 7 | 4. 如何检测Unstable code? 8 | 9 | ##概要 10 | `Undefined behavior` 是编程语言规范对某段代码可能产生的某些执行结果未定义。 11 | `Unstable code` 就是在程序实际的执行过程中,由于涉及到undefined behavior,从而无法被编译器翻译(直接略过)的代码段。 12 | 13 | 类似于这种undefined behavior在C编译器中还有很多: 14 | * Pointer overflow: if ( p + 100 < p) 15 | * Signed integer overflow: if (x + 100 < x) 16 | * Oversized shift: if (!(1 << x)) 17 | * Null pointer dereference: *p; if (p) 18 | * Absolute value overflow: if (abs(x) < 0) 19 | 20 | ![alt text](/12_Bugs_夏亦谦_刘宁/example.png) 21 | 22 | 上面的代码中,buf + off < buf 就是一个undefined behavior:当off非常大的时候,会导致溢出,但gcc会认为(buf + off)总是小于buf_end的。所以当溢出的时候,gcc检测不到,会绕过这一段溢出检测代码,然后去读取buf段以外的内存。 23 | 24 | 本文对12种C/C++编译器做了测试,发现: 25 | 1. 有一些编译器会在加优化选项时删掉unstable code; 26 | 2. 不同的编译器会有不同的编译规则,不同版本的编译器对同一段代码会有不同的处理方式。 27 | 因此,需要一个成体系的approach来解决以上问题。 28 | 29 | ###本文的解决方案-方法论 30 | 令Assumption Δ = 代码中不会出现undefined behavior 31 | 用公式表示Assumption Δ: 32 | ![alt text](/12_Bugs_夏亦谦_刘宁/equation.png) 33 | Reach(e): when to reach and execute code fragment e 34 | Undef(e): when to trigger undefined behavior at e 35 | 36 | STACK会在Assumption Δ被允许和不允许的情况下分别模拟编译。 37 | * Step1 - 先模拟假设不成立的情况进行一次编译; 38 | * Step2 - 模拟假设成立的情况进行一次编译; 39 | * Step3 - 查看前两步的执行结果有没有区别,有区别的地方就是unstable code。 40 | 41 | ###举个栗子 42 | INT_MIN在标准头文件limits.h中的定义如下: 43 | `#define INT_MIN (-INT_MAX - 1)` 44 | 45 | ![alt text](/12_Bugs_夏亦谦_刘宁/example2.png) 46 | 在这个代码段中,当((y==0) or (y == -1 and x == INT_MIN))时,x/y可能出现溢出。 47 | 因此,Assumption Δ:(y != 0) 且 (y != -1 and x != INT_MIN) 48 | 49 | 根据 Δ = ∀e:Reach(e) → ¬Undef(e),可以对以上的三行代码列出公式如下,最终得到Δ的具体表达式。 50 | ![alt text](/12_Bugs_夏亦谦_刘宁/example3.png) 51 | 解释说明一下Δ怎么计算出来的:Δ = ∀e:Reach(e) → ¬Undef(e)的意思是:对于任意的代码行e,在执行到了e的情况下,不会触发undefined behavior。由于本例中的代码段一共有三行,所以是对三行取交集。 52 | 第一行:Reach(e)为true,Undef(e)为(y == 0) or (y == -1 and x == INT_MIN)。 53 | 第二行:Reach(e)为true,由于本行不存在undefined behavior,因此Undef(e)为false。 54 | 第三行:由于本行是在第二行成立的条件下才会执行到,因此Reach(e)=(y == -1 && x < 0 && x/y < 0),同时本行也没有undefined behavior,因此Undef(e)为false。 55 | 56 | ###本文的Limitation 57 | 1. 如果执行第二步时得不到准确的结果,那么会漏报一些unstable code; 58 | 2. 如果执行第一步时得不到准确的结果,就会产生误报(false warning / false positive)。 59 | 3. 目前stack给出的Undefined behavior pattern 可能不齐全。 60 | 61 | ###如何避免Unstable Code 62 | * 对于程序员来说,通过fix bug或者去掉一些会被编译器当做是undefined behavior的代码; 63 | * 对于编译器来说,可以集成一些现有的bug-finding的工具,或者利用STACK的方式来判定unstable code; 64 | * 完善编程语言的specification,定义更多的代码执行规则,减少undefined behavior的产生。 65 | 66 | ###STACK为了更好的扩展性,做了什么权衡? 67 | 1. STACK为了使可扩展性更高,在计算Δ = ∀e:Reach(e) → ¬Undef(e)的时候做了一些近似运算,使最后得到的结果可能会漏掉一些unstable code。 68 | 2. STACK为了简化和滤过某些查询用到的constraint solver如果发生了timeout,也会出现漏报的情况。 69 | 因此,STACK为了更好的扩展性,牺牲了一定的可靠性(精度)。 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /12_Bugs_夏亦谦_刘宁/equation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/12_Bugs_夏亦谦_刘宁/equation.png -------------------------------------------------------------------------------- /12_Bugs_夏亦谦_刘宁/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/12_Bugs_夏亦谦_刘宁/example.png -------------------------------------------------------------------------------- /12_Bugs_夏亦谦_刘宁/example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/12_Bugs_夏亦谦_刘宁/example2.png -------------------------------------------------------------------------------- /12_Bugs_夏亦谦_刘宁/example3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/12_Bugs_夏亦谦_刘宁/example3.png -------------------------------------------------------------------------------- /13_NFV_谭钧升_李宗垚_陈文康.md: -------------------------------------------------------------------------------- 1 | #The Click Modular Router 2 | 3 | ##助教提供的考点 4 | 1. 为什么要提出这个技术?与传统的区别? 5 | Unfortunately, most routers have closed, static, and inflexible designs. 现有的大多数路由器的设计都是封闭的,静态的和刻板的。 6 | Network administrators may be able to turn router functions on or off, but they cannot easily specify or even identify the interactions of different functions. 7 | 网络管理员很难指定甚至定义不同functions之间的交互。 8 | Furthermore, network administrators and third party software vendors cannot easily implement new functions. 9 | 而且,要实现新的functions很难。 10 | 与传统的区别:(1)灵活:很容易添加新的功能 (2),模块化:通过组合元素来实现功能; (3), Open:允许开发者添加元素(Elements);(4),效率:与硬件性能相差不大。 11 | 12 | 2. Pull和Push 13 | push和pull是Click里elements直接的两种连接方式。 14 | Push是从源Element到下游的元素,通过事件触发,比如有包到达。在Push连接方式里,上游的元素会提交一个包到下游的元素; 15 | Pull是从目的地元素到上游元素。在Pull的连接方式里,是下游的元素发送包请求到上游的元素。当输出接口准备好发包时,会开始一个pull过程:该过程会一直向后遍历图直到遇到某个元素‘吐出’一个包。 16 | 17 | 3. 包调度 18 | 在Click里,调度器就是一个有着多个输入端口,一个输出端口的pull element, 19 | 并通过一定的调度策略来决定从这多个input进来的包应该如何共享这个Output。 20 | 在论文里提到click实现了两个调度器:Round-Robin Sched 和 PrioSched (Priority Scheduler)。 Round-Robin Sched 就是对input进行轮询。 PrioSched (Priority Scheduler)就是每次都从第一个input开始pull packets。 21 | 22 | 4. Dropping Policies 23 | Click通过队列元素来实现一个简单的Dropping策略, 也就是当包数目超过配置的最大长度时,这些包都会被扔掉。 24 | 论文提到的Dropping策略:(1),RED:Random Early Detection。 该Element以下游的最近的队列长度作为Dropping的依据。(2), RED over multiple queues: 如果RED的下游有多个队列,那就将这些队列的长度都加起来作为Dropping的依据;(3),weight RED: 每个包根据它的优先级有不同的Drop的概率。 25 | 26 | 5. NFV:Network Function Virtualization 27 | Network Functions(Middleboxes): Firewall, IDS, DNS。网络功能虚拟化就是虚拟化这些网络功能,也就是通过软件模拟实现这些网络功能。 28 | 或者说是:运行在云设施的网络服务。 29 | NFV目标:(1)省钱:使用更便宜的商业服务器来实现网络功能;减少专有硬件的使用,从而降低能耗,并且能够方便地维护。(2)赚钱:加速网络服务部署; 30 | 网络基础设施作为服务; 31 | 32 | 33 | ##提纲 34 | 35 | Click是一个能够灵活配置的软件路由结构。它是由一系列包处理模块(被称为组件)按照特定方式组合而成。组件是click中最小的功能单元,它只实现最简单最单一的功能如排队、调度、分类、复制等,稍微复杂的功能可以用若干个组件复合而成。一个路由配置是一个有向图,组件位于顶点,数据包沿着有向图的边传输,如图1所示。Click的一些特性如下拉连接、基于设备的上下文,能增强组的功能,使配置更容易更利于编写。一个click组件代表着一个最基本的功能单元,例如减少一个IP包的生存时间域,而不是实现一个复杂的功能如IP路由。用户通过选择组件并连接它们来实现不同的路由功能。在一个运行的路由器中,每一个组件是一个属性为私有的C++对象,连接用一个指向组件对象的指针代替,通过一个简单的虚拟函数实现一个数据包沿着一个方向连接。 36 | 37 | ![](img/13_click_1.png) 38 | 图 1 一个click组件连接图 39 | 40 | 41 | Click系统用C++语言已经封装了很多的组件,每一个组件都实现一个简单的功能。我们要做的工作就是通过click配置语言对这些组件进行连接,从而完成特定的路由功能,因此,click语言对于实现路由功能是特别方便的。Click配置语言由两个部分构成:声明组件和连接。它的语法非常简单易懂,例如图1的配置可以写成如下形式 42 | FromDevice(eth0)->Counter()->Discard(); 43 | 44 | 建立一个文本将保存上述内容,如保存为/home/click-2.0/test.click,这样就生成了一个自 45 | 已的配置文件。切换到click程序目录下,运行click /home/click-2.0/test.click就可以把该配置运行起来. 46 | 47 | 一般而言Click路由器包含以下凡个组成部分 48 | 49 | (1) 组件(Element)。组件是路由体系结构中最基本的部分,每个组件完成一个最基本的功能,路由能实际就是有限个组件的组合,组件有四个重要的组成部分 50 | 51 | (a)组件类:click中组件类是用C++编写的,每个组件都属于一个组件类,通过组件类即可声明组件对象。从组件类声明对象即可得到实现特定功能的组件,可通过类代码决定该类组件对象的接口、组件初始化、以及数据处理流程等。 52 | 53 | (b)端口:click组件在设计时便考虑到对外的接口,一个组件有多少输入端口与多少输出端口往往取决于该组件实现的具体功能,如一个counter组件,它的作用是测量包数和速率,那么它只需要一个输入端口用于输入数据包,一个输出端口将数据包无改变地输出即可。除此之外,在click中一些特定端口有相同的语义,例如,第二输出端口经常用于发送错误的数据包。 54 | 55 | (c)配置串:配置串是用逗号进行分隔的参数列表,它包含了一些用于组件初始化的附加参数,用户可以显示提供或使用默认串作为参数。 56 | (d)方法接口:每一个组件都支持一个或多个方法接口,它类似于C++类中的公有函数,因此也可称为函数接口,通过调用它可实现特定功能,它是类对象的功能函数。每一个组件都支持简单的数据包传输接口,但组件可以创建任意数量的函数接口,例如,一个队列能通过创建接口函数来报告它的长度。组件通过这样功能性函数接口来进行数据通信。 57 | 58 | 图 2显示了一个简单的click组件Tee(2), "Tee"是组件类,该类组件的功能是将输入端口接收的数据包发送到它的输出端口。配置串“2"表示有两个输出端口。 59 | 60 | ![](img/13_click_2.png) 61 | 图 2 一个click组件 62 | 63 | (2)数据包结构。这是在click内部定义的一种数据结构,用于存储经过处理的特定格式的数据包。一般情况下,路由器对数据包处理时所需信息只在前三层,即最多解析到IP头部,便可以获得所需信息,如果进行处理时是针对整个数据包,则毫无疑问会降低处理效率。无论怎么样,路由器都不会去对数据部分进行处理,更不会对数据部分进行修改,为了提高click组件内部的处理效率,click内部定义了一种被称为注释结构的数据结构。Click路由器在接收到数据包后,会提取数据包包头。放入到注释结构中,值得注意的是,注释结构除了包含数据包包头,还包含一个指向数据内容的指针,这样注释结构才能完整的表达出整个数据包。这样以后对数据包的处理变为对注释结构的处理,使路由器处理效率大大增强。 64 | 65 | 66 | (3)连接方式。Click支持上拉、下推、不定方式这三种连接方式。在一个下推连接中,数据包的发起者为源组件,它从一个源组件发送到一个目的组件,目的组件只是被动的负责接收,这跟数据包通过大多数软件路由器是一样的。相反,在上拉链中,数据包的传送发起者为目的组件,它要求源组件发送一个数据包,或者当没有数据包时发送一个空指针。每一种形式的数据包传输通过一个虚函数进行实现。不定方式是该组件在初始化时并不确定为下推型或是上拉型,它根据所直接连接的组件决定,如果它与上拉组件直接连接,则表现为上拉方式,否则表现为下推方式。必须说明的是,不定方式并不意味着能随意更改连接方式,它的连接类型一经确定,就不会再更改。上推与下拉两种方式不能直接连接,原因是它们的主动性相反,不能判断出数据包的发起者,容易出现逻辑上的错误,不利于系统的扩展。 67 | 68 | (4)数据包存储。Click系统中有单独的组件类,有相应的组件能对队列直接进行操作,因此不会在输入输出端口上出现内置队列,也省去了相对复杂的开销;队列在click中是明确的对象,它是通过一个独立的对队组件进行实现的。这就使得设计者能够对路由器数据包的存储有一个很明确的控制。一个队列有一个下推输入端口和上拉输出端口,输入端口通过入队操作将数据包推进,输出端口通过出队操作对上拉操作进行响应并返回它们。 69 | 70 | (5)CPU调度。组件是CPU调度的基本单元,同样是数据包处理的基本单元。Click通过一个任务队列对CPU资源进行调度,一个组件在同一时间通过循环方式从任务队列中获取一个任务。然而,大多数组件从不被放到任务队列中,当它们的下推或上拉方法被调用时它们才被调度。如果组件如果经常初始化下推或上拉请求而得不到应答,那么它应该被放入到消息队列中。Click当前运行在一个单线程上,任何数据包传输函数必须在下一个任务开始前返回到调用它的函数那里。路由器继续处理每一个下推数据包,直到它被存储或者丢弃,因此,在一个配置图中队列的位置决定了它以何种方式进行CPU调度。 71 | 72 | (6)基于流的上下文关系。如果组件A想调用组件B的函数接口,它首先必须能够定位,基于流的上下文关系能够通过数据包的连接方式获取传递关系,进而对所需组件进行定位,例如它可以定位从一个给定组件开始的数据包将要在哪个地方结束,或者在一个给定组件结束的数据包从哪里开始。基于流的上下文关系通过一种数据流算法进行计算,在初始化阶段,组件对它调用一次,存储结果以进行快速查询,任何组件使用基于流的上下文关系时都必须处理若干个结果组件,如果结果组件数量异常则报错,通过这种方式Click能对系统结构进行错误检查,避免出现冲突现象。 73 | 74 | (7)组件实现。在Click中组件是采用C++语言实现,每个组件类都派生于基础类Element,它有约20个虚函数并提供了默认实现,用户开发自已的组件类时,根据实际需要对其中的某些虚函数进行重载即可,因此,组件的实现非常容易。 75 | 76 | (8)设备轮循方式。Click对设备的调用是采用轮循方式,这是由于路由器工作时处理的数据量很大,采用轮循方式可以有效地减少对系统CPU的访问,因而大大提高路由器的综合性能 77 | 78 | (9)丢包策略:Click通过队列元素来实现一个简单的Dropping策略, 也就是当包数目超过配置的最大长度时,这些包都会被扔掉。同时click支持将其他元素与队列元素组合,构成更复杂的丢包策略。(1),RED:Random Early Detection。 该Element以下游的最近的队列长度作为Dropping的依据。(2), RED over multiple queues: 如果RED的下游有多个队列,那就将这些队列的长度都加起来作为Dropping的依据;(3),weight RED: 每个包根据它的优先级有不同的Drop的概率。 79 | 80 | (10)调度策略:通过pull处理,一个调度器可以在一个click元素中实现,并且只维护本地的路由配置。Click实现了RoundRobinSched 和 PrioSched这两个元素,并且可以通过元素之间的组合实现更加复杂的调度策略。 81 | 82 | 83 | ##课后题解答 84 | 1. Why must Click provide both `push' and `pull' methods? Can we eliminate one of the two operations? How/Why? 85 | 86 | Push and pull are duals of one another: the upstream end of a connection initiates a push call, while the downstream end initiates a pull call. Together, push and pull allow the appropriate end of a connection to initiate packet transfer, solving several router control flow problems. For example, packet scheduling decisions— choosing which queue to ask for a packet—are easily expressed as composable pull elements, as we show in Section 4.1. As another example, the system should not send packets to a busy transmitting interface. If it did, the interface would have to store the packet, and the router would lose the ability to affect it later (to throw it away, to modify its precedence, and so forth). This restriction can be simply expressed by giving the transmitting interface a pull input; then the interface is in control of packet transfer, and can ask for packets only when it’s ready. 87 | 88 | push和pull是Click里elements直接的两种连接方式。 Push是从源Element到下游的元素,通过事件触发,比如有包到达。在Push连接方式里,上游的元素会提交一个包到下游的元素; Pull是从目的地元素到上游元素。在Pull的连接方式里,是下游的元素发送包请求到上游的元素。Push和pull的共同使用可以使包转发连接的适当终止,可以很好地解决路由器控制流问题。例如包调度的决定——选择哪个队列去请求一个包对于组合的pull元素来说是非常容易实现的。另外,系统不应该向繁忙的转发接口发送包,否则,这个接口就必须存储包,并且路由器会失去处理这些包的能力(丢弃,修改优先级等)。这个约束可以由简单的给转发接口一个pull输入实现。然后这个接口就可以控制包转发,并且可以在它准备好的时候请求包。 89 | 90 | With pull processing, a packet scheduler can be implemented in Click as a single element that maintains only local knowledge of the router configuration. 91 | 92 | 通过pull处理,一个维护本路由配置简单的click元素就可以实现包的调度器。 93 | 94 | 普通的路由器的实现只需要通过push操作就可以实现了,但是只实现push操作对于click来说,会带来包调度方面的巨大的复杂性,而通过实现pull操作,就可以通过一个简单的pull元素或者pull元素与队列的组合实现复杂的调度策略,pull操作对于click来说是必须的。 95 | 96 | 2. One limitation of Click is the difficulty of scheduling CPU time among pull and push paths. Why is it difficult? What would you do to improve it? 97 | 98 | We have not yet fully investigated how to schedule CPU time among competing push and pull paths, a problem that arises whenever multiple devices simultaneously receive or are ready to send packets. Currently, Linux handles much of this scheduling, and the work list described in the next section controls the rest. Eventually all of it should be controlled by a single mechanism. 99 | 100 | Work list. A lightweight work list can be used to schedule Click elements for later processing. It is effectively a simple, single-priority CPU scheduler, and is run after every 8th input packet or whenever there are no more input packets. Queues and Shapers currently use the work list to delay packet upstream notification . This improves i-cache performance: under high load, 8 packets will be enqueued before the work list is run and pull processing begins. 101 | Click通过一个任务队列对CPU资源进行调度,一个组件在同一时间通过循环方式从任务队列中获取一个任务。然而,大多数组件从不被放到任务队列中,当它们的下推或上拉方法被调用时它们才被调度。如果组件如果经常初始化下推或上拉请求而得不到应答,那么它应该被放入到消息队列中。Click当前运行在一个单线程上,任何数据包传输函数必须在下一个任务开始前返回到调用它的函数那里。路由器继续处理每一个下推数据包,直到它被存储或者丢弃,因此,在一个配置图中队列的位置决定了它以何种方式进行CPU调度。而不是只通过单一的机制进行调度的 102 | 103 | 第二问是开放性问题,没有具体答案。在这里认为可以对组件的配置信息进行静态的分析或者进行多线程的调度支持。 104 | 105 | 3. How can batch processing be applied to Click? Be specific and consider the impact on latency and throughput. 106 | 107 | 这一题是开放性问题,没有具体答案。 108 | 109 | A、 Batch Operation: 110 | I/O Batching. packet I/O batching can be accomplished by using psio. It passes the received packets in large batches to the user-level Click. By default, individually-transmitted packets are queued at the end of processing path up to the batch size. When computation batching is applied, the group of packets processed together is immediately sent out to NICs. 111 | Computation Batching. We can apply batching to the intermediate elements between the packet source and sink. The received pack is passed down to the processing path, being handled by elements in the middle. Each element takes it and applies their operations by iterating over the packets in the same pack. 112 | B、Impact: Since the performance heavily depends on the size of a pack, If we set buffer size too large, the throughput will improve with some more latency. 113 | 114 | -------------------------------------------------------------------------------- /14-RDMA-吴强强.md: -------------------------------------------------------------------------------- 1 | # 论文背景 2 | + 数据库领域 3 | + 内存数据库的兴起 4 | + 传统事务为实现ACID的代价(Logging/Locking/...) 5 | + 分布式事务的梦魇(分布式锁要比本地锁慢好几个数量级, 加锁时间越长, 并发程度越低, 延迟越高) 6 | + 硬件领域 7 | + HTM: 在硬件层面提供了 **原子性** 支持(ACI, 注意没有D) 8 | + 限制 9 | + HTM内不能有IO 10 | + HTM不保证一定成功, 需要fallback 11 | + RDMA: 提供了 **强一致性** 的远程访问支持 12 | + 注意, 这里的一致性是内存层面的一致性, 当作Coherence来理解 13 | + 强一致性即: read last write 14 | + 三种接口 15 | + 模拟TCP/IP: ~100us 16 | + recv/send: ~7us 17 | + **单向访问**: 最快, ~3us级(原生TCP/IP大约是500us) 18 | + RDMA CAS有多种级别. 在论文所有的机器上, RDMA CAS与Local CAS **不兼容** 19 | 20 | # 论文实现 21 | 论文实现了一个 **分布式** **内存数据库**. 22 | 23 | 论文实现可以分成两层: 24 | 25 | + 事务层: 处理事务, 所有并发控制在这一层做 26 | + 存储层: 处理存储, 不考虑并发控制, 从而使得逻辑更加清晰, 并且实现更加简洁 27 | 28 | ## 事务层 29 | 基本思想是做HTM(本地)+Locking(远程)来实现事务(ACI), D由Logging + UPS(断电时将内存刷回持久化存储)来保证. 30 | 31 | + 使用Transaction Chopping的技术将大的事务切分为小事务, 保证能够在HTM内执行 32 | + 读写事务与只读事务的分别对待 33 | + 由于只读事务通常需要读取很多内容, 容易造成HTM失效, 因此, DrTM将两者区别对待 34 | + 分布式事务的处理 35 | + 思路: 将远程机器上的内存读到本地来, 从而转化为单机事务(用HTM解决) 36 | + HTM失效时Fallback的处理 37 | 38 | ### 锁 39 | 当事务在同一台机器上, HTM可能保证Serializability, 但如果是在多台机器上, 就需要用锁来保证(2PL可以提供Serializability). 40 | 41 | + 写锁: 保证唯一访问 42 | + 读锁: 基本Lease的共享锁 43 | + Synchroinzed Time提供分布式的时间服务 44 | + 验证锁是否Expired: now > lease_end_time + DELTA 45 | + 验证锁是否可用: now < lease_end_time - DELTA 46 | + \(..., lease_end_time - DELTA\): 当前时间在此范围内, 表示仍然持有读锁 47 | + \[lease_end_time - DELTA, lease_end_time + DELTA\], 此为误差范围, 必须避开它 48 | + \(lease_end_time + DELTA, ...\): 当前时间在此范围内, 表示已经过期 49 | + 实现: RDMA CAS 50 | 51 | ### 只读事务 52 | + 不使用HTM: 因为读的东西一般很多, 时间太长, 容易abort 53 | + 需要使用 **CAS** 来加锁. 本地CAS与RDMA CAS冲突, 因此, 统一使用RDMA CAS 54 | + 只读事务的并发控制 55 | + 本地/本地: 由锁控制 56 | + 本地/远程: 由锁控制 57 | + 远程/本地: 由锁控制 58 | 59 | ### 读写事务 60 | + 思路: 61 | + 对于read_set与write_set, 判断是否在都在本地, 如果不是, 则有RDMA加锁并读取(加锁之后, 其他事务就无法访问它们了) 62 | + end-time = minimum(endtime of lock(x) for x in read_set): 取最小的lease time 63 | + 涉及到的数据都到了本地后, 使用HTM进行事务处理 64 | + 事务提交前, 检查now < end-time - DELTA, 如果为真, 说明所有的读锁都没有过期, 事务可以提交, 否则abort 65 | + 事务提交后, 将write_set加的数据写回, 并释放写锁, 读锁会自动过期 66 | + 读写事务冲突检测: 67 | + 先本地/再本地: 由HTM保证原子性 68 | + 先本地/再远程: 远程的RDMA请求, 会打断当前机器的HTM. 当前事务, 只能等待远程RDMA加的写锁释放后再继续进行. 如果远程RDMA加的是读锁, 则本地HTM可以同时执行 69 | + 先远程/再本地: 远程的RDMA会先加锁数据, 本地HTM要去访问时, 如果发现数据上有写锁, 则abort, 如果有读锁, 则可并行执行 70 | + 先远程/再远程: 远程的RDMA会先加锁数据, 另一个远程RDMA来访问时, 如果发现已经有写锁, 则中止, 如果发现是读锁, 则取其中止时间为自己锁的中止时间, 然后正常执行 71 | + Fallback实现 72 | + HTM不保证一定可以成功, 即使没有冲突, 因此, 需要一个fallback(基于锁) 73 | + 步骤 74 | + 放锁 75 | + 对所有锁排序后重新加锁, 以避免死锁 76 | + 执行操作 77 | + 放锁 78 | + 写回 79 | + 注意: 以上操作需要记Log, 以应对错误情况 80 | 81 | ### 重点强调 82 | + 冲突检测 83 | + 对于同样使用了HTM的两个事务, HTM保证它们的before-or-after 84 | + 对于一个已经存在的HTM事务(读ab, 写cd), 如果另一个远程事务需要访问abcd, 则需要先用RDMA加锁, 并把数据读回来, 加锁过程会abort已有的HTM事务, 从而后面的这个远程事务会先发生. 之前的HTM事务需要重试 85 | + 对于两个远程事务, 锁会保证其先后 86 | + 所有只读事务都当作远程事务来处理(使用加锁来保证顺序) 87 | + HTM + 读写锁机制等价于2PL, 从而, 可以保证Serializability 88 | + 使用HTM完成的事务与使用2PL完成的事务是等价的. 89 | + 在HTM中, 如果两个事务冲突, 且其中至少一个为写, 则HTM保证abort掉其中一个, 从而保证了顺序. 90 | + 在HTM中, 如果两个事务冲突, 且其中一个涉及到了远程对象, 且加锁机制可以保证两个事务的顺序 91 | + DrTM中的基于Lease的读锁是于传统的shared read lock是等价的 92 | + 在DrTM中, 如果机器A对x加了读锁, 则机器B可以共享此读锁. 93 | + 当事务执行完毕, DrTM会检测所有读锁是否过期, 如果已经过期, 事务不会commit, 没有影响. 如果没有过期, 则提交. 这等价于2PL中, 所以锁在Shrink阶段提交, 并且不会加新的锁. 94 | + DrTM中, 所以锁都会在`Shrink`阶段提交, 等价于2PL中的行为 95 | + HTM执行完毕后, 所有本地'锁'都会被释放. 96 | + 在HTM的confirmation阶段, 即检测读锁是否过期, 检测通过, 则先行于所有锁释放, 且不会再拿新的锁. 97 | + 在所有HTM事务提交后, 写锁最终都被会释放. 98 | 99 | ## 存储层 100 | + DrTM提供了两类存储 101 | + 有序存储: 基于B+ Tree, 没有充分利用RDMA的特性 102 | + 无序存储: 基于Hash, 充分利用了RDMA的特性, 并且, 在事务层的保护下, 不需要考虑冲突检测问题 103 | + 无序存储实现 104 | + ![](img/14-RDMA-hash-imp.png) 105 | + 记住这张图! 记住这张图! 记住这张图! 106 | + 这张图配着5.2节看更有味道哦 107 | + 有序存储实现 108 | + 只需要知道它没有充分利用RDMA特性, 是个future work就是了 109 | + 其他: 论文中比较了DrTM与其他另外两个平台的Hash实现(但是比较过程中涉及了许多知识点, 要是真考这个, 大家洗好脖子吧), DrTM的优点大概是 110 | + 分离了存储操作与冲突检测 111 | + 之前的实现偏向于RDMA(而不是Local), 增加了延迟 112 | + 本地内存操作还是比RDMA快, 但是之前的实现没有考虑本地缓存 113 | + ![](img/14-RDMA-hash.png) 114 | 115 | ## 缺点 116 | + 事务需要事先知道write-set与read-set 117 | + 无序存储是HTM/RDMA友好的, 而有序存储则没有对RDMA做过优化 118 | + 当有机器失效时, 考虑Durability而不是可用性 119 | + 读写事务提交后, 需要写回, 并释放写锁, 如果此时, 此机器挂了, 其锁永远无法被释放 120 | 121 | ## 其他实现细节 122 | + Synchroinzed Time 123 | + 理想情况是使用TrueTime API(From Spanner) 124 | + 论文中使用的是PTP 125 | + 每个Node上, 一个timer thread会周期性同步software time, 其他线程直接用 126 | + Fallback Handler是如何保证Serializability的 127 | + RDMA CAS与Local CAS的不兼容(RDMA CAS分成很多级别, 某些级别是兼容的, 但是论文所用的硬件不支持) 128 | 129 | # 课后习题 130 | + How does DrTM detect conflicts between remote reads and local writes? 131 | + Local writes会被包裹在HTM中. 远程reads使用的RDMA会打断本地HTM, 从而避免了冲突 132 | + Why DrTM has the deadlock issue and how does it avoid the deadlock? 133 | + 在fallback handler中会出现, 详见上文事务层中fallback实现 134 | + What’s the limitation of DrTM? 135 | + 见上文 136 | -------------------------------------------------------------------------------- /2-F2FS-朱丽叶-刘同科-F2FS.md: -------------------------------------------------------------------------------- 1 | #flash的技术特性(背景了解:为什么不用传统的数据库在flash上写) 2 | 3 | -flash的读/写和擦除操作是不对称的(读/写是以页为单位,擦除是以块为单位) 4 | 5 | -写前必须擦除,每个块的擦除操作是有一定寿命的。 6 | 7 | #六个技术点, 8 | 9 | -Flash-friendly on-disk layout:文件系统分为superblock(SB),Checkpoint(CP),Segment Info(SIT),Node Address 10 | 11 | Table(NAT),Segment Summary Area(SSA),和Main Area六个部分存储,在分配存储块的时候用块做单位,cleaning的时候用section做单位,与FTL操作单元对齐,减少不必要的数据拷贝。 12 | 13 | -Cost-effective index structure:解决了wandering tree的问题。 14 | 15 | -multi-head logging:有多个log区域,有助于数据冷热分离。 16 | 17 | -adaptive logging:使用append-only 18 | 19 | logging让随机写变顺序写,在内存利用率较高的时候,进行thread logging。 20 | 21 | -cleaning:Foreground cleaning(空间不足自动触发)background cleaning(周期执行) 22 | 23 | 选择vitcim(greed(FC),cost-benefit(BC))->有效块的确认和迁移(通过SIT确认有效块,然后把块放到page cache里面标记为dirty,不会发送I/O)->发送清理进程(victim section被标记为pre-free section,checkpoint做了后,标记为free) 24 | 25 | -Fsync acceleration with roll-forward recovery:对small writes进行优化,降低fsync的延迟(使用specicial降低checkpoint开销) 26 | 27 | #checkpoint怎么做 28 | 29 | 1. flush所有在page cache中的脏节点和目录项 30 | 31 | 2. 挂起写活动,包括系统调用如create,unlinkandmkdir; 32 | 33 | 3. 文件系统的元数据,NAT,SIT和SSA被写入磁盘中特定区域 34 | 35 | 4. 写一个包括以下信息的checkpoint pack到CP区域(Header and footer/NAT and SIT bitmaps/NAT and SIT journals/Summary blocks of active segments/Orphan blocks) 36 | 37 | #Wandering tree问题 38 | 39 | -wandering tree问题是log-structured文件系统(LFS)特有的一个问题,因为LFS的脏数据是追加更新的,所以如果一个数据块变脏了,那么那个数据块的直接索引块、间接索引块都会变脏(因为索引的地址变脏)。 40 | 41 | -在F2FS中使用NAT表来存储node id和实际物理地址的映射关系,这样一个数据块变脏的时候,只需要更新直接节点块和它的NAT表项。 42 | 43 | #Why does F2FS try to reducethe effects of random write? What techniques does it use? 44 | 45 | 1.随机写写入的性能差,因为块擦除时间在ms级; 46 | 47 | 2.不断的对同一Block块进行擦除操作,那么该块将会在短时间内磨损写坏,并且极易导致存储在该块上的数据丢失。 48 | 49 | Adaptive logging中的normal logging(append only logging),能将随机写变成顺序写 50 | 51 | #What is 'wandering tree'problem? How does F2FS mitigate it? 52 | 53 | 见上 54 | 55 | #What is the design rationale of multi-head logging? How does F2FS separate cold/hot data? 56 | 57 | F2FS维护六个主要日志区域来进行冷热数据的分离,冷热数据有三个温度(hot/warm/cold),目录项的node和data都是hot,文件直接节点和用户数据是warm,间接节点和cleanning清掉的数据/用户标记的冷数据/多媒体文件都是cold。通过预期的更新频率进行冷热数据的分离。 58 | 59 | ##roll-forward的问题,说一下自己的理解。 60 | 首先roll-forward recovery是为了解决经常写一些小数据,引发fsync操作,然后支持fsync操作的一个比较差的方法是做cp,然后回滚。因为cp会产生把所有节点和无关项之类的都写一遍。造成性能问题。 61 | 但是做cp和回滚肯定是要做的,为了改进性能,对一些小的写操作,F2FS在他们直接的节点块里面设立一个特殊的flag。这样就可以找到这个写操作。 62 | 在实际recovery操作中,F2FS收集在上一个有效cp后的对某个文件的写操作,可能是cp后的n个操作,记为N+n,N就是做cp的点。 63 | 然后找到在做这个cp之前的对这个文件的写操作,因为这个写操作做了cp,所以是一个稳定的写入磁盘的点。记为N-n。 64 | 比较cp前和cp后,N+n和N-n的不同,如果不同,使用N+n,刷新了N-n在cp的稳定存储,就可以跳过cp后一系列的操作,直接进行small write的更新,从而进行优化。 65 | 概括一下。。。。。。。。。。。。。。。。。。分割线 66 | 就是N+n是做了cp后这个数据更新的点,因为有特殊的flag,所以我们可以找到它,记为N+n。然后recovery的时候,使用cp,cp有个稳定的记录,能得到这个数据在cp之前的稳定的状态,记为N-n,进行对比,不同就使用最新的那个。 67 | 68 | 我个人的理解,有问题欢迎拍砖。最近很多人问这个问题,不过这个应该不是重点。。。 69 | 70 | -------------------------------------------------------------------------------- /3_system_software_for_persistent_memory_黎哲明.md: -------------------------------------------------------------------------------- 1 | #System Software for Persistent Memory ##助教提供的考点 2 | 1. NVM技术特性跟flash有什么不同 2. consistency重点【Copy on write/journaling/log-structured】 3. pm_w_barrier为什么引入 CoLFLUSH 4. reoder 3个指令引入原因,指令作用,相互关系 3 | ###概述: 4 | PMFS利用PM的byte-addressability来避免传统面相块的存储方式的overhead,使得上层应用可以直接访问PM。 5 | 6 | ### **一. NVM特性** 7 | ~~NVM是RAM而flash是ROM,~~
8 | NVM即非易失性存储器,所有在掉电后仍能保持其内容的内存组件都可以称为NVM,fast, byte-addressable, non-volatile
9 | NVM的读写速度比flash快,NVM是byte-addressable的而flash不是 10 | 11 | ### **二. consistency** 12 | ####保证一致性的三种方式: 13 | 1. copy-on-write: 先复制一个数据副本,在副本上进行修改,最后将指针指向新数据 14 | 2. journaling(logging): 通过写日志记录操作,根据日志进行恢复,需要执行写日志和写文件两次操作 15 | 3. log-structured updates: 以日志的形式组织文件系统并执行更新 16 | 17 | ####journaling的两种方式: 18 | 1. redo: 新数据被写入日志中并持久化,直到事务成功提交才写入文件系统中。 19 | 2. undo: 旧数据被写入日志中并持久化,新数据直接写入文件系统中,若事务失败则通过重写日志中的旧数据来回滚。 20 | 3. 比较:undo实现简单,对事务中的每个log entry都需要一次pm_wbarrier;redo对一次事务操作只需要两次pm_wbarrier,但对事务中的所有读操作都带来额外的overhead。PMFS采用undo。 21 | 22 | ####PMFS采用的方式: 23 | 对小的metadata的更新采用atomic in-place and fin-grained logging, 而对于文件数据的更新则采用CoW。 24 |
25 | hybrid approach: atomic in-place updates and fine-grained logging for the (usually small) metadata updates, and CoW for file data updates。 26 |
27 | atomic in-place updates:in order to minimize journaling overhead[8/16/64-bytes] 28 | 29 | ### **三. 三条指令** 30 | 1. clflush: 将cache中修改后的数据写进memory system中(但不一定能写进nvm)flush the cacheline 31 | 2. sfence: 保证存储操作的完成 ensure the completion of store 32 | 3. pm_wbarrier: 保证数据一定能写进nvm中。 ensure the durability of every store to PM 33 |
34 | 引入pm_wbarrier是为了解决clflush不能保证一定将数据写入nvm的问题,三条指令共同保证durability 35 | 36 | ### **四. 补充** 37 | 1. Mmap: Mmap in PMFS maps file data directly into the application’s virtual address space,出于性能的考虑。 38 | 2. 解决reorder问题:将log entry的大小设置成与一个cacheline相同(64bytes),并且保证对同一个cacheline的写操作不会被reordered。 39 | 3. write protection: a). 利用SMAP,Prohibit writes into user area; b). PMFS write windows: mount as read-only, when writing CR0.WP is set to zero 40 | 41 | ### **五. 课前习题(个人理解,仅供参考)** 42 | 43 | --- 44 | > Why do memory reordering and cache play an important role in PMFS consistency? How does PMFS maintain consistency? 45 | 46 | 因为在PMFS中采用clflush将cpu cache中的内容写回,但为了提高性能不保证clflush的顺序,可能被reorder。而系统的recovery是依赖于写的顺序的,因此需要采用memory reordering也就是sfence这条指令来保证。cache重要是因为PMFS保证consistency的log都是先记录在cache中然后才将其持久化的。 47 | 48 | PMFS采用保证一致性的方式是对metadata使用logging,而对data使用Cow。 49 | 50 | --- 51 | 52 | > There are three techniques to support consistency: copy-on-write, journaling and log-structured updates. Please explain the differences among them. Which technique does PMFS choose? 53 | 54 | 1. cow: 先复制一个数据副本,在副本上进行修改,最后将指针指向新数据 55 | 2. journaling: 通过写日志记录操作,根据日志进行恢复 56 | 3. log-structrued updates: 以日志的形式组织文件系统并执行更新 57 |
58 | cow即使在做很小的更新的情况下可能也需要先复制一个很大的数据副本造成额外的overhead;journaling需要执行两次写操作,一次写日志,一次写文件;log-structrued fs在log的末尾追加数据(顺序写),写操作高效但随机读的效率低。 59 |
60 | PMFS采用的方式是对小的metadata的更新采用atomic in-place and fin-grained logging, 而对于文件数据的更新则采用CoW。 61 | 62 | --- 63 | > What granularity does PMFS use for log? Please explain the reason. 64 | 65 | PMFS中log entry的大小和采用和一个cacheline一样的64 byte 66 | 67 | 因为利用两次pm_wbarrier或者checksum的方式来保证PMFS-Log有效的方式代价太大,作者将log entry的大小设置成与一个cacheline相同,并且利用cacheline中保证写顺序不被reorder的特性来保证log的validate。 68 | 69 | --- -------------------------------------------------------------------------------- /4-RDDspark_cuiyi.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #RDD / Spark 4 | 5 | 6 | ##现有模式存在的问题: 7 | 在Spark之前,很多大数据的计算架构效率比较低,尤其是对于需要迭代计算和交互计算的应用,因为之前的架构不是in-memory computing的,而很多大数据的算法都需要重用中间结果,在中间结果的基础上进行计算,所以处理这类问题的时候,之前的解决方只能是向外部的storage输出计算的中间结果,这就因为disk I/O的限制,导致性能的问题,虽然也有很多框架着手解决这一问题,但没法高效地解决诸如faultTolerance的问题 8 | 9 | ##本文的解决的方案: 10 | 提出了Resilient Distributed Dataset,以及基于RDD计算模型的Spark。可以在大规模的计算集群上面运行in-memory computing的算法,和基于coarse-grained transformation。 11 | 12 | 13 | ###RDD的概念: 14 | RDD是一个数据模型,可以把RDD理解成Spark当中的一种数据结构,Spark当中的操作都是对于RDD这种数据结构的操作 15 | 16 | ###RDD的特点 17 | 18 | #####1. RDD是只读的,分区的记录集合。 19 | 一个RDD只能从另一个RDD或者源数据修改过来,而不能对一个RDD进行修改 20 | 通过并行转换的方式来创建(如map, filter, join) 21 | #####2. RDD支持两种操作: 转换和动作 22 | 转换( transformation ) 从现有的数据集创建一个新的数据集; 动作(actions)是在数据集上运行计算后,返回一个值给驱动程序。Spark中的所有转换都是惰性的,也就是说,他们并不会直接计算结果。相反的,它们只是记住应用到基础数据集(例如一个文件)上的这些转换动作。只有当发生一个要求返回结果给Driver的动作时,这些转换才会真正运行。 23 | 24 | ###RDD的优点 25 | ####1.In-memory computing 26 | 数据集的全部或者部分可以缓存在内存中,以便在迭代计算的时候可以重复利用,减少了I/O的开销,当内存不够时,可以与磁盘进行交换。用户可以显式地指定哪一部分分区的数据可以缓存在内存中,以及内存不够时缓存这些分区的优先级。RDD只能通过对稳定物理存储中的数据或者其他已有的RDD执行一些确定性操作来创建。这些操作被称作transformation,常见的transformation包括map, filter,join等。而且,用户可以显式地指定RDD的存储方式和分区方式(比如指定哪个RDD会被重用,用persist指令让它保留在内存中)决定RDD中的元素按照某个key来划分,及其划分的方式等。 27 | ####2.Fault-Tolerance 28 | 一个RDD有足够的信息(lineage)关于它自身是如何从其他数据集或其他RDD衍生而来的,使用这些信息,利用稳定存储中的数据可以再次计算出这个RDD中的分区。Spark可以根据lineage的信息重建丢失的RDD分区,而不需在各个结点之间拷贝大量的数据副本,没有checkpoint的开销。 29 | ####3.Straggler Mitigation 30 | 因为产生的RDD都是只读的,要做出修改只能新产生RDD,所以对于那些运算的比较慢的节点,可以并行地进行计算避免被拖延,而像DSM这种计算模型就只能等待 31 | ####4.Data Locality and Graceful Degration 32 | 系统可以对RDD进行划分和安排,这就可以利用计算几点的locality来提高计算性能;在内存不够的时候,那些大的、没有被特别指定的RDD会被降级,写到硬盘里面,这个时候性能会有所下降但不会低于Mapreduce 33 | 34 | ###RDD(Spark)提供的接口: 35 | 36 | ####1. transformation: 37 | 这些操作创建一个RDD或者转换出一个新的RDD 38 | - map: 39 | 对RDD中的每个元素都执行一个指定的函数来产生一个新的RDD 40 | - filter: 41 | 对之前的RDD进行筛选 42 | - flatMap: 43 | 与map类似,区别是经map处理后只能生成一个元素,而经flatmap处理后可生成多个元素来构建新RDD 44 | 45 | ####2. action: 46 | 这些操作给应用程序返回一个结果或者向存储系统中写入数据 47 | - reduce: 48 | 将RDD中元素两两传递给输入函数,同时产生一个新的值,新产生的值与RDD中下一个元素再被传递给输入函数直到最后只有一个值为止 49 | - count: 50 | 返回数据集中元素的个数 51 | - collect: 52 | 返回元素本身 53 | - save: 54 | 向存储系统写入数据集 55 | - persist: 56 | 指定以后要复用的RDD,spark默认将要复用的RDD放在内存中 57 | Spark中RDD的内部接口: 58 | - partitions(): 59 | 返回一组Partition对象 60 | - preferredLocations(p): 61 | 根据数据存放的位置,返回分区p在哪些节点访问更快 62 | - dependencies(): 63 | 返回一组依赖 64 | - iterator(p, parentIters): 65 | 按照父分区的迭代器,逐个计算分区p的元素 66 | - partitioner(): 67 | 返回RDD是否被hash/range分区的元数据信息 68 | 69 | 70 | ###论文中代码的执行: 71 | 72 | ####一、 73 | 74 | ![](img/4_code1.1.png) 75 | 76 | ![](img/4_code1.2.png) 77 | 78 | 第一行定义了一个由HDFS保存的RDD 79 | 第二行将第一行产生的RDD过滤掉不必要的信息,保留以ERROR开头的信息,产生新的RDD 80 | 第三行将error RDD保留在内存中,以便于它可以被跨查询共享。Spark会将error的分区保存在内存中 81 | 然后对error RDD再次过滤出包含"HDFS"的数据项,然后筛选出以制表符分隔的第三个字段。Spark调度器会将这些transformation指令发送给缓存着errors分区的结点。最后将结果记录返回。 82 | 83 | ####二、逻辑回归 84 | 85 | ![](img/4_code2.png) 86 | 87 | 将文本文件的每一行做map转换解析成Point对象,然后将所得的points RDD保留在内存中。 88 | 随机生成一个向量赋给w 89 | 在缓存有points RDD的结点中反复调用map和reduce转换,在每一步的迭代中利用w的函数计算gradient的值。然后将w与gradient的值相减得到新的w,带入下一次迭代。迭代多次后,w会收敛,得到最终的结果。 90 | 91 | ####三、PageRank 92 | 93 | ![](img/4_code3.png) 94 | 95 | 首先将图分解成url和它所指向的链接的对组成的RDD links,然后将这个RDD缓存在内存中 96 | 随机初始化一个url和它所对应的rank值组成的RDD ranks。 97 | 构建一个contribs RDD,该RDD包含了url以及指向它的url对其rank值所做的贡献。在每一步的迭代中都用links和当前ranks的值更新contribs的值,然后再用计算得到的contribs的值更新ranks的值,然后进行下一次迭代。迭代多次后,ranks的值会收敛。每一步迭代都会更新ranks的值,因此为了减少错误恢复的时间,用户可以在迭代一定次数后将ranks的值写入到磁盘做备份,这样以来当ranks的分区丢失时,就不需要从头开始迭代计算了。此外,可以人为地将links RDD根据URL在结点之间进行分区,然后将ranks按照同样的方式进行分区,这样以来在join的时候就不需要跨结点进行通讯了。Spark将每个url当前的贡献值发送到它的link lists所在的机器结点上,在那些结点机器上计算对应的URL的新的rank值,然后再与其link lists做join,依此类推。迭代多次后ranks值会收敛。 98 | 99 | ###系统流程 100 | 101 | 102 | ![](img/4_system_2.jpg) 103 | - 有两种dependency,每个parent RDD最多被一个child RDD用到,叫做narrow dependency;会被多个RDD用到,叫做wide dependency 104 | 105 | ![](img/4_system_3.jpg) 106 | - 每个stage包含尽可能多的narrow dependency,构成一个stage,stage之间的关系构成一个有向无环图,系统会对这个DAG进行优化 107 | 108 | ![](img/4_system_1.jpg) 109 | - 不同的task被TaskScheduler进行分配,给不同的节点进行执行 110 | 111 | ##作业题: 112 | 113 | 114 | ###1.What are the advantages of Spark compared to MapReduce? 115 | 1. 性能:MapReduce在使用Map和Reduce计算完成之后,必须要写到磁盘上等待下一个MapReduce计算,带来大量的磁盘读写而降低性能;而Spark在将数据读入内存中后可以一直在内存中处理数据;Spark也实现了很多的优化,对于很多应用来说,Spark比MapReduce效率要高。 116 | 2. 易用性:使用Mapreduce的时候需要将原来的算法转化为Map和Reduce操作,Job之间的依赖关系由开发者自己管理,相比之下增加了编程的难度,尤其是很多问题并不适合这样来解决。而Spark提供多种接口,可以自动处理Stage之间的关系,使得编程更加简单。 117 | 118 | 119 | ###2.Describe the pros and cons of lineage and checkpoint? 120 | 121 | - lineage的优点: 122 | 一个RDD通过lineage记录它如何从其他RDD转化而来,如果一个RDD出错,就可以通过lineage的链进行还原。而且因为lineage记录的是数据之间的变化而不是数据本身,所以在不同的节点之间传输数据速度更快。 123 | - lineage的缺点: 124 | 如果有一个任务计算时间需要很长,而中间发生错误,如果使用lineage的方法的话需要从头开始进行计算,额外开销会比较大。 125 | - Checkpoint优点: 126 | 可以很容易地进行recover,与使用lineage进行恢复相比,而使用checkpoint就可以直接恢复到之前的某一个状态 127 | - Checkpoint的缺点: 128 | 占用额外的储存空间,如果没有及时做checkpoint的话会丢失数据。 129 | 130 | ###3.Describe which applications are RDD suitable for and not suitable for? 131 | 132 | - RDD适合计算时对内存需要比较小的,需要进行迭代计算的应用。尤其适合应对批处理命令比较多的应用,在对同样的数据集进行相同的操作的情况下优势会比较的明显。 133 | - RDD不适合对内存需求比较大的,需要不断从存储器读取数据的应用,尤其是那些需要异步地,细粒度地修改共享数据的应用,会显著地降低计算的效率。 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /5_ramcloud_李然.md: -------------------------------------------------------------------------------- 1 | #RAMCloud(本人能力有限,如有错误请及时指出 wechat:elso002) 2 | ##助教说的考点 3 | 4 | * 优缺点? 5 | * 它的Recovery为什么是快的? 6 | * 应该用多大的Segment进行配置,论文是怎么evaluation的? 7 | 8 | ##课前问题(后面解答) 9 | * RAMCloud为什么使用log-structured策略? 10 | * RAMCloud用了什么策略是放置Segment replicas,并且它在恢复的时候是怎么找到Segment relica的? 11 | * RAMCloud是否支持随机访问? 12 | 13 | ##Motivation & Intro. 14 | DRAM常被用作数据的缓存,但是Cache miss和backing store overhead都让DRAM的潜能无法激发出来。 15 | 16 | -->因此论文作者设计了RAMCloud,它把所有的数据都置于DRAM中(这样不会出现Cache miss)。 17 | 18 | RAMCloud的核心就是提供`durability & availability` 19 | 20 | RAMCloud有一下几个特性: 21 | 22 | * 利用scale: RAMCloud利用系统的大规模(large scale)来提供快速高效的错误恢复 23 | * Log-structured storage: log-structured不仅用在了disk,也用在了DRAM。它为系统的提供了较高的性能 24 | * 随机化(Randomization): RAMCloud使用随机化的策略来进行一些决策 25 | * Tablet profiling: 为了实现快速恢复,它帮助划分服务器的数据到partition中 26 | 27 | ##数据模型 28 | 典型的key-value存储 --> 在RAMCloud中,数据被已表(table)的方式组织起来,表内存储的是大量的对象(object)。 29 | 30 | 31 | RAMCloud管理replica是通过,在中DRAM只保存每个object的一个备份,而大量的冗余备份是位于二级存储中的(如disk或flash)。 32 | 通过这种方式备份会带来两个问题 33 | 34 | * 将数据放入disk,必须要同步地对disk写入-->非常慢! 35 | * 在宕机恢复的过程中,由于需要从二级存储读取数据来重构-->还是慢! 36 | 37 | 既然把数据备份在二级存储中慢,那备份到DRAM中呢?这就需要DRAM不能断电,而且会产生高昂的费用! 38 | 因此,RAMCloud还是选择把数据备份到二级存储中。 39 | 40 | ##Log-structured storage 41 | 上面提到了RAMCloud管理replica的策略,并且产生了两个问题(disk写入慢和恢复慢的问题)。为了解决这两个问题,RAMCloud采用了带buffer 的log-structured的方式解决了disk写入慢的问题,而恢复慢的问题利用了scaling解决,后面会仔细提到。 42 | 43 | 如上面图片所示,RAMCloud的存储的过程是当一个master接收到写请求,它将要写入的object append到内存里的log中,然后再将log entry通过网络分发到各个backup去。backup将接收到的信息buffer到内存中,然后立即向master返回一个RPC请求,而不需要立即写入到disk。master接收到backup的请求之后,向client返回。对于backup,在buffer满了之后才将buffer的数据写入disk。 44 | 45 | 可是考虑到一点,log是被buffer到backup的DRAM中的,那么一旦断电,就会导致数据的丢失---->为了解决这个问题,RAMCloud采用DIMM的方式,加入电池来保证buffer可以flush到disk上去。 46 | 47 | 注意到Hash table,hash table在RAMCloud是以做key来定位内存中log的位置,在每次appen log的时候,都会在hash table建立索引--->hash table支持了对数据的随机访问 48 | 49 | 那么log-structured logging有如下特点: 50 | 51 | * 由于backup是采用buffering的方式存储log(这是异步的、批量化的),这样避免了对disk的同步地写 52 | * 只有在backup的buffer满了之后,才将数据flush到disk上,这样做就不会浪费大量的时间在等待磁盘写入 53 | * log的结构是一致的,这句话的意思就是disk和DRAM的log结构一致,因此不需要额外的管理开销 54 | * 使用了hash table实现了数据的随机访问 55 | 56 | 综上,log-structure的实现解决了RAMCloud管理replica的策略带来的第一个问题,并且它有***优点***在于充分利用sequential I/O带宽,避免了高延迟。而***缺点***在于,需要定期地对log进行cleaning操作。 57 | 58 | ##Recovery 59 | 60 | ###Using scaling 61 | 在论文中,argue了三种方法,分别是disk、CPU带来的bottleneck等等。 62 | 最Naive的想法就是,一台机器宕机之后,立刻从多个backup读取信息重新构建crash的节点。可是如果backup的数目过少,磁盘带宽就成为了瓶颈。那么,在上文中提到,RAMCloud充分利用了scale来实现快速recovery,既然backup数目少会让磁盘带宽成为瓶颈,那么增加backup势必会减轻disk带来的瓶颈。增加backup的数量之后,大量的数据通过网络抵达master节点,然后master节点再来replay。可是,当数据量变大之后,master的CPU性能就变成了瓶颈,这样也同样限制了master的网络接口。 63 | 64 | 而为了解决上述问题,RAMCloud采用了`Partitioned Recovery`策略,来进行错误恢复。思路也十分简单,RAMCloud将crashed master的object分割到若干recovery master上,让它们分开处理。并且,这些recovery master把这些数据整合到自己的log和hash table中。这样就解决了CPU/disk/network对性能带来的瓶颈,它的核心方法就是利用了系统的scaling。而这种方法的缺点就是需要与所有的host进行通信。 65 | 66 | ###Scatter Log segments 67 | 68 | 考虑到backup的热度,所处网络带宽,磁盘读写速度等问题,为了保证segment的放置尽可能的平均,RAMCloud采用了随机化(Randomization)和微调(refinement)相结合的方式来决定log segment的放置问题。它首先随机产生一个list,然后从总挑出一个最佳候选(根据磁盘读写速度等各个因素)。使用随机化,避免了多台master选择相同backup的情况,而随机化仍然会带来分配不均匀的情况,所以就需要微调来去解决这种情况。 69 | 70 | 71 | ###(后面的也没做笔记 -。- 大家看看论文自己理解吧) 72 | 73 | 对Recovery的总结: 74 | 75 | * 利用scale去获得高可用性 --> 分散写来克服disk的bottleneck/分散rebuilding来克服CPU和网络的bottleneck 76 | * Scale驱使了更低的latency 77 | 78 | 对于课前问题,1)在***Log-structured storage***这一章节已经给出答案;2)为啥是快的,因为他用了scaling啊(摊手) (大家自己总结吧 3)随机化,在log cleaning的时候用到了,在scatter log segment的时候用到了,还有在错误检测的时候用到了(错误检测的时候,RAMCloud需要定期的知道哪些节点故障了,它就随机地去ping一些机器)(这个我没写 79 | 80 | 81 | ##Evaluation 82 | 83 | 对于助教说的应该用多大的segment进行配置,臣妾没找到啊 :( 84 | evaluation部分只说了partition大小的选取,过大的object在恢复的时候受到网络速度的限制,过小的object在恢复的时候受到更新hash table和tablet profile的限制。 85 | 86 | 对于segment的,论文在2.5节说到segment定位8MB大小。在evaluation部分,论文在论证系统可扩展性的时候,说道控制segment很小保证它全部都在内存中,然后partition的大小对scalability的影响(类似于控制变量法)。可以没有说对segment大小的evaluation - - (可能我没找到,找到的同学一定要告诉我 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /6_Scalable-lock_李智.md: -------------------------------------------------------------------------------- 1 | ## 提出的新技术、为什么要提出、解决什么问题、优缺点是什么、challenge、技术细节 2 | 这篇文章就是提出了non-scalable lock是不好的,提出要用scalable lock提到前者,没有新技术,之前两者都是有的。 3 | 4 | 原因是non-scalable lock哪怕在N个核的时候表现不错,也很有可能在N+1或者N+2个核的时候突然collapse。 5 | 6 | scalable lock解决的就是这个问题,优点就是不会产生这种突然的大幅度的collapse,锁的性能不会随着核的数量增加而下降太大。缺点就是要对现有的源代码做改动,但是改动并不复杂。 7 | 8 | 技术细节见第二个课后题。 9 | 10 | ## 助教提供的复习点:为什么non-scalable lock是dangerous的?性能为什么会突然下降? 11 | dangerous在上面已经说过了,为什么性能突然下降,见第一个课后题。 12 | 13 | ## 课后习题 14 | - **Why does the performance of ticket lock collapse with a small number of cores? (Hint: cache coherence protocol)**
15 | 16 | 这是因为在ticket lock中,会需要记录两个变量,一个是now_serving表示正在使用lock的ticket(一个整数),另一个是next_ticket记录着当前最后一张ticket(就是拿票等待的核的号码)。当任何一个核去拿锁的时候,都会需要读取now_serving这个变量判断跟自己的ticket是否相等。这样一来,每个核都会对now_serving做cache,一旦这个锁被释放,ticket lock中的now_serving就会增加1,这个操作会invalidate所有核的cache里的now_serving,这会触发所有的核来重新读取now_serving这个值所在的cacheline,论文说明了在现有的架构中,这个read会被串行化处理,一个一个来,这就导致消耗的时间与等待锁的核的数量呈线性增长关系。 17 | 18 | 至于为什么会突然发生collapse,大家可以参看3.4的第一个implication。我觉得是说,当很多核想得到同一个锁,并且进入了contend状态之后,一个锁从一个核转移到另一个核的时间是与等待锁的核的数量呈线性关系的,但是这个时间会极大地增加串行部分代码的长度(the length of the serial section),所以当某个锁积累到一定量的waiter,就会突然collapse。 19 | 20 | - **To mitigate the performance collapse problem of ticket lock, we can replace it with MCS lock. Please describe the MSC lock algorithm in C.**
21 | 22 | mcs_node{ 23 | 24 | mcs_node next; 25 | int is_locked; 26 | } 27 | 28 | mcs_lock{ 29 | 30 | mcs_node queue; 31 | } 32 | 33 | function Lock(mcs_lock lock, mcs_node my_node){ 34 | 35 | my_node.next = NULL; 36 | mcs_node predecessor = fetch_and_store(lock.queue, my_node); 37 | //if its null, we now own the lock and can leave, else.... 38 | if (predecessor != NULL){ 39 | my_node.is_locked = true; 40 | //when the predecessor unlocks, it will give us the lock 41 | predecessor.next = my_node; 42 | while(my_node.is_locked){} 43 | } 44 | 45 | function UnLock(mcs_lock lock, mcs_node my_node){ 46 | 47 | //is there anyone to give the lock to? 48 | if (my_node.next == NULL){ 49 | //need to make sure there wasn't a race here 50 | if (compare_and_swap(lock.queue, my_node, NULL)){ 51 | return; 52 | } 53 | else{ 54 | //someone has executed fetch_and_store but not set our next field 55 | while(my_node.next==NULL){} 56 | } 57 | } 58 | //if we made it here, there is someone waiting, so lets wake them up 59 | my_node.next.is_locked=false; 60 | } 61 | 62 | - **Why does MCS lock have better scalability than ticket lock?**
63 | 64 | 从上面的代码其实可以很快地找到答案,在ticket lock中是所有的核都去读一个变量来判断自己是不是能够拿到锁,但是MCS不用,MCS是当一个正在使用锁的核把锁放掉之后,它会主动检查是不是有核在等这把锁,如果有会去通知这个核,代码上就是前一个核会去修改后一个核的is_locked变量,后一个核会一直spin在这个变量上,当这个变量被修改,它就知道自己能够拿到这把锁了。 65 | 所以,每个核都spin在自己的is_locked变量上,而不是全局去读某一个变量,这样就会有更好的scalability。 66 | -------------------------------------------------------------------------------- /7_PowerGraph_李宁_赵志勇.md: -------------------------------------------------------------------------------- 1 | #PowerGraph: Distributed Graph-Parallel Computation on Natural Graphs 2 | 3 | ##助教提供的考点 4 | 1. 图计算遇到的问题,演变过程(可结合PPT) 5 | 2. GAS模型 6 | 3. greedy vs random 划分比较 7 | 4. **课后题(重要)** 8 | 9 | ##概要(包含考点的解答) 10 | 图编码了关系,边和节点信息可以用来存储现实世界中的各种关系。 11 | **发展过程**:在机器学习和数据挖掘中,存在大规模的图结构的计算,随着数据集的增长,使用现有的计算框架无法解决这个问题,单台机器也不堪重负,因此提出来新型的graph-parallel计算框架,以Pregel 与GrpahLab为代表,把计算任务编码成节点程序,让节点之间并行运行这些程序,通过边进行交互。 这种框架的设定是每个节点都有少量的比较均等的邻居节点,使用edge-cut将节点平均分配到各台机器上,这样可以最大化并行效率,减少通讯开销。 12 | 但是现实世界中的图并没有这么理想,存在power-law degree distribution现象,如果采用按照edge-cut的划分方式,度数高的节点和度数低的同等对待,就会带来负载、存储、通信、计算等不均衡的问题,图计算的效率也就会大大下降。为解决这个问题,本文提出了powergraph 这个系统。 13 | 14 | ####问题: 15 | 自然图highly skewed,有power-law degree distribution现象(少数节点拥有极大多数边,大多数节点只有少量边),目前的按边划分(edge-cut)的质量差,使得对自然图的计算存在以下挑战: 16 | * work imbalance(gather、scatter的工作量与degree数量成正比), 17 | * partition(目前直接hash,随机划分节点,poor locality,高度数节点与低度数节点被同样地划分,不合理), 18 | * 通信开销不平衡(度数高的节点所在机器需要较多的通信); 19 | * 存储不均衡,一个节点的所有邻边信息可能超过机器容量; 20 | * computation(之前的计算模式是多个节点之间可以并行,单个节点内无法并行,对于高度数节点来说可扩展性不强) 21 | 22 | 图并行化抽象流行的两种方式: 23 | 24 | -- 使用消息 Pregel 25 | 26 | Pregel向单个worker发送大量消息给邻居节点,成本较高。同步执行但容易产生straggler,straggler可以理解为执行比较慢的节点。 27 | 28 | -- 共享状态 GraphLab 29 | 30 | GraphLab共享状态时异步执行,需要大量锁。会触到图的大部分,并且对于单台机器边的元数据太大 31 | 32 | --> 33 | GraphLab和Pregel是不适合处理natural graphs 34 | 主要的两大挑战是高度数的点和低质量的分区策略。 35 | 36 | PowerGraph的贡献: 37 | * 第一,采用"think like a vertex"思想,提出GAS模型分解单个节点的vertex-program,使节点内程序并行化,对高度数节点非常有利 38 | * 第二,采用点切分(vertex-cut)策略,将高度数节点分配到多台机器上,来保证整个集群的均衡性,该策略对大量密率图分区是非常高效 39 | 40 | #### GAS模型 41 | 这是paper提出的一种图计算程序的三个概念上的阶段: 42 | * gather,收集计算所需的邻接节点和边的信息 43 | * apply,将结果作用于中心节点 44 | * scatter,分发新的中心节点的值给各条邻边 45 | 46 | 47 | #### 划分方式 48 | * edge-cut 通常通过对每个节点编号进行hash,把节点平均分配到各台机器,有很多边被切分开,所以在对应机器上会重新构建这些边,对于边的数量较少的节点采用这种方式并没有什么坏处,但对于度数很高的节点,重构边的工作量就会很大,造成负载不均衡 49 | 50 | 例子:pregel(synchronous bulk message passing) graphLab asynchronous shared memory 51 | 52 | 53 | * vertex-cut 将节点切分成多个mirror节点,每个mirror节点负责处理一部分的边,这样边就被平均分配到各台机器上,度数高的节点的边被平均分布在多台机器上,每台机器的负载相对均衡 54 | 55 | 例子:powergraph (balanced p-way vertex-cut) 56 | 57 | 58 | PowerGraph使用的不是边切分,边切分前面已经提到会同步大量的边的信息。而是采用点切分,点切分只要同步一个点的节点信息。 59 | 60 | #### random vs greedy 61 | * random placement 优点:可并行、分布式地执行,高效; 缺点:replica数量较多,通信开销大 62 | * greedy placement replica数量少,需要协调各程序,复杂,ingress(构建划分)时间较长、最小化机器所跨的机器数目 63 | 64 | #####greedy placement的两种实现方式: 65 | * coordinated greedy placement:速度慢质量高 少replication factor, 高runtime (维护一张全局u顶点放置的历史纪录表,在执行贪心切分之前都要去查询这张表,在执行的过程中需要更新这张表) 66 | * oblivious greedy placement: 速度快质量稍低 优点兼而有之,独立运行,无额外通信开销,相对较少的replica,较少的运行时间(每台机器自己维护这张表,不需要做机器间的通信) 67 | 68 | 总结: 69 | 协同的贪婪分区:机器跨度最小,但构建时间最长。 70 | 随机策略:构建时间短,但平局的机器跨度最大。 71 | Oblivious的贪婪分区策略:折中 72 | ##课后题解答 73 | 74 | 75 | 1.1. How skewed degree distribution challenges the original graph parallel computation? Give a brief summary of the computation procedure and then analysis the challenges. 76 | 自然图highly skewed,有power-law degree distribution现象(少数节点拥有极大多数边,大多数节点只有少量边),此外目前的按边划分的质量差导致 77 | * work imbalance(gather、scatter与degree数量成正比), 78 | * partition(目前直接hash,随机划分节点,poor locality,高度数节点与低度数节点被同样地划分,不合理), 79 | * 通信开销不平衡(度数高的节点所在机器需要较多的通信); 80 | * 存储不均衡,一个节点的所有邻边信息可能超过机器容量; 81 | * computation(之前的计算模式是多个节点之间可以并行,单个节点内无法并行,对于高度数节点来说可扩展性不强) 82 | 83 | PowerGraph提出了自己的一套计算模型,叫GAS分解。G是Gather的意思,A是Apply的意思,S是Scatter的意思。 84 | GAS分解过程如下, 85 | Gather:收集邻居信息 先收集同一台机器的信息,然后对不同主机收集的信息进行汇总。得到最后的求和信息。 86 | Apply:用收集到的信息来更新中心节点的信息 87 | Scatter(分散):更新邻居点和边,触发邻居点进行下一轮迭代。 88 | 89 | 2.2. In your opinion, what are the advantages of graph parallel computation comparing with traditional data parallel processing (e.g. map-reduce)? 90 | 91 | 随着数据集的增长,复杂的数据计算模型和存储已经达到单个机器的极限,图计算可以提高并行性且降低网络通信和存储成本 92 | graph-parallel computation是对图进行专门优化的计算模式,它的并行计算是以节点为单位,各节点同时运行自己的vertex-program,达到最终计算目的,单个节点的程序相对比较简单,而且比较独立,所以并行程度高,计算速度远远快于同一功能的data-parallel程序。 93 | Map-Reduce每轮迭代需要传递整张拓扑图作参数,Graph-parallel只需将状态告诉邻接节点即可,每轮迭代之间无需传送整张拓扑图。 94 | Map-reduce需要写额外程序检测fix-point(循环次数终止条件) 95 | Map-Reduce中间结果是存储在磁盘中,后续处理还需读磁盘,速度受影响,图计算是在内存中完成,速度较快。 96 | 另外,现实世界中的很多问题更适合用图来描述,能够获得数据间更深层次的关系,如社交网络、自然语言的处理、广告精准投放等,建模过程更自然,更符合人类的思维习惯 97 | (感觉还有优势,欢迎补充) 98 | 99 | 3.3 Brief explain vertex-cut and G A S steps using following small graph (each src-dst pair is a directed edge of the graph). Assume we have 3 nodes, and hash (vertex)=vertex%3, hash(src,dst)= (src+dst)%3. You may need to draw a graph. 100 | 101 | 0-1 0-2 0-3 102 | 0-4 1-3 1-4 103 | 2-4 2-5 3-5 104 | 105 | 使用点切割将6个vertices划分成3个nodes(0,1,2),并使用hash(src,dst)= (src+dst)%3将边划分到3个nodes 106 | 107 | ![alt text](/img/7-3-1.png "graph1") 108 | 109 | GAS steps: 110 | 不失一般性,选择vertex 0 in node 0 作为master(主节点), 其余mirrors(备份节点). 111 | * Gather: 112 | gather在每个机器上单独运行,即从本机上邻近的vertices收集信息(sum过程),收集完成后mirror将accumulator传输到master 113 | * Apply: 114 | master利用收集到的信息更新vertices的数据 115 | * Scatter 116 | master将更新后的数据传输到各个mirrors,然后各自并行传播到邻接节点,并更新邻边的数据,再进行下一次迭代。 117 | 118 | ![alt text](/img/7-3-2.png "result") 119 | 120 | 121 | 往年题目:Assume there are 3 machines of one cluster, and the partition functions are showed below. Please draw the edge-cut partition as in Graphlab and vertex-cut partition as in Powergraph respectively for the sample graph given below. (In your answer, you should differentiate the master from mirrors (or ghosts), and master is defined based on “getPartitionV” function.) 122 | ![alt text](/img/7-3-3.jpg "wangnian") 123 | 124 | 点划分(红点表示master) 125 | ![alt text](/img/7-3-4.png "vetex") 126 | 127 | 边划分 128 | ![alt text](/img/7-3-5.png "edge") 129 | -------------------------------------------------------------------------------- /8_TransactionalMemory_李阳德.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | # Transactional Memory/DB,Transactional memory: architectural support for lock-free data structures 8 | ## 助教提供的考点 9 | 10 | --- 11 | 12 | 1. 与传统锁的区别 13 | 2. 为什么要引入Transaction Memory 14 | 3. 具体的Transaction memory有哪些技术,__熟悉技术细节__ 15 | 16 | --- 17 | 18 | ## 关键科学问题 19 | 多核处理器越来越普及,如何利用硬件性能,更加高效的提升并行程序的性能成为难点。基于锁的同步机制存在以下问题: 20 | 21 | * 粗粒度的锁虽然易于使用,但是性能低下,锁竞争频繁,无关的操作会出现顺序执行的情况 22 | * 细粒度的锁虽然性能高,但是实现难度大,例如双端队列的并行版本使用细粒度的锁难以实现 23 | * 存在优先级反转(priority inversion)、护航(conveying)、死锁(dead lock)等问题 24 | * _优先级反转_ 一个高优先级任务间接被一个低优先级任务所抢先(preemtped),使得两个任务的相对优先级被倒置 25 | * _护航_ 多线程程序中相同优先级任务反复竞争同一个锁。当一个持有锁的进程被解调度了,可能是因为调度时间到了,发生了一个页错误,或者是其他一些终端。当有类似终端发生时,其他本该可以运行的进程却无法运行。 26 | * _死锁_ 两个或以上任务在执行过程中,由于竞争资源造成阻塞 27 | * 可组合型差。例如从一个链表移动一个元素到另一个链表时,必须使用低性能的粗粒度的锁来避免可能发生的死锁 28 | 29 | 30 | 31 | --- 32 | 33 | ## 解决方法 34 | 新的同步机制==> __无锁算法__、__无锁的数据机构__ 35 | 36 | 参考数据库系统中的事务的概念,引入事务的ACID概念。本篇论文介绍事务性内存--一种新的多核架构,使用无锁的同步机制并使其与传统锁机制下达到同样的性能并保持简单。 37 | 38 | 可以使用软件来模拟事务执行,也可以使用硬件来加速支持(硬件更加可靠?) 39 | 40 | * STM: Software Transactional Memory 41 | * HTM: Hardware Transactional Memory 42 | 43 | intel Haswell 使用了Restricted Transactional Memory (RTM),一种受限制的硬件事务内存实现。新增了三个 44 | 指令如下所示,但是work set是受限的,一些System event废弃了TX 45 | 46 | ``` 47 | Xbegin 48 | Xend 49 | Xabort 50 | 51 | ``` 52 | 53 | 54 | --- 55 | 56 | ### 技术细节 57 | 58 | 事务是有限的机器指令序列,被单个进程所执行,并满足有序性和原子两个特性。有序性意味着一个事务执行的步骤不会和另一个事务相交错。原子性指每个事务都会暂时的修改共享内存,当一个事务完成后,要么commit,使修改对其他进程可见;要么abort,废弃自己的修改,不存在其他的中间状态。 59 | 60 | 61 | 62 | #### 基本指令 63 | 64 | * 访问内存的基本指令 65 | 66 | ``` 67 | 68 | Load-transactional LT //从共享内存出读取值到私有寄存器 69 | 70 | Load-transactional exclusive LTX // 从共享内存出读取值到私有寄存器并标记 即将更新 71 | 72 | Store-transactional ST //将私有寄存器中的值写入共享内存中(write set),尚不对外可见 73 | 74 | 75 | ``` 76 | 77 | 一个事务的read set是指被LT指令读取的位置,它的write set指被 LTX 或者 ST 指令访问的位置。read和write组成了事务的data set. 78 | 79 | 80 | * 修改状态的基本指令 81 | 82 | ``` 83 | Commit(COMMIT) //尝试使事务所做的修改持久化,其他事务不再更新本事务的data set,没有别的事务读取过本事务的write set,即commit成功,并使该事务对于write set所做的修改对别的事务可见。 84 | 85 | Abort(ABORT) // 取消所有对于write set的临时修改。 86 | 87 | Validate(VALIDATE) // 测试当前事务的状态,返回true说明当前还没有abort执行,返回false说明当前事务已经被abort 88 | 89 | ``` 90 | 91 | 通过对以上指令的组合和使用,开发者可以实现自己的一套read-modify-write操作。本文所实现的transacnal memory访问指令与原有的指令并不冲突,也兼容原有的 `LOAD`、`STORE`指令。 92 | 93 | 94 | 95 | ### 基本原理 96 | 97 | * 具体执行流程 98 | 99 | ![img](/img/8_1.png) 100 | 101 | 1. 使用`LT`或者`LTX`指令从共享内存中读取数据 102 | 2. 使用`VALIDATE`指令确认读取数据的有效性 103 | 3. 使用`ST`指令来修改共享内存中的数据 104 | 4. 使用`COMMIT`指令使修改生效;如果`VALIDATE`或者`COMMIT`失败,返回步骤1 105 | 106 | ### 具体实现 107 | 108 | 本论文基于多核缓存一致性协议进行了扩展,实现对事务性内存的支持。具体的协议包括: 109 | 110 | * 共享总线(Snoopy cache) 111 | * 基于目录(directory) 112 | 113 | 两种结构的支持。任何具有冲突访问检测能力的协议都可以用来检测事务冲突,不需要带来额外的开销。所以本文的实现直接复用了原有的协议。 114 | 115 | __以共享总线结构的协议为例:__ 116 | 117 | #### 缓存 118 | 119 | 为了最小化对非事务性内存指令影响,每个处理器持有两种cache: 120 | 121 | * regular cache (非事务性内存) 122 | * transactional cache (事务性内存) 123 | 124 | 这两种cache是互斥的,一次访问只可能访问一种cache,而且这两种cache都是一级cache或者二级cache,可以被处理器直接访问。regular cache是传统的直接映射cache;transactional cache空间较小,完全相连的由额外的逻辑来实现事务的提交和废弃。事务cache存储所有临时写的副本,除非事务提交,否则缓存内容将不会传给其他处理器或内存。 125 | 126 | 遵循`Goodman`大神的指示,每个cache行的状态如下表所示 127 | 128 | | Name | Access | Shared? | Modified?| 129 | | :-------------: |:-------------:| :-----:|:-----:| 130 | | INVALID | none | | | 131 | | VALID | R | Yes | Yes | 132 | | DIRTY | R,W | No | Yes | 133 | | RESERVED | R,W | No | No | 134 | 135 | 136 | 但是这些状态不够描述事务性内存访问,所以我们扩充了一些状态来描述事务内存的执行状态 137 | 138 | | Name|Meaning| 139 | |:----:|:----:| 140 | |EMPTY| contains no data| 141 | |NORMAL| contains committed data| 142 | |XCOMMIT|discard on commit| 143 | |XABORT|discard on abort| 144 | 145 | ![img](/img/8_2.jpg) 146 | 147 | 事务性操作缓存有两个标志位,分别为`XCOMMIT`,`XABORT`。当事务提交时,会将`XCOMMIT`标记为`EMPTY`,将`XABORT`标记为`NORMAL`;当事务废弃时,会将`XABORT`标记为`EMPTY`,`XCOMMIT`标记为`NORMAL` 148 | 当事务性缓存需要空间时,首先搜索被标记为`EMPTY`的位置,之后再搜索被标记为`NORMAL`的位置,最后才是`XCOMMIT`的位置,由此来确定优先级,并且避免访问竞争资源提升性能。 149 | 150 | #### 总线 151 | 152 | 除了缓存状态,对于共享总线结构的协议来说,还有总线周期,具体见下表 153 | 154 | | Name | Kind | Meaning | New access| 155 | | :-------------: |:-------------:| :-----:|:-----:| 156 | | READ | regular | read value| shared | 157 | | RFO | regular | read value | exclusive | 158 | | WRITE | both | write back | exclusive | 159 | | T_READ | trans | read value | shared | 160 | | T_RFO | trans | read value | exclusive | 161 | | BUSY | trans | refuse access | unchanged | 162 | 163 | 以上的cycle中前三个是`Goodman`大大协议总已经定义过了的,本文扩展了三个周期,`T_READ`、`T_RFO`和`BUSY`,前两个就是对原有周期的简单扩展,`BUSY`周期是为了防止各个transation之间过于频繁的互相abort而设立的,当事务接收到BUSY回应后,会立即abort并retry,理论上防止了资源饥饿。 164 | 165 | #### 处理器 166 | 每个处理器持有两个状态标识位: 167 | 168 | ``` 169 | TACTIVE(transaction active)//是否有事务在运行 170 | TSTATUS(transaction status)//事务是否是active还是aborted 171 | 172 | ``` 173 | 174 | **注**:`TACTIVE`标识会在事务在执行第一次事务操作时被隐式(偷偷地)设定。 175 | 176 | --- 177 | 178 | 一个被标记为`TSTATUS`为TRUE的事务LT指令的具体操作流程: 179 | 180 | 1. 探测事务缓存中的`XABORT`入口,如果有则返回值 181 | 2. 如果没有但是有`NORMAL`入口,将`NORMAL`改为`XABORT`入口,使用相同的标记`XCOMMIT`和相同的数据再分配一个入口 182 | 3. 如果没有`XABORT`或`NORMAL`入口,进入`T_READ`周期,如果成功,我们会设置两个事务性内存的入口:`XCOMMIT`、`XABORT`。每个都满足之前的协议,并进入到`READ`周期 183 | 4. 如果收到`BUSY`信号,则abort事务 184 | 185 | 对于LTX指令,我们使用T_RFO周期取代T_READ,如果上一周期成功就将cache line的状态标记为RESERVED。ST指令类似LTX,只是更新了XABORT入口的数据。cache line的标记,LT、LTX指令类似LOAD,ST类似STORE。VALIDATE指令返回TSTATUS状态,如果返回false,则将TACTIVE标志指定为false,并将TSTATUS指定为ture。ABORT指令会忽略cache入口,将TSTATUS置为true,TACTIVE置为false。COMMIT指令会返回TSTATUS,设置TSTATUS为true,TACTIVE为false,放弃所有的XCOMMIT缓存入口,并将所有XABORT标志变为NORMAL。最后,遇到终端和缓存溢出则会abort当前事务 186 | 187 | 188 | 189 | --- 190 | 191 | 192 | ## 课后题解答 193 | 194 | --- 195 | 196 | > What is lock-free data structure? Why do we need lock-free data structure? 197 | > 什么是无锁的数据结构,为什么我们需要无锁的数据结构? 198 | 199 | 无锁的数据结构是一种能够`无需使用锁`就能支持`多重读写`操作的数据结构。使用无锁的数据结构我们能够避免大量因为维持全局锁操作所带来的额外开销。 200 | 201 | --- 202 | 203 | > What is orphan transaction? Why do we need VALIDATE instruction? 204 | > 什么是孤儿事务?为什么我们需要VALIDATE指令? 205 | 206 | __孤儿事务__ 是指在被废弃(aborted)之后继续执行的事务(例如,在另一个已经提交过的事务更新了自己的read set之后)**update:** 孤儿事务是指本身事务在执行时产生了错误(而非读写数据被其他事务提前修改)导致的abort,以至于这个事务会一个retry占据资源无法commit/rollback. 207 | 208 | 事务在执行过程中利用`VALIDATE`指令来确保自己所读取的值是正确的,防止读取过期的数据,即保证了数据的一致性。 209 | 210 | --- 211 | 212 | 213 | > Open question: What is the shortcoming of transactional memory compared with 214 | > conventional locking techniques? Do you have any suggestion to mitigate those 215 | > problems? 216 | > 开放问题: 事务性内存相比于传统锁技术有哪些缺点? 你对这些缺点有哪些解决方案? 217 | 218 | **局限性** 219 | 220 | * 事务性内存是基于事务具有短周期和小的数据集的假定。如果一个事务运行地越久,它就越有可能被中断或同步冲突所中止。同样地,数据集越大,其所需的事务性缓存也就越大,也就使得同步冲突发生的可能性越大。 221 | * 本实现过程不支持将冲突的事务提前处理,而是依赖于软件层面上的以加大冲突事务的间隔时间的方式来自适应的后移事务以减少事务的中止比例。 222 | * 本实验中的仿真所采用的缓存一致性协议提供了顺序一致性内存。但有一些研究者提出采用更加弱化的一致性协议以期达到更加高效的实现。如处理器一致性(Processor consistency)、弱一致性(week consistency)、释放一致性(release consistency)等等。 223 | 224 | **解决方案** 225 | 226 | * 为了支持更大更长的事务,可以采用更加复杂且精心设计的硬件机制; 227 | * 为了降低事务中止的比例,可以采用硬件上的排队机制; 228 | * 为了达到更加高效地实现,可以采用弱化的缓存一致性协议。如为了保障内存的顺序一致性,编程人员可以在每一个临界区的开始和结束时执行一个屏障指令(barrier instruction)。为提供事务性内存语义上的弱一致性内存,最直接的方式是使每一个事务性指令执行一个隐性障碍(implicit barrier)。 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /9_Bigtable_王英艺_张金石_姜德智.md: -------------------------------------------------------------------------------- 1 | #Bigtable: A Distributed Storage System for Structured Data 2 | 3 | ##助教提供的考点 4 | 1. Data model是什么 2. Bigtable的构件(Building Blocks)用了什么?——(GFS、SSTable)提供什么功能? 3. Refinement讲了什么 4. Bigtable的优点和limitation 5 | 6 | ##概要(包含考点的解答) 7 | ### 背景 1. 关系型数据库遵循ACID规则 2. NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。 3. (CAP theorem): 在计算机科学中, CAP定理(CAP theorem), 又被称作 布鲁尔定理(Brewer's theorem), 它指出对于一个分布式计算系统来说,不可能同时满足以下三点:
a) 一致性(Consistency) (所有节点在同一时间具有相同的数据)
b) 可用性(Availability) (保证每个请求不管成功或者失败都有响应)
c) 分隔容忍(Partition tolerance) (系统中任意信息的丢失或失败不会影响系统的继续运作)
4. CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,最多只能同时较好的满足两个。因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:
a) CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。 例子:RDBMS
b) CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。 例子:MogoDB、BigTabHBase、Redis
c) AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。 例子:CouchDB,DynamoDB、Riak
5. NoSQL的优点/缺点
a) 优点:
高可扩展性、分布式计算、低成本、架构的灵活性、半结构化数据、没有复杂的关系
b) 缺点:
没有标准化、有限的查询功能(到目前为止)、最终一致是不直观的程序 8 | ### Bigtable主要内容 9 | BigTable其实主要说了下面两个事情,其它的东西在助教的问题和课后习题有了,看完课这两点,再看看课后习题和助教的问题即可。
1.Table、Tablet、SStable的关系
这个其实在前面助教的问题里已经回答过了,但为了下面数据模型的理解,还是再简单说一下,Tablet是从Table中若干范围的行组成的一个相当于原Table的子表,所以多个Tablet就能组成一个Tablet.
而SSTable是Tablet在GFS文件系统中的持久化存储的形式,即我们平常说的Tablet,在Bigtable中,是存在一个一个SSTable格式的文件中的。 它们的关系可以由下图来总结: 10 | ![alt text](/img/9-1.png "graph1") 11 | 2.数据模型
Bigtable是一个用于存储稀疏的、分布式的、持久化存储的多维度排序Map,这个Map由行关键字、列关键字以及时间戳作为索引。即: (row:string, column:string,time:int64)->string 结合下面这张Webtable表的存储形式示意图,关于行、列关键字以及时间戳的说明如下: ![alt text](/img/9-2.png "graph2") 2.1 行
12 | 表中的行关键字可以是任意的字符串(目前支持最大64KB的字符串,但是对大多数用户,10-100个字节就足够了)。对同一个行关键字的读或者写操作都是原子的(不管读或者写这一行里多少个不同列),这个设计决策能够使用户很容易的理解程序在对同一个行进行并发更新操作时的行为。 Bigtable通过行关键字的字典顺序来组织数据。表中的每个行都可以动态分区。每个分区叫做一个"Tablet",Tablet是数据分布和负载均衡调整的最小单位。这样做的结果是,当操作只读取行中很少几列的数据时效率很高,通常只需要很少几次机器间的通信即可完成。用户可以通过选择合适的行关键字,在数据访问时有效利用数据的位置相关性,从而更好的利用这个特性。举例来说,在Webtable里,通过反转URL中主机名的方式,可以把同一个域名下的网页聚集起来组织成连续的行。具体来说,我们可以把maps.google.com/index.html的数据存放在关键字com.google.maps/index.html下。把相同的域中的网页存储在连续的区域可以让基于主机和域名的分析更加有效。 13 | 2.2 列族
14 | 列关键字组成的集合叫做“列族“,列族是访问控制的基本单位。存放在同一列族下的所有数据通常都属于同一个类型(我们可以把同一个列族下的数据压缩在一起)。列族在使用之前必须先创建,然后才能在列族中任何的列关键字下存放数据;列族创建后,其中的任何一个列关键字下都可以存放数据。根据我们的设计意图,一张表中的列族不能太多(最多几百个),并且列族在运行期间很少改变。与之相对应的,一张表可以有无限多个列。列关键字的命名语法如下:列族:限定词。 列族的名字必须是可打印的字符串,而限定词的名字可以是任意的字符串。比如,Webtable有个列族language,language列族用来存放撰写网页的语言。我们在language列族中只使用一个列关键字,用来存放每个网页的语言标识ID。Webtable中另一个有用的列族是anchor;这个列族的每一个列关键字代表一个锚链接,如图一所示。Anchor列族的限定词是引用该网页的站点名;Anchor列族每列的数据项存放的是链接文本。访问控制、磁盘和内存的使用统计都是在列族层面进行的。在我们的Webtable的例子中,上述的控制权限能帮助我们管理不同类型的应用:我们允许一些应用可以添加新的基本数据、一些应用可以读取基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因为隐私的原因不能浏览所有数据)。 15 | 2.3 时间戳
在Bigtable中,表的每一个数据项都可以包含同一份数据的不同版本;不同版本的数据通过时间戳来索引。Bigtable时间戳的类型是64位整型。Bigtable可以给时间戳赋值,用来表示精确到毫秒的“实时”时间;用户程序也可以给时间戳赋值。如果应用程序需要避免数据版本冲突,那么它必须自己生成具有唯一性的时间戳。数据项中,不同版本的数据按照时间戳倒序排序,即最新的数据排在最前面。为了减轻多个版本数据的管理负担,我们对每一个列族配有两个设置参数,Bigtable通过这两个参数可以对废弃版本的数据自动进行垃圾收集。用户可以指定只保存最后n个版本的数据,或者只保存“足够新”的版本的数据(比如,只保存最近7天的内容写入的数据)。 在Webtable的举例里,contents:列存储的时间戳信息是网络爬虫抓取一个页面的时间。上面提及的垃圾收集机制可以让我们只保留最近三个版本的网页数据。 16 | 17 | ### Data model是什么 18 | Bigtable的Data model是一个稀疏的、分布式的、持久化存储的多维度排序Map。Map由key和value组成:Map的索引key是由行关键字、列关键字以及时间戳组成;Map中的每个value都是一个未经解析的byte数组。即(row:string, column:string,time:int64)->string 19 | 20 | ### Bigtable的构件 21 | * GFS
22 | Bigtable使用Google分布式文件系统(GFS)存储日志和数据文件。(GFS提供Raw storage的功能?) 23 | * SSTable
24 | BigTable数据在内部使用SSTable文件格式存储。SSTable提供一个从键(key)到值(value)的持久化的、已排序、不可更改的映射(Map),对SSTable提供了如下操作:查询与一个指定key值相关的value,或者遍历指定key值范围内的所有键值对
25 | 从内部看,SSTable是一连串的数据块(通常每个块的大小是64KB,但是这个大小是可以配置的)。SSTable使用块索引(通常存储在SSTable 的最后)来定位数据块;在打开SSTable的时候,索引被加载到内存。一次查找可以通过一次磁盘搜索完成:首先执行二分查找在内存索引里找到合适数据块的位置,然后在从硬盘中读取合适的数据块。也可以选择把整个SSTable都映射到内存中,这样就可以在不用访问硬盘的情况下执行查询搜索了。 26 | * Scheduler
27 | BigTable集群往往运行在一个共享的机器池中,池中的机器还会运行其它各种各样的分布式应用程序,BigTable的进程经常要和其它应用的进程共享机器。BigTable依赖集群管理系统在共享机器上调度作业、管理资源、处理机器的故障、以及监视机器的状态。 28 | * 分布式锁服务Chubby
29 | 负责master的选举,保证在任意时间最多只有一个活动的Master;存储BigTable数据的引导程序的位置;发现tablet服务器,以及在Tablet服务器失效时进行善后;存储BigTable的模式信息(每张表的列族信息);以及存储访问控制列表。 30 | 31 | ### Refinement 32 | * 局部性群组(Locality groups)
33 | 客户程序可以将多个列族组合成一个局部性群族。对每个tablet中的每个局部性群组都会生成一个单独的SSTable。将通常不会一起访问的列族分割成单独的局部性群组使读取操作更高效。此外,可以以局部性群组为单位指定一些有用的调整参数。比如,可以把一个局部性群组设定为全部存储在内存中。设定为存入内存的局部性群组的SSTable依照惰性加载的策略装载进tablet服务器内存。加载完成之后,属于该局部性群组的列族的不用访问硬盘即可读取。 34 | * 压缩
35 | 客户程序可以控制一个局部性群组的SSTable是否压缩;用户指定的压缩格式应用到每个SSTable的块中(块的大小由局部性群组的调整参数操纵)。尽管为每个分别压缩浪费了少量空间,我们却受益于在只读取小部分数据SSTable的时候就不必解压整个文件了。许多客户程序使用双步(two-pass)定制压缩模式。第一步采用Bentley and McIlroy’s模式,这种模式横跨一个很大窗口压缩常见的长字符串;第二步采用快速压缩算法,即在一个16KB数据的小窗口中寻找重复数据。在Webtable的例子里,我们使用这种压缩方式来存储网页内容,在空间上达到了10:1的压缩比。 36 | * Bloom过滤器
37 | 一个读操作必须读取组成tablet状态的所有SSTable的数据。如果这些SSTable不在内存中,那么就需要多次访问硬盘。我们通过允许客户程序对特定局部性群组的SSTable指定Bloom过滤器,来减少硬盘访问的次数。通过bloom过滤器我们可以查询一个SSTable是否包含了特定行/列对的数据。对于某些应用程序,只使用了少量的tablet服务器内粗来存储Bloom过滤器,却大幅度减少了读操作需要的磁盘访问次数。Bloom过滤器的使用也意味着对不存在的行或列的大多数查询不需要访问硬盘。 38 | * 通过缓存提高读操作的性能
39 | 为了提高读操作的性能,tablet服务器使用二级缓存的策略。对tablet服务器代码而言,扫描缓存是第一级缓存,其缓存SSTable接口返回的键值对;Block缓存是二级缓存,其缓存从GFS读取的SSTable块。对于趋向于重复读取相同数据的应用程序来说,扫描缓存非常有效;对于趋向于读取刚读过的数据附近的数据的应用程序来说,Block缓存很有用。 40 | * 提交日志的实现
41 | 为了避免重复读取日志文件,我们首先把提交日志的条目按照关键字(table,row name,log sequence number)排序。排序之后,对一个特定tablet的修改操作连续存放,因此,随着一次询盘操作之后的顺序读取,修改操作的读取将更高效。为了并行排序,我们将日志文件分割成64MB的段,之后在不同的tablet服务器对每段进行并行排序。这个排序过程由master来协调,并且当一个tablet服务器指出它需要从一些提交日志文件中回复修改时排序被初始化。为了使修改操作免受GFS瞬时延迟的影响,每个tablet服务器实际上有两个日志写入线程,每个线程写自己的日志文件,并且同一时刻,两个线程只有其中之一是活跃的。如果写入活跃日志文件的效率很低,日志文件写入切换到另外一个线程,在提交日志队列中的修改操作就会由新的活跃日志写入线程写入。日志条目包含序列号,这使得恢复进程可以省略掉由于日志进程切换而造成的重复条目。 42 | * Tablet恢复提速
43 | 如果master将一个tablet从一个tablet服务器移到另外一个tablet服务器,源tablet服务器会对这个tablet做一次Minor Compaction。这个Compaction操作减少了tablet服务器日志文件中没有压缩的状态的数目,从而减少了恢复的时间。Compaction完成之后,该tablet服务器停止为该tablet提供服务。在真正卸载tablet之前,tablet服务器还会再做一次Minor Compaction,以消除tablet服务器日志中第一次minor compaction执行过程中产生的未压缩的状态残留。当第二次minor compaction完成以后,tablet就在不需要任何日志条目恢复的情况下被装载到另一个tablet服务器上了。 44 | * 利用不变性
45 | 因为SSTable是不变的,所以永久移除已被删除数据的问题就转换成对废弃的SSTable进行垃圾收集的问题了。每个tablet的SSTable都在注册在元数据表中。Master将废弃的SSTable作为对SSTable集合的“标记-清除”的垃圾回收而删除,元数据表则保存了root的集合。最后,SSTable的不变性使得分割tablet的操作非常快捷。与为每个子tablet生成新的SSTable集合相反,我们让子tablet共享父tablet的SSTable。 46 | 47 | ### Bigtable的优点和limitation 48 | 课后题3 49 | 50 | ##课后题解答 51 | 1、What is the relationship between table , tablet and sstable?
Tablet是从Table中若干范围的行组成的一个相当于原Table的子表,所以一个Table可以有很多的Tablet. 而SSTable则是一个持久化了的,有序且不可变的Map键值对文件形式(这个Map的键和值都是string类型的)。对于Table来说,SSTable是它的子表Tablet在GFS文件系统中的持久化存储的形式,即我们平常说的Tablet在Bigtable中,是存在一个一个SSTable格式的文件中的。 2、Describe what will happen when a read operation or write operation arrives.
这部分主要是针对Tablet服务器的,谷歌的Bigtable的读写请求都是通过Tablet服务器进行响应的。
当一个写操作到达时,Tablet服务器首先要检查这个操作格式是否正确、操作发起者是否有执行这个操作的权限。权限验证的方法是通过从一个Chubby文件里读取出来的具有写权限的操作者列表来进行验证(这个文件几乎一定会存放在Chubby客户缓存里)。成功的修改操作会记录在提交日志里。可以采用批量提交方式来提高包含大量小的修改操作的应用程序的吞吐量。当一个写操作提交后,写的内容插入到memtable里面。
当读操作到达时,Tablet服务器会作类似的完整性和权限检查。一个有效的读操作在一个由一系列SSTable和memtable合并的视图里执行,因为内存表和SSTable中都保存了数据。 3、Describe which applications are BigTable suitable for and not suitable for. Answer:
由于BigTable针对数据存储进行包括压缩等各方面的优化,以及在事务的一致性上做出了让步,BigTable对于那些需要海量数据存储,高扩展性以及大量的数据处理,但又不要求强一致性的应用是十分适合的,比如Google Earth等。
也因此,对于那些需要强一致性,需要同步更改多行数据的应用来说,BigTable是不合适的。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSDI,Just for Fun 2 | >宝宝们团结起来,只要人人献出一点爱,世界就会变成美好的明天 :) 3 | * 1. **书写内容包括paper的考点、概要和重点,以及课前题目的参考答案** 4 | * 2. 为方便在线审阅,书写格式为markdown,语法非常简单,2min学会,可参考http://www.jianshu.com/p/q81RER 提交的文件名:星期号_paper名字_主笔宝宝名字.md , *eg: 7_PowerGraph_赵志勇.md* 5 | * 3. 第一版提交截止时间为**6月17日晚20点**,给大家充足的时间复习 :) 6 | * 4. 写完后互相审阅,互相补充,吃瓜群众有问题可以给主笔宝宝讨论或提issue,最终交给TA去修订,commit log大致写一下修改内容 7 | 8 | 一个人写难免会有不完整或纰漏的地方,请吃瓜群众不要只是看看,有改进的地方可以提issuse或直接与主笔宝宝探讨,不做伸手党,只要人人献出一点爱,世界就会变成美好的明天 :) 9 | 10 | 11 | >伟大的主笔宝宝们: 12 | * 2. Flash File System, F2FS: A New File System for Flash Storage 朱丽叶 13 | * 3. Non-volatile memory, System software for persistent memory 黎哲明 14 | * 4. In-memory computing, Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing 崔毅,黄鑫 15 | * 5 Crash-Recovery, Fast Crash Recovery at RAMCloud 李然 16 | * 6 Multiprocessor, Concurrency & Locking, Non-scalable locks are dangerous 李智 17 | * 7 Graph Computation, PowerGraph: Distributed Graph-Parallel Computation on Natural Graphs 李宁,赵志勇 18 | * 8 Transactional Memory/DB,Transactional memory: architectural support for lock-free data structures 李阳德 19 | * 9 Distributed Storage, Bigtable: A Distributed Storage System for Structured Data 王英艺,张金石,姜德治 20 | * 10 Distributed Transactions,Spanner: Google’s Globally-Distributed Database 薛翔 21 | * 11 Low (Tail) latency, The Tail at Scale 李春淼 22 | * 12 Bugs, Towards Optimization-Safe Systems: Analyzing the Impact of Undefined Behavior 夏亦谦、刘宁 23 | * 13 NFV (Network Function Virtualization), The Click Modular Router 李宗垚,陈文康,谭钧升 24 | * 14 RDMA, Fast In-memory Transaction Processing using RDMA and HTM 吴强强 25 | 26 | 鸣谢 胡洁琼提供重点,石培涛同学提供去年资料 27 | -------------------------------------------------------------------------------- /img/10_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/10_1.png -------------------------------------------------------------------------------- /img/10_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/10_2.png -------------------------------------------------------------------------------- /img/11-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/11-1.jpg -------------------------------------------------------------------------------- /img/11-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/11-2.jpg -------------------------------------------------------------------------------- /img/13_click_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/13_click_1.png -------------------------------------------------------------------------------- /img/13_click_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/13_click_2.png -------------------------------------------------------------------------------- /img/14-RDMA-hash-imp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/14-RDMA-hash-imp.png -------------------------------------------------------------------------------- /img/14-RDMA-hash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/14-RDMA-hash.png -------------------------------------------------------------------------------- /img/4_code1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/4_code1.1.png -------------------------------------------------------------------------------- /img/4_code1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/4_code1.2.png -------------------------------------------------------------------------------- /img/4_code2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/4_code2.png -------------------------------------------------------------------------------- /img/4_code3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/4_code3.png -------------------------------------------------------------------------------- /img/4_system_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/4_system_1.jpg -------------------------------------------------------------------------------- /img/4_system_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/4_system_2.jpg -------------------------------------------------------------------------------- /img/4_system_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/4_system_3.jpg -------------------------------------------------------------------------------- /img/7-3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/7-3-1.png -------------------------------------------------------------------------------- /img/7-3-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/7-3-2.png -------------------------------------------------------------------------------- /img/7-3-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/7-3-3.jpg -------------------------------------------------------------------------------- /img/7-3-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/7-3-4.png -------------------------------------------------------------------------------- /img/7-3-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/7-3-5.png -------------------------------------------------------------------------------- /img/8_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/8_1.png -------------------------------------------------------------------------------- /img/8_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/8_2.jpg -------------------------------------------------------------------------------- /img/9-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/9-1.png -------------------------------------------------------------------------------- /img/9-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/9-2.png -------------------------------------------------------------------------------- /img/README.txt: -------------------------------------------------------------------------------- 1 | markdown只能以链接的方式插入图片, 2 | “使用相对路径插入图片。比如你把一个叫做1.png的图片和*.md文件放在一起,那么你就可以用这种方式插入图片:![](1.png)” 3 | 如果图片是放在文件夹中,如同目录的img文件夹下,则写![](img/1.png) 4 | -------------------------------------------------------------------------------- /img/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Emilio66/CSDI/685b999c02ab2a78d00ac8721b0c09f8847b54b2/img/Thumbs.db --------------------------------------------------------------------------------