Redis持久化机制分析

news/2024/5/19 3:41:07/文章来源:https://blog.csdn.net/qq_35267557/article/details/126669845

什么是持久化?

简单来说持久化就是将数据保存到磁盘,让即使服务宕机、重启、断电等操作后数据仍热存在,并且是完整的。

1、为什么要持久化?

  • 1、Redis是一个内存数据库,宕机之后存储在内存的数据会消失。
  • 2、Redis重启之后需要恢复数据,需要提供持久化机制用于恢复数据。

Redis的持久化方式有两种:RDB方式持久化、AOF方式持久化。但值得注意的是:Redis的持久化并不能保证数据的完整性。

如果使用Redis作为数据库,数据库的数据需要保证完整性时,一定需要有一个完整的数据源(比如mysql, oracle等)。

在redis启动时,从这个完整的数据源中将数据加载到redis中。一般适用与数据量较小且不容易变化的数据,比如字典表。

使用info命令查看持久化

# Server 服务相关信息,就列举几个
redis_version:5.0.14  # 版本号
redis_mode:standalone # 启动模式 standalone表示单机  cluster表示集群
os:Linux 3.10.0-1160.11.1.el7.x86_64 x86_64 # 操作系统版本
multiplexing_api:epoll  # 事件分离器reactor模式为epoll
process_id:1  # 进程IO
tcp_port:6379 # 服务端口号
hz:10  # 时间事件周期为1秒钟执行10次# Clients 客户端信息
connected_clients:4  # 已连接的客户端数量# Memory 内存相关喜喜
used_memory:8169136  # 已使用内存
maxmemory:0 # 最大内存 0 表示不限制
maxmemory_policy:noeviction # 接近最大内存的淘汰策略 noeviction表示禁止驱逐# Persistence 持久化相关配置 主要时rdb和aof的相关配置
loading:0
rdb_changes_since_last_save:50371
rdb_bgsave_in_progress:0
rdb_last_save_time:1661309348
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:1   # 是否启动aof 1表示开启 0 表示没有开启 没有开启就是使用的rdb
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
aof_current_size:2580696
aof_base_size:2580591
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0# Stats 统计相关信息
total_connections_received:5
total_commands_processed:7# Replication 主从相关配置
role:master # 角色 master
connected_slaves:0  # 连接的slave数量# CPU
used_cpu_sys:238.610704# Cluster 集群配置
cluster_enabled:0  # 0表示没有开启集群# Keyspace key相关信息 db0:keys=50042表示key的数量为50042 expires表示过期的数量
db0:keys=50042,expires=0,avg_ttl=0

2、RDB持久化

RDB(Redis DataBase),是Redis默认的持久化方式,是以快照的方式进行持久化的。

2.1、什么是快照方式?

简单来说就是每隔多少秒,如果满足什么条件,就执行一次把内存的数据刷新到磁盘的操作。

2.2、快照的触发方式

  • 1、满足了自定义配置的快照规则
  • 2、执行了save或者bgsave命令进行了手动触发备份操作
  • 3、执行flushall命令
  • 4、第一次执行主从复制时,会生成一个快照

2.3、自定义配置快照生成规则

在redis.conf中配置,save多少秒内数据变了多少

# redis是基于内存的数据库,可以通过设置该值定期写入磁盘。
# 注释掉“save”这一行配置项就可以让保存数据库功能失效
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
save 900 1
save 300 10
save 60 10000

间隔时间越长,要求就越低。就像一个漏斗一样。这就是漏斗设计,目的是为了提升性能。

2.4、使用命令显式触发

比如:在客户端输入bgsave命令

127.0.0.1:6379> bgsave
Background saving started

2.5、RDB持久化流程

持久化过程图解

在这里插入图片描述

