WEB后端_Day10(NoSQL:介绍、应用场景、不足之处、分类、常见 NoSQL 介绍、关系型数据库与NoSQL的区别、Redis)

WEB后端_Day10(NoSQL:介绍、应用场景、不足之处、分类、常见 NoSQL 介绍、关系型数据库与NoSQL的区别、Redis)

1.NoSQL 介绍

随着大数据时代的到来,越来越多的网站、应用系统需要支撑海量数据存储、高并发请求、高可用、高可扩展性等特性要求,传统的关系型数据库在应付这些调整已经显得力不从心,暴露了许多难以克服的问题。由此,各种各样的NoSQL(Not Only SQL)数据库作为传统关系型数据的一个有力补充得到迅猛发展。
在这里插入图片描述
NoSQL(Not only SQL)数据库,可以理解为区别于关系型数据库如mysql、oracle等的非关系型数据库。

2.NoSQL 应用场景

NoSQL 作为分布式系统的实现,海量数据永久性存储、非结构化数据存储、超大规模数据高效读写、超强水平扩展能力等这些特征让 NoSQL 得到了广泛应用。

3.NoSQL 不足之处

事务支持、关联特性,甚至于 SQL 查询,这些却是 NoSQL 的短板,也决定了 NoSQL 尚且取代不了关系型数据库。

4.NoSQL 分类

通常情况下,我们将 NoSQL 按功能特性不同分为4大类,即键值型、面向列族存储、文档型以及图数据库,参考下面表格:

类型特点应用案例
键值型简单数据存储形式,通过键来访问值图像存储RedisMemcacheDBBerkeley DB
可以通过key快速查询到其value基于键的文件系统
一般来说,存储不管value的格式,照单全收设计为可扩展系统
列族稀疏矩阵存储形式,通过行列作为键网络爬虫结果存储HbaseCassandraAccumulo
方便存储结构化和半结构化数据大数据交互式查询
方便数据压缩提供数据查询IO优势软一致性
文档型讲层次化的数据结构存储形式文档搜索MongoDBCouchDBCouchbase
文档存储一般用类似json的格式存储互联网内容管理
对某些字段建立索引以实现关系型数据库的某些功能高度变化的数据
图存储适用于关联性要求高的问题社交网络Neo4jFlockDBInfiniteGraph
图形关系的最佳存储欺诈侦测
使用传统关系数据库来解决的话性能低下,而且设计使用不方便强关联的数据

5.常见 NoSQL 介绍

以下四种 NoSQL 是最常使用的:

  • Redis:基于内存、支持持久化的键值型数据库
  • HBase:面向列、高效随机读写的 NoSQL
  • MongoDB:查询高效、支持多索引的文档型数据库。

1、Redis

主要特点:
在这里插入图片描述

  • 高性能
  • 纯内存访问(非数据同步无需读取磁盘)
  • 单线程
  • 非阻塞多路IO复用

2、HBase:

HBase是一个开源的非关系型分布式数据库,它参考了谷歌的BigTable建模,实现的编程语言为Java。它是Apache软件基金会的Hadoop项目的一部分,运行于HDFS文件系统之上,为 Hadoop 提供类似于BigTable 规模的服务。因此,它可以容错地存储海量稀疏的数据。

HBase是一个高可靠、高性能、面向列、可伸缩的分布式数据库,是谷歌BigTable的开源实现,主要用来存储非结构化和半结构化的松散数据。HBase的目标是处理非常庞大的表,可以通过水平扩展的方式,利用廉价计算机集群处理由超过10亿行数据和数百万列元素组成的数据表。

主要应用在海量数据存储、超大规模随机读写访问的场景。

主要特点:
在这里插入图片描述

  • 随机读写访问
  • 分布式、面向列
  • 强一致性
  • 底层数据存储在 HDFS 之上

3、MongoDB:

MongoDB 是一个分布式、面向文档的 NoSQL 数据库,用于大容量数据存储,提供统一的数据格式(bson),支持不同类型的索引。适用于存放对象或Json格式数据、追求高性能的业务场景。

