MongoDB快速精通

一、MongoDB简介

英文文档地址:https://www.mongodb.com/docs/manual/

中文文档地址:https://docs.mongoing.com/

1.认识NoSQL

NoSQL最常见的解释是“non-relational”,“Not Only SQL”也被很多人接受。NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它不保证关系数据的ACID特性。

1.1关系型与非关系型对比

传统关系型数据库数据存储是结构化的,每一张表都有严格的约束信息;而对于NoSQL数据库没有严格的约束信息,数据存储自由松散。

结构化是指每一张表都有严格的约束信息。比如很受企业欢迎的MySQL数据库,每一列都由字段名,字段数据类型,字段约束等信息构成,数据插入必须遵守这些约束和规范。

非结构化是存储数据没有固定的字段名称,没有固定的长度、没有字段约束的存储格式。非结构化存储的常见类型有:键值型、文档存储型、图存储型数据库。

image-20230124151929748

2.常见应用场景存在问题

2.1常见应用场景

  • 社交场景:
    • 存储用户与好友间聊天消息
    • 存储朋友圈海量数据
    • 搜索附近的人
  • 电商场景:
    • 查询快递位置
  • 金融场景:
    • 查询用户征信信息
    • 查询用户运营商数据

image-20230124152233833

2.2常见应用场景特点

  • 数据量大
  • 读写操作频繁
  • 价值较低的数据,对事务性要求低

2.3产生问题

这些应用场景产生了”三高”问题:

  • High performance - 对数据库高并发读写的需求。
  • Huge Storage - 对海量数据的高效率存储和访问的需求。
  • High Scalability && High Availability- 对数据库的高可扩展性和高可用性的需求。

2.4三高问题解决方式

  • 千万级数据量,单机MySQL服务就很香

    image-20230124152545993

  • 上亿数量级,使用MySQL分库分表解决

    image-20230124152630900

分库分表解决存在运维复杂,拓展不方便的问题,那有没有一种更高效、更低成本的技术来解决呢?答案是有的,使用MongoDB就可以很好解决这一问题。

3.MongoDB概述

MongoDB是基于分布式文件存储的数据库,是一个开源、高性能、无模式的文档型数据库,是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。

3.1MongoDB与MySQL对比

  • 结构上

在这里插入图片描述

  • 数据查询上

    image-20230124152958236

  • 术语上

    image-20230124153018254

3.2MongoDB数据模型

MongoDB的最小存储单位就是文档(document)对象。文档(document)对象对应于关系型数据库的行。数据在MongoDB中以 BSON(Binary-JSON)文档的格式存储在磁盘上。BSON和JSON一样,支持内嵌的文档对象和数组对象,但是BSON有JSON没有的一些数据类型,如定义代码。

BSON数据类型参考列表:

image-20230124153129932

3.3MongoDB特点

  • 高性能

    • MongoDB提供高性能的数据持久性
    • 对嵌入式数据模型的支持减少了数据库系统上的I/O活动
    • 支持索引查询,并且可以包含来自嵌入式文档和数组的键。(文本索引解决搜索的需求、TTL索引解决历史数据自动过期的需求、地理位置索引可用于构建各种 O2O 应用)
  • 高可用性

    • MongoDB提供了复制功能副本集(replica set),提供自动故障转移
  • 高扩展性

    • MongoDB提供了水平可扩展性,提供了分片集群功能,能实现服务能力水平扩展,实现海量数据的存储
  • 丰富的查询支持

    • MongoDB支持丰富的查询语言,支持读和写操作(CRUD),比如数据聚合、文本搜索和地理空间查询等
  • 其他特点:如无模式(动态模式)、灵活的文档模型

4.MongoDB单机版安装

4.1下载MongoDB安装包

MongoDB下载地址:https://www.mongodb.com/try/download/community

image-20230124153524355

MongoDB的版本命名规范如:x.y.z;

  • y为奇数时表示当前版本为开发版,如:1.5.2、4.1.13;
  • y为偶数时表示当前版本为稳定版,如:1.6.3、4.0.10;
  • z是修正版本号,数字越大越好。

image-20230124153803123

