Redis入门教程

Nosql概述

什么是NoSQL

NoSQL最常见的解释是“non-relational”, “Not Only SQL”(不仅仅是SQL)也被很多人接受。NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。随着互联网web2.0网站的兴起,传统的关系数据库在处理web2.0网站,特别是超大规模和高并发的SNS类型的web2.0纯动态网站已经显得力不从心,出现了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。Redis是当下发展得最迅速的一个非关系型数据库。

NoSQL特点

  • 易扩展

    NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。无形之间,在架构的层面上带来了可扩展的能力。

  • 大数据量,高性能

    NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。一般MySQL使用Query Cache。NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说性能就要高很多。(比如Redis:一秒写8万次、读取11万次)

  • 灵活的数据模型

    NoSQL无须事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的Web 2.0时代尤其明显。

  • 高可用

    NoSQL在不太影响性能的情况,就可以方便地实现高可用的架构。比如Cassandra、HBase模型,通过复制模型也能实现高可用。

RDBMS和NoSQL区别

RDBMS:传统关系型数据库,NoSQL:非关系型数据库。

RDBMS NoSQL
高度组织化结构化数据 没有预定义的模式
结构化查询语言(SQL) 代表着不仅仅是SQL,没有声明性查询语言
数据和关系都存储在单独的表中 键 - 值对存储,列存储,文档存储,图形数据库
数据操纵语言,数据定义语言 非结构化和不可预知的数据
严格的一致性 最终一致性,而非ACID属性
基础事务 CAP定理,高性能,高可用性和可伸缩性

大数据时代的“3V和3高”指的的是?

3V:主要是描述问题的:海量(Volume)、多样(Variety)、实时(Velocity)
3高:主要是对程序的要求:高并发、高可扩(随时水平拆分,机器不够了,可以扩展机器)、高性能(保证用户体验和性能)

真正在公司中的实践:NoSQL+RDBMS一起使用才是最强的。

NoSQL四大分类

键值存储数据库

这一类数据库主要会使用到一个哈希表,这个表中有一个特定的键和一个指针指向特定的数据。Key/value模型对于IT系统来说的优势在于简单、易部署。但是如果DBA只对部分值进行查询或更新的时候,Key/value就显得效率低下了。举例如:Tokyo Cabinet/Tyrant,Redis,Voldemort,Oracle BDB。

列存储数据库

这部分数据库通常是用来应对分布式存储的海量数据。键仍然存在,但是它们的特点是指向了多个列。这些列是由列家族来安排的。如:Cassandra,HBase,Riak.

文档型数据库

文档型数据库的灵感是来自于Lotus Notes办公软件的,而且它同第一种键值存储相类似。该类型的数据模型是版本化的文档,半结构化的文档以特定的格式存储,比如JSON。文档型数据库可以看作是键值数据库的升级版,允许之间嵌套键值,在处理网页等复杂数据时,文档型数据库比传统键值数据库的查询效率更高。如:CouchDB,MongoDb。国内也有文档型数据库SequoiaDB,已经开源。

图形数据库

图形结构的数据库同其他行列以及刚性结构的SQL数据库不同,它是使用灵活的图形模型,并且能够扩展到多个服务器上。NoSQL数据库没有标准的查询语言(SQL),因此进行数据库查询需要制定数据模型。许多NoSQL数据库都有REST式的数据接口或者查询API。如:Neo4J,InfoGrid,Infinite Graph。

不同分类特点对比

分类 Example举例 典型应用场景 数据模型 优点 缺点
键值(key-value)存储数据库 Tokyo Cabinet/Tyrant,Redis,Voldemort,Oracle BDB 内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 Key指向Value的键值对,通常用hash table类实现 查找速度快 数据无结构化,通常只被当作字符串或者二进制数据
列存储数据库 Cassandra,HBase,Riak 分布式的文件系统 以列簇式存储,将同一列数据存在一起 查找速度快,可扩展性强,更容易进行分布式扩展 功能相对局限
文档型数据库 CouchDB,MongoDB Web应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容) Key-Value对应的键值对,Value为结构化数据 数据结构要求不严格,表结构可变,不需要像关系型数据库一样预先定义表结构 查询性能不高,而且缺乏统一的查询语法。
图形(Graph)数据库 Neo4J,InfoGrid,Infinite Graph 社交网络,推荐系统等。专注于构建关系图谱 图结构 利用图结构相关算法,比如最短路径寻址,N度关系查找等 很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案

NoSQL体系框架

