ext4的fsync性能和nodelalloc参数的分析

ext4延迟分配致断电后文件空洞:原理、排查与数据恢复 在Linux运维中,文件系统异常断电后出现文件大小正常但实际数据缺失的“空洞文件”问题,常被误判为磁盘损坏或文件系统故障。实际上,这往往源于ext4的延迟分配机制——数据先缓存在页缓存中,异常断电导致未落盘数据丢失,而inode中的文件大小已更新,造成逻辑与物理存储不一致。理解从write()到落盘的完整流程,以及extent树如何记录块分配,是定位问题的关键。fsck仅检查元数据一致性,无法识别此类逻辑空洞。通过ls与du对比、filefrag和debugfs工具可确认故障范围,而应用层调用fsync/f 阅读详情

原文:http://blog.thinksrc.com/?p=189001

感叹归感叹,发泄完了还得继续过。

前几天忙的不可开交,周报上面竟然能列出11项,想想以前在T公司时候的清闲,现在的老板的真幸运了。

好了,言归正传。

我们的系统是使用ext4作为文件系统的,ext4怎么好呢? 主要是我对它感觉比较好,呵呵,开玩笑的。还记得第一次使用一个全新的ext4作为文件系统(不是ext3转过来的)时候感觉性能的feeling,应该用神奇来形容。

在我们android系统上使用ext4呢,主要是觉得它mount比较快,这样开机时间会很快。还记得当年在一点一点的抠启动时间,从40秒终于搞到30秒以内了,结果现在到了gingerbread(2.3)以后,没什么特别优化感觉都已经跑入15秒以内了。

现在遇到的问题是我们在跑一个benchmark的时候,分数比竞争对手低好几倍。总是自我感觉不良好的我们认为这可能就是我们比人家慢吧。这种故事通常的结果就是,到了实在要命的时候,比如一个很大的客户在挑战的时候。就要开始进去查了,好的,这次是我进去了。

调查的手段呢,第一个想到的就是strace,因为是IO嘛,必定和系统调用有关,所以strace肯定能够看出来一个一二三的,再加上strace的时间打印,就可以大概看出来哪些操作比较慢了。果然有发现,通过strace,发现fsync(3)消耗很多时间,中间甚至进程都出现了明显的调度出去,至于write,read这些操作,倒也不知道快慢。就先看这个fsync()为什么这么费时间吧。 其实一开始就怀疑是fsync()搞的鬼,因为有一个问题就是我们之前的kernel版本是2.6.31,这个bechmark跑的就很高,而升级到2.6.35上以后,这个分数就下降到1/3这么多。

还有一个类似的问题就是USB Mass Stroage的性能,在2.6.31上的写性能就很快,而2.6.35上的写性能就奇慢。而USM的f_storage.c里面是调用vfs_write()来进行写Block设备。通过把vfs_write()和mmc的command dump出来发现。原来在2.6.31上,加上了F_SYNC参数的vfs_write()在mmc这层,还是乱序的。而在2.6.35上,发现每一条vfs_write()都对应几条mmc命令,等这几条命令发完以后,才去从USB那里传数据,这样就成了一个很傻很慢的家伙了。而为什么2.6.31上明明加上了F_SYNC参数还是会乱序的写,我想这是一个BUG吧,在2.6.35上修复了而已。

所以这里的ext4文件系统fsync()慢可能也是和这个有关系的。但是作为嵌入式设备,随处会面对掉电的风险。所以掉电保护就很重要,不能说为了性能吧所有sync的写都变成un-sync的写,那些数据丢失会比较严重。

Google了两天,发现很多关于fsync和ext4的讨论,放在这里一些万一别人要看呢, [1]

无头绪,于是继续看ext4在kernel里面的文档,看到了mount参数这节,忽然灵机一动想起换换mount参数跑这个benchmark会不会有所改进呢?

于是就把那些看似和write相关的参数都做了一个表格。

Ext4 with different option nobarrier nodelalloc journal_async_commit  
         
