数据库优化原理解析(三):内存原理与数据库优化

内存寻址

首先内存的寻址模式主要和CPU有关系,在80x86微处理器中,地址有以下三种模式:

  • 逻辑地址(logical address):

    包含在机器语言指令中用来指定一个操作数或一条指令的地址。这种寻址方式在80x86注明的分段结构中表现的尤为具体,它促使MS-DOS活Windows程序员吧程序分成若干段。每一个逻辑地址都有一个段(segment)和偏移量(offset或displacement)组成,偏移量指明了从段开始的地方到实际地址之间的距离。

  • 线性地址(linear address)(也称虚拟地址virtual address):

    是一个32位无符号整数,可以用来表示到达4GB的地址,也就是,高达4 294 967 296个内存单元。先行地址通常用十六进制数字表示,值的范围从0X00000000到0xffffffff。是逻辑地址到物理地址变换之间的中间层。程式代码会产生逻辑地址,或说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址能再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。Intel 80386的线性地址空间容量为4G(2的32次方即32根地址总线寻址)。

  • 物理地址(physical address)

    用于内存芯片级内存单元寻址。它们与从微处理器的地址引脚发送到内存总线上的电信号相对应。物理地址由32位或36位无符号整数表示。

内存控制单元(MMU)通过一种称为分段单元(segmentation unit)的硬件电路把一个逻辑地址转换成线性地址;接着,第二个称为分页单元(paging unit)的硬件电路把线性地址转换成一个物理地址。

逻辑地址转换

在多处理器系统中,所有CPU都共享同一内存;这意味着RAM芯片可以由独立的CPU并发的访问。因为在RAM芯片上的读或写操作必须串行的执行,因此一种所谓内存仲裁器(memory arbiter)的硬件电路插在总线和每个RAM芯片之间。其作用是富国某个RAM芯片空闲,就准予一个CPU访问,如果该芯片忙于为另一个处理器提出的请求服务,就延迟这个CPU访问。

硬件中的分段

从80286模型开始,Inter微处理器以两种不同的方式执行地址转换,这两种方式分贝为实模式(real mode)和保护模式(protected mode)。实模式存在的主要原因是要维持处理器和早期模型兼容。

段选择符和段寄存器

一个逻辑地址由两部分组成:一个段标识符和一个指定段内相对地址的偏移量。段标识符是一个16位长的字段,称为段选择符(Segment Selector),而偏移量是一个32位长的字段。

TI表示段描述符存放在GDT还是存放在LDT,0表示存放在GDT。1存放在LDT。 INDEX表示的是选择索引号。就是用此索引号来在GDT中检索相应的段描述符。 RPL(requested privilege level)表示的是请求者的特权级。

为了快速方便的找到段选择符,处理器提供段寄存器,段寄存器的唯一目的是存放段选择符。这些段寄存器称为cs,ss,ds,es,fs和gs。

  • cs:代码段寄存器,指向包含程序指令的段。
  • ss:栈段寄存器,指向包含当前程序栈的段。
  • ds:数据段寄存器,指向包含今天数据或者全局数据段。

其他三个寄存器作一般用途,可以指向任意的数据段。

以上的寄存器仅表示80x86的CPU,X86-64或者其他CPU与他的架构以及指令集都不尽相同。

cs寄存器还有一个很重要的功能:它含有一个两位的字段,用以知名CPU的当前特权级(Current Privilege Level,CPL)。值为0代表最高优先级,而值为3代表最忌优先级。Linux只有0级和3级,分别称之为内核态和用户态。

段描述符

每个段由一个8字节的段描述符(Segment Descriptor)表示,它描述了段的特征。段描述符放在全局描述表(Global Descriptor Table,GDT)或局部描述符表(Local Descriptor Table,LDT)中。

通常只定义一个GDT,而每个进程除了存放在GDT中的段之外如果还需要创建附加的段,就可以有自己的LDT。GDT在主存中的地址和大小存放在gdtr控制寄存器中,当前整备使用的LDT地址和大小放在ldtr控制寄存器中。