NoSQL框架体系NosoL整体框架分为四层,由下至上如下所述,层次之间相辅相成,协调工作。

  • 数据持久层(data persistence)

    数据持久层定义了数据的存储形式,主要包括基于内存、基于硬盘、内存和硬盘接口、订制可拔插四种形式。基于内存形式的数据存取速度最快,但可能会造成数据丢失。基于硬盘的数据存储可能保存很久,但存取速度较基于内存形式的慢。内存和硬盘相结合的形式,结合了前两种形式的优点,既保证了速度,又保证了数据不丢失。订制可拔插则保证了数据存取具有较高的灵活性。

  • 整体分布层(data distribution model)

    数据分布层定义了数据是如何分布的,相对于关系型数据库,NoSQL可选的机制比较多,主要有三种形式:一是CAP支持,可用于水平扩展。二是多数据中心支持,可以保证在横跨多数据中心是也能够平稳运行。三是动态部署支持,可以在运行着的集群中动态地添加或删除节点。

  • 数据逻辑模型层(data logical model)

    数据逻辑层表述了数据的逻辑变现形式,与关系型数据库相比,NoSQL在逻辑表现形式上相当灵活,主要有四种形式:一是键值模型,这种模型在表现形式上比较单一,但却有很强的扩展性。二是列式模型,这种模型相比于键值模型能够支持较为复杂的数据,但扩展性相对较差。三是文档模型,这种模型对于复杂数据的支持和扩展性都有很大优势。四是图模型,这种模型的使用场景不多,通常是基于图数据结构的数据定制的。

  • 接口层(interface)

    接口层为上层应用提供了方便的数据调用接口,提供的选择远多于关系型数据库。接口层提供了五种选择:Rest,Thrift,Map/Reduce,Get/Put,特定语言API,使得应用程序和数据库的交互更加方便。

NoSQL分层架构并不代表每个产品在每一层只有一种选择。相反,这种分层设计提供了很大的灵活性和兼容性,每种数据库在不同层面可以支持多种特性。

NoSQL适用场景

NoSQL数据库在以下的这几种情况下比较适用:

  1. 数据模型比较简单
  2. 需要灵活性更强的IT系统
  3. 对数据库性能要求较高
  4. 不需要高度的数据一致性
  5. 对于给定的key,比较容易映射复杂值的环境

NoSQL开源数据库软件

MongoDB

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似Json的Bjson格式,因此可以存储比较复杂的数据类型。MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,还支持为数据建立索引。它的特点是高性能、易部署、易使用、存储数据非常方便

主要功能特性

  • 面向集合存储,易存储对象类型的数据

    “面向集合”(Collenction-oriented),意思是数据被分组,存储在数据集中,被称为一个集合。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库里的表,不同的是它不需要定义任何模式(Schema)。

  • 模式自由

    模式自由,意味着对于存储在Mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,我们完全可以把不同结构的文件存储在同一个数据库里。

Redis

Redis概述

定义

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如字符串(strings),散列(hashes),列表(lists),集合(sets),有序集合(sorted sets)与范围查询bitmapshyperloglogs和地理空间(geospatial)索引半径查询。 Redis内置了复制(replication),LUA脚本(Lua scripting),LRU驱动事件(LRU eviction),事务(transactions)和不同级别的磁盘持久化(persistence), 并通过Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)

与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

特点

  1. Redis是内存数据库,速度快,也支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  2. Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  3. Redis支持数据的备份,即master-slave模式的数据备份。
  4. 支持事务

优势

  1. 性能极高 – Redis能读的速度是11w次/s,写的速度是8.1w次/s 。
  2. 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  3. 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。(事务)
  4. 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

与其他key-value存储有什么不同?

  1. Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
  2. Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。

Redis安装

Linux安装

  1. 下载安装包,在命令Xshell页面输入命令

    wget http://download.redis.io/releases/redis-5.0.8.tar.gz

image-20200722131409554

  1. 一般把我们的程序放在根目录下的opt目录中,使用命令 mv redis-5.0.8.tar.gz /opt将当前压缩包移动到opt目录下,image-20200722131832633

​ 进入opt目录:cd /opt

​ 解压文件:tar xzf redis-5.0.8.tar.gz

​ 进入解压后的文件,可以看到redis的配置文件 image-20200722132135081

  1. 基本的环境安装

    yum install gcc-c++

image-20200722132334217

​ 在redis解压的目录下输入命令make

image-20200722132547815

image-20200722132607582

​ 在redis解压的目录下输入命令make install

image-20200722132649728

  1. 进入redis默认安装路径:cd /usr/local/bin

    image-20200722132904730

  2. 在此目录下新建一个文件夹myconfig,用来保存我们自定义的redis配置文件:mkdir myconfig

    image-20200722133104650

  3. 复制一份redis配置文件到myconfig目录下:

    cp /opt/redis-5.0.8/redis.conf myconfig

image-20200722133339655

  1. redis默认不是后台启动的,修改配置文件,在myconfig目录下输入命令vim redis.conf来修改配置文件