(no combine) 558 1087 524  
nobarrier NA NA 522  
nodelalloc 1052 N\A 1051  
journal_aysnc_commit        
& nobarrier & nodelalloc        

可以看的出来,nodelalloc在这里贡献非常大。几乎是一倍的分数。

为什么这个参数nodelalloc会这样呢,这是它的文档中的解释:

delalloc        (*)     Defer block allocation until just before ext4
                        writes out the block(s) in question.  This
                        allows ext4 to better allocation decisions
                        more efficiently.

nodelalloc              Disable delayed allocation.  Blocks are allocated
                        when the data is copied from userspace to the
                        page cache, either via the write(2) system call
                        or when an mmap'ed page which was previously
                        unallocated is written for the first time.
先看这个delalloc, 这是默认值, 就是说把所有的block分配推后到真正要写数据的时候,当有sync调用的时候,也就是这种时候。

而关掉这个默认feather以后,块号就会在page cache的时候分配。 如果区别只是这里, 就无法解释为什么分配块号会花费这么多的时间了。是的,瓶颈不在这里。

我们接着看fsync()这个系统调用,它在手册里面的解释是:

 fsync() transfers ("flushes") all modified in-core data of (i.e., modi‐
       fied  buffer cache pages for) the file referred to by the file descrip‐
       tor fd to the disk device (or other  permanent  storage  device)  where
       that  file  resides.

所以它仅仅要求文件系统把所有*该文件*的修改写到磁盘中。

然后我们去看看ext4对于它的实现。

 

/*
 * akpm: A new design for ext4_sync_file().
 *
 * This is only called from sys_fsync(), sys_fdatasync() and sys_msync().
 * There cannot be a transaction open by this task.
 * Another task could have dirtied this inode.  Its data can be in any
 * state in the journalling system.
 *
 * What we do is just kick off a commit and wait on it.  This will snapshot the
 * inode to disk.
 *
 * i_mutex lock is held when entering and exiting this function
 */

int ext4_sync_file(struct file *file, int datasync)
{
        struct inode *inode = file->f_mapping->host;
        struct ext4_inode_info *ei = EXT4_I(inode);
        journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
        int ret;
        tid_t commit_tid;

        J_ASSERT(ext4_journal_current_handle() == NULL);

        trace_ext4_sync_file(file, datasync);

        if (inode->i_sb->s_flags & MS_RDONLY)
                return 0;

        ret = flush_completed_IO(inode);
        if (ret < 0)
                return ret;
        if (!journal) {
                ret = generic_file_fsync(file, datasync);
                if (!ret && !list_empty(&inode->i_dentry))
                        ext4_sync_parent(inode);
                return ret;
        }

        /*
         * data=writeback,ordered:
         *  The caller's filemap_fdatawrite()/wait will sync the data.
         *  Metadata is in the journal, we wait for proper transaction to
         *  commit here.
         *
         * data=journal:
         *  filemap_fdatawrite won't do anything (the buffers are clean).
         *  ext4_force_commit will write the file data into the journal and
         *  will wait on that.
         *  filemap_fdatawait() will encounter a ton of newly-dirtied pages
         *  (they were dirtied by commit).  But that's OK - the blocks are
         *  safe in-journal, which is all fsync() needs to ensure.
         */
        if (ext4_should_journal_data(inode))
                return ext4_force_commit(inode->i_sb);

        commit_tid = datasync ? ei->i_datasync_tid : ei->i_sync_tid;
        if (jbd2_log_start_commit(journal, commit_tid)) {
                /*
                 * When the journal is on a different device than the
                 * fs data disk, we need to issue the barrier in
                 * writeback mode.  (In ordered mode, the jbd2 layer
                 * will take care of issuing the barrier.  In
                 * data=journal, all of the data blocks are written to
                 * the journal device.)
                 */
                if (ext4_should_writeback_data(inode) &&
                    (journal->j_fs_dev != journal->j_dev) &&
                    (journal->j_flags & JBD2_BARRIER))
                        blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL,
                                        NULL, BLKDEV_IFL_WAIT);
                ret = jbd2_log_wait_commit(journal, commit_tid);
        } else if (journal->j_flags & JBD2_BARRIER)
                blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL,
                        BLKDEV_IFL_WAIT);
        return ret;
}

 