持久化过程解析

  • 1、Redis父进程判断是否正在执行save,bgsave,bgrewriteaof(aof文件重写)的子进程。如果在,则直接返回。
  • 2、父进程执行fork(调用操作系统复制主进程)来创建一个子进程(用于生成RDB文件),该过程父进程阻塞,Redis不i能执行来自客户端的命令。
  • 3、父进程fork结束后,bgsave命令返回“Background saving started”并不再阻塞主进程,且可以开始响应来自客户端的命令。
  • 4、子进程创建RDB文件,根据父进程内存进行快照生成一个临时的快照文件,完成后对原来的RDB文件进行原子替换,因此RDB始终完整。
  • 5、子进程发送信号给父进程表示完成,父进程更新统计信息。

我们保存了RDB文件,那么RDB文件结构是怎么样的呢?接下来看一下RDB的结构。

2.6、RDB文件结构

在这里插入图片描述

  • 1、第一部分:头部5个字节,固定为REDIS字符串
  • 2、第二部分:4个字节指的是“RDB”版本号(注意不是Redis的版本号),比如为9,会进行填充,填充后为0009。
  • 3、第三部分:辅助字段,以key-value形式存储
    • redis-ver 5.0.5
    • redis-bits 64/32
    • ctime 当前时间戳
    • used-mem 使用内存
    • aof-preamble 是否开启aof
    • repl-stream-db 主从复制相关参数值
    • repl-id 主从复制相关参数值
    • repl-offset 主从复制相关参数值
  • 第四部分:存储数据库号码
  • 第五部分:字典大小
  • 第六部分:过期key字典大小
  • 第七部分:主要数据,以key-value的形式存储
  • 第八部分:结束标志
  • 第九部分:校验和,就是看文件是否损坏,或者是否被修改

可以使用winhex工具打开dump.rdb查看

在这里插入图片描述

2.7、RDB持久化的优缺点

优点

RDB是二进制压缩文件,占用空间小,便于传输(传给slave)

主进程fork子进程,可以最大化Redis性能,主进程不能太大,复制过程中主进程阻塞

缺点

不能保证数据完整性,会丢失最后一次快照以后更改的所有数据

3、AOF持久化

3.1、AOF持久化简介

AOF(Append Only File)是Redis的另一种持久化方式。默认不开启,开启后采用AOF方式持久化。

开启之后,Redis将所有对数据库的写入命令和参数通过RESP协议记录到AOF文件,从而达到操作过程记录,持久化数据。并且可以通过该文件进行数据恢复操作。

因为Redis重启之后,只需要把这些命令解析出来,重新执行一遍就能恢复数据了。

总的来说,AOF就是记录过程,RDB就是记录结果。

3.2、AOF持久化过程分析

3.2.1、开启AOF持久化

开启aof持久化,在redis.conf中配置

############### APPEND ONLY 持久化方式 ###############
#默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,#根据save来策略进行持久化,Append Only File是另
一种持久化方式,可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,#每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
appendonly yes#aof文件名
appendfilename "appendonly.aof"

3.2.2、AOF持久化流程

AOF文件中存储的是redis的命令,从接受命令到写入AOF文件的整个过程可分为三个阶段

  • 1、命令传播:Redis将执行完的命令、命令参数、参数个数等发送到AOF文件。
  • 2、缓存追加:AOF程序根据接受到的数据,将命令转换为RESP协议,然后将协议内容追加到服务器的AOF缓存中。
  • 3、文件写入和保存:AOF缓存中的内容被追加写入到末尾,如果设定的条件满足的话。fsync或fdatasync会被调用,将写入的的内容真正的保存到磁盘。

3.2.2.1、命令传播

  • 1、当一个客户端执行Redis命令时,它通过网络连接,将RESP协议文本发送给Redis服务器。
  • 2、Redis服务器接受到客户端请求后,根据协议文本内容,选择适当的命令函数,并将各个参数从字符串文本转换为Redis的字符串对象(StringObject).
  • 3、命令执行结束后,命令参数会被传播到AOF程序。