image-20200722133515931

  1. 按下键盘的insert键,进入输入模式:

image-20200722133614624

设置后台启动参数,修改为yes

然后按esc键退出输入模式,输入命令:wq,回车,即保存并退出。

image-20200722133819747

  1. 启动redis服务,进入redis安装目录下的bin目录,输入命令:redis-server myconfig/redis.conf

image-20200722134229268

  1. 连接测试,输入命令:redis-cli -p 6379,6379是redis默认端口号,输入ping命令测试连接

image-20200722134533413

image-20200722134553645

  1. 输入命令ps -ef|grep redis查看redis进程是否已经开启

image-20200722134745585

  1. 输入命令shutdown可以关闭redis服务,输入命令exit退出redis客户端

image-20200722134857880

性能测试

redis-benchmark是一个压力测试工具,官方自带的性能测试工具。Redis性能测试是通过同时执行多个命令实现的。

redis 性能测试的基本命令如下:

redis-benchmark [option] [option value]

注意:该命令是在 redis 的目录下执行的,而不是 redis 客户端的内部指令。

测试时,使用命令cd /usr/local/bin进入redis安装目录,使用命令以自定义配置文件启动redisredis-server myconfig/redis.conf

image-20200722152856440

然后进入bin目录下输入redis-cli -p 6379连接上redis,

实例

以下实例同时执行 10000 个请求来检测性能:

redis-benchmark -n 10000  -q

使用多个参数来测试 redis 性能:

redis-benchmark -h 127.0.0.1 -p 6379 -t set,lpush -n 10000 -q

以上实例中主机为 127.0.0.1,端口号为 6379,执行的命令为 set,lpush,请求数为 10000,通过 -q 参数让结果只显示每秒执行的请求数。

image-20200722153823981

redis 性能测试工具可选参数如下所示:

image-20200722152544718

Redis基础知识

Redis默认有16个数据库,默认使用的是第0个。

image-20200722154607503

127.0.0.1:6379> select 3  	#切换到第3个数据库
OK
127.0.0.1:6379[3]> DBSIZE  	#查看数据库大小
(integer) 0
127.0.0.1:6379> set name yang   #设置key
OK
127.0.0.1:6379> get name 	#获取key
"yang"
127.0.0.1:6379> keys * 		#查看数据库索引的key
1) "mylist"
2) "key:__rand_int__"
3) "name"
4) "counter:__rand_int__"
5) "myset:__rand_int__"
127.0.0.1:6379> flushdb		#清除当前数据库的内容
OK
127.0.0.1:6379> flushdb		#删除所有数据库的内容
OK

Redis是单线程的

Redis是基于内存操作的,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了,所以就使用了单线程。

Redis是C语言写的,官方提供的数据为100000+的QPS,完全不比同样是使用key-value的Memcache差。

Redis为什么单线程还这么快?

  1. 误区1:高性能的服务器一定是多线程的?

  2. 误区2:**多线程(CPU上下文会切换)**一定比单线程效率高!

    速度:CPU>内存>硬盘

核心: redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!

Redis基本命令

127.0.0.1:6379> keys * # 查看所有的key
(empty list or set)
127.0.0.1:6379> set name yang 	# set key
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> set age 1
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> EXISTS name 	# 判断当前的key是否存在
(integer) 1
127.0.0.1:6379> EXISTS name1
(integer) 0
127.0.0.1:6379> move name 1 	# 移除当前的key
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> set name yangshukai
OK
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> clear
127.0.0.1:6379> keys *
1) "age"
2) "name"
127.0.0.1:6379> get name
"yangshukai"
127.0.0.1:6379> EXPIRE name 10 		# 设置key的过期时间,单位是秒
(integer) 1
127.0.0.1:6379> ttl name 	# 查看当前key的剩余时间
(integer) 4
127.0.0.1:6379> ttl name
(integer) 3
127.0.0.1:6379> ttl name
(integer) 2
127.0.0.1:6379> ttl name
(integer) 1
127.0.0.1:6379> ttl name
(integer) -2
127.0.0.1:6379> get name
(nil)
127.0.0.1:6379> type name 	# 查看当前key的一个类型!
string
127.0.0.1:6379> type age
string

Redis五大数据类型

Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial)索引半径查询。 Redis内置了复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的磁盘持久化(persistence), 并通过Redis哨兵(Sentinel)和自动分区(Cluster)提供高可用性(high availability)。

String(字符串)

String,二进制安全的字符串,是Redis的基本值类型。

String类似的使用场景:value除了是字符串还可以数字

  1. 计数器
  2. 统计多单位的数量
  3. 对象缓存存储