我们的文件系统是以ordered的方式mount的,所以要调到的函数基本是:

flush_completed_IO()
jdb2_log_start_commit()
jdb2_log_wait_commit()

所以我们可以看到,对于一条fsync(), ext4会把所有的日志都commit掉,所以这才是真正慢的地方。 所以在需要经常做fsync()的应用下,比如sqltie就是一点典型例子。但是我觉得这个功能对于磁盘设备得大于失,但是对于闪存类型的设备,就没什么优势了。

后来又做一个一个在O_SYNC参数下面的write性能对于关不关delalloc的对比:  这里是的Y轴是差值,高于0就是delalloc的性能好,低于就是差。 X轴代表一次write操作的单元, 不同颜色的线代表不同的文件大小。 单位都是KB 

 

从图上可以看出,对于很大的文件,16M的文件,几乎所有的情况都是delolloc的性能要好。 但是对于64K-512K的文件, 性能就要差很多。

对于文件unit的大小,可以看得出来256K是一个分水岭。 在接近256K的时候,延迟分配性能就要好很多,这个原因是因为我们的L2缓存是256K, 所以当写的数据接近256K的时候,由于延迟分配技术不用去分配Block 块,所以大部分的memory write都可以用来作为文件写page cache,如果有了分配block这些数据,就会导致cache不对其,所以性能就会比延迟分配差很多。

还有一个地方是L1缓存(我们的是32K),这里前面小于L1的写都是延迟分配要快很多。原因和前面类似,但是不同的是接近L1的时候,反而都是不延迟分配要快一些,这点不知道怎么解释。可能的原因是在L1从L2中取数据的时延比较小.

这里还有一个有趣的地方是,对于512K大小的unit,delalloc的性能就要明显达到一个最高点。这是为啥呢?

【注】想起一个事情, 为什么512K是一个特殊的点呢? 因为512K是mmc设备的defualt block size. 但是对于为什么去掉延迟写入的性能会高那么多呢?很有意思。

[1] right thing, but really affect performance  http://postgresql.1045698.n5.nabble.com/ext4-finally-doing-the-right-thing-td2076089.html

[2] This patch fix it. http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=5f3481e9a80c240f169b36ea886e2325b9aeb745

阿里云本地存储技术演进:从临时盘到FAST26最佳论文 在云存储体系中,本地盘与云盘是两条截然不同的技术路径。云盘具备分布式冗余和弹性扩展能力,但IO路径长、延迟受网络限制;本地盘则依托NVMe SSD直连物理机,能以极低延迟和超高吞吐释放硬件极限,却长期因数据可靠性不足而被视为临时存储。如何让本地盘兼顾性能与可靠,是云基础设施领域公认的技术难题。阿里云通过重构IO软件栈、引入故障预测与快速重建机制,将本地盘从“临时工”升级为企业级生产存储,并凭借此项工作斩获FAST26最佳论文。这项技术突破不仅支撑了Redis缓存、Elasticsearch索引、大数据Shu 阅读详情

相关推荐

【QNX+Android虚拟化方案】37 - Android Kernel 各 IOSCHED 内核 IO 调度算法性能测试评估(MQ_DEADLINE、KYBER、BFQ)

【QNX+Android虚拟化方案】37 - Android Kernel 各 IOSCHED 内核 IO 调度算法性能测试评估(MQ_DEADLINE、KYBER、BFQ)

|~~~热爱生活、努力学习的小伙汁~~~| 1564

tidb磁盘挂载参数

ext4是第四代扩展文件系统,是ext3文件系统的后继版本,相比ext3,ext4取消子目录限制,支持的文件卷达到1EB,最大文件支持达到16TB,对于大数据存储足够,ext4在fsck(file system check)做了大量优化,相比ext3用时少很多。读取文件时,将禁用对元数据的更新。提到nodealloc需要先知道delalloc, delalloc是说把所有的block分配推到真正要写数据的时候,之前有淘宝内核在线上业务中反馈出delalloc延迟分配导致的写延迟问题,所以禁用延迟分配。