3.2.2.2、缓存追加

  • 1、当命令被传播到AOF程序后,程序根据命令及参数,将命令从字符串对象转换为原来的协议文本。
  • 2、协议文本生成后,被追加到aof_buf(AOF缓存:保存着等待写入AOF文件的协议文本)的末尾。

3.2.2.3、文件写入和保存

当事件处理器执行时,函数aof.c/flushAppendOnlyFile都会被调用,该函数做了两件事。

  • 1、WRITE:根据条件,将aof_buf中的缓存写入到AOF文件中
  • 2、SAVE:根据条件,调用同fsync/fdatasync将AOF文件保存到磁盘,也叫刷盘。

既然时根据条件进行刷盘操作,那么条件是什么呢?就是AOF的保存模式。

3.2.2.4、AOF文件保存模式

文件保存模式分为三种:

  • 1、AOF_FSYNC_NO: 不保存
  • 2、AOF_FSYNC_EVERYSEC: 每秒钟保存一次(默认的)
  • 3、AOF_FSYNC_ALWAYS:每执行一个命令保存一次(性能差,不推荐).
#aof持久化策略的配置
#no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快。
#always表示每次写入都执行fsync,以保证数据同步到磁盘。
#everysec表示每秒执行一次fsync,可能会导致丢失这1s数据。
appendfsync everysec

不保存

在这种模式下,每次调用flushAppendOnlyFile函数,WRITE都会被执行,但SAVE会被略过。

每秒保存一次

在这种模式中,SAVE原则上每隔一秒钟就会执行一次, 因为SAVE操作是由后台子线程(fork)调用的,所以它不会引起服务器主进程阻塞。

每执行一个命令保存一次

在这种模式下,每次执行完一个命令之后,WRITE和SAVE都会被执行。

因SAVE是由Redis主进程执行的,所以在SAVE执行期间,主进程会被阻塞,不能接受命令请求。

AOF保存模式对性能和安全性是存在影响的。所以如果需要高性能,最好还是使用RDB。但是就不能使用AOF也保证性能吗?后面会有使用场景比较说明的。

AOF三种模式对主进程阻塞情况

在这里插入图片描述

可以看到丢失数据最少的也是阻塞最多的,性能最差的。所以一般都是设置为每秒保存一次。

3.2.2.5、AOF文件重写瘦身

AOF记录数据的变化过程,越来越大,并且存在大量无效的命令记录过程,因此需要重写“瘦身”。

Redis可以在AOF体积变得过大时,自动在后台(也就是fork一个子进程)对AOF进行重写。

重写后新的AOF文件就存储了能够恢复当前数据所需的最小命令集合。

重写并不是需要对原有的文件进行写入和读取,针对的是数据库中key的当前值做处理。

set name zhangsan
set name lisi
set name wangwu

可以优化为只执行最后一条命令。

Redis不希望AOF重写过程造成服务器无法处理请求,因此Redis决定将AOF重写程序使用一个后台子进程执行。

优点

  • 1、子进程进行AOF重写期间,主进程可以继续处理请求
  • 2、子进程带有主进程的数据副本,而不是数据本身,这样避免了锁资源竞争,保证了数据的安全性。

缺点

子进程重写AOF期间,主进程还需要继续处理命令,而新的命令可能对现有数据修改,这会导致当前数据库的数据和重写后不一致。从而增加了AOF重写缓存。

AOF重写缓存

Redis增加了一个AOF重写缓存,在主进程fork子进程后开始启用。

Redis主进程接受到新的命令后,除了将写命令的协议文本追加到AOF文件之外,还会追加到
AOF重写缓存中。

重写过程分析

Redis在创建新的AOF文件的过程,会继续将命令追加到AOF文件末尾,即使重写过程出现停机等异常情况,现有的AOF文件也不会丢失。

一旦新的AOF文件创建完成,Redis就会从旧的AOF文件切换到新的AOF文件,后续操作将会对新的AOF进行追加操作。