字段名描述
Base包含段的首字节的线性地址
G粒度标志:入伙改为清0,则段大小以字节为单位,否则为4096字节的倍数
Limit存放段中最后一个内存单元的偏移量,从而决定段的长度。如果G被置为0,则一个段的大小在1个字节到1MB之间变化;否则,将在4KB到4GB之间变化
S系统标志:如果它被清0,则这是一个系统段,存储注入LDT这种关键的数据结构,否则它是一个普通的代码段或数据段
Type描述了段的类型特征和它的存取权限
DPL描述符特权级(Descriptor Privilege Level)字段:用于限制对这个段的存取。它表示为访问这个段而要求的CPU最小的优先级。
因此,DPL设为0的段只能当CPL为0时(即在内核态)才是可访问的,而DPL设为3的段对任何CPL值都是可访问的
PSegment-Present标志:等于0表示段当前不在主存中。Linux总是吧这个标志(第47位)设为1,因为它从来不把整个段交换到磁盘上去
D或B称为D或B的标志,取决于是代码段还是数据段。D或B的含义在两种抢矿洗啊稍微有所区别,但是如果段偏移量的地址是32位长,就基本上把它置为1,如果这个偏移量是16位长,它被清0
AVL标志Linux忽略

有几种不同类型的段以及和 他们对应的段描述符。

  • 代码段描述符

    表示这个段描述符代表一个代码段,它可以放在GDT或LDT中。改描述符置S标志为1(非系统段)。

  • 数据段描述符

    表示这个段描述符代表一个数据段,它可以放在GDT或LDT中。改描述符置S标志为1。栈段是通过一般数据段实现的。

  • 任务状态段描述符(TSSD)

    表示这个段描述符代表一个任务状态段(Task State Segment,TSS),也就是说这个段用于保存处理器寄存器的内容。根据相应的进程是否正在CPU上运行,其Type字段的值分别为11或9。这个描述符的S标志置为0。

快速访问段描述符

逻辑地址由16位段选择符和32位偏移量组成,寄存器仅仅存放段选择符。

为了加速逻辑地址到线性地址的转换,80x86处理器提供一种附加的非变成的寄存器(一个不能被程序员锁设置的寄存器)。每一个非编程的寄存器含有8个字节的段描述符,由响应的段寄存器中的段选择符来指定。每当一个段选择符被装入段寄存器时,相应的段描述符就由内存装入到对应的非变成CPU寄存器。从那时起,针对哪个段的逻辑地址转换就可以不访问主存中的GDT活LDT,处理器秩序直接饮用存放段描述符的CPU寄存器即可。晋档段寄存器的内容改变时,才有必要访问GDT和LDT。

段选择符字段

字段名描述
index指定了放在GDT或LDT中的相应段描述符的入口
TITI标志:指明段描述符是在GTD中(TI=0)或在LDT中(TI=1)
RPL请求者特权级:当相应的段选择符装入到cs寄存器中时指示出CPU当前的特权级;它还可以用于在访问数据段时有选择的小若处理器的特权级

Linux分段

80x86微处理器中的分段鼓励程序员把他们的程序分化成逻辑上相关的实体,例如子程序或者全局与局部数据区。然而,Linux以非常有限的方式使用分段。实际上,分段和分页在某种程度上有些重复,因为他们都可以划分进程的物理地址空间:分段可以给每一个进程分配不同的线性地址空间,而分页可以把同一线性地址空间映射到不同的物理空间。

  • 当所有进程使用相同的段寄存器值时,内存管理变得更简单,也就是说它们能共享同样的一组线性地址。
  • Linux设计目标之一是可以把它一直到绝大多数流行的处理器平台上。然而,RISC体系结构对分段的支持很有限。

硬件中的分页

分页单元(paging unit)把线性地址转换成物理地址。其中的一个关键任务是把所请求的访问类型与线性地址的访问权限相比较,入伙这次内存访问时无效的,就产生一个缺页异常。

为了效率起见,线性地址被分成以固定长度为单位的组,称为页(page)。页内部连续的线性地址被映射到连续的物理地址中。这样,内核可以指定一个页的屋里地址和其存取权限,而不用指定页所包含的全部线性地址的存取权限。我们遵循通常习惯,使用术语“页”既指一组线性地址,又指包含在这组地址中的数据。