u012565458的博客 997

【开源技术随笔】007 - 如何将Android Userdata镜像从EXT4格式修改为F2FS 格式

【开源技术随笔】007 - 如何将Android Userdata镜像从EXT4格式修改为F2FS 格式

|~~~热爱生活、努力学习的小伙汁~~~| 721

centos7 挂载硬盘命令

查看数据盘 fdisk -l 创建分区表 parted -s -a optimal /dev/sdb mklabel gpt -- mkpart primary ext4 1 -1 格式化文件系统 mkfs.ext4 /dev/sdb 查看数据盘分区 UUID lsblk -f 编辑 /etc/fstab 文件,添加 nodelalloc 挂载参数 vi /etc/fstab UUID=0415a884-7026-4d92-b9cd-d264b621819f /data1 ext4 defaults,no.

jk6801251的专栏 1301

【QNX+Android虚拟化方案】36 - 将Android GVM userdata文件系统从 EXT4 修改为 F2FS,及各fsync_mode 读写性能对比测试

【QNX+Android虚拟化方案】36 - 将Android GVM userdata文件系统从 EXT4 修改为 F2FS,及各fsync_mode 读写性能对比测试

|~~~热爱生活、努力学习的小伙汁~~~| 1348

别再算错了!深入SAP FI后台,看懂外币清账时汇兑损益的自动计算逻辑

刚接手一家跨国企业的SAP财务模块优化项目时,我遇到了一个棘手案例:每月末外币清账产生的汇兑损益金额总是与财务团队的预期存在差异。最令人困惑的是,系统似乎"擅自"计算出了1989.11元的损益,而财务人员反复手工验算都无法复现这个数字。这让我意识到,要真正掌握SAP FI的外币处理能力,必须深入理解系统后台的汇兑损益计算逻辑——不是停留在操作层面,而是穿透到算法内核。这个过程的精妙之处在于,损益金额不是简单比较两个时点的市场汇率,而是反映业务执行全周期的汇率影响。:损益金额与手工计算不符。

weixin_32932149的博客 473

深入解析ext4文件系统:大文件与稀疏文件处理机制

文件系统作为操作系统管理存储资源的核心组件,其设计直接影响数据存取效率与可靠性。ext4作为Linux主流文件系统,通过extent树状结构和延迟分配等创新机制,显著提升了大文件处理性能。在存储技术领域,ext4的稀疏文件实现通过智能块管理,为虚拟化环境和科学计算等场景提供高效存储方案。工程师在实际部署时,需要特别关注ext4的块分配策略与xattr扩展属性系统,这些特性在处理视频编辑、数据库备份等IO密集型任务时表现尤为突出。合理利用fallocate预分配和e4defrag碎片整理工具,可以进一步优化存

weixin_34318272的博客 335

win10+conda+pytorch-gpu安装

pytorch 官网上只有linux和Mac的程序包,没有windows系统的,但是windows系统还是可以用pytorch的。  github: https://github.com/peterjc123/pytorch-scripts 首先要安装anaconda: 推荐清华镜像 https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ ...

一摩尔自由的博客 1万+

ext4延迟分配引发掉电断尾:fsck查不出、恢复不了的数据丢失

在Linux服务器运维中,文件系统写入机制远比表面复杂。当突发掉电导致文件大小正常但内容缺失时,很多人第一反应是运行fsck修复,却往往发现文件系统报告clean、数据却永久丢失。这背后是ext4延迟分配机制在起作用:数据先进入页缓存,物理块映射稍后才会写入磁盘,掉电瞬间未兑现的块映射随内存消失,形成“断尾”现象。fsck只检查元数据一致性,无法识别这种“数据从未真正落盘”的逻辑缺失。理解从write()调用到数据落盘的完整链路,掌握stat、debugfs等工具分析extent映射,才能定位问题本质。对于

