缓存概念、redis介绍、redis安装及使用、redis配置介绍、RDB和AOF持久化、redis数据类型与操作

缓存概念

缓存是为了调节速度不一致的两个或多个不同的物质的速度,在中间对速度较快的一方起到一个加速访问速度较慢的一方的作用。

比如 CPU 的一级、二级缓存是保存了 CPU 最近经常访问的数据,内存是保存 CPU 经常访问硬盘的数据,而且硬盘也有大小不一的缓存,甚至是物理服务器的 raid 卡有也缓存,都是为了起到加速 CPU 访问硬盘数据的目的。

因为 CPU 的速度太快了,CPU 需要的数据硬盘往往不能在短时间内满足 CPU 的需求,因此 PCU 缓存、内存、Raid 卡以及硬盘缓存就在一定程度上满足了 CPU 的数据需求,即 CPU 从缓存读取数据可以大幅提高 CPU 的工作效率。

系统缓存

buffer 与 cache:
buffer:缓冲也叫写缓冲,一般用于写操作,可以将数据先写入内存在写入磁盘,buffer 一般用于写缓冲,用于解决不同介质的速度不一致的缓冲,先将数据临时写入到里自己最近的地方,以提高写入速度,CPU 会把数据先写到内存的磁盘缓冲区,然后就认为数据已经写入完成看,然后由内核在后续的时间在写入磁盘,所以服务器突然断电会丢失内存中的部分数据。

cache:缓存也叫读缓存,一般用于读操作,CPU 读文件从内存读,如果内存没有就先从硬盘读到内存再读到 CPU,将需要频繁读取的数据放在里自己最近的缓存区域,下次读取的时候即可快速读取。

cache 的保存位置:
客户端:浏览器
内存:本地服务器、远程服务器
硬盘:本机硬盘、远程服务器硬盘

cache 的特性:
自动过期:给缓存的数据加上有效时间,超出时间后自动过期删除
过期时间:强制过期,源网站更新图片后 CDN 是不会更新的,需要强制是图片缓存过期
命中率:即缓存的读取命中率

用户层缓存

DNS 缓存:
默认为 60 秒,即 60 秒之内在访问同一个域名就不在进行 DNS 解析

浏览器缓存过期机制:最后修改时间。
系统调用会获取文件的最后修改时间,如果没有发生变化就返回给浏览器 304 的状态码,表示没有发生变化,然后浏览器就使用的本地的缓存展示资源

Egtag标记 :
基于 Etag 标记是否一致做判断页面是否发生过变化,比如基于 Nginx 的 etag on 来实现

过期时间:
以上两种都需要发送请求,即不管资源是否过期都要发送请求进行协商,这样会消耗不必要的时间,因此有了缓存的过期时间,即第一次请求资源的时候带一个资源的过期时间,默认为 30 天,当前这种方式使用的比表较多,但是无法保证客户的时间都是准确并且一致的,因此假如一个最大生存周期,使用用户本地的时间计算缓存数据是否超过多少天,下面的过期时间为 2027 年,但是缓存的最大生存周期计算为天等于 3650 天即 10 年

CDN缓存

什么是 CND:
内容分发网络(Content Delivery Network),通过将服务内容分发至全网加速节点,利用全球调度系统使用户能够就近获取,有效降低访问延迟,提升服务可用性,
CDN 第一降低机房的使用带宽,因为很多资源通过 CDN 就直接返回用户了。
第二解决不同运营商之间的互联,因为可以让联通的网络访问联通让电信的网络访问电信,起到加速用户访问的目的。
第三:解决用户访问的地域问题,就近返回用户资源。

用户请求 CDN 流程:
提前对静态内容进行预缓存,避免大量的请求回源,导致主站网络带宽被打满而导致数据无法更新,另外 CDN 可以将数据根据访问的热度不同而进行不同级别的缓存,例如访问量最高的资源访问 CDN边缘节点的内存,其次的放在 SSD 或者 SATA,再其次的放在云存储,这样兼顾了速度与成本。