分页单元把所有的RAM分成固定长度的页框(page frame)(有时叫做物理页)。每一个页框包含一个页(page),也就是说一个页框的长度与一个页的长度一致。页框是主存的一部分,因此也是一个存储区域。区分一页和一个页框是很重要的,前者只是一个数据块,可以存放在任何页框或磁盘中。

把线性地址银蛇到物理地址的数据结构称为页表(page table)。页表存放在主存中,并在启用分页单元之前必须由内核对页表进行适当的初始化。

从80386开始,所有的80x86处理器都支持分页,它通过设置cr0寄存器的PG标志启用。当PG=0时,线性地址就被解释成物理地址。

常规分页

从80386起,Intel处理器的分页单元处理4KB的页。

32位的线性地址被分成3个域:

  • Directory(目录)

    最高10位

  • Table(页表)

    中间10位

  • Offset(偏移量)

    最低12位

线性地址的转换分两步完成,每一步都基于一种转换表,第一种转换表称为页目录表(page directory),第二种转换称为页表(page table)。

使用这种二级模式的目的在于减少每个进程页表所需的RAM的数量。如果使用简单的一级页表,那将需要高达\(2^20\)个表项(也就是,在每项4个字节时,需要4MB的RAM)来表示每个进程的页表(如果进程使用全部4GB线性地址空间),即使

每个活动进程必须有一个分配给他的页目录。不过,没有必要马上为进程的所有页表都分配RAM。只有在进程实际需要一个也表示才给该页表分配RAM会更为有效率。

扩展分页

从Pentium模型开始,80x86微处理器引入了扩展分页(extended paging),它允许页框大小4MB而不是4KB。扩展分页用于把大段的线性地址转换成相应的物理地址,在这些情况下,内核可以不用中间页表进行地址转换,从而节省内存并保留TLB项。

正如前面所述,通过设置页目录项的Page Size标志启用扩展分页功能。在这种情况下,分页单元吧32位线性地址分成两个字段:

  • Directory

    最高10位

  • Offset

    其余22位

扩展分页和正常分页的页目录项基本相同,除了:

  • Page Size标志必须被设置。

  • 20位物理地址字段只有最高10位是有意义的。这是因为每一个物理地址都是在以4MB为边界的地方开始的,故这个地址的最低22位为0。

通过设置cr4处理器寄存器的PSE标志能使扩展分页与常规分页共存。

64位系统中的分页

32位微处理器普遍采用两级分页,然而两级分页并不适用于采用64位系统的计算机。

首先假设一个大小为4KB的标准页。因为1KB覆盖\(210\)个地址的范围,4KB覆盖\(212\)个地址,所以offset字段是12位。这样线性地址就剩下52位分配给Table和Directory字段。如果我们现在决定仅仅使用过64,剩下的48-12=36位将被分配给Table和Directory字段,如果我们现在决定为两个字段各预留18位,那么每个进程的页目录和页表都含有\(2^18\)个项,即超过256000个项。

Linux中的分页

Linux采用了一种同事适用于32位和64位系统的普通分页模型。

  • 页全局目录(Page Global Directory)
  • 页上级目录(Page Upper Directory)
  • 页中间目录(Page Middle Directory)
  • 页表(Page Table)

Linux的进程处理很大程度上依赖于分页。事实上,线性地址到物理地址的自动转换使下面的设计目标变得可行:

  • 给每一个进程分配一块不同的物理地址空间,这确保了可以有效地防止寻址错误。
  • 区别页(即一组数据)和页框(即主存中的物理地址)之不同。这就允许存放在某个页框中的一个页,然后保存到磁盘上,以后从新装入这同一页时又可以被装在不同的页框中。

伙伴系统算法

内核应该为分配一组连续的页框而建立一种健壮、高效的分配策略。为此,必须解决著名的内存管理问题,也就是所谓的外碎片(external fragmentation)。频繁的请求和释放不同大小的一组连续页框,必然导致在以分配的页框的块内分散了许多小块的空闲页框。由此带来的问题是,即使有足够的空闲页框可以满足请求,但是要分配一个大块的联系页框就可能无法满足。