在这里插入图片描述

  • 1、将命令追加到现有的AOF文件中
  • 2、将命令追加到AOF重写缓存中
    • fork一个子进程,将所有的命令记录到AOF重写缓存中
    • 完成AOF重写后,向父进程发送一个完成信号
    • 将AOF重写缓存中的内容全部写入到新的AOF文件中
    • 对新的AOF文件进行改名,原子替换原有的AOF文件

只有最后的写入缓存和原子替换新旧AOF文件操作会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞, 这将AOF重写对性能造成的影响降到了最低。

触发重写条件

1、重写缓存配置,在redis.conf中

#aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候Redis能够调用bgrewriteaof对日志文件进行重写。#当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100
#设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写
auto-aof-rewrite-min-size 64mb

2、使用bgrewriteaof显式触发

127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started

3.2.2.6、混合持久化

RDB和AOF各有优缺点,Redis4.0开始支持rdb和aof的混合持久化。

如果把混合持久化打开,aofrewrite的时候就直接把rdb的内容写到aof文件开头。

也就是RDB的头+AOF的身体,最终生成的文件还是appendonly.aof

开启混合持久化

# 开启混合持久化
aof-use-rdb-preamble yes

此时AOF文件是rdb文件的头和aof格式的内容,在加载时,首先会识别AOF文件是否以REDIS字符串开头,如果是就按RDB格式加载,加载完RDB后继续按AOF格式加载剩余部分。

3.2.2.7、AOF文件数据恢复

AOF文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态。

Redis如何读取AOF文件并恢复数据的。

  • 1、创建一个不带网络连接的伪客户端(fake client)
  • 2、从AOF中分析并读取出一条写命令
  • 3、使用fake client执行被读出的写命令
  • 4、读完一条命令之后继续读取,并循环该过程,直到命令被fake client执行完毕为止。

为什么是创建一个不带网络连接的伪客户端?

因为Redis的命令只能早客户端的上下文执行,并且载入的文件是直接来源于AOF文件而不是网络连接。

因此,没有必要去创建网络连接,从而增加连接开销。所以创建一个不带网络连接的伪客户端执行AOF保存的命令。它的执行效果与真正的客户端完全一致,只是少了网络连接而已。

4、RDB与AOF持久化对比

  • 1、RDB是某一个时刻数据的快照,使用二进制格式存储。AOF是存储的命令过程,采用文本存储。
  • 2、RDB性能很高,AOF性能较低
  • 3、在服务器宕机情况下,RDB会丢失最后一次快照以后更改的所有数据,AOF如果设置为每秒保存一次,最多会丢失2s的数据。
  • 4、Redis以主服务器(master)模式运行,RDB不会保存过期的数据。以从服务器(slave)运行,RDB会保存过期键值对,当主从同步数据时,才会清空过期键值对。
  • 5、AOF写入文件时,会对过期key增加一条del命令,当AOF重写时,会忽略过期key和del命令。

5、不同场景的持久化方式

5.1、使用Redis作为数据库使用

使用Redis作为数据库,一般是存储一些数据量小,不易变化的数据。比如:字典库。

此时为了追求高性能,什么都不用开,redis宕机,直接从数据源恢复。key的淘汰策略也应该设置为禁止驱逐,从而保证数据的完整性。

5.2、作为高性能缓存服务器

因为是缓存,所以不需要保证数据的完整性,也需要保证高性能,因此使用RDB持久化是比较合适的。

5.3、相对保证数据完整性+高性能

使用rdb+aof方式,数据即不容易丢,性能也还是不错。

5.4、只使用AOF

因为单独使用AOF性能比较低,一般不推荐。如果需要保证数据完整性,最好从指定数据源加载到Redis。而不是有定要使用Redis来保证数据完整性。

6、小结