CDN 主要优势:
提前对静态内容进行预缓存,避免大量的请求回源,导致主站网络带宽被打满而导致数据无法更新,另外 CDN 可以将数据根据访问的热度不同而进行不同级别的缓存,例如访问量最高的资源访问 CDN边缘节点的内存,其次的放在 SSD 或者 SATA,再其次的放在云存储,这样兼顾了速度与成本。缓存-缓存到最快的地方如内存,缓存的数据准确命中率高,访问速度就快调度准确-将用户调度到最近的边缘节点性能优化-CDN 专门用于缓存响应速度快
安全相关-抵御攻击
节省带宽:由于用户请求由边缘节点响应,因此大幅降低到源站带宽。

应用层缓存

Nginx、PHP 等 web 服务可以设置应用缓存以加速响应用户请求,另外有些解释性语言比如 PHP/Python不能直接运行,需要先编译成字节码,但字节码需要解释器解释为机器码之后才能执行,因此字节码也是一种缓存,有时候会出现程序代码上线后字节码没有更新的现象。

其他层缓存

例如有:
CPU 缓存(L1 的数据缓存和 L1 的指令缓存)、二级缓存、三级缓存
磁盘缓存
RAID 卡
分布式缓存:redis、memcache

redis介绍

redis简介

Redis和Memcached是非关系型数据库也称为NoSQL数据库。
MySQL、Mariadb、SQL Server、PostgreSQL、Oracle 数据库属于关系型数据(RDBMS, Relational Database Management System)

Redis(Remote Dictionary Server)在 2009 年发布,开发者 Salvatore Sanfilippo 是意大利开发者,他本想为自己的公司开发一个用于替换 MySQL 的产品 Redis,但是没有想到他把 Redis 开源后大受欢迎,短短几年,Redis 就有了很大的用户群体。

目前国内外使用的公司有知乎网、新浪微博、GitHub 等redis是一个开源的、遵循BSD协议的、基于内存的而且目前比较流行的键值数据库(key-value database),是一个非关系型数据库,redis 提供将内存通过网络远程共享的一种服务,提供类似功能的还有memcache,但相比 memcache,redis 还提供了易扩展、高性能、具备数据持久性等功能。Redis 在高并发、低延迟环境要求比较高的环境使用量非常广泛。

redis 对比 memcached:
1.支持数据的持久化:可以将内存中的数据保持在磁盘中,重启 redis 服务或者服务器之后可以从备份文件中恢复数据到内存继续使用。
2.支持更多的数据类型:支持 string(字符串)、hash(哈希数据)、list(列表)、set(集合)、zet(有序集合)
3.支持数据的备份:可以实现类似于数据的 master-slave 模式的数据备份,另外也支持使用快照+AOF。
4.支持更大的 value 数据:memcache 单个 key value 最大只支持 1MB,而 redis 最大支持 512MB。
5.Redis 是单线程,而 memcache 是多线程,所以单机情况下没有 memcache 并发高,但 redis 支持分布式集群以实现更高的并发,单 Redis 实例可以实现数万并发。
6.支持集群横向扩展:基于 redis cluster 的横向扩展,可以实现分布式集群,大幅提升性能和数据安全性。
7.都是基于 C 语言开发。

redis 典型应用场景:
Session 共享:常见于 web 集群中的 Tomcat 或者 PHP 中多 web 服务器 session 共享
消息队列:ELK 的日志缓存、部分业务的订阅发布系统
计数器:访问排行榜、商品浏览数等和次数相关的数值统计场景
缓存:数据查询、电商网站商品信息、新闻内容
微博/微信社交场合:共同好友、点赞评论等

redis安装及使用

1.yum 安装 redis,需要epel源。
2.官网下载release版本redis源码包:
https://download.redis.io/releases/

编译安装参考:

tar xf redis-5.0.3.tar.gz
cd redis-5.0.3
make PREFIX=/usr/local/redis install

如果需要使用systemctl来启动,需要添加选项USE_SYSTEMD=yes
make USE_SYSTEMD=yes PREFIX=/usr/local/redis install
注意:这需要安装systemd.devel包

mkdir /usr/local/redis/etc
cp redis.conf /usr/local/redis/etc/

前台启动redis:

 /usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf

