redis(非关系数据库)

 Redis 基础

(http://redisdoc.com/ "推荐站点")

Reids和Memcache的区别

  • Redis支持持久化
  • Redis支持更丰富的数据类型
  • 仅键值存储场景,Memcache性能优于Redis

Redis注意

不同db中相同key值同

基础命令

#选择DB
select 5
#Redis统计信息
info
#在线修改配置
192.168.0.238:6379> config get maxmemory
1) "maxmemory"
2) "1073741824"
192.168.0.238:6379> config set maxmemory 5000000
OK
192.168.0.238:6379> config get maxmemory
1) "maxmemory"
2) "5000000"

必要配置

系统配置

#内核优化
vm.overcommit_memory = 1

#启动配置
echo never > /sys/kernel/mm/transparent_hugepage/enabled

必须修改参数配置

bind 192.168.0.238
daemonize yes
logfile "/var/log/redis.log"
requirepass ding
dir /data/redis/6379/

Redis安全

漏洞介绍

Redis 默认情况下,会绑定在 0.0.0.0:6379,这样将会将 Redis 服务暴露到公网上,如果在没有开启认证的情况下,可以导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下可以利用 Redis 的相关方法,可以成功在 Redis 服务器上写入公钥,进而可以使用对应私钥直接登录目标服务器。

入侵特征:

  • Redis 可能执行过 FLUSHALL 方法,整个 Redis 数据库被清空
  • 在 Redis 数据库中新建了一个名为 crackit(网上流传的命令指令) 的键值对,内容为一个 SSH 公钥。
  • 在 /root/.ssh 文件夹下新建或者修改了 authorized_keys 文件,内容为 Redis 生成的 db 文件,包含上述公钥

修复建议

  • 禁止一些高危命令
    修改 redis.conf 文件,添加
rename-command FLUSHALL ""
rename-command CONFIG   ""
rename-command EVAL     ""

来禁用远程修改 DB 文件地址

  • 以低权限运行 Redis 服务
为 Redis 服务创建单独的用户和家目录,并且配置禁止登陆

  • 为 Redis 添加密码验证
修改 redis.conf 文件,添加
requirepass mypassword
  • 禁止外网访问 Redis
修改 redis.conf 文件,添加或修改
bind 127.0.0.1
使得 Redis 服务只在当前主机可用

Redis持久化

RDB(时间点快照)

优点

RDB支持分析工具
性能好(fork一个子进程)
适用灾难恢复
恢复大数据集比AOF快

缺点

消耗IO
会丢失部分数据
大数据集下,会卡顿

AOF(写日志)

新命令追加到文件末尾,Redis会在后台进行重写,对相同Key的写入,去除重复日志

优点

每秒写一次硬盘

缺点

体积比较大

混合RDB和AOF

优先使用AOF

###主不开持久化,从库开持久化

坑?

RDB运行后,开启AOF的正确方式
1.在线修改配置
config set appendonly=1
2.检查appendonly.aof
3.修改配置文件

注意:如果配置文件开启AOF,默认从AOF启动,里面并没有任何数据,

 

 

 Redis 4.0变化