cuiji1279的博客 385

Qt Creator菜单栏消失的终极修复指南:从快捷键冲突到设置恢复

本文详细解析了Qt Creator菜单栏消失的常见原因及修复方法,包括快捷键冲突排查、配置文件重置和界面模式切换等解决方案。特别针对Qt开发者遇到的菜单栏显示问题,提供了从简单快捷键操作到深度配置修改的全方位指南,帮助用户快速恢复Qt Creator的正常使用。

weixin_27240351的博客 338

使用Qt Creator调用eBUS SDK

在新界面,勾选左下角“ Show unreachable Network Devices",可以看到没有成功连接的网络相机,原因是IP地址配置错误。每个版本的OpenCV的依赖项的名字都不同,我这里是opencv_world490.lib与opencv_world490d.lib。函数,可以看到在这里是图像缓存区,能够对捕获的图像进行一些操作。在Visual Studio打开项目属性,在VC++目录这里,把Opencv的路径添加进去。这时,会自动跳转到mainwindow.cpp,生成该按钮对应的槽函数。

qq_35685051的博客 1287

GE-Proton 快速上手:5步装好+调优

GE-Proton 是基于 Wine 的社区版 Steam 兼容层,当前版本 GE-Proton11-11,适合装了 Steam 的 Linux 玩家。相比官方 Proton,它多给了视频过场修复、全屏 FSR 超分、NVIDIA NVAPI/PhysX 支持,以及按游戏自动生效的 protonfixes 补丁。下文按"装 → 选 → 调 → 救火"的顺序讲,每一节都能单独看。 ## 装好之后,

gitblog_00047的博客 853

《点亮创新灯塔!AI应用架构师与中小学初等教育AI智能体,共筑智能化教育辅助创新灯塔》

中小学AI智能体,是基于AI技术,能感知教育场景、理解教育规律、辅助教师教学和学生学习的智能系统。做老师“做不了”的事:比如5分钟批改45本作业,精准统计每个学生的易错点;做老师“做不好”的事:比如根据学生的学习风格(视觉/听觉/动觉)推荐个性化内容;绝不做老师“该做的事”:比如情感交流、价值观引导、创意教学设计——这些“人的温度”,AI永远无法替代。我是林宇,资深AI应用架构师,专注教育AI领域8年,参与过10多个中小学AI智能体项目,曾为北京、上海、深圳等城市的100多所学校提供AI教育解决方案。

Java大师兄的博客 432

F2FS学习一:简介