部分问题解决方法

1.tcp-backlog
The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow. If a connection request arrives when the queue is full, the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission, the request may be ignored so that a later reattempt at connection succeeds.

backlog 参数控制的是三次握手的时候 server 端收到 client ack 确认号之后的队列值。

打开配置文件
vim /etc/sysctl.conf

添加内核参数

net.core.somaxconn = 512

2.vm.overcommit_memory
0、表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1、表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2、表示内核允许分配超过所有物理内存和交换空间总和的内存

添加内核参数

vm.overcommit_memory = 1

3.transparent hugepage
开启大页内存动态分配,需要关闭让 redis 负责内存管理。

echo never > /sys/kernel/mm/transparent_hugepage/enabled
redis服务启动脚本
vim /usr/lib/systemd/system/redis.service
[Unit]
Description=Redis persistent key-value database
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/redis-server /etc/redis.conf --supervised systemd
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
Type=auto
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755

[Install]
WantedBy=multi-user.target
创建用户和数据目录
groupadd -g 1000 redis && useradd -u 1000 -g 1000 redis -s /sbin/nologin
mkdir -pv /usr/local/redis/{etc,logs,data,run}
chown redis.redis -R /usr/local/redis/

使用客户端连接redis
/usr/local/redis/bin/redis-cli -h IP/HOSTNAME -p PORT -a PASSWORD
创建命令软连接
 ln -sv /usr/local/redis/bin/redis-* /usr/bin/
附:python 连接方式
#!/bin/env python
#Author: ZhangJie
import redis
import time
pool = redis.ConnectionPool(host="192.168.7.101",
port=6379,password="")
r = redis.Redis(connection_pool=pool)
for i in range(100):
  r.set("k%d" % i,"v%d" % i)
  time.sleep(1)
  data=r.get("k%d" % i)
  print(data)

redis配置介绍

bind 0.0.0.0 #监听地址,可以用空格隔开后多个监听 IP
protected-mode yes #redis3.2 之后加入的新特性,在没有设置 bind IP 和密码的时候只允许访问127.0.0.1:6379
port 6379 #监听端口

tcp-backlog 511 #三次握手的时候 server 端收到 client ack 确认号之后的队列值。
timeout 0 #客户端和 Redis 服务端的连接超时时间,默认是 0,表示永不超时。
tcp-keepalive 300 #tcp 会话保持时间

daemonize no #认情况下 redis 不是作为守护进程运行的,如果你想让它在后台运行,你就把它改成
yes,当 redis 作为守护进程运行的时候,它会写一个 pid 到 /var/run/redis.pid 文件里面
supervised no #和操作系统相关参数,可以设置通过 upstart 和 systemd 管理 Redis 守护进程centos 7以后都使用 systemd
pidfile /var/run/redis_6379.pid #pid 文件路径
loglevel notice #日志级别
logfile “” #日志路径

databases 16 #设置 db 库数量,默认 16 个库
always-show-logo yes #在启动 redis 时是否显示 log
save 900 1 #在 900 秒内有1个键内容发生更改就出就快照机制
save 300 10 #在 300 秒内有10个键内容发生更改就出就快照机制
save 60 10000 #在 60 秒内有10000个键内容发生更改就出就快照机制
stop-writes-on-bgsave-error no #快照出错时是否禁止 redis 写入操作
rdbcompression yes #持久化到 RDB 文件时,是否压缩,"yes"为压缩,"no"则反之
rdbchecksum yes #是否开启 RC64 校验,默认是开启
dbfilename dump.rdb #快照文件名

replica-serve-stale-data yes #当从库同主库失去连接或者复制正在进行,从机库有两种运行方式:1) 如果 replica-serve-stale-data 设置为 yes(默认设置),从库会继续响应客户端的读请求。2) 如果 replica-serve-stale-data 设置为 no,除去指定的命令之外的任何请求都会返回一个错误"SYNC with master inprogress"。

replica-read-only yes #是否设置从库只读