主要特点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FSccm9Pc-1620654699914)(assets/clipboard-1618279281740.png)]

  • 面向文档,非常灵活
  • 支持各种类型的索引
  • 复制和故障切换,实现高可用性
  • 自动分片,易于扩展

6.关系型数据库与NoSQL的区别

6.1 RDBMS

  • 高度组织化结构化数据
  • 结构化查询语言(SQL)
  • 数据和关系都存储在单独的表中。
  • 数据操纵语言,数据定义语言
  • 严格的一致性
  • 基础事务
  • ACID

6.2 NoSQL

  • 代表着不仅仅是SQL
  • 没有声明性查询语言
  • 没有预定义的模式
  • 键 - 值对存储,列存储,文档存储,图形数据库
  • 最终一致性,而非ACID属性
  • 非结构化和不可预知的数据
  • CAP定理
  • 高性能,高可用性和可伸缩性

6.3.分布式数据库中的CAP原理(了解)

聊到NoSQL不得不提著名的CAP理论,全称 Consistency Available and Partition tolerance,即一致性(C)、可用性(A)与分区容错性(P),这是 Eric Brewer 教授提出的分布式系统设计理念,并给出了定论:任何分布式系统只能同时满足其中二点,无法做到三者兼顾。这可以说是 NoSQL 数据库的理论基石,至今 NoSQL 领域也称得上是百花齐放了,一直也没有哪一款 NoSQL 同时兼顾着这三点特性。

CAP定理:

    • Consistency(强一致性), 数据一致更新,所有数据变动都是同步的
      • Availability(高可用性), 好的响应性能
      • Partition tolerance(分区容错性) 可靠性

P: 系统中任意信息的丢失或失败不会影响系统的继续运作。

定理:任何分布式系统只可同时满足二点,没法三者兼顾。

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:

  • CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
  • CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
  • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求

因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三 大类:

  • CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
  • CP - 满足一致性,分区容忍性的系统,通常性能不是特别高。
  • AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。

而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。

所以我们只能在一致性和可用性之间进行权衡,没有NoSQL系统能同时保证这三点。

举例:

  • CA:传统Oracle数据库
  • AP:大多数网站架构的选择
  • CP:Redis、Mongodb

注意:分布式架构的时候必须做出取舍。

一致性和可用性之间取一个平衡。多余大多数web应用,其实并不需要强一致性。因此牺牲C换取P,这是目前分布式数据库产品的方向。

7.Redis

7.1.简介

Redis:REmote DIctionary Server(远程字典服务器)是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器

Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50个并发执行100000个请求,读的速度是110000次/s,写的速度是81000次/s ,且Redis通过提供多种键值数据类型来适应不同场景下的存储需求。

7.1.1.目前为止Redis支持的键值数据类型如下:

  1. 字符串类型 string

  2. 哈希类型 hash

  3. 列表类型 list

  4. 集合类型 set

  5. 有序集合类型 sortedset

7.1.2. redis的应用场景

• 缓存(数据查询、短连接、新闻内容、商品内容等等)

• 聊天室的在线好友列表

• 任务队列。(秒杀、抢购、12306等等)

• 应用排行榜

• 网站访问统计