(http://blog.csdn.net/xiangzhihong8/article/details/53513759 "原文地址")

Redis 的作者 antirez 在三天之前通过博客文章《The first release candidate of Redis 4.0 is out》发布了 redis 4.0 的第一个 RC 版本, 在博文中他说, 因为这个新版本的 Redis 出现了多项改变, 所以他决定从原来的 3.x 版本直接跳到 4.0 版本, 以此来强调这次更新的变化之大。

模块系统

Redis 4.0 发生的最大变化就是加入了模块系统, 这个系统可以让用户通过自己编写的代码来扩展和实现 Redis 本身并不具备的功能, 具体使用方法可以参考 antirez 的博文《Redis Loadable Module System》: http://antirez.com/news/106
因为模块系统是通过高层次 API 实现的, 它与 Redis 内核本身完全分离、互不干扰, 所以用户可以在有需要的情况下才启用这个功能, 以下是 redis.conf 中记载的模块载入方法:

################################## MODULES #####################################

# Load modules at startup. If the server is not able to load modules
# it will abort. It is possible to use multiple loadmodule directives.
#
# loadmodule /path/to/my_module.so
# loadmodule /path/to/other_module.so

目前已经有人使用这个功能开发了各种各样的模块, 比如 Redis Labs 开发的一些模块就可以在 http://redismodules.com 看到, 此外 antirez 自己也使用这个功能开发了一个神经网络模块: https://github.com/antirez/neural-redis
模块功能使得用户可以将 Redis 用作基础设施, 并在上面构建更多功能, 这给 Redis 带来了无数新的可能性。

PSYNC 2.0

新版本的 PSYNC 命令解决了旧版本的 Redis 在复制时的一些不够优化的地方:
在旧版本 Redis 中, 如果一个从服务器在 FAILOVER 之后成为了新的主节点, 那么其他从节点在复制这个新主的时候就必须进行全量复制。 在 Redis 4.0 中, 新主和从服务器在处理这种情况时, 将在条件允许的情况下使用部分复制。
在旧版本 Redis 中, 一个从服务器如果重启了, 那么它就必须与主服务器重新进行全量复制, 在 Redis 4.0 中, 只要条件允许, 主从在处理这种情况时将使用部分复制。

缓存驱逐策略优化

新添加了 Last Frequently Used 缓存驱逐策略, 具体信息见 antirez 的博文《Random notes on improving the Redis LRU algorithm》: http://antirez.com/news/109
另外 Redis 4.0 还对已有的缓存驱逐策略进行了优化, 使得它们能够更健壮、高效、快速和精确。
非阻塞 DEL 、 FLUSHDB 和 FLUSHALL
在 Redis 4.0 之前, 用户在使用 DEL 命令删除体积较大的键, 又或者在使用 FLUSHDB 和 FLUSHALL 删除包含大量键的数据库时, 都可能会造成服务器阻塞。
为了解决以上问题, Redis 4.0 新添加了 UNLINK 命令, 这个命令是 DEL 命令的异步版本, 它可以将删除指定键的操作放在后台线程里面执行, 从而尽可能地避免服务器阻塞:
redis> UNLINK fruits
(integer) 1
因为一些历史原因, 执行同步删除操作的 DEL 命令将会继续保留。
此外, Redis 4.0 中的 FLUSHDB 和 FLUSHALL 这两个命令都新添加了 ASYNC 选项, 带有这个选项的数据库删除操作将在后台线程进行:

redis> FLUSHDB ASYNC
OK

redis> FLUSHALL ASYNC
OK

交换数据库

Redis 4.0 对数据库命令的另外一个修改是新增了 SWAPDB 命令, 这个命令可以对指定的两个数据库进行互换: 比如说, 通过执行命令 SWAPDB 0 1 , 我们可以将原来的数据库 0 变成数据库 1 , 而原来的数据库 1 则变成数据库 0 。
以下是一个使用 SWAPDB 的例子:

redis> SET your_name "huangz"  -- 在数据库 0 中设置一个键
OK
redis> GET your_name
"huangz"
redis> SWAPDB 0 1  -- 互换数据库 0 和数据库 1
OK
redis> GET your_name  -- 现在的数据库 0 已经没有之前设置的键了
(nil)
redis> SELECT 1  -- 切换到数据库 1
OK
redis[1]> GET your_name  -- 之前在数据库 0 设置的键现在可以在数据库 1 找到
"huangz"                 -- 证明两个数据库已经互换

混合 RDB-AOF 持久化格式

Redis 4.0 新增了 RDB-AOF 混合持久化格式, 这是一个可选的功能, 在开启了这个功能之后, AOF 重写产生的文件将同时包含 RDB 格式的内容和 AOF 格式的内容, 其中 RDB 格式的内容用于记录已有的数据, 而 AOF 格式的内存则用于记录最近发生了变化的数据, 这样 Redis 就可以同时兼有 RDB 持久化和 AOF 持久化的优点 —— 既能够快速地生成重写文件, 也能够在出现问题时, 快速地载入数据。
这个功能可以通过 aof-use-rdb-preamble 选项进行开启, redis.conf 文件中记录了这个选项的使用方法:

# When rewriting the AOF file, Redis is able to use an RDB preamble in the
# AOF file for faster rewrites and recoveries. When this option is turned
# on the rewritten AOF file is composed of two different stanzas:
#
#   [RDB file][AOF tail]
#
# When loading Redis recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# tail.
#
# This is currently turned off by default in order to avoid the surprise
# of a format change, but will at some point be used as the default.
aof-use-rdb-preamble no

内存命令

新添加了一个 MEMORY 命令, 这个命令可以用于视察内存使用情况, 并进行相应的内存管理操作:

redis> MEMORY HELP
1) "MEMORY USAGE <key> [SAMPLES <count>] - Estimate memory usage of key"
2) "MEMORY STATS                         - Show memory usage details"
3) "MEMORY PURGE                         - Ask the allocator to release memory"
4) "MEMORY MALLOC-STATS                  - Show allocator internal stats"
其中, 使用 MEMORY USAGE 子命令可以估算储存给定键所需的内存:
redis> SET msg "hello world"
OK