repl-diskless-sync no #是否使用 socket 方式复制数据,目前 redis 复制提供两种方式,disk 和 socket,如果新的 slave 连上来或者重连的 slave 无法部分同步,就会执行全量同步,master 会生成 rdb 文件,有2 种方式:disk 方式是 master 创建一个新的进程把 rdb 文件保存到磁盘,再把磁盘上的 rdb 文件传递给 slave,socket 是 master 创建一个新的进程,直接把 rdb 文件以 socket 的方式发给 slave,disk 方式的时候,当一个 rdb 保存的过程中,多个 slave 都能共享这个 rdb 文件,socket 的方式就是一个个 slave顺序复制,只有在磁盘速度缓慢但是网络相对较快的情况下才使用 socket 方式,否则使用默认的 disk方式

repl-diskless-sync-delay 30 #diskless 复制的延迟时间,设置 0 为关闭,一旦复制开始还没有结束之前,master 节点不会再接收新 slave 的复制请求,直到下一次开始

repl-ping-slave-period 10 #slave 根据 master 指定的时间进行周期性的 PING 监测
repl-timeout 60 #复制链接超时时间,需要大于 repl-ping-slave-period,否则会经常报超时

repl-disable-tcp-nodelay no #在 socket 模式下是否在 slave 套接字发送 SYNC 之后禁用 TCP_NODELAY,如果你选择“yes”Redis 将使用更少的 TCP 包和带宽来向 slaves 发送数据。但是这将使数据传输到 slav 上有延迟,Linux 内核的默认配置会达到 40 毫秒,如果你选择了 “no” 数据传输到 salve 的延迟将会减少但要使用更多的带宽

repl-backlog-size 1mb #复制缓冲区大小,只有在 slave 连接之后才分配内存。
repl-backlog-ttl 3600 #多次时间 master 没有 slave 连接,就清空 backlog 缓冲区。
replica-priority 100 #当 master 不可用,Sentinel 会根据 slave 的优先级选举一个 master。最低的优先级的 slave,当选 master。而配置成 0,永远不会被选举。

requirepass foobared #设置 redis 连接密码
rename-command #重命名一些高危命令
maxclients 10000 #最大连接客户端
maxmemory #最大内存,单位为bytes 字节,8G内存的计算方式 8(G)*1024(MB)*1024(KB)*1024(Kbyte),需要注意的是 slave 的输出缓冲区是不计算在 maxmemory 内。

appendonly no #是否开启 AOF 日志记录, 默认 redis 使用的是 rdb 方式持久化,这种方式在许多应用中已经足够用了。但是 redis 如果中途宕机,会导致可能有几分钟的数据丢失,根据 save 来策略进行持久化,Append Only File 是另一种持久化方式,可以提供更好的持久化特性。Redis 会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时 Redis 都会先把这个文件的数据读入内存里,先忽略 RDB 文件。

appendfilename “appendonly.aof” #AOF 文件名

appendfsync everysec #aof 持久化策略的配置 , no 表示不执行 fsync,由操作系统保证数据同步到磁盘,always 表示每次写入都执行 fsync,以保证数据同步到磁盘,everysec 表示每秒执行一次fsync,可能会导致丢失这 1s 数据。

no-appendfsync-on-rewrite no 在 aof rewrite 期间,是否对 aof 新记录的 append 暂缓使用文件同步策略,主要考虑磁盘 IO 开支和请求阻塞时间。默认为 no,表示"不暂缓",新的 aof 记录仍然会被立即同步,Linux 的默认 fsync 策略是 30 秒,如果为 yes 可能丢失 30 秒数据,但由于 yes 性能较好而且会避免出现阻塞因此比较推荐。

auto-aof-rewrite-percentage 100 # 当 Aof log 增长超过指定百分比例时,重写 log file, 设置为 0 表示不自动重写 Aof 日志,重写是为了使 aof 体积保持最小,而确保保存最完整的数据。
auto-aof-rewrite-min-size 64mb #触发 aof rewrite 的最小文件大小
aof-load-truncated yes #是否加载由于其他原因导致的末尾异常的 AOF 文件(主进程被 kill/断电等)