什么是闪存友好文件系统(F2FS)? ================================================== ============================= 基于NAND闪存的存储设备(例如SSD,eMMC和SD卡)具有 已安装在从移动系统到服务器系统的各种系统上。自从 已知它们具有与传统旋转不同的特性 磁盘,文件系统,存储设备的上层,应适应 在设计级别从草图更改。 F2FS是利用基于NAND闪存的存储设备的文件系统, 基于日志结构的文件系统(L...

guozhidixian的博客 4885

HFSS实战:手把手教你设计微带发夹线滤波器(附完整参数计算流程)

本文详细介绍了使用HFSS设计微带发夹线滤波器的完整流程,包括参数计算、HFSS建模和优化策略。通过实战案例,帮助读者掌握微波滤波器设计的核心技巧,特别是奇偶模阻抗计算和HFSS仿真中的关键设置,适用于通信系统设计和射频工程应用。

weixin_29251567的博客 440

把核心变干净的代价与回报:为什么推动 Clean Core,几乎绕不开 SAP Integration Suite 与 API Management

Clean Core的核心价值在于构建可持续升级的ERP系统架构,其关键不在于减少代码或限制扩展,而是通过解耦和治理实现系统边界的清晰划分。这一理念将推动企业从传统的"私搭乱建"式集成向平台化工程转型,而SAP Integration Suite和API Management正是实现这一转型的核心组件。 Integration Suite作为集成中枢,通过统一平台、可复用内容库和事件驱动架构,将碎片化连接转变为标准化能力;API Management则负责建立接口治理体系,通过统一入口、

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 241

Centos7.4下搭建生产环境的TiDB集群

参考官网:https://docs.pingcap.com/zh/tidb/stable/hardware-and-software-requirements 一、在 TiKV 部署目标机器上添加数据盘 EXT4 文件系统挂载参数 生产环境部署,建议使用 EXT4 类型文件系统的 NVME 类型的 SSD 磁盘存储 TiKV 数据文件。这个配置方案为最佳实施方案,其可靠性、安全性、稳定性已经在大量线上场景中得到证实。 使用 root 用户登录目标机器,将部署目标机器数据盘格式化成 ext4 文件系统,挂载

qq_42869878的博客 976

【科研数据获取】根据Excel表个中的企业/公司信息批量获取经纬度坐标,并进行地图可视化

批量获取企业/公司的经纬度坐标,并进行地图可视化1 需求2 实现途径3 需求逐步实现 1 需求 在做项目的时候,老板或者项目答辩时候甲方最喜欢看到的就是可视化结果,尤其是涉及到地图可视化。很多时候会遇到企业/公司的数据,想要做地图可视化,必不可少的就是要获取它们具体地址:第一种就是像查户口一样精准一下具体的城镇街道,还有一种就是经纬度信息,前一种属于文字内容往往用于管理方面,第二种数字信息多用于数据处理与分析。 2 实现途径 获取地理信息数据,自然需要使用到地图的功能,这里直接采用百度地图的拾取坐标系统,

lys_828的博客 2336

Android文件系统

Android文件系统 device/qcom/trinket/AndroidBoard.mk ifeq ($(strip $(BOARD_DYNAMIC_PARTITION_ENABLE)),true) include $(CLEAR_VARS) LOCAL_MODULE := fstab.qcom LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := ETC ifeq ($(ENABLE_AB), true) LOCAL_SRC_FILE

特立独行的博客 5287

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

在开发环境使用 TiUP安装TiDB集群

TiUP 是 TiDB 4.0 版本引入的集群运维工具,TiUP cluster 是 TiUP 提供的使用 Golang 编写的集群管理组件,通过 TiUP cluster 组件就可以进行日常的运维工作,包括部署、启动、关闭、销毁、弹性扩缩容、升级 TiDB 集群;管理 TiDB 集群参数。 目前TiUP 可以支持部署 TiDB、TiFlash、TiDB Binlog、TiCDC,以及监控系统。 一 实验环境 本文实验环境如下: 这里将Tiup,监控,grafana,告警灯都部署在PD上。.

雅冰石的专栏 2562

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

RK Android平台磁盘读写性能优化

RK Android平台磁盘读写性能优化 适用于RK平台的Android10/11/12支持的各个芯片如:RK3326/RK3399/RK3368/RK3288/RK3126C/RK356X/RK3588

weixin_43245753的博客 2898

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

内核点滴——delalloc

延迟分配是一个性能优化技术,好几个文件系统中都使用延迟分配技术,比如XFS,ZFS,btrfs和Ext4。和传统文件系统Ext2/Ext3块分配相比,延迟分配尽可能的延缓块的分配。传统的nodelalloc 数据从用户空间复制到page cache时,立即在磁盘上分配块空间。 举例来说,对于一个写调用,文件系统代码立刻分配数据块的存放位置,甚至是数据还会在cache中存放一段时间才写回磁盘的情况

gzxultra的博客 1029

在线安装TiDB集群

服务器准备 说明:TiDB8需要能够连接外网,以便下载各类安装包 TiDB4非必须,但最好是有一台,因为后续测试Mysql数据同步或者进行性能比较时,都要用到 TiKV最好是采用Ext4文件格式,所以使用挂载盘的方式增加(如果没有数据盘,那么不配置也能安装成功) 机器名 IP 操作系统 配置 用途 TiDB...

weixin_34365417的博客 179
上一篇: build/envsetup.sh分析
下一篇: Mount 使用方法 (中文版man mount)
evilcode
博客等级 码龄20年 307粉丝 16原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值