[root@linux7 bin]# redis-cli -p 6379	#连接上redis客户端
127.0.0.1:6379> flushall	#清空所有数据库中的key
OK
127.0.0.1:6379> keys *		#查看当前所有的key
(empty list or set)			  #当前没有
127.0.0.1:6379> set name yang	#set key
OK
127.0.0.1:6379> get name
"yang"
127.0.0.1:6379> exists name	#判断数据库中是否存在名字为name的key,存在则返回1
(integer) 1
127.0.0.1:6379> append name yangshukai	# 追加字符串,如果当前key不存在,就相当于set key
(integer) 14
127.0.0.1:6379> get name
"yangyangshukai"
127.0.0.1:6379> strlen name		#获取字符串长度
(integer) 14
127.0.0.1:6379> set view 0		#初始浏览量为0,可以用来表示微信推文的浏览量等数据
OK
127.0.0.1:6379> get view
"0"
127.0.0.1:6379> incr view		# 自增1 浏览量变为1
(integer) 1
127.0.0.1:6379> incr view
(integer) 2
127.0.0.1:6379> get view
"2"
127.0.0.1:6379> decr view		# 自减1 浏览量-1
(integer) 1
127.0.0.1:6379> decr view
(integer) 0
127.0.0.1:6379> decr view
(integer) -1
127.0.0.1:6379> get view
"-1"
127.0.0.1:6379> incrby view 10	 # 可以设置步长,指定增量!
(integer) 9
127.0.0.1:6379> incrby view 10
(integer) 19
127.0.0.1:6379> decrby view 5
(integer) 14
127.0.0.1:6379> get name
"yangyangshukai"
127.0.0.1:6379> getrange name 0 3	# 截取字符串 [0,3]
"yang"
127.0.0.1:6379> getrange name 0 -1	# 获取全部的字符串 和 get key是一样的
"yangyangshukai"
127.0.0.1:6379> set name2 xbq
OK
127.0.0.1:6379> get name2
"xbq"
127.0.0.1:6379> setrange name2 1 yang	# 替换指定位置开始的字符串!
(integer) 5
127.0.0.1:6379> get name2
"xyang"
127.0.0.1:6379> setex name3 30 "yang"	# 设置name3 的值为yang,30秒后过期
OK
127.0.0.1:6379> ttl name3	#查看key的剩余过期时间
(integer) 26
127.0.0.1:6379> get name
"yangyangshukai"
127.0.0.1:6379> get name3
"yang"
127.0.0.1:6379> setnx mykey "redis"	# 如果mykey 不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys *
1) "name"
2) "view"
3) "name2"
4) "mykey"
5) "age"
6) "name3"
127.0.0.1:6379> setnx mykey "mongodb"	# 如果mykey存在,创建失败!
(integer) 0
127.0.0.1:6379> get mykey
"redis"
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3	# 同时设置多个值
OK
127.0.0.1:6379> keys *
1) "k2"
2) "name"
3) "view"
4) "name2"
5) "k1"
6) "mykey"
7) "age"
8) "k3"
127.0.0.1:6379> mget k1 k2 k3	# 同时获取多个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4	# msetnx 是一个原子性的操作,要么一起成功,要么一起失败!
(integer) 0
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379> set user:1 {
   name:zhangsan,age:3}	# 设置一个user:1 对象值为json字符来保存一个对象!
OK
# 这里的key是一个巧妙的设计: user:{id}:{filed} , 如此设计在Redis中是完全OK了!
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:1:age
1) "zhangsan"
2) "2"
#先get然后在set
127.0.0.1:6379> getset db redis	# 如果不存在值,则返回 nil
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb	# 如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> get db
"mongodb"

List(列表)

Redis lists基于Linked Lists实现。这意味着即使在一个list中有数百万个元素,在头部或尾部添加一个元素的操作,其时间复杂度也是常数级别的。用LPUSH 命令在十个元素的list头部添加新元素,和在千万元素list头部添加新元素的速度相同。虽然在数组实现的list中利用索引访问元素的速度极快,而同样的操作在linked list实现的list上没有那么快。

Redis Lists用linked list实现的原因是:对于数据库系统来说,至关重要的特性是:能非常快的在很大的列表上添加元素。另一个重要因素是,正如你将要看到的:Redis lists能在常数时间取得常数长度。

**在redis里面,list可以当成栈、队列、阻塞队列等,所有的list命令都是用l开头的,redis命令不区分大小写。**Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),在两边插入或者改动值,效率最高;在中间插入元素,相对来说效率会低一点。

一个列表最多可以包含2^32 - 1 个元素(4294967295, 每个列表超过40亿个元素)。

如果key不存在,创建新的链表;如果key存在,新增内容;如果移除了所有值,空链表,也代表不存在

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> lpush list one		# 将一个值或者多个值,插入到列表头部 (左)
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

T Head

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值