从本质上说,避免外碎片的方法有两种:

  • 利用分页单元把一组非连续的空闲页框映射到连续的线性地址空间。
  • 开发一种适当的技术来记录现存的空闲连续页框块的情况,以尽量避免为满足对小块的请求而分割大的空闲块。

基于以下三个原因,内核首选第二种方法:

  • 在某些情况下,连续的页框确实是必要的,因为连续的线性地址不足以满足请求。一个典型的例子就是给DMA处理器分配缓冲区的内存请求。因为当在一次单独的I/O操作中传送几个磁盘扇区的数据时,DMA忽略分页单元直接访问地址总线,因此,锁清秋的缓冲区就必须位于连续的页框中。
  • 即使连续页框的分配并不是很必要,但它在保持内核页不变方面索契的作用也是不容忽视的。频繁的修改页表势必导致平均访问内存次数的增加,因为这会使CPU频繁的刷新转换后援缓冲器(TLB)的内容。
  • 内核通过4MB的页可以访问大块连续的物理内存。这样减少了转换后援缓冲器的失效率,因此提高了访问内存的平均速度。

内核要分配一组连续的页框,必须建立一种健壮、高效的分配策略。为此,必须解决著名的外部碎片(external fragmentation)问题。频繁地请求和释放不同大小的一组连续页框,必然导致在已分配页框的块内分散了许多小块的空闲页框。由此带来的问题是,即使有足够的空闲页框可以满足请求,但要分配一个大块的连续页框就可能无法满足。

Linux 采用伙伴系统(buddy system)算法来解决外碎片问题。把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1, 2, 4, 8, 16, 32, 64, 128, 256,512和1024 个连续的页框。对1024 个页框的最大请求对应着4MB 大小的连续RAM块。每个块的第一个页框的物理地址是该块大小的整数倍。例如,大小为16 个页框的块,其起始地址是16 × 212(212 = 4096,这是一个常规页的大小)的倍数。

我们通过一个简单的例子来说明该算法的工作原理。

假设要请求一个256 个页框的块(即1MB)。算法先在256 个页框的链表中检查是否有一个空闲块。如果没有这样的块,算法会查找下一个更大的页块,也就是,在512 个页框的链表中找一个空闲块。如果存在这样的块,内核就把256 的页框分成两等份,一半用作满足请求,另一半插入到256 个页框的链表中。如果在512 个页框的块链表中也没找到空闲块,就继续找更大的块 —— 1024个页框的块。如果这样的块存在,内核把1024个页框块的256 个页框用作请求,然后从剩余的768 个页框中拿512个插入到512个页框的链表中,再把最后的256个插入到256个页框的链表中。如果1024个页框的链表还是空的,算法就放弃并发出错信号。

内存去管理

Linux内核中基于伙伴算法实现的分区页框分配器适合大块内存的请求,它所分配的内存区是以页框为基本单位的。对于内核中小块连续内存的请求,比 如说几个字节或者几百个字节,如果依然分配一个页框来来满足该请求,那么这很明显就是一种浪费,即产生内部碎片(internal fragmentation),用slab处理。

进程地址空间

分配内核内存的默认原则

  • 内核是操作系统中优先级最高的成分。如果某个内核函数请求动态内存,那么,必定有正当的理由发出那个请求,因此,没有道理试图推迟这个请求。
  • 内核信任自己。所有的内核函数都被嘉定是没有错误的,因此内核函数不必插入针对变成错误的任何保护措施。

当个用户态进程分配内存时,情况完全不同:

  • 进程对动态内存的请求被认为是不紧迫的。例如,当进程的可执行文件被装入时,进程并不一定立即对所有的代码页进行访问。类似的,当进程调用malloc()以获得请求的动态内存时,也并不意味着进程很快就会访问所有所获得的内存。因此,一般来说,内核总是尽量推迟给用户态进程分配动态内存。

  • 由于用户进程是不可信任的,因此,内核必须能随时准备捕获用户态进程引起的所有寻址错误。

进程地址空间

进程的地址空间(address space)由允许进程使用的全部线性地址组成。每个进程所看到的线性地址是不同的,一个进程所使用的地址与另外一个进程所使用的地址之间没有什么关系。