aof-use-rdb-preamble yes #redis4.0 新增 RDB-AOF 混合持久化格式,在开启了这个功能之后,AOF 重写产生的文件将同时包含 RDB 格式的内容和 AOF 格式的内容,其中 RDB 格式的内容用于记录已有的数据,而 AOF 格式的内存则用于记录最近发生了变化的数据,这样 Redis 就可以同时兼有 RDB 持久化和AOF 持久化的优点(既能够快速地生成重写文件,也能够在出现问题时,快速地载入数据)。

lua-time-limit 5000 #lua 脚本的最大执行时间,单位为毫秒
cluster-enabled yes #是否开启集群模式,默认是单机模式
cluster-config-file nodes-6379.conf #由 node 节点自动生成的集群配置文件
cluster-node-timeout 15000 #集群中 node 节点连接超时时间

cluster-replica-validity-factor 10 #在执行故障转移的时候可能有些节点和 master 断开一段时间数据比较旧,这些节点就不适用于选举为 master,超过这个时间的就不会被进行故障转移

cluster-migration-barrier 1 #一个主节点拥有的至少正常工作的从节点,即如果主节点的 slave 节点故障后会将多余的从节点分配到当前主节点成为其新的从节点。

cluster-require-full-coverage no #集群槽位覆盖,如果一个主库宕机且没有备库就会出现集群槽位不全,那么 yes 情况下 redis 集群槽位验证不全就不再对外提供服务,而 no 则可以继续使用但是会出现查询数据查不到的情况(因为有数据丢失)。

#Slow log 是 Redis 用来记录查询执行时间的日志系统,slow log 保存在内存里面,读写速度非常快,因此可以放心地使用它,不必担心因为开启 slow log 而损害 Redis 的速度。

slowlog-log-slower-than 10000 #以微秒为单位的慢日志记录,为负数会禁用慢日志,为 0 会记录每个命令操作。
slowlog-max-len 128 #记录多少条慢日志保存在队列,超出后会删除最早的,以此滚动删除

RDB和AOF持久化

redis 虽然是一个内存级别的缓存程序,即 redis 是使用内存进行数据的缓存的,但是其可以将内存的数据按照一定的策略保存到硬盘上,从而实现数据持久保存的目的,redis 支持两种不同方式的数据持久化保存机制

RDB 模式:

基于时间的快照,只保留当前最新的一次快照,特点是执行速度比较快,缺点是可能会丢失从上次快照到当前快照未完成之间的数据。

RDB 实现的具体过程 Redis 从主进程先 fork 出一个子进程,使用写时复制机制,子进程将内存的数据保存为一个临时文件,比如 dump.rdb.temp,当数据保存完成之后再将上一次保存的 RDB 文件替换掉,然后关闭子进程,这样可以保存每一次做 RDB 快照的时候保存的数据都是完整的,因为直接替换 RDB文件的时候可能会出现突然断电等问题而导致 RDB 文件还没有保存完整就突然关机停止保存而导致数据丢失的情况,可以手动将每次生成的 RDB 文件进程备份,这样可以最大化保存历史数据。

RDB模式的优缺点:
优点:
-RDB 快照保存了某个时间点的数据,可以通过脚本执行 bgsave(非阻塞)或者 save(阻塞)命令自定义时间点北备份,可以保留多个备份,当出现问题可以恢复到不同时间点的版本。
-可以最大化 o 的性能,因为父进程在保存 RDB 文件的时候唯一要做的是 fork 出一个子进程,然后的
-操作都会有这个子进程操作,父进程无需任何的 IO 操作RDB 在大量数据比如几个 G 的数据,恢复的速度比 AOF 的快

缺点:
-不能时时的保存数据,会丢失自上一次执行 RDB 备份到当前的内存数据
-数据量非常大的时候,从父进程 fork 的时候需要一点时间,可能是毫秒或者秒

AOF模式:

AOF模式 :
按照操作顺序依次将操作添加到指定的日志文件当中,特点是数据安全性相对较高,缺点是即使有些操作是重复的也会全部记录。