4.2上传解压

  • 解压压缩包到指定位置

    #解压到刚创建好的目录
    tar -xvf mongodb-linux-x86_64-rhel70-6.0.4.tgz -C /usr/local
    #重命名
    mv mongodb-linux-x86_64-rhel70-6.0.4/ ./mongodb
    
  • 创建目录存放数据和日志文件

    #创建单机版数据存放文件夹
    mkdir -p /usr/local/mongodb/single/data/db
    #创建单机版日志存放文件夹
    mkdir -p /usr/local/mongodb/single/log 
    
  • 创建配置文件

    vim /usr/local/mongodb/single/mongod.conf
    
  • 配置文件内容如下

    systemLog:
      #MongoDB发送所有日志输出的目标指定为文件
      #The path of the log file to which mongod or mongos should send all diagnostic logging information
      destination: file
      #mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径
      path: "/usr/local/mongodb/single/log/mongod.log"
      #当mongos或mongod实例重新启动时,mongos或mongod会将新条目附加到现有日志文件的末尾。
      logAppend: true
    storage:
      #mongod实例存储其数据的目录。storage.dbPath设置仅适用于mongod。
      ##The directory where the mongod instance stores its data.Default Value is "/data/db".
      dbPath: "/usr/local/mongodb/single/data/db"
      journal:
        #启用或禁用持久性日志以确保数据文件保持有效和可恢复。
        enabled: true
    processManagement:
      #启用在后台运行mongos或mongod进程的守护进程模式。
      fork: true
    net:
      #服务实例绑定的IP,默认是localhost
      bindIp: 192.168.222.135
      #bindIp
      #绑定的端口,默认是27017
      port: 27017
    

4.3安装MongoDB Shell

下载地址:https://www.mongodb.com/try/download/shell

image-20230124164802532

  • 上传解压

    tar -xvf mongosh-1.6.2-linux-x64.tgz -C /usr/local/
    #重命名
    mv mongosh-1.6.2-linux-x64/ mongosh
    
  • 添加命令到环境变量

    vim ~/.bash_profile
    
    #添加值如下
    PATH=$PATH:$HOME/bin:/usr/local/mongodb/bin:/usr/local/mongosh/bin
    
  • 重新加载环境变量

    source ~/.bash_profile
    

4.4启动MongoDB服务

  • 命令

    /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/single/mongod.conf
    
  • 查看是否启动成功

    ps -ef|grep mongodb
    

    image-20230124160307056

  • 开放端口方便windows远程访问

    firewall-cmd --zone=public --add-port=27017/tcp --permanent
    firewall-cmd --reload
    

4.5Shell 连接 MongoDB服务

  • 通过mongosh 连接到 MongoDB连接

    #默认连接到 mongodb:localhost:27017
    mongosh
    #或者指定ip + port
    mongosh --host=192.168.222.135 --port=27017
    
  • 查看所有数据库

    show databases
    
  • 退出

    test> exit
    #或者按两次 ctrl + c
    

4.4compass客户端

  • 下载MongoDB客户端连接工具 https://www.mongodb.com/try/download/compass

image-20230124161735146

  • 运行客户端
image-20230124171956330

二、MongoDB CRUD操作

1.数据库操作

  • 查看所有数据库

    show dbs
    或
    show databases
    

    默认数据库说明:

    • ① admin 管理每个数据库用户名密码,权限相关。
    • ② config 使用分片的时候保存分片相关信息。
    • ③ local 集合不会被复制,用来存储本地单台服务器的任意集合。
  • 选择和创建数据库

    use 数据库名称
    

    :如果数据库不存在,则新建,只有插入内容之后,才会真正创建数据库

  • 查看正在使用的数据库

    db
    

    :MongoDB 中默认的数据库为 test,如果你没有选择数据库,集合将存放在 test 数据库中

  • 删除数据库

    db.dropDatabase()(主要用来删除已经持久化的数据库)
    

2.集合操作

  • 显示创建集合

    db.createCollection(“name”) 
    

    name 表示集合名称

  • 查看当前数据库中的集合

    show collections
    show tables
    
  • 隐示创建集合

    #向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合
    
  • 删除集合

    db.集合.drop()
    #删除成功返回 true
    

3.文档CRUD

3.1新增操作

  • 插入单个文档

    db.collection.insertOne()
    
    db.sytest.insertOne({"name":"sy"})
    

    image-20230125142649753

  • 插入多个文档

    db.collection.insertMany([])
    
    db.sytest.insertMany([{"name":"sy"},{"name":"zs"}])
    

    image-20230125142702854

3.2查询操作

先添加数据进 mongoDB 中

