当前位置: 首页 > 编程日记 > 正文

Rocksdb 的 rate_limiter实现 -- compaction限速

文章目录

    • 前言
    • 1. Compaction为什么会影响Client qps
      • 1.1 基本LSM介绍
      • 1.2 LSM internal ops
      • 1.3 长尾延时的原因
    • 2. Rate limiter 基本限速接口
    • 3. Rate Limiter 限速原理实现
      • 3.1 Rate Limiter的传入
      • 3.2 Rate Limiter 控制 sync datablock的速率
      • 3.3 Rate Limiter控制写入速率
    • 4. rocksdb Rate Limiter的限制

前言

LSM 引擎针 的业界相关优化方案已经有很多了,优化的方向也是在不同workload纬度上进行取舍。
比如头条的Amap ,中科的dCompaction 是为了降低Compaction的写放大(写放大除了消耗ss),却在一定程度上降低了读性能;

奥斯汀大学 17年发表在顶会SOSP上的pebblesdb 较为有效得降低了写放大,写吞吐相比于原LSM实现提升2-2.7x,同时在range query有一定的优化,性能甚至超过了原有LSM的range query。

悉尼大学 19年发表在顶会UTC上的SILK 通过rocksdb的rate limiter来 通过客户端压力的监控来对 LSM内部整个IO进行限速控制,随未减少写放大,但却有效得降低了compaction对上层吞吐的qps和抖动的影响,尤其是长尾延时的优化上有效降低且极为稳定。

还有很多其他的优化方案,甚至专门有人整理了一篇LSM的优化论文,对业界数十篇LSM相关优化论文做了一个总结:
LSM-based Storage Techniques: A Survey
当然,rocksdb社区也在努力研究有效的compaction优化方案,本节想要介绍一下rocksdb 原生实现的compaction IO层的限速方案,通过compaction限速来降低compaction的IO对Client的 qps 和 波动的影响。

通过本文,你能够了解到如下关于rocksdb/LSM的知识点:

  1. compaction为什么会影响上层qps (纯写场景)?
  2. rocksdb rate limiter 基本限速接口
  3. rocksdb rate limiter 原理实现
  4. rocksdb rate limter 的限制

ps: 文中涉及到的源代码 都是基于rocksdb 6.4.6版本,不同的版本之间可能有实现细节上的差异,不过主体逻辑都是差不多的。

1. Compaction为什么会影响Client qps

1.1 基本LSM介绍

如下图 是一个基本的LSM 树的分层结构(以下的操作描述是针对传统LSM 的实现进行描述的,没有完整描述整个rocksdb的基本实现):
在这里插入图片描述
内存中有一个 write-buffer, 在rocksdb 上 也叫做 memtable
磁盘上从 L0-Ln也是一个分层结构: 每一层可以有很多有序字节表(sstables),其中L0层的sst之间可以有重叠,大于L0层的sst文件严格有序 且层内sst之间不能有重叠keys.

如下图 两种请求落到LSM 中:

  • Client 的update请求 会写入到内存中的write buffer之中即可返回,后续的写入由internal operation – flush and compaction来负责。

  • Client 的read 请求 会先读 write buffer,如果读不到则按照磁盘上的L0 - Ln逐层读取。
    在这里插入图片描述

1.2 LSM internal ops

LSM三种internal操作:

  • Flush 由write-buffer 写入 L0
  • L0-L1 Compaction 因为L0 层文件之间允许有key的重叠(LSM 为了追求写性能,使用append only方式写入key,write-buffer一般是skiplist的结构),所以只允许单线程将L0的文件通过compaction写入L1
  • Higher Level Compactions 大于L0层 的文件严格有序,所以可以通过多线程进行compaction.
    在这里插入图片描述

Flush 操作如下:

  • 请求写入到(write-buffer)memtable之中,当达到write_buffer_size大小 进行memtable switch
  • 旧的memtable变成只读的用来 Flush,同时生成一个新的memtable用来接收客户端请求
  • Flush的过程就是在L0 生成一个sst文件描述符,将immutable 中的数据通过系统调用写入该文件描述符代表的文件中。
    在这里插入图片描述

L0–> L1 Compaction 操作如下:

  • 将一个L0的sst文件和多个L1 的文件进行合并
  • 目的是节省足够的空间来让write-buffer持续向L0 Flush
    在这里插入图片描述