内核通过所谓线性区的资源来表示线性地址区间,线性区是由其实线性地址、长度和一些访问权限来描述的。为了效率起见,起始地址和线性区的长度都必须是4096的倍数。

操作系统为每个进程提供了一个独立的页表,因而也就是一个独立的地址空间。

缺页

为了有助于清晰理解存储层结构中不同的缓存概念,我们将使用术语SRAM缓存来表示位于CPU和主存之间的L1、L2、L3高速缓存,并且用术语DRAM缓存来表示虚拟内存系统的缓存,他在主存中缓存虚拟页。

在虚拟内存的习惯说法中,DRAM缓存不命中称为缺页(page fault)。

DRAM比SRAM要慢大约10倍,而磁盘要比DRAM慢大约100 000多倍。因此DRAM缓存中的不命中比起SRAM缓存中的不命中要昂贵的多,这是因为DRAM缓存不命中要由磁盘来服务,而SRAM缓存不命中通常是由基于DRAM的主存来服务的。

TLB简介

页表一般都很大,并且存放在内存中,所以处理器引入MMU后,读取指令、数据需要访问两次内存:首先通过查询页表得到物理地址,然后访问该物理地址读取指令、数据。为了减少因为MMU导致的处理器性能下降,引入了TLB,TLB是Translation Lookaside Buffer的简称,可翻译为“地址转换后援缓冲器”,也可简称为“快表”。简单地说,TLB就是页表的Cache,其中存储了当前最可能被访问到的页表项,其内容是部分页表项的一个副本。只有在TLB无法完成地址翻译任务时,才会到内存中查询页表,这样就减少了页表查询导致的处理器性能下降。

TLB中的项由两部分组成:标识和数据。标识中存放的是虚地址的一部分,而数据部分中存放物理页号、存储保护信息以及其他一些辅助信息。虚地址与TLB中项的映射方式有三种:全关联方式、直接映射方式、分组关联方式。OR1200处理器中实现的是直接映射方式,所以本书只对直接映射方式作介绍。直接映射方式是指每一个虚拟地址只能映射到TLB中唯一的一个表项。假设内存页大小是8KB,TLB中有64项,采用直接映射方式时的TLB变换原理如图10.4所示。

由于页表存放在主存中,因此程序每次访存至少需要两次:一次访存获取物理地址,第二次访存才获得数据。提高访存性能的关键在于依靠页表的访问局部性。当一个转换的虚拟页号被使用时,它可能在不久的将来再次被使用到,。

TLB是一种高速缓存,内存管理硬件使用它来改善虚拟地址到物理地址的转换速度。当前所有的个人桌面,笔记本和服务器处理器都使用TLB来进行虚拟地址到物理地址的映射。使用TLB内核可以快速的找到虚拟地址指向物理地址,而不需要请求RAM内存获取虚拟地址到物理地址的映射关系。这与data cache和instruction caches有很大的相似之处。

Linux 大页面

随着计算需求规模的不断增大,应用程序对内存的需求也越来越大。为了实现虚拟内存管理机制,操作系统对内存实行分页管理。自内存“分页机制”提出之始,内存页面的默认大小便被设置为 4096 字节(4KB),虽然原则上内存页面大小是可配置的,但绝大多数的操作系统实现中仍然采用默认的 4KB 页面。 4KB 大小的页面在“分页机制”提出的时候是合理的,因为当时的内存大小不过几十兆字节,然而当物理内存容量增长到几 G 甚至几十 G 的时候,操作系统仍然以 4KB 大小为页面的基本单位,是否依然合理呢?