注意:Redis的持久化并不能保证数据的完整性,持久化主要用于数据恢复。它本质上是一个高性能的缓存服务器,所以为了保证性能,是不会为了保证完整性而降低性能的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_4951.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

传述最详细的干货,让简历面试不再成为你找工作的绊脚石

📢📢📢📣📣📣 哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步🤝🤝 一位上进心十足的【Java ToB端大厂…

【蓝桥杯省赛真题37】Scratch三国演义字数统计 少儿编程scratch编程蓝桥杯省赛真题讲解

​​​​​​​ 目录 scratch三国演义字数统计 一、题目要求 编程实现 二、案例分析 1、角色分析

Linux内核设计与实现 第三章 进程管理

3.1进程 实际上,进程就是正在执行的程序代码的实时结果。 进程是出于执行期的程序以及相关的资源的总称。 进程的另一个名字是任务。 进程不仅仅局限于一段可执行程序代码通常进程还要包含其他资源,像打开的文件,挂起的信号,内核…

springboot项目整理(持续更新)

SpringSecurity 1.导入依赖&#xff1a; 在pom.xml中导入依赖&#xff0c;再访问页面就会出现login&#xff0c;这是SpringSecurity自己写的页面&#xff0c;用于登录认证 <dependency><groupId>org.springframework.boot</groupId><artifactId>spr…

整合流量与资源的分享购商业模式,实现整个生态布局

大多数企业都很容易忽视一个市场&#xff0c;就是我们的日常生活服务板块&#xff0c;所谓民以食为天&#xff0c;我们应该顺应人们的生活习惯而做出来的电商商业模式&#xff0c;才是最贴合民心的&#xff0c;也能够从用户的最基础的需求出发来为其打造商业模式。 将目标放在生…

Room (三) RecyclerView 呈现列表数据

1. 用到的组件 Room&#xff0c;ViewModel&#xff0c;LiveData&#xff0c;Repository&#xff0c;AsyncTack 2. Module 中 build.gradle 文件中添加 dependencies {def room_version "2.4.3"implementation "androidx.room:room-runtime:$room_version&quo…

【Linux操作系统】-- 多线程(三)-- 线程池+单例模式

目录 线程池 场景 代码实现 线程安全的单例模式 懒汉实现方式和懒汉实现方式 饿汉方式实现单例模式 懒汉方式实现单例模式 实战代码演练单例模式 线程池 在C中用户使用new/malloc都是向操作系统OS申请的&#xff0c;在系统的角度&#xff0c;就相当于new/malloc在底层封…

MySQL之临时表

写在前面 本文一起看下MySQL的临时表。 1&#xff1a;什么是临时表 通过create temporary table t语句创建的表&#xff0c;就是临时表&#xff0c;临时表的临时体现在其是其生命周期是和会话一样的&#xff0c;当会话结束&#xff0c;即连接关闭时MySQL会自动将创建的临时表…

氨丙基咪唑离子液体(AMIBr)改性纤维素气凝胶吸附剂(CAgAMIBr)的实验要求

氨丙基咪唑离子液体(AMIBr)改性纤维素气凝胶吸附剂(CAgAMIBr)的实验要求 离子液体(ILs)&#xff0c;是完全由离子组成的液体&#xff0c;可以进一步定义为熔点低于100C的熔盐。 离子液体是在室温或接近室温下可呈现液体的液态有机盐。离子液体因具有一些优良的特性使其在分离…

树的直径 树形dp+2次dfs

题目描述 给定一棵树 T &#xff0c;树 T 上每个点都有一个权值。 定义一颗树的子链的大小为&#xff1a;这个子链上所有结点的权值和 。 请在树 T 中找出一条最大的子链并输出。 输入描述: 第一行输入一个 n,1≤n≤105。 接下来一行包含n个数&#xff0c;对于每个数 ai,−10^5…

我赢助手小技巧:学会这三招,爆款内容视频完播率提高50%(下)