Higher Level Compactions操作如下:
对整个LSM进行GC,主要丢弃一些多key的副本 和 删除对应的key的values

这个过程并不如L0–>L1的compaction 紧急,但是会产生巨量的IO操作,这个过程可以后台并发进行。
在这里插入图片描述

1.3 长尾延时的原因

  • L0满, 无法接收 write-buff不能及时Flush,阻塞客户端
    在这里插入图片描述
    因果链如下:
    没有协调好internal ops – 》 Higher Level Compactions 占用了过多的IO --》L0–>L1 compaction 过慢 --》L0没有足够的空间 --》Write-buffer无法继续刷新。
  • Flush 太慢,客户端阻塞
    在这里插入图片描述
    因果链如下:
    没有协调好internal ops --》 Higher Level Comapction占用了过多的I/O --》 Flushing 过程中没有足够的IO资源 --》Flushing 过慢 --》Write-buffer提早写满而无法切换成immutable memtable,阻塞客户端请求。

综上我们知道了在LSM 下客户端长尾延时主要是由于三种 内部操作的IO资源未合理得协调好导致 最终的客户端操作发生了阻塞。

针对长尾延时的优化我们需要通过协调内部的internal 操作之间的关联,保证Flushing 优先级最高,能够占用最多的IO资源;同时也需要在合理的时机完成L0–L1的Compaction 以及 优先级最低但是又十分必要的Higher Level Compaction。

以下简单介绍一下Rocksdb 内部原生的Rate Limiter对这个过程的优化。

2. Rate limiter 基本限速接口

社区Rate Limiter 介绍
核心接口:

RateLimiter* rate_limiter = NewGenericRateLimiter(rate_bytes_per_sec /* int64_t */, refill_period_us /* int64_t */,fairness /* int32_t */);

将该接口加入到应用中的option的方式就是:

options.rate_limiter.reset(rocksdb::NewGenericRateLimiter(rate_bytes_per_sec /* int64_t */,refill_period_us /* int64_t */,fairness /* int32_t */));

核心参数如下三个:

  • rate_bytes_per_sec 这个是最常用也是大家使用起来最有效的一个参数,用来控制compaction或者flush过程中每秒写入的量。比如,设置了200M, 表示当compaction 累积的总写入token达到 200M /s 时才会触发系统调用的write.
  • refill_period_us 用来控制 token 更新的频率;比如设置的rate_bytes_per_sec是10M/s, 且refill_period_us 设置的是100ms,那么表示 每100ms即可重新调用一次compaction的写入。针对1M的大value 可以立即写入,而小于1M的数据则需要消耗CPU, 累积到1M 触发一次写入。
  • fairness 表示低优先级请求获得处理的概率。
    RateLimiter 支持接受高优先级线程 和 低优先级线程的请求,一半flush操作是最高的优先级,其次是 L0 --> L1 compaction优先级较高,最后则是Higher Level compactions 优先级最低。那么这个参数 fairness表示 即使现在有较多的高优先级任务在调度,低优先级的任务也有 1 / fairness 的机会能够被调度,从而防止被饿死。

3. Rate Limiter 限速原理实现

3.1 Rate Limiter的传入

先从我们的客户端入口来看 创建了RateLimiter 都做了一些什么?
rocksdb::NewGenericRateLimiter 接口主要是做一些初始化变量的工作:
在这里插入图片描述
以上类是继承自RateLimiter 类,能够提供更加精确的限速控制,比如初始化变量中的RateLimiter::Mode mode来制定限速是针对只读 或者 只写 或者 所有的读写。
在这里插入图片描述

这个时候我们初始化好了rate_limter的对象,并将其传给options中,应用拿着options打开了rocksdb,具体的DB::Open过程中会初始化DBImpl对象
在这里插入图片描述
初始化该对象的过程中会拿着我们之前创建好的rate_limiter 进行全局option的初始化工作:
在这里插入图片描述
DBOptions SanitizeOptions函数中 能够看到通过rate_limiter 结合其他配置 或者交给指定的配置来达到后续限速的目的。
在这里插入图片描述
同时还会有在其他地方直接使用的rate_limiter对象 达到限速的目的:
在从sst file中调用read接口读取数据时能够通过rate_limiter 限制读请求的速率
在这里插入图片描述
同理当需要通过flush或者compaction 过程向sst文件中写入数据的时候可以通过rate limiter限制写入的速率
在这里插入图片描述