• 数据过期处理(可以精确到毫秒

• 分布式集群架构中的session分离

7.2.下载

下载 https://redis.io/
在这里插入图片描述

中文网:http://www.redis.cn/
在这里插入图片描述

7.3.安装

7.3.1.环境准备

redis是C语言开发,安装redis需要先将官网下载的源码进行编译,编译依赖gcc环境。如果没有gcc环境,需要安装gcc:(环境已经导入完成)

yum install gcc-c++

在这里插入图片描述

7.3.2.开始安装

步骤1:将Windows下下载的压缩文件redis-5.0.7.tar.gz上传到Linux下。

在这里插入图片描述

步骤2: 解压文件

tar -zxvf /opt/softes/redis-5.0.5.tar.gz  -C redis/

步骤3:编译redis (编译,将.c文件编译为.o文件)

进入到解压后的目录 进行编译

make install

在这里插入图片描述

安装完后,在/usr/local/redis/bin下有几个可执行文件

redis-benchmark ----性能测试工具

redis-check-aof ----AOF文件修复工具

redis-check-rdb----RDB文件检查工具(快照持久化文件)

redis-cli ----命令行客户端

redis-server ----redis服务器启动命令

在这里插入图片描述

步骤5: copy文件

redis启动需要一个配置文件,可以修改端口号等信息。

将加压目录下的redis.conf 拷贝到/usr/local下

7.3.3.启动-前端启动:(了解)-无法部署集群

在这里插入图片描述
在这里插入图片描述

启动redis,客户端连接: 连接6379端口

redis-cli -h ip地址 -p 端口

在这里插入图片描述

退出客户端:quit

redis的关闭:

  • ①查询到PID,kill -9 pid 【断电,非正常关闭,一般不用,否则造成数据丢失】

在这里插入图片描述

  • ②正常关闭 【正常关闭,数据保存】

停止redis服务

[root@localhost bin]# ./redis-cli shutdown

7.3.4.Redis启动-后端模式

修改redis.conf配置文件, daemonize yes 以后端模式启动。

vim /usr/local/redis.conf

在这里插入图片描述

[root@localhost local]# ./bin/redis-server redis.conf 

在这里插入图片描述
在这里插入图片描述

7.4.Redis客户端-RedisDesktopManager

7.4.1.下载 Redis Desktop Manager

官网下载:https://redisdesktop.com/download

github地址:https://github.com/uglide/RedisDesktopManager/releases

7.4.2.安装Redis Desktop Manager

redis-desktop-manager-0.8.8.384.exe 傻瓜式安装,点击下一步就行。

安装完成桌面会生成如下快捷图标,双击启动如右图。
在这里插入图片描述

7.4.3.RedisDesktopManager连接redis

在使用这个连接redis的时候,发现连接不上。

第一步:在自己的本机外面试下能不能连接上虚拟机,打开cmd,使用 ping 192.168.65.128 (192.168.65.128这个为虚拟机的ip地址 查看虚拟机的ip地址:命令 ifconfig)

如果能ping的通,这个没问题,表示外部可以访问得到。访问不到可以,把虚拟机的防火墙给关了,最省事,命令: service iptables stop

设置防火墙规则:firewall-cmd --zone=public --add-port=6379/tcp --permanent

第二步:在redis的配置文件(redis.conf)里面是否设置了requirepass 表示连接的密码,如果没有设置,不好意思,连接不上

在这里插入图片描述

第三步: 在redis的配置文件里面,是否把bind 127.0.0.1 给注释掉,如果没有,连接不上

在这里插入图片描述

以上这些你都做了,应该就没有问题了

如果还是连接不上,你首先在你虚拟机里面登录上redis的客户端 进入到src目录层级下面。

和启动redis一样,命令: ./redis-cli

进入到客户端之后:命令:auth 1234(你设置的密码)

看一下是否ok。

我之前在这里踩了坑,明明我的配置文件里面设置了访问密码,却没有生效,为了保险起见,还是再来看下,密码是否生效了

如果ok,那就没有问题,如果出现了错误,说你密码没有设置,那就直接用命令设置:config set requirepass 1234(这个为你设置的访密码)

然后再试下命令 auth 1234(出现ok就好了)

这样就应该可以顺利的连接上redis了。

设置密码之后通过客户端退出服务端:

在这里插入图片描述
在这里插入图片描述

7.5.Redis相关基础知识

7.5.1.查看数据库

默认16个数据库,类似数组下表从零开始,初始默认使用零号库

config get databases
在这里插入图片描述

7.5.2.切换数据库

SELECT index

默认使用0号库

设置默认库

在这里插入图片描述

7.5.3.Dbsize查看当前数据库的key的数量

在这里插入图片描述

7.5.4.Flushdb:清空当前库

7.5.5.Flushall;通杀全部库

在这里插入图片描述

7.5.6.统一密码管理,16个库都是同样密码,要么都OK要么一个也连接不上

7.5.7.Redis索引都是从零开始

7.6.Redis的五大数据类型

Redis是一个“数据库”,当然它是一个基于缓存的非关系型数据库。

Redis一共有5种常用的数据类型:字符串(string)、列表(list)、哈希(hash)、集合(set)、有序集合(zset)。Redis是一个key-value形式的存储系统,key是一个“字符串”,而value对应的则是前面提到的5种数据类型

在对Redis进行正式接触前,我们先来认识这5种数据类型。

字符串(string)

这是最常见和最容易理解的一种数据类型,它表示存储在redis中的值是一个“字符串”类型的数据。但实际上,它还能存储整型数据,后面我们将通过INCR命令,对值进行自增操作。

列表(list)

列表(list)也可以理解为数组,和在Java中的List类型类似。略微不同的是,Java中的列表可以是泛型类型,也就是说Java中的List数据结构可以是字符串、整型等。而在redis中列表中的数据类型则只有字符串类型。

哈希(hash)

又称“散列”,这种数据类型类似于Java中的Map类型。初学者可能会疑惑,前面的“字符串”类型,一个key一个value不就是Map类型么。

实际上,redis是一种key-value形式的存储系统,我们所说的redis数据类型指的是value的数据类型。所以哈希(hash)也就是value是类似Map的一种数据类型。在后面的章节中我们会更直观的感受到。

集合(set)

set类型在redis中被称为集合,同样它和Java的Set集合相同。和redis的列表(list)类似,不同地是,列表(list)的数据是可以重复的且是插入有序,而集合(set)中的数据是不可重复的且是无序。

有序集合(zset)

有序集合(zset)尽管看起来是集合(set)类型多了“有序”的特性。但实际上,可以说它和哈希(hash)更相似。因为它和哈希(hash)一样也是Map类型,不同地是它的key是实际上的成员,而value则是用于排序的“分值”。这个特性能帮助我们快速的实现“点赞数最高倒序排列”等功能。

7.7.命令

我们通过redis-cli命令进入redis命令行交互。

Redis命令十分丰富,包括的命令组有Cluster、Connection、Geo、Hashes、HyperLogLog、Keys、Lists、Pub/Sub、Scripting、Server、Sets、Sorted Sets、Strings、Transactions一共14个redis命令组两百多个redis命令,Redis中文命令大全。您可以通过下面的检索功能快速查找命令,已下是全部已知的redis命令列表。如果您有兴趣的话也可以查看我们的网站结构图,它以节点图的形式展示了所有redis命令。

http://www.redis.cn/commands.html#set

7.7.1.字符串(string)

7.7.1.1.读/写/删简单命令

写命令通过set关键字实现,set [key] [value]。

读命令通过get关键字实现,get [key]。

在这里插入图片描述

字符串数据类型还有一个mset表示同时写入一个或多个字符串值,mset [key1] [value1] [key2] [value2]。

在这里插入图片描述

7.7.1.2.自增/自减命令

自增+1命令通过incr关键字实现,incr [key]。

在这里插入图片描述

在这里插入图片描述

自减 -1 decr

在这里插入图片描述

增加/减去一个指定的值

在这里插入图片描述

自增任意浮点数通过incrbyfloat,incrbyfloat [key] [float]。

在这里插入图片描述

浮点数只能加 不能减

7.7.1.3.字符串操作命令

redis中对字符串类型的数据类型不仅可以使用上述命令,它甚至还能像Java一样进行值得追加、获取子串等。

追加value值给指定key到末尾通过append,apppend [key] [append_string]。

在这里插入图片描述

接着这个示例,通过命令getrange获取子字符串,getrange [key] [start] [end]。

在这里插入图片描述

同样是这个示例,我们通过命令setrange替换子字符串为给定值,我们会给出两个关键参数,第一个参数是[start]表示从哪里开始替换,第二个参数是[value]表示替换的内容,setrange [key] [start] [value]。
在这里插入图片描述

7.7.1.4.二进制位命令

任何数据在操作系统中都是以二进制位形式存储的,字符串类型中redis提供了对其进行二进制位操作。通常情况下运用可能不多,但可以通过它实现一些“巧妙”的设计。

例如,在钉钉消息中,我们发送一条消息会显示“已读”和“未读”的人,我们需要将这两个信息存储在redis中,应该怎么设计?

我们设计一条消息的key值结构为“[user_id]:[msg_id]”,所以key=“1:100”就表示“用户ID为1发送的消息ID为100”。注意,此时如果用户ID=2的人读了这条消息,就通过命令setbit 1:100 2 1写入,如果用户ID=100的人读了这条消息,就通过setbit 1:100 10 1。这条命令的含义表示对key=1:100的二进制第2位写入1,对key=1:100的二进制第10位写入1,1表示已读,0则表示未读。

在这里插入图片描述

上面我们就初始化好了一个bitmap(位图)。接下来,当用户ID=2和用户ID=10读了这条消息。

在这里插入图片描述
我们通过getbit命令可以判断出哪些用户是否已读这条消息,例如,我们判断用户ID=3是否已读这条消息
在这里插入图片描述

我们还可以通过bitcount统计值为1的数量,也就是有多少人已读这条消息。
在这里插入图片描述

最后还有一个关于二进制位的命令bittop [operation] [result] [key1] [key2],可以对多个key值的二进制位进行二进制运算,包括并AND、或OR、异或XOR、非NOT,计算结果保存在[result]中。

查看库中所有的键
在这里插入图片描述

7.7.2.列表(list)

列表就是一个键 对应该多个值

7.7.2.1.推入/弹出常用命令

通过rpush、lpush,将一个或多个值向右或向左推入。

rpush [key] [value1] [value2],将value值推入到列表的右端

lpush [key] [value1] [value2],将value值推入到列表的左端

在这里插入图片描述

通过rpop、lpop,移除并返回列表中最后端、最左端的元素。

在这里插入图片描述

在介绍完推入和弹出命令后,接下来将介绍与列表范围查看的命令。

lrange [key] [start] [end]命令用于返回列表从[start]到[end]位置范围内的所有元素,注意,位于[start]、[end]的元素也会被返回,

在这里插入图片描述

lindex [key] [index]命令用于返回指定位置[index]的元素,
在这里插入图片描述

lrange和lindex均不会修改原本的列表值,但ltrim则用于“修建”列表值。

ltrim [key] [start] [end]表示只保留列表从[start]到[end]范围的所有元素,注意,包含位于[start]、[end]的元素,

在这里插入图片描述

列表基本的命令就是上面这些,还有一些比较“高级”的命令:将元素从一个队列移动到另外一个队列,或者阻塞式的执行弹出命令知道有新元素新加入到列表中。

7.7.3.哈希(hash)

hash可以理解为map 键还是String 值是map类型

hmset命令可写入hash类型的值,hmset [key] [field1] [value1] [field2] [value2]。

在这里插入图片描述

hlen返回hash包含的键值对数量,hlen [key]

在这里插入图片描述

hgetall返回hash包含的所有键值对,hmgetall [key]。

在这里插入图片描述

hexists命令检查给定的field是否存在hash值中,返回0表示不存在,返回1表示存在,hexists [key] [field]。

在这里插入图片描述

hkeys获取hash包含的所有field键,hkeys [key]。

在这里插入图片描述
hvals获取hash包含的所有field对应的value值,hvals [key]。
在这里插入图片描述

hincrby给hash中指定的field键自增任意整数(和字符串类型的incrby类似),hincrby [key] [field] [number]。

在这里插入图片描述

hincrbyfloat给hash中指定的filed键自增任意浮点数(和字符串类型的incrbyfloat类似),hincrbyfloat [key] [field] [number]。
在这里插入图片描述

hdel删除hash中指定的filed,hmdel [key] [field]。
在这里插入图片描述

7.7.4.集合(set)

在上一章节讲到,集合(set)是以无序方式存储各不相同元素的数据类型。它和Java中的Set类型类似。它同样具有新增、删除、读取等基本操作,还有两个集合之间运算的操作。

7.7.4.1.读/写等常用命令

sadd命令将一个或多个元素添加到集合里,并返回被添加元素中原本并不存在集合中的元素数量,sadd [key] [member] [member]。

在这里插入图片描述
在这里插入图片描述

集合的差集
在这里插入图片描述
在这里插入图片描述

7.7.5.有序(sorted set)

这个集合和上边的集合的区别就在于 有序集合会给每一个元素指定一个分数 有序也是根据元素后边的分数来进行排序的

在这里插入图片描述

zscore命令返回成员member的分数
在这里插入图片描述

zrange [key] [start] [stop] (withscores)
在这里插入图片描述

zcount [key] [min_score] [max_score]返回分数介于min_score和max_score之间的成员数量。

在这里插入图片描述

zincrby [key] [incrment] [member]命令用于给member成员的分数加上incrment。
在这里插入图片描述

zrem命令用于删除有序集合中指定的成员,zrem [key] [member1] [member2]……。

在这里插入图片描述

7.8.Transactions

7.8.1.事务定义

可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞

7.8.2.事务的作用

一个队列中,一次性、顺序性、排他性的执行一系列命令

在这里插入图片描述
在这里插入图片描述

7.8.3.取消事务执行

取消事务执行,命令将不会被执行。

在这里插入图片描述

执行过程中,当其中的命令出现错误命令时
在这里插入图片描述

事务中出现执行时错误,类似Java的运行时异常,执行事务时,部分命令会被执行成功,也即是不保证原子性

7.8.4.使用watch监视

使用watch监视key在事务之前被改动,正常未被改动时的情况,所有命令正常执行
在这里插入图片描述
在这里插入图片描述

使用watch监视key,此时在事务执行前key被改动,事务将取消不会执行所有命令。

我们现在一个redis客户端中执行watch命令。
在这里插入图片描述

我们再次回到第一个客户端,开始输入事务的命令块。
在这里插入图片描述

可看到通过exec执行事务时,事务并没有执行成功,而是返回“nil”。

三阶段

  • 开启:以MULTI开始一个事务
  • 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
  • 执行:由EXEC命令触发事务
    在这里插入图片描述

redis 的事务拥有以下特性

  1. 如果在执行 exec 之前事务中断了,那么所有的命令都不会执行
  2. 如果某个命令语法错误,不仅会导致该命令入队失败,整个事务都将无法执行
  3. 如果执行了 exec 命令之后,那么所有的命令都会按序执行
  4. 当 redis 在执行命令时,如果出现了错误,那么 redis 不会终止其它命令的执行,这是与关系型数据库事务最大的区别,redis 事务不会因为某个命令执行失败而回滚

7.8.5.redis 事务的缺陷

不满足原子性

  • 与关系型数据库的事务不同,redis 事务是不满足原子性的,一个事务执行过程中,其他事务或 client 是可以对相应的 key 进行修改的
  • 想要避免这样的并发性问题就需要使用 WATCH 命令,但是通常来说,必须经过仔细考虑才能决定究竟需要对哪些 key 进行 WATCH 加锁
  • 额外的 WATCH 会增加事务失败的可能,而缺少必要的 WATCH 又会让我们的程序产生竞争条件

后执行的命令无法依赖先执行命令的结果

  • 由于事务中的所有命令都是互相独立的,在遇到 exec 命令之前并没有真正的执行,所以我们无法在事务中的命令中使用前面命令的查询结果
  • 我们唯一可以做的就是通过 watch 保证在我们进行修改时,如果其它事务刚好进行了修改,则我们的修改停止,然后应用层做相应的处理

事务中的每条命令都会与 redis 服务器进行网络交互

  • redis事务开启之后,每执行一个操作返回的都是queued,这里就涉及到客户端与服务器端的多次交互
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值