redis> SADD fruits apple banana cherry
(integer) 3

redis> MEMORY USAGE msg
(integer) 62

redis> MEMORY USAGE fruits
(integer) 375
使用 MEMORY STATS 子命令可以查看 Redis 当前的内存使用情况:
redis> MEMORY STATS
1) "peak.allocated"
2) (integer) 1014480
3) "total.allocated"
4) (integer) 1014512
5) "startup.allocated"
6) (integer) 963040
7) "replication.backlog"
8) (integer) 0
9) "clients.slaves"
10) (integer) 0
11) "clients.normal"
12) (integer) 49614
13) "aof.buffer"
14) (integer) 0
15) "db.0"
16) 1) "overhead.hashtable.main"
    2) (integer) 264
    3) "overhead.hashtable.expires"
    4) (integer) 32
17) "overhead.total"
18) (integer) 1012950
19) "keys.count"
20) (integer) 5
21) "keys.bytes-per-key"
22) (integer) 10294
23) "dataset.bytes"
24) (integer) 1562
25) "dataset.percentage"
26) "3.0346596240997314"
27) "peak.percentage"
28) "100.00315093994141"
29) "fragmentation"
30) "2.1193723678588867"
使用 MEMORY PURGE 子命令可以要求分配器释放更多内存:
redis> MEMORY PURGE
OK
使用 MEMORY MALLOC-STATS 子命令可以展示分配器内部状态:
redis> MEMORY MALLOC-STATS
Stats not supported for the current allocator

兼容 NAT 和 Docker

Redis 4.0 将兼容 NAT 和 Docker , 具体的使用方法在 redis.conf 中有记载:

########################## CLUSTER DOCKER/NAT support  ########################

# In certain deployments, Redis Cluster nodes address discovery fails, because
# addresses are NAT-ted or because ports are forwarded (the typical case is
# Docker and other containers).
#
# In order to make Redis Cluster working in such environments, a static
# configuration where each node known its public address is needed. The
# following two options are used for this scope, and are:
#
# * cluster-announce-ip
# * cluster-announce-port
# * cluster-announce-bus-port
#
# Each instruct the node about its address, client port, and cluster message
# bus port. The information is then published in the header of the bus packets
# so that other nodes will be able to correctly map the address of the node
# publishing the information.
#
# If the above options are not used, the normal Redis Cluster auto-detection
# will be used instead.
#
# Note that when remapped, the bus port may not be at the fixed offset of
# clients port + 10000, so you can specify any port and bus-port depending
# on how they get remapped. If the bus-port is not set, a fixed offset of
# 10000 will be used as usually.
#
# Example:
#
# cluster-announce-ip 10.1.1.5
# cluster-announce-port 6379
# cluster-announce-bus-port 6380