3.2 Rate Limiter 控制 sync datablock的速率

接着上文 RateLimiter 不为空时会将rate_bytes_per_sec 数值作为delayed_write_rate的速率。
这个delayed_write_rate参数会在rocksdb的Write Stall 的限速中进行描述,这里简单说一下 rocksdb 触发Write Stall 的几种原因:

  • 过多memtables. 当此时内存中有 max_write_buffer_number 个memtables等待被flush,写会被完全Stall
    在rocksdb的日志中会有如下记录:
    Stopping writes because we have 5 immutable memtables (waiting for flush), max_write_buffer_number is set to 5
    
  • 过多的L0 SST files. 当L0的sst 文件个数超过level0_slowdown_writes_trigger个之后,会触发write stall;当L0文件个数达到level0_stop_writes_trigger 之后写入会完全阻塞。
    在rocksdb的日志中会有类似如下记录:
    Stalling writes because we have 4 level-0 files
    Stopping writes because we have 20 level-0 files
    
  • 过多的待处理 Compaction-bytes. 当预估的待处理Compaction 总大小达到了soft_pending_compaction_bytes会触发Stall,达到了hard_pending_compaction_bytes 会触发stop write。
    在rocksdb的日志中会有类似记录如下:
    Stalling writes because of estimated pending compaction bytes 500000000
    Stopping writes because of estimated pending compaction bytes 1000000000
    

回到上文中提到的bytes_per_sync参数:
在rocksdb 进行Compaction或Flush过程中 ,写入数据之前 会通过OpenCompactionOutputFile函数 创建WritableFileWriter 对象,来负责将即将写入的数据通过posix接口进行数据处理写入到文件系统的page cache或者 direct写入到磁盘之中。
创建WritableFileWriter 对象的过程中会将 通过option 传入的rate_limiter 传入到该对象之中。
在这里插入图片描述
到实际写入时,会通过BlockBasedTableBuilder::Add --> BlockBasedTableBuilder::Flush() --> BlockBasedTableBuilder::WriteBlock() --> BlockBasedTableBuilder::WriteRawBlock() --> WritableFileWriter::Append() --> WritableFileWriter::Flush() 以及WritableFileWriter::WriteBuffered()这两个函数

其中WritableFileWriter::Flush()这个函数中会调用如下逻辑:
即当前写入到内存中缓存的数据偏移地址 相比于上一次的偏移地址 大小超过了1M,则触发一次RangeSync
在这里插入图片描述
RangeSync中也需要strict_bytes_per_sync_ 参数为1 才会是一次真正的sync系统调用。

这里为什么会有这样的配置呢,简单描述一下rocksdb写sst文件的逻辑:
原生配置是将所有的sst文件中的datablock,filterblock, index block,等所有的数据block写到内存(page cache)之后统一调用一次对当前文件句柄的sync操作,从而有效减少IO次数。

但是问题是类似这样大批量的累积sync 可能会导致compaction/flush 在每隔一段时间占用巨量的IO带宽,从而造成client的latency spike或者qps下降。

所以通过 rate_limiter配置 + bytes_per_sync + strict_bytes_per_sync_ 能够减少大批量的累积sync,而让整个IO均匀分不到整个compaction/flush的写入链路,可能client 的qps还是会有下降,但是不会出现过高的latency spike.

3.3 Rate Limiter控制写入速率

回到上文 Compaction / Flush的写入链条:
BlockBasedTableBuilder::Add --> BlockBasedTableBuilder::Flush() --> BlockBasedTableBuilder::WriteBlock() --> BlockBasedTableBuilder::WriteRawBlock() --> WritableFileWriter::Append() --> WritableFileWriter::Flush() 以及WritableFileWriter::WriteBuffered()这两个函数
看看WritableFileWriter::WriteBuffered() 函数,它是负责向缓冲区中添加数据:
在这里插入图片描述
通过 调用rate_limiter的RequestToken函数 --》 调用GenericRateLimiter::Request函数,来检测添加的数据大小left 是否满足开始配置的rate_limiter的rate_bytes_per_sec限速大小,如果未达到,则会让当前线程休眠,并按照refill_period_us频率来定时更新待写入的bytes是否满足写入的速率要求,并及时填充写入缓冲区。