上一篇我们说了爆款内容的四大共性和底层逻辑&#xff0c;今天我们来看一看如何去设置标题、封面和剧情&#xff0c;实现视频的完播率。 第三个技巧叫内容高潮。 要在3秒钟之内让用户兴趣高涨&#xff0c;把这样的脚本写出来&#xff0c;怎么样去做&#xff1f;你要把特效、悬…

PCL 生成空间圆点云

目录 一、算法原理二、代码实现三、结果展示一、算法原理 三维空间圆形式如下: 三维空间圆的参数方程: { x ( θ ) = c

蚂蚁核心架构师内部Java并发编程进阶笔记,白嫖简直太香了!

并发编程作为Java开发者很重要以及非常核心的知识&#xff0c;我希望读者朋友具备以下的预备知识&#xff1a; 希望你不是一个初学者线程安全问题,需要你接触过Java Web开发、Jdbc 开发、Web服务器、分布式框架时才会遇到基于JDK8 ,最好对函数式编程、lambda 有一定了解采用了…

thinkphp使用dompdf导出pdf(html转pdf)

目录一 、安装二、安装字体&#xff08;解决无法输出中文&#xff09;三、使用3.1 示例3.2 入参声明3.3 调用声明四、总结一 、安装 命令行安装&#xff1a; composer require dompdf/dompdf下载 GitHub Dompdf库 二、安装字体&#xff08;解决无法输出中文&#xff09; 因…

关于内存条的知识要点⑴

这些天在安装神州网信政府版的过程中&#xff0c;遇到很多计算机配置比较低&#xff0c;比如2009、2010、2012年的计算机&#xff0c;为了让用户使用顺畅一些&#xff0c;需要做一些硬件上的更改&#xff0c;比如加装内存条或者更换固态硬盘等。很多人即使是写代码的IT技术人员…

599. 两个列表的最小索引总和

599. 两个列表的最小索引总和https://leetcode.cn/problems/minimum-index-sum-of-two-lists/ 难度简单224 假设 Andy 和 Doris 想在晚餐时选择一家餐厅&#xff0c;并且他们都有一个表示最喜爱餐厅的列表&#xff0c;每个餐厅的名字用字符串表示。 你需要帮助他们用最少的索…

计算机毕业论文选题java毕业设计软件基于SSM实现的固定资产管理系统

&#x1f345;文末获取联系&#x1f345; 目录 一、项目介绍 二、开题报告 三、截图 四、源码获取 一、项目介绍 计算机毕业设计java毕设之固定资产管理系统_哔哩哔哩_bilibili计算机毕业设计java毕设之固定资产管理系统共计2条视频&#xff0c;包括&#xff1a;IT实战营…

【文献研究】国际班轮航运的合作博弈:The coopetition game in international liner shipping

背景&#xff1a;本人在整理资料时翻找出来的以前做的研究自己写的总结&#xff0c;2017年发布在《Maritime Policy & Management》期刊的一篇关于国际班轮航运合作博弈的英文文献&#xff0c;本人本着学习的目的就文献的重点内容进行了浅层次的解读&#xff0c;就自己的理…

技术状态管理计划-模板

1 引言 1.1 目的和范围 本计划规定了XXX项目技术状态管理的原则、主要内容和要求&#xff0c;是指导XXX项目以及技术状态项研制全过程的技术状态管理的基本文件&#xff0c;也是各配套研制单位在研制过程中实施技术状态管理必须遵循的基本规定。   本计划适用于XXX项目以及技…

JdbcTemplate操作数据库

文章目录一、JdbcTemplate&#xff08;概念和准备&#xff09;1、什么是JdbcTemplate2、准备工作二、JdbcTemplate操作数据库(增删改)1、对应数据库创建实体类2、编写service和dao3、测试类三、JdbcTemplate操作数据库&#xff08;查询&#xff09;1、对应数据库创建实体类2、编…