在 Linux 操作系统上运行内存需求量较大的应用程序时,由于其采用的默认页面大小为 4KB,因而将会产生较多 TLB Miss 和缺页中断,从而大大影响应用程序的性能。当操作系统以 2MB 甚至更大作为分页的单位时,将会大大减少 TLB Miss 和缺页中断的数量,显著提高应用程序的性能。这也正是 Linux 内核引入大页面支持的直接原因。好处是很明显的,假设应用程序需要 2MB 的内存,如果操作系统以 4KB 作为分页的单位,则需要 512 个页面,进而在 TLB 中需要 512 个表项,同时也需要 512 个页表项,操作系统需要经历至少 512 次 TLB Miss 和 512 次缺页中断才能将 2MB 应用程序空间全部映射到物理内存;然而,当操作系统采用 2MB 作为分页的基本单位时,只需要一次 TLB Miss 和一次缺页中断,就可以为 2MB 的应用程序空间建立虚实映射,并在运行过程中无需再经历 TLB Miss 和缺页中断(假设未发生 TLB 项替换和 Swap)。

为了能以最小的代价实现大页面支持,Linux 操作系统采用了基于 hugetlbfs 特殊文件系统 2M 字节大页面支持。这种采用特殊文件系统形式支持大页面的方式,使得应用程序可以根据需要灵活地选择虚存页面大小,而不会被强制使用 2MB 大页面。本文将针对 hugetlb 大页面的应用和内核实现两个方面进行简单的介绍,以期起到抛砖引玉的作用。

大页优化

适用于 x86 平台的虚拟内存架构

与最初时相比,x86 和 x86-64 芯片组的内存架构已经发生巨大变化;但默认内存页面大小却一直未变。遇到使用大量内存的大型应用程序(如数据库)时,这可能导致效率低下或开销过大。

x86 架构是一种虚拟内存架构,其允许寻址范围超过硬件中的可用物理内存。这通过允许每个进程拥有自己可寻址的内存来实现。该进程认为此内存是专供自己使用的。这称为进程的虚拟内存。实际上,此内存可以是实际驻留于 RAM 芯片上的物理内存,也可以存储在物理磁盘上被称作交换区 或分页区 的专用区域中。

进程不知道虚拟内存是存储在 RAM 中还是磁盘上;内存由操作系统管理。如果所需内存超过可用物理内存,操作系统会将一些内存移出到分页区。这种活动效率极低,是导致性能问题的常见原因。由于磁盘的存取速度远低于 RAM,“分页”的进程会遇到显著的性能问题。

Oracle 数据库和 Linux 内存管理

系统中使用的内存越多,管理该内存所需的资源也就越多。对于 Linux 操作系统,通过 Linux kswapd 进程和页表内存结构(针对系统中存在的每个进程包含一条记录)实现内存管理。每条记录包含进程使用的每页虚拟内存及其物理地址(RAM 或磁盘)。通过使用处理器的转换旁路缓冲区(TLB,一小块缓存)为该进程提供帮助。

当大量内存用于 Oracle 数据库时,操作系统将消耗大量资源来管理虚拟地址到物理地址转换,其结果往往是一个非常大的页表结构。由于每条页表条目包含进程正在使用的所有内存页面的虚拟地址到物理地址的转换,因此对于非常大的系统全局区 (SGA),每个进程的页表条目都可能很大。例如,使用 8 GB 内存的 Oracle 数据库进程的页表条目将达 8 GB/4 KB(即 2097152 条记录或页面)。如果有 100 个 Oracle 数据库会话/进程,则将页面数乘以 100。您可以看到,要管理的页面数量巨大。

同样,操作系统使用页表条目管理系统中进程所用的内存。在 Linux 中,执行此管理的操作系统进程被称作 kswapd,可在操作系统工具中找到。

TLB 缓存将缓存页表条目来提高性能。典型的 TLB 缓存可保存 4 到 4096 个条目。对于数百万甚至数十亿个页表条目,这种缓存就不够用了。

如上所述,对于使用大型 SGA 的系统,页表结构可能会变得非常大。清单 1 中的 Linux 系统输出示例显示页表条目占用了 766 MB 的 RAM。这可能导致显著的系统开销。我亲眼见过数 GB 的页表条目。

HugePages 是 Linux 操作系统的一个内核特性,让操作系统可以支持现代硬件架构的大页面容量功能。对于 Oracle 数据库,通过启用 HugePages 并使用大页面,可以用一个页表条目代表一个大页面,而不是使用许多条目代表较小的页面,从而可以管理更多内存,减少操作系统对页面状态的维护并提高 TLB 缓存命中率。在 Linux 中,大页面大小为 2 MB。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值