此时当前的写入过程会阻塞,直到left累积到rate_limiter的写入限速阈值,才会继续向当前的文件句柄中正常写入。

4. rocksdb Rate Limiter的限制

静态的限速控制:
无法灵活变通, 后台internal ops的写入速率,比如偶尔Client 压力较大,需要降低internal ops写入速率对client的影响;偶尔Client 压力较小, 又可以增加internal ops,将之前累积的待写入的数据写入。

实际的测试,刚开始能够提供较为稳定的qps latency。但是在写入一段时间之后,随着数据量的增加,静态的Rate limter无法保证 internal ops(flush , L0–>L1 compaction 以及 higher level compactions) 在不影响client latency的情况下及时有效的处理,从而出现了较高的latency spike.
在这里插入图片描述
当然rocksdb后续推出的 auto tune 以及 业界的 TRIAD 和 SILK设计 看他们的描述 都能够有效的降低latency spike,后续会尝试有效测试 并发掘背后实现机理 之后分享出来。

相关文章:

Oracle --获取绑定变量的值.

SELECT * FROM DBA_HIST_SQLBIND WHERE SNAP_ID>67073 AND SNAP_ID<67079 AND SQL_ID3DR3410F086P4;SELECT * FROM v$sql_bind_capture where sql_id http://blog.itpub.net/22034023/viewspace-689802/ 通过v$sql_bind_capture视图&#xff0c;可以查看绑定变量&#xf…

计算机网络TCP/IP协议-从双绞线到TCP

消息响应也是同理,这种带端口的消息发送方式,其实就是UDP协议,UDP简单粗暴,但是UDP存在很多问题,所以我们需要设计一个稳定可靠的协议,TCP协议,首先,网络是不稳定的,我们发送的消息很有可能会在中途丢失,所以需要设置重试机制,当消息发送失败时重新发送,为了判断是否成功,还需要要求接收方收到消息后,必须发送确认消息,这样就可以保证消息必达,另外大段的内容发送,很容易造成部分丢失,导致全部内容都要重新发送,于是我们可以将数据分包,分成多个包发送。到这,也行你会发现了,演示中的IP地址是怎么设置的呢?

使用HTML CSS完成初步的页面,任务九:使用HTML/CSS实现一个复杂页面(示例代码)

任务目的通过实现一个较为复杂的页面&#xff0c;加深对于HTML&#xff0c;CSS的实战能力实践代码的复用、优化任务描述整个页面内容宽度固定&#xff0c;但头部的蓝色导航和浏览器宽度保持一致任务注意事项只需要完成HTML&#xff0c;CSS代码编写&#xff0c;不需要写JavaScri…

Yii-yiic使用

原文在&#xff1a;http://blog.sina.com.cn/s/blog_862b12fb0101n00v.htmlyii提供了强大的命令行工具来快速的创建相关组件和应用。下面就来讲解用yiic工具快速创建yii应用我的web目录在d:\ EasyPHP-DevServer\data\localweb下&#xff1b;yiiframework在D:\EasyPHP-DevServer…

nginx语法

一、 1. 配置文件由指令与指令块构成&#xff1b; 2. 每条指令以&#xff1b;分号结尾&#xff0c;指令与参数间以空格符号分隔&#xff1b; 3. 指令块以{} 大括号将多条指令组在一起&#xff1b; 4. include语句允许组合多个配置文件以提升可维护性&#xff1b; 5. 使用#符号添…

如何对 Rocksdb以及类似存储引擎社区 提出 有效的性能问题?

性能 是rocksdb的优点&#xff0c;活跃的社区十分欢迎大家对各自使用rocksdb 过程中性能相关的疑惑点进行提问。提问的时候如果能够提供更多&#xff0c;更详细的信息 是可以增加快速得到恢复回复的概率。当然&#xff0c;性能是一个非常广泛且有巨量影响因素的话题&#xff0c…

我已经喜欢上了Python

早就听说了Python语言&#xff0c;今天试了试&#xff0c;挺喜欢她了。 Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 6 2014, 22:15:05) [MSC v.1600 32 bit (Intel)] on win32Type "copyright", "credits" or "license()" for more information.&g…

layui跳转html如何带参数,Layui跳转页面代码(可携带复杂参数)