AOF 和 RDB 一样使用了写时复制机制,AOF 默认为每秒钟 fsync 一次,即将执行的命令保存到 AOF 文件当中,这样即使 redis 服务器发生故障的话顶多也就丢失 1 秒钟之内的数据,也可以设置不同的 fsync策略,或者设置每次执行命令的时候执行 fsync,fsync 会在后台执行线程,所以主线程可以继续处理用户的正常请求而不受到写入 AOF 文件的 IO 影响

AOF模式优缺点:
AOF 的文件大小要大于 RDB 格式的文件根据所使用的 fsync 策略(fsync 是同步内存中 redis 所有已经修改的文件到存储设备),默认是appendfsync everysec 即每秒执行一次 fsync

redis数据类型与操作

1.字符串类型

字符串是所有编程语言中最常见的和最常用的数据类型,而且也是 redis 最基本的数据类型之一,而且 redis 中所有的 key 的类型都是字符串。

添加一个key:

set key1 value1
set key2 value2 ex 3 #设置自动过期时间

读取一个key的内容:

 get key1

删除一个key:

DEL key1 value1

批量添加key:

MSET key1 value1 key2 value2

批量读取key:

MGET key1 key2 

追加数据:

APPEND key1 append
key1内容:
"value1"
追加后key1内容
"value1append"

数值递增:

set num 1
INCR num
此时num的值为2

数值递减:

set num 1
DECR num
此时num的值为0

返回字符串key长度:

STRLEN key1

2.列表

列表是一个双向可读写的管道,其头部是左侧尾部是右侧,一个列表最多可以包含 2^32-1 个元素即4294967295 个元素。
返回字符串key长度:

LPUSH list1 l1 l2 l3
或
RPUSH list1 l1 l2 l3

区别在于LPUSH是从左边添加,RPUSH是从右边添加

LPUSH后的列表如下:
l3 l2 l1

RPUSH后的列表如下:
l1 l2 l3

获取列表长度:

LLEN list1

移除列表数据:

RPOP list1 #最后一个LPOP list1 #第一个

3.集合(set)

集合是一个String类型的无序集合,且成员是唯一的,集合中不能出现重复的数据。

生成集合 key:

SADD set1 v1

追加数据:

追加的时候不能追加已经存在的数值
SADD set1 v1
SADD set1 v2 v3 v4
此时再使用
SADD set1 v2 
则没有追加成功

查看集合的所有数据:

SMEMBERS set1

取集合的差集:

差集:已属于A而不属于B的元素称为AB的差集
SDIFF set1 set2

取集合的交集:

交集:已属于A且属于B的元素称为AB的交集
SINTER set1 set2

取集合的并集:

交集:已属于A或属于B的元素为称为AB的并集
SUNION set1 set2

4.有序集合(sorted set)

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员,不同的是每个元素都会关联一个 double(双精度浮点型)类型的分数,redis 正是通过分数来为集合中的成员进行从小到大的排序。

有序集合的成员是唯一的,但分数(score)却可以重复,集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1), 集合中最大的成员数为 2^32 - 1 (4294967295, 每个集合可存储 40 多亿个成员)。

生成有序集合:

ZADD zset1 1 v1

批量添加多个数值:

ZADD zset2 1 v1 2 v2 4 v3 5 v5

排行示例:

ZADD zset1 10 key1 20 key2 30 key3
ZREVRANGE zset1 0 -1 withscores #显示指定集合内所有 key 和数值情况
输出结果
1) "key3"
2) "30"
3) "key2"
4) "20"
5) "key1"
6) "10"

获取集合的长度数:

ZCARD zset1

基于索引返回数值:

输出索引1-3的数据
RANGE zset1 1 3

返回某个数值的索引:

输出索引1-3的数据
ZRANK zset1 v2

5.哈希(hash)

hash是一个string类型的field和value的映射表,hash特别适合用于存储对象,Redis中每个hash可以存储232-1键值对(40多亿)。
生成hash key:

HSET hset1 name h1 age 18

获取hashy key字段值:

HGET hset1 name
输出结果
"h1"
HGET hset1 ag3
输出结果
"18"

删除一个hashy key的字段:

HDEL hset1 age

获取所有hash表中的字段:

生成数据
HSET hset1 name h1 age 18

获取字段
HKEYS hset1

输出结果
1)"name"
2)"age"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值