结语

关于 Redis 4.0 的主要更新就介绍到这里, 想要详细了解 Redis 4.0 各项修改的读者可以参考 Release Note :https://raw.githubusercontent.com/antirez/redis/4.0/00-RELEASENOTES
想要试用新版本的读者, 只要直接获取 https://github.com/antirez/redis 的 unstable 分支即可。

 

 

Codis实现Redis的集群

 

Codis简介

Codis 是一个分布式 Redis 解决方案, 对于上层的应用来说, 连接到 Codis Proxy 和连接原生的 Redis Server 没有明显的区别 (不支持的命令列表https://github.com/CodisLabs/codis/blob/release3.2/doc/unsupported_cmds.md), 上层应用可以像使用单机的 Redis 一样使用, Codis 底层会处理请求的转发, 不停机的数据迁移等工作, 所有后边的一切事情, 对于前面的客户端来说是透明的, 可以简单的认为后边连接的是一个内存无限大的 Redis 服务:
Codis是豌豆荚的开源方案,目前在redis集群实现方式对比,codis集群比较稳定的方案,并且客户端不需要做任何修改,相对redis cluster兼容性更强,可节约大量开发成本并减少大量后期维护成本, 主要由以下特点:

  • 可以无缝迁移到codis,自带迁移工具,并且案例较多
  • 可以动态扩容和缩容
  • 多业务完全透明,业务不知道运行的是codis
  • 支持多核心CPU,twemproxy只能单核
  • codis是中心基于proxy的设计,是客户端像连接单机一样操作proxy
  • 有部分命令不能支持,比如keys *等
  • 支持group划分,组内可以设置一个主多个从,通过sentinel 监控redis主从,当主down了自动将从切换为主
  • 设置的进程要最大等于CPU的核心,不能超过CPU的核心数
  • 其依赖于zookeeper,里面保存的是key保存的redis主机位置,因此zookeeper要做高可用
  • 监控可以使用接口和dashboard

环境介绍

环境初始化

Go语言环境安装

cd /opt/
wget https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz
tar xzf go1.8.1.linux-amd64.tar.gz
mv go /usr/local/
mkdir /usr/local/go/work

cat >> /etc/profile <<EOF
export PATH=$PATH:/usr/local/go/bin
export GOROOT=/usr/local/go
export GOPATH=/usr/local/go/work
EOF
. /etc/profile

go version
go version go1.8.1 linux/amd64

Zookeeper部署(详见zookeeper章节)

Codis部署

源码部署

二进制部署

下载并建立目录

cd /opt
wget https://github.com/CodisLabs/codis/releases/download/3.2.0/codis3.2.0-go1.8.1-linux.tar.gz
tar xzf codis3.2.0-go1.8.1-linux.tar.gz
mv codis3.2.0-go1.8.1-linux /usr/local/ && cd /usr/local/
ln -s codis3.2.0-go1.8.1-linux codis-server
mkdir /usr/local/codis-server/logs
#根据不同角色定义目录
#ln -s codis3.2.0-go1.8.1-linux codis-dashboard
#mkdir /usr/local/codis-dashboard/logs
#ln -s codis3.2.0-go1.8.1-linux codis-proxy
#mkdir /usr/local/codis-proxy/logs

将源码包中的config目录拷贝到/usr/local/codis/目录下

cd /opt/
wget https://codeload.github.com/CodisLabs/codis/tar.gz/3.2.0
tar xzf 3.2.0
cp -a  /opt/codis-3.2.0/config  /usr/local/codis-server/
#cp -a  /opt/codis-3.2.0/config  /usr/local/codis-dashboard/
#cp -a  /opt/codis-3.2.0/config  /usr/local/codis-proxy/

修改目录权限

chown root.root -R /usr/local/codis

配置Codis-Server(Redis)

主机名监听端口session_auth数据目录最大内存IP地址
rds016900codis6m4zGKYz/data/codis_6900/1G192.168.0.106
rds016901codis6m4zGKYz/data/codis_6901/1G192.168.0.106
rds026902codis6m4zGKYz/data/codis_6902/1G192.168.0.227
rds026903codis6m4zGKYz/data/codis_6903/1G192.168.0.227

初始化目录和配置文件

192.168.0.116

mkdir /data/codis_690{0..1}
cd /usr/local/codis-server/config/
cp redis.conf 6900_codis.conf
cp redis.conf 6901_codis.conf

192.168.0.227

mkdir /data/codis_690{2..3}
cd /usr/local/codis-server/config/
cp redis.conf 6902_codis.conf
cp redis.conf 6903_codis.conf

配置文件(主要参数)

#grep ^[a-Z] redis_6900.conf 
bind 192.168.0.227
port 6902
daemonize yes
pidfile /data/codis_6902/redis_6902.pid
loglevel notice
logfile "/data/codis_6902/redis_6902.log"
databases 16
dbfilename dump_6902.rdb
dir /data/codis_6902/
requirepass codis6m4zGKYz
maxmemory 1G
appendonly yes
appendfilename "appendonly.aof"

启动并测试

/usr/local/codis-server/codis-server /usr/local/codis-server/config/6900_codis.conf
/usr/local/codis-server/redis-cli -h 192.168.0.106 -p 6900 -a codis6m4zGKYz

配置codis-proxy

基础环境

主机名代理端口管理端口product_authsession_authIP地址
dal011900011080dashfpyYmglCcodis6m4zGKYz192.168.0.111
dal021900011080dashfpyYmglCcodis6m4zGKYz192.168.0.239

写在之前

  • product_auth是Codis各个角色间通讯的密码,Codis-server中的requirepass就是设置的这个密码
  • session_auth是把Codis-Server中的requirepass的功能,提升到了Codis-proxy来实现
    最终就是客户端连接Codis集群时,所带的密码是session_auth密码,而所有Codis角色间通讯,都使用product_auth密码

配置

# Set Codis Product Name/Auth.
product_name = "codis-cluster"
product_auth = "dashfpyYmglC"

# Set auth for client session
#   1. product_auth is used for auth validation among codis-dashboard,
#      codis-proxy and codis-server.
#   2. session_auth is different from product_auth, it requires clients
#      to issue AUTH <PASSWORD> before processing any other commands.
session_auth = "codis6m4zGKYz"

admin_addr = "192.168.0.110:11080"

proto_type = "tcp4"
proxy_addr = "192.168.0.110:19000"

jodis_name = "zookeeper"
jodis_addr = "192.168.0.112:2181,192.168.0.112:2182,192.168.0.112:2183"
jodis_timeout = "20s"
jodis_compatible = false

疑问?proxy_addr配置所有proxy还是自己?

启动服务

nohup /usr/local/codis-proxy/codis-proxy --ncpu=1 -c /usr/local/codis-proxy/config/proxy.toml -l /usr/local/codis-proxy/logs/proxy.log --log-level=WARN >> /usr/local/codis-proxy/logs/nohup_proxy.log &

配置codis-dashboard

基础环境

主机名管理端口product_authIP地址
dashboard18080dashfpyYmglC192.168.0.111
fe8888-192.168.0.111

配置修改

修改存储为zookeeper并修改product_name名称

# Set Coordinator, only accept "zookeeper" & "etcd" & "filesystem".
# Quick Start
#coordinator_name = "filesystem"
#coordinator_addr = "/tmp/codis"
coordinator_name = "zookeeper"
coordinator_addr = "192.168.0.112:2181,192.168.0.112:2182,192.168.0.112:2183"

# Set Codis Product Name/Auth.
product_name = "codis-cluster"
product_auth = "dashfpyYmglC"

# Set bind address for admin(rpc), tcp only.
admin_addr = "192.168.0.111:18080"

启动Dashboard

nohup /usr/local/codis/codis-dashboard --ncpu=1 -c /usr/local/codis/config/dashboard.toml -l /usr/local/codis/logs/dashboard.log --log-level=WARN  >>  /usr/local/codis/logs/nohup_dashboard.log &

查看端口和zookeeper是否有数据

[zk: 192.168.0.112:2181,192.168.0.112:2181,192.168.0.112,2183(CONNECTED) 2] get /codis3/codis-cluster/topom
{
    "token": "a3eefb8d4762addf12265a5c6f847ea8",
    "start_time": "2017-08-23 13:01:44.961022186 +0800 CST",
    "admin_addr": "192.168.0.111:18080",
    "product_name": "codis-cluster",
    "pid": 13773,
    "pwd": "/usr/local/codis-dashboard/config",
    "sys": "Linux dashboard.prod.ding 3.10.0-514.16.1.el7.x86_64 #1 SMP Wed Apr 12 15:04:24 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux"
}
c

启动codis-fe

nohup /usr/local/codis-dashboard/codis-fe --ncpu=1 -l /usr/local/codis-dashboard/logs/fe.log --log-level=WARN --zookeeper=192.168.0.112:2181,192.168.0.112:2182,192.168.0.112:2183 --listen=192.168.0.111:8888 >> /usr/local/codis-dashboard/logs/nohup_fe.log &

配置哨兵,通过codis-fe添加到集群中

注意,配置文件只添加如下内容,剩下的系统自动添加

#cat 26379_sentinel.conf 
bind 192.168.0.110
port 26379
daemonize yes
protected-mode yes
dir "/tmp"
loglevel notice
logfile "/data/setinel-26379.log"

启动哨兵

/usr/local/codis-proxy/codis-server /usr/local/codis-server/config/26379_sentinel.conf --sentinel

在codis-fe中增加哨兵

最终图形界面

疑问

图形界面按钮含义
内部运行机制

后续工作

完成故障测试切换
了解分片技术

 

Redis故障处理

overcommit_memeory 问题

公司的几台Redis服务器出现不明故障,查看Redis日志,发现如下提示:

WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.

overcommit_memory参数说明

设置内存分配策略(可选,根据服务器的实际情况进行设置) /proc/sys/vm/overcommit_memory 可选值:0、1、2。 0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。 1, 表示内核允许分配所有的物理内存,而不管当前的内存状态如何。 2, 表示内核允许分配超过所有物理内存和交换空间总和的内存

注意

redis在dump数据的时候,会fork出一个子进程,理论上child进程所占用的内存和parent是一样的,比如parent占用的内存为8G,这个时候也要同样分配8G的内存给child,如果内存无法负担,往往会造成redis服务器的down机或者IO负载过高,效率下降。所以这里比较优化的内存分配策略应该设置为 1(表示内核允许分配所有的物理内存,而不管当前的内存状态如何)。

Overcommit和OOM。

什么是Overcommit和OOM 在Unix中,当一个用户进程使用malloc()函数申请内存时,假如返回值是NULL,则这个进程知道当前没有可用内存空间,就会做相应的处理工作。许多进程会打印错误信息并退出。

Linux使用另外一种处理方式,它对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存。这种技术叫做Overcommit。 当内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程),以便释放内存。

Overcommit的策略 Linux下overcommit有三种策略(Documentation/vm/overcommit-accounting): 0. 启发式策略。合理的overcommit会被接受,不合理的overcommit会被拒绝。 1. 任何overcommit都会被接受。 2. 当系统分配的内存超过swap+N%*物理RAM(N%由vm.overcommit_ratio决定)时,会拒绝commit。 overcommit的策略通过vm.overcommit_memory设置。 overcommit的百分比由vm.overcommit_ratio设置。

echo 2 > /proc/sys/vm/overcommit_memory

echo 60 > /proc/sys/vm/overcommit_ratio

当oom-killer发生时,linux会选择杀死哪些进程 选择进程的函数是oom_badness函数(在mm/oom_kill.c中),该函数会计算每个进程的点数(0~1000)。 点数越高,这个进程越有可能被杀死。 每个进程的点数跟oom_score_adj有关,而且oom_score_adj可以被设置(-1000最低,1000最高)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值