今天用了Layui的“数据表格 - 数据操作”示例代码&#xff0c;结果发现点击“编辑”按钮出出来一个弹出消息框&#xff0c;效果如下&#xff1a;虽然说也可以用“弹出层”做编辑页面&#xff0c;但是&#xff0c;由于我编辑的东西很多&#xff0c;用“弹出层”不太理想。我就想…

java获取日期

/* * 获取昨天日期 方法一&#xff0c;这个方法好像有点慢 */Date dt new Date(); Calendar cal Calendar.getInstance();cal.add(Calendar.DATE, -1);time new SimpleDateFormat( "yyyy-MM-dd").format(cal.getTime()); /* * 获取昨天日期 方法二 */Date as ne…

DRF (Django REST framework) 中的视图类

视图说明 1. 两个基类 1&#xff09;APIView rest_framework.views.APIView APIView是REST framework提供的所有视图的基类&#xff0c;继承自Django的View父类。 APIView与View的不同之处在于&#xff1a; 传入到视图方法中的是REST framework的Request对象&#xff0c;而不是…

Go:分布式学习利器(1) -- 开发环境搭建 + 运行第一个go程序

文章目录为什么要学习 go开发环境搭建 -- MAC运行第一个go程序go 函数的返回值设置go 函数的命令行参数为什么要学习 go 在如下几个应用场景的需求下产生了go: 超大规模分布式计算集群多核硬件的架构web模式下的大规模开发和频繁的进度更新 所以go 语言有如下几个特点&#…

html生成的超级链接预览功能,超链接特效

功能说明超链接特效功能是基于报表特殊效果功能的一种扩展实现。报表特殊效果功能的作用是为单元格添加一些特殊的显示效果。超链接特效可以给超链接添加特殊显示效果&#xff0c;实现超链接功能的扩展增强。当产品默认生成的超链接显示效果不能满足用户的个性化需求时&#xf…

ROOT android 原理。 基于(zergRush)

出自&#xff1a; http://bbs.gfan.com/android-2996211-1-1.html 须要ROOT的同学请去上面的地址下载。 a.控制手机创建个暂时目录,然后把zergRush脚本写入此目录,并改动此文件权限使之能够运行(这一步无需ROOT权限); adb shell rm -r /data/local/tmpadb shell mkdir /data/lo…

Oracle数据库日常维护知识总结

DBA要定时对数据库的连接情况进行检查&#xff0c;看与数据库建立的会话数目是不是正常&#xff0c;如果建立了过多的连接&#xff0c;会消耗数据库的资源。同时&#xff0c;对一些“挂死”的连接&#xff0c;可能会需要DBA手工进行清理。首先要说的是&#xff0c;不同版本数据…

JAVA 第五周学习总结

20175304 2018-2019-2 《Java程序设计》第五周学习总结 教材学习内容总结 Java为什么要定义接口&#xff1a;接口的作用是实现多重继承&#xff0c;因为一个子类只能继承一个父类&#xff0c;但是可以实现一个或多个接口。使用关键字interface来定义一个接口&#xff0c;定义方…

Go: 分布式学习利器(2)-- Go中的变量,常量 以及与其他语言变量之间的差异

文章目录1. Go 语言编写测试代码2. Go 的变量3. Go 常量定义1. Go 语言编写测试代码 源码文件以 _test结尾&#xff1a; xxx_test.go测试方法名需以Test开头&#xff1a; func TESTXXX(t *testing.T) {..} &#xff0c;且参数列表直接用go 默认的test参数即可 如下first_test…

Scala:Functions and Closures