db.sytest.insertMany([
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
  • 全部查询

    db.collection.find({})
    
  • 条件查询

    • 等值查询 { : , … } 只需要在{}中添加查询条件即可

      db.sytest.find({"name":"sy"})
      

      image-20230125143630027

    • in 操作

      db.sytest.find( { status: { $in: [ "A", "D" ] } } )
      
    • and 操作

      db.sytest.find( { status: "A", qty: { $lt: 30 } } )
      

      image-20230125144045474

    • or 操作

      db.sytest.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
      
    • 同时使用AND和OR条件

      db.sytest.find( {
           status: "A",
           $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
      } )
      
    • 查询只想要返回部分数据字段

      db.sytest.find({ status: "A", $or: [{ qty: { $lt: 30 } }, { item: /^p/ }] },{item:1,size:1,_id:0})
      

      image-20230125144844322

3.3更新操作

  • 全覆盖修改

    db.sytest.replaceOne({_id:ObjectId("63d0cb9990e0fd6b2a39efbc")},{qty:NumberInt(1)})
    

    image-20230125194430795

  • 局部修改

    db.sytest.updateOne({_id:ObjectId("63d0cb9990e0fd6b2a39efbc")},{$set:{qty:222}})
    

    image-20230125194759659

    image-20230125194854101

  • 更新多个文档

    db.sytest.updateMany( 
      { "qty": { $gt: 100 } },
      {  
          $set: { "size.uom": "in", status: "P" }, 
          $currentDate: { lastModified: true }  
      }
    )
    

    image-20230125195126386

  • 列值增长操作

    db.sytest.updateOne({_id:ObjectId("63d0cb9990e0fd6b2a39efbc")},{$inc:{qty:1}})
    

    image-20230125195809770

3.4删除操作

  • 删除一个文档(注意:哪怕条件匹配多个也只删除一个)

    db.sytest.deleteOne({_id:ObjectId("63d0cb9990e0fd6b2a39efbc")})
    

    image-20230125195953473

  • 删除多个文档

    db.sytest.deleteMany({item:"postcard"})
    

    image-20230125200129479

  • 删除满足条件的文档

    db.sytest.remove({status:"D"})
    

    image-20230125200412068

3.5分页操作

  • 查询总条数

    db.sytest.countDocuments()
    
  • 带条件查询总条数

    db.sytest.countDocuments({status:"A"})
    

    image-20230125200942233

  • 分页查询:使用limit做过滤操作,skip 跳过某些条数

    db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
    
    • 每页查询2条

      db.sytest.find().limit(2)
      

      image-20230125201512482

3.6排序操作

  • 排序查询

    #升序
    db.sytest.find().sort({qty:1})
    #降序
    db.sytest.find().sort({qty:-1})
    
  • 注意:skip()、 limilt()、 sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit(),和命令编写顺序无关。

3.7模糊查询

  • 正则查询

    db.集合.find({字段:/正则表达式/})
    
  • 查询名字是z开头的

    db.sytest.find({name:/^z/})
    

    image-20230125202237544

  • 查询名字包含s的

    db.sytest.find({name:/s/})
    

    image-20230125202828771

三、索引

1.索引的概述

索引支持在MongoDB中有效地执行查询。如果没有索引,MongoDB必须执行集合扫描,即扫描集合中的每个文档,以选择那些与查询语句匹配的文档。如果一个查询存在适当的索引,MongoDB可以使用该索引来限制它必须检查的文档数量。

索引是特殊的数据结构,它以一种易于遍历的形式存储集合数据集的一小部分。MongoDB使用B树数据结构做为索引,索引存储一个或一组特定字段的值,按字段的值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作。此外,MongoDB可以通过使用索引中的排序返回排序后的结果。

可以参见官方下图:

image-20230125203857123

 db.student.find({name:"sy"},{_id:0,name:1})

默认的Id索引。在创建集合期间,MongoDB 在_id字段上创建唯一索引。该索引可以防止客户端插入两个具有相同值的文档。你不能将 _id字段上的index删除。

索引分类:

  • 单字段索引
  • 复合索引
  • 多键索引
  • 地理空间索引
  • 文本索引
  • Hashed索引

2.索引的基本操作

2.1查看索引

  • 查看sytest集合中的索引信息

    db.sytest.getIndexes()
    

    image-20230125205255904

MongoDB在创建集合的过程中,在 _id 字段上创建一个唯一的索引,默认名字为 id ,该索引可防止客户端插入两个具有相同值的文档,您不能在_id字段上删除此索引。

2.2创建索引

  • 给qty建立索引(对应的值1表示升序,-1表示降序)

    db.sytest.createIndex({qty:1})
    

    image-20230125205640003

2.3创建复合索引

  • 按照item升序,按照status降序建立索引(Compound索引)

    db.sytest.createIndex({userid:1,nickname:-1})
    

    image-20230125210135308

2.4删除索引

  • 具体索引删除

    • db.collection.dropIndex(index)

    • 删除索引qty

      db.sytest.dropIndex({qty:1})
      

      image-20230125210645797

  • 删除全部索引

    • db.collection.dropIndexes()

    • 删除sytest中全部索引

      db.sytest.dropIndexes()
      

      image-20230125210823668

    • 注意:_id 索引是不会被删除的

3.执行计划

​ 分析查询性能(Analyze Query Performance)通常使用执行计划(解释计划、Explain Plan)来查看查询的情况,如查询耗费的时间、是否基于索引查询等。

3.1explain

  • 查看某条查询执行计划

    • db.collection.find(query,options).explain(options)

    • 根据name:sy来查询一条信息**(关键点看: “stage” : “COLLSCAN”, 表示全集合扫描)**

      db.sytest.find({"name":"sy"}).explain()
      
      sy> db.sytest.find({"name":"sy"}).explain()
      {
        explainVersion: '1',
        queryPlanner: {
          namespace: 'sy.sytest',
          indexFilterSet: false,
          parsedQuery: { name: { '$eq': 'sy' } },
          queryHash: '64908032',
          planCacheKey: '64908032',
          maxIndexedOrSolutionsReached: false,
          maxIndexedAndSolutionsReached: false,
          maxScansToExplodeReached: false,
          winningPlan: {
            stage: 'COLLSCAN',
            filter: { name: { '$eq': 'sy' } },
            direction: 'forward'
          },
          rejectedPlans: []
        },
        command: { find: 'sytest', filter: { name: 'sy' }, '$db': 'sy' },
        serverInfo: {
          host: 'rabbit1@localhost',
          port: 27017,
          version: '6.0.4',
          gitVersion: '44ff59461c1353638a71e710f385a566bcd2f547'
        },
        serverParameters: {
          internalQueryFacetBufferSizeBytes: 104857600,
          internalQueryFacetMaxOutputDocSizeBytes: 104857600,
          internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
          internalDocumentSourceGroupMaxMemoryBytes: 104857600,
          internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
          internalQueryProhibitBlockingMergeOnMongoS: 0,
          internalQueryMaxAddToSetBytes: 104857600,
          internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600
        },
        ok: 1
      }
      

      image-20230125211640519

3.2建立索引后观察

  • 给name建立索引**(关键点看: “stage” : “IXSCAN” ,基于索引的扫描)**

    db.sytest.createIndex({name:1})
    
    sy> db.sytest.createIndex({name:1})
    name_1
    sy> db.sytest.find({"name":"sy"}).explain()
    {
      explainVersion: '1',
      queryPlanner: {
        namespace: 'sy.sytest',
        indexFilterSet: false,
        parsedQuery: { name: { '$eq': 'sy' } },
        queryHash: '64908032',
        planCacheKey: 'A6C0273F',
        maxIndexedOrSolutionsReached: false,
        maxIndexedAndSolutionsReached: false,
        maxScansToExplodeReached: false,
        winningPlan: {
          stage: 'FETCH',
          inputStage: {
            stage: 'IXSCAN',
            keyPattern: { name: 1 },
            indexName: 'name_1',
            isMultiKey: false,
            multiKeyPaths: { name: [] },
            isUnique: false,
            isSparse: false,
            isPartial: false,
            indexVersion: 2,
            direction: 'forward',
            indexBounds: { name: [ '["sy", "sy"]' ] }
          }
        },
        rejectedPlans: []
      },
      command: { find: 'sytest', filter: { name: 'sy' }, '$db': 'sy' },
      serverInfo: {
        host: 'rabbit1@localhost',
        port: 27017,
        version: '6.0.4',
        gitVersion: '44ff59461c1353638a71e710f385a566bcd2f547'
      },
      serverParameters: {
        internalQueryFacetBufferSizeBytes: 104857600,
        internalQueryFacetMaxOutputDocSizeBytes: 104857600,
        internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
        internalDocumentSourceGroupMaxMemoryBytes: 104857600,
        internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
        internalQueryProhibitBlockingMergeOnMongoS: 0,
        internalQueryMaxAddToSetBytes: 104857600,
        internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600
      },
      ok: 1
    }
    

    image-20230125212040072

3.3回表查询

Covered Queries 当查询条件和查询的投影仅包含索引字段时,MongoDB直接从索引返回结果,而不扫描任何文档或将文档带入内存。 这些覆盖的查询可以非常有效。

  • 查询name:sy 的信息

    db.sytest.find({name:"sy"},{name:1,_id:0}).explain()
    {
      explainVersion: '1',
      queryPlanner: {
        namespace: 'sy.sytest',
        indexFilterSet: false,
        parsedQuery: { name: { '$eq': 'sy' } },
        queryHash: '4B1D5B79',
        planCacheKey: 'A40C523D',
        maxIndexedOrSolutionsReached: false,
        maxIndexedAndSolutionsReached: false,
        maxScansToExplodeReached: false,
        winningPlan: {
          stage: 'PROJECTION_COVERED',
          transformBy: { name: 1, _id: 0 },
          inputStage: {
            stage: 'IXSCAN',
            keyPattern: { name: 1 },
            indexName: 'name_1',
            isMultiKey: false,
            multiKeyPaths: { name: [] },
            isUnique: false,
            isSparse: false,
            isPartial: false,
            indexVersion: 2,
            direction: 'forward',
            indexBounds: { name: [ '["sy", "sy"]' ] }
          }
        },
        rejectedPlans: []
      },
      command: {
        find: 'sytest',
        filter: { name: 'sy' },
        projection: { name: 1, _id: 0 },
        '$db': 'sy'
      },
      serverInfo: {
        host: 'rabbit1@localhost',
        port: 27017,
        version: '6.0.4',
        gitVersion: '44ff59461c1353638a71e710f385a566bcd2f547'
      },
      serverParameters: {
        internalQueryFacetBufferSizeBytes: 104857600,
        internalQueryFacetMaxOutputDocSizeBytes: 104857600,
        internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
        internalDocumentSourceGroupMaxMemoryBytes: 104857600,
        internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
        internalQueryProhibitBlockingMergeOnMongoS: 0,
        internalQueryMaxAddToSetBytes: 104857600,
        internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600
      },
      ok: 1
    }
    

    image-20230125213729392

四、SpringBoot整合MongoDB

1.模仿实现知乎文字评论点赞功能

  • 单个文章信息

image-20230331142435778

  • 文字评论信息

在这里插入图片描述

2.需要实现功能点分析

  • 基本增删改查

  • 根据文章id查询评论信息

  • 用户评论点赞

3.表结构设计

  • 问题文章表设计

    字段名称字段说明字段类型说明
    id文章idString每一个文章标识
    title文章标题String
    content文章内容String
    nickname发布人名称String
    totalLikeCount总点赞数Int32
    totalReplyCount总评论数Int32
    createDatetime文章发布时间Date
  • 评论表设计

    字段名称字段说明字段类型说明
    id评论idString每一个评论标识
    content评论内容String
    publishTime评论发布日期Date
    userId评论人idString
    nickname评论人名称String
    likeCount点赞数Int32
    replyCount回复数Int32
    parentId上级idString""表示第一层
    articleId文章idString

4.SpringDataMongoDB

SpringData家族成员之一,用于操作MongoDB的持久层框架,封装了底层的mongodb-driver(mongodb-driver和jdbc驱动一样,比较原生,我不做讲解了,直接用spring家族现成产品)。

官网主页:

https://projects.spring.io/spring-data-mongodb/

5.搭建springboot项目

5.1引入pom依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

5.2创建application.yml

spring:
  data:
    mongodb:
      host: 192.168.222.128
      database: articleData
      port: 27017
      #也可以使用uri连接
      #uri: mongodb://192.168.222.128:27017/articleData

6.功能实现

6.1新增文章
6.2文章点赞
6.3评论文章
6.4评论评论
6.5给评论点赞
6.6查看文章的直接评论
6.7查询评论下的评论

| 文章id | String | |

4.SpringDataMongoDB

SpringData家族成员之一,用于操作MongoDB的持久层框架,封装了底层的mongodb-driver(mongodb-driver和jdbc驱动一样,比较原生,我不做讲解了,直接用spring家族现成产品)。

官网主页:

https://projects.spring.io/spring-data-mongodb/

5.搭建springboot项目

5.1引入pom依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

5.2创建application.yml

spring:
  data:
    mongodb:
      host: 192.168.222.128
      database: articleData
      port: 27017
      #也可以使用uri连接
      #uri: mongodb://192.168.222.128:27017/articleData

6.功能实现

6.1新增文章
6.2文章点赞
6.3评论文章
6.4评论评论
6.5给评论点赞
6.6查看文章的直接评论
6.7查询评论下的评论
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值