1 object Functions {2 def main(args: Array[String]) {3 // 本地函数4 def localFun(msg: String) println(msg)5 localFun("Hi")6 7 // 函数对象8 var list List(1, 2, 3)9 list.foreach((x: Int) > println(x)) 10 list.fore…

云计算机机房怎么样,如何知道云电脑配置多少?怎么选择云电脑机房?

一般在玩一款游戏时&#xff0c;需要考虑玩游戏的配置&#xff0c;云电脑帮助我们实现配置的需求&#xff0c;那如何才能知道云电脑配置是多少&#xff0c;该怎么选择云电脑机房。在使用云电脑时&#xff0c;我们不用考虑自己的手机、平板和电脑的硬件&#xff0c;只要设备能正…

eclipse中新建android项目,不自动生成R.java

http://huyuantai000.iteye.com/blog/1681582转载于:https://www.cnblogs.com/wmm3416/p/3386698.html

获取子iframe的属性

第一种方法&#xff1a; <iframe name"iframeName" src"http://www.test.com"></iframe> // name"iframeName" 取值 window.frames[iframeName] 第二种方法&#xff1a; <iframe id"iframeId" src"http://www.t…

Go: 分布式学习利器(3) -- Go的数据类型和运算符

文章目录1. Go的数据类型1.1 类型转化1.2 类型的预定义1.3 指针类型2. Go 的运算符1. Go的数据类型 GO的基本数据类型如下&#xff1a; bool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // 基本和uint8 类型一样 rune // 代表unicode …

云职教课堂计算机文化基础,2020智慧职教云课堂计算机文化基础答案最新最全单元测试答案...

参考答案如下智慧职教最新最全【判断题】两种气体在相同温度下放入同一个容器中,测得的压强分别为 和 ,则把它们同时放入容器中后的总压强为 .云课【单选题】根据物质的导电能力,将物质分为哪些类别?A. 导体 B. 半导体 C. 绝缘体 D. 以上都对堂计【单选题】按照数的进位制概念…

测试用例挑选策略

在软件开发过程中&#xff0c;无论是在feature testing还是在final regression testing中&#xff0c;测试策略的好坏在整个质量保证过程中起着至关重要的作用&#xff0c;尤其是在测试资源有限的情况下&#xff0c;影响更为突出。好的测试策略能够更快速的发现软件最有value的…

php json josn_decode()返回的是对像,如何把对像转成数组

php json josn_decode()返回的是对像&#xff0c;如何把对像转成数组 a.php传值页面&#xff0c;使用 json_encode($array)对数组进行加密码. b.php页面在接收a.php传过来的页面的值使用的是 json_decode($array),发现解密出来的数据是对象形式的&#xff1a; array(2) {[0]>…

【题解】黑格覆盖

题目描述 在一张由MN个小正方形格子组成的矩形纸张上&#xff0c;有k个格子被涂成了黑色。给你一张由mn个同样小正方形组成的矩形卡片&#xff0c;请问该卡片最多能一次性覆盖多少个黑格子&#xff1f; 输入输出格式 输入格式 输入共k1行&#xff1a; 第1行为5个整数M、N、m、n…

Go 分布式学习利器(4)-- 条件和循环

文章目录1. 循环语句2. 条件语句2.1 if...else 条件2.2 switch 条件1. 循环语句 Go语言和其他语言在循环语句上的主要差异是 Go语言仅支持for关键字。 语法形式&#xff1a;for i : 5; i > 0; i--,不像其他语言有两个小括号左右围着。 两种不同的循环体语法如下&#xff1…

php 对象的执行

1.BNF范式 %token T_OBJECT_OPERATOR "-> (T_OBJECT_OPERATOR)"unticked_statement: | expr ; { zend_do_free(&$1 TSRMLS_CC); }expr:r_variable { $$ $1; }| expr_without_variable { $$ $1; } ;r_variab…

golang不编译.html,golang之条件编译

Go语言能够经过go/build包里定义的tags和命名约定来让Go的包能够运行不一样的代码。html标签编译在源代码里添加标注&#xff0c;一般称之为编译标签(build tag)。编译标签采用靠近源代码文件顶部用注释的方式添加。go build在构建一个包的时候会读取这个包里的每一个源文件而且…

深入理解Struts2中的OGNL表达式

Struts 2中的表达式语言Struts 2支持以下几种表达式语言&#xff1a; OGNL&#xff08;Object-Graph Navigation Language&#xff09;&#xff0c;可以方便地操作对象属性的开源表达式语言&#xff1b; JSTL&#xff08;JSP Standard Tag Library&#xff09;&#xff0c;JSP …

[ZJOI2010]网络扩容

Description 给定一张有向图&#xff0c;每条边都有一个容量C和一个扩容费用W。这里扩容费用是指将容量扩大1所需的费用。 求&#xff1a; 1、在不扩容的情况下&#xff0c;1到N的最大流&#xff1b; 2、将1到N的最大流增加K所需的最小扩容费用。 Input 第一行包含三个整数N,M,…