Redis入门及环境搭建

Redis入门及环境搭建

一:Redis简介

Redis(Remote Dictionary Server 远程字典服务)是一个开源的(BSD许可的)内存数据结构存储,用作数据库、高速缓存和消息队列代理。 Redis提供五大基本类型和三大特殊类型数据如基本类型:字符串(String)、散列(hash)、列表(list)、集合(set)、有序集合(sorted);如特殊类型:位图(bitmaps)、超长日志(hyperloglog)、地理空间(geospatial)。Redis内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区。

1:为什么在项目中使用Redis

平常在项目中设计客户端访问数据库数据时往往采用直连方式访问数据库,也有采用数据库连接池技术来实现访问,但是我们要清楚,当以万次的访问量同时涌入我们数据库,或者频繁的操作数据库,将会对我们的数据库造成很大的压力; 为了解决这一问题,早些年前Memcached(缓存数据库)脱颖而出,Memcached它是一个非关系型数据库(NoSQL),这个数据库在项目中可以起到缓存作用,把一些经常使用的数据加载到缓存数据库中,防止了每次请求都往最终数据库查询数据; 后来Memcached随着时代的进步,Redis(NoSQL)数据库慢慢取代了它,成为新时代的霸主,Redis不仅可以存储简单的key-value类型,还有各式各样的方法类型,而且还可以持久化;所以它慢慢的以内存缓存的形式应用到大型企业项目中;

注:C/S/D = controller/server/dao

①:单机数据库问题:如果数据量太大导致一台服务器放不下了那么多数据的存储;访问量大幅增加导致数据库无法支撑

单机数据库转变为集群数据库来解决问题

②:集群数据库问题:网站一般都是以读为目录,虽是读写分离,大大提高性能,但是大量的读操作(读取相同数据)也会影响性能

集群数据库转变为集群数据库+缓存数据库

一般如电商网站有10W+个用户同时或者间接查看此热门商品,没有缓存数据库的支持的话,我这用户全部去请求MySQL数据库,这样会造成极大压力,但是我们有了高性能的内存缓存服务器,当第一个用户去服务器请求热门商品时,就把此商品保存在缓存服务器上(也可提前保存),这样每次请求直接走缓存数据库上即可,这就是我下面要介绍的缓存数据库(Redis)每秒可并发可达10W/s

2:Redis是什么及特点

Redis作用:

①:Redis是一个以Key-Value形式存储的数据库,可用来搭建数据唯一存储系统

②:Redis是一个把数据存储在内存中做高速缓存,用来在应用和数据库之间做缓存,替代Memcached

③:Redis对复杂类型结构有着高速操作的特点,提供某些特点业务场景下的计算和展现能力,比如排行榜

Redis特点:

①:Redis是一个完全开源免费的,遵守BSD协议,并且是一个高性能的key-value数据库

②:Redis支持数据库的持久化操作,可以将内存中的数据保存到磁盘中,重启时可以再次加载到内存中使用

③:Redis有着丰富的数据类型,并且还有三种复杂的特殊类型

Redis优势:

①:性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。这主要归功于这些数据都存在于内存中,并且是单线程

②:丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。

③:原子性 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

④:丰富的特性 – Redis还支持 publish/subscribe(发布/订阅)、通知,、key 过期、Lua 脚本、事务、Pipeline(管道,即当指令 到达一定数量后,客户端才会执行)等等特性。

⑤:兼容性强 – 支持多种编程语言。支持Java、PHP、Golang、Python、Ruby、Lua、Nodejs等。

⑥:高可用和分布式 – Redis-Sentinel(v2.8)支持高可用,Redis-Cluster(v3.0)支持分布式

3:Redis使用场景

①:缓存

这是Redis使用最多的场景,Redis可以代替Memcached,让我们的缓存只能存储数据变为能够更新数据,我们不再需要每次都重新生成数据

②:计数器

比如日常网站的统计点击次数,转发数,评论数等,由于是单线程,可以避免并发问题,保证不会错;计算器使用INCRBY命令

③:消息队列

相当于消息系统,运行稳定并且快速,支持模式匹配,能够实时订阅与取消频道,和ActiveMQ,RocketMQ等工具类似;如果对于数据一致性要 求高的话还是用RocketMQ等专业系统。 由于redis把数据添加到队列是返回添加元素在队列的第几位,所以可以做判断用户是第几个访问这种 业务 队列不仅可以把并发请求变成串行,并且还可以做队列或者栈使用

③:位处理(大数据处理)

用于数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,某用户是否在线状态等等。这里我们会用到bitmaps数据类型;这些 统计都是一些单一性的,比如签到记录,要么签到,要么未签到,我要统计一个人七天的签到情况只需要一个字节(八位)大小的空间即可记录, 从右往左,每位用0或1来记录是否签到(最后一位每用上,因为只有七天记录),放眼看,大数据量下是很省空间的

⑤:分布式锁与单线程机制

验证前端的重复请求,可以通过redis进行过滤:每次请求将request ip、参数、接口等hash作为key存储redis(幂等性请求),设置多长 时间有效期,然后下次请求过来的时候先在redis中检索有没有这个key,进而验证是不是一定时间内过来的重复提交 秒杀系统,基于redis是 单线程特征,防止出现数据库“爆破” 全局增量ID生成,类似“秒杀”

⑥:获取最新列表

例如新闻列表页面最新的新闻列表,如果总数量很大的情况下,尽量不要使用select a from A limit 10,尝试redis的 LPUSH命令构 建List,一个个顺序都塞进去就可以啦。不过万一内存清掉了咋办?也简单,查询不到存储key的话,用mysql查询并且初始化一个List到 redis中就好了。

⑦:排行榜

谁得分高谁排名往上。命令:ZADD(有续集,sorted set)

4:什么是NoSQL

NoSQL最常见的解释是“non-relational”, “Not Only SQL”也被很多人接受。NoSQL仅仅是一个概念,泛指非关系型的数据库,区别于关系数据库,它们不保证关系数据的ACID特性。NoSQL是一项全新的数据库革命性运动,其拥护者们提倡运用非关系型的数据存储,相对于铺天盖地的关系型数据库运用,这一概念无疑是一种全新的思维的注入。

NoSQL有如下优点:易扩展,NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。无形之间也在架构的层面上带来了可扩展的能力。大数据量,高性能,NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单

二:Linux安装Redis单机部署

1:Redis官方下载

其实redis主要使用在Linux系统中,我们在官网上下载一般都是Linux安装包,我就以window(redis-x64-6.2.5.zip) (win多版本)和 Linux(redis-6.2.5.tar.gz)来使用

2:安装Redis单机版

下载redis-6.2.5.tar.gz压缩包后上传到Linux的/home目录下,我们最终安装到 /usr/local/redis/ 目录下

Linux为Centos7系统安装redis6.2.5版本

①:安装最基本的环境

安装Redis编译的gcc-c++环境(全部我干脆都下载更新),这里我就直接以yum安装

yum -y install cpp binutils glibc glibc-kernheaders glibc-common glibc-devel gcc make gcc-c++ libstdc++-devel tcl

注:Centos6官方停止了对yum的维护,我们要么使用rpm安装yum后再操作,或者直接使用rpm安装Redis、

注:6.0以上的redis需要安装最新的gcc,4.8.3版本的gcc查看gcc版本:gcc -v

②:解压编译及安装

①:进入我们redis安装包位置并解压

cd /home

tar -zxvf redis-6.2.5.tar.gz

②:进入解压好的redis文件里

cd redis-6.2.5/

③:使用gcc编译redis,因为是C语言编写的

make && make PREFIX=/usr/local/redis install

注:make编译代码;make PREFIX=/usr/local/redis install安装到指定的位置

④:创建配置文件的目录

mkdir /usr/local/redis/conf

⑤:把配置文件拷贝到我们安装的位置

cp /home/redis-6.2.5/redis.conf /usr/local/redis/conf/

⑥:跳转到安装目录下创建sh脚本

cd /usr/local/redis

cat > startRedis.sh <

#!/bin/sh

/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf 2>&1 &

EOF#脚本说明:# /usr/local/redis/redis-server 开启服务执行文件位置# /usr/local/redis/conf/redis.conf 配置文件位置# 2>&1 标准错误2重定向到标准错误1中,数据脚本里面的语法# & 设置为守护进程,就是说后台运行

⑦:设置权限及运行

chmod 744 startRedis.sh./startRedis.sh

⑧:查看Redis启动状态

ps -ef | grep redis

⑨:杀死运行的全部redis

kill -9 `ps -ef | grep redis | grep -v grep | awk '{print $2}'`

③:本地客户端连接redis

在安装完redis后我们就可以在本地连接我们的redis

我们要去到安装redis的目录下 /usr/local/redis/bin/ 里面启动一个客户端命令

[root@VM-8-15-centos bin]# ls

redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server

[root@VM-8-15-centos bin]# ./redis-cli #客户端连接命令

127.0.0.1:6379> PING #测试连接

PONG

127.0.0.1:6379> SHUTDOWN #关闭服务端

not connected>

[root@VM-8-15-centos bin]#

3:Redis中bin目录文件说明

在安装完redis后,会有一个目录,里面全部是一些可执行文件

[root@VM-8-15-centos bin]# ll

总用量 18880

-rwxr-xr-x 1 root root 4829552 9月 25 13:32 redis-benchmark

lrwxrwxrwx 1 root root 12 9月 25 13:32 redis-check-aof -> redis-server

lrwxrwxrwx 1 root root 12 9月 25 13:32 redis-check-rdb -> redis-server

-rwxr-xr-x 1 root root 5003168 9月 25 13:32 redis-cli

lrwxrwxrwx 1 root root 12 9月 25 13:32 redis-sentinel -> redis-server

-rwxr-xr-x 1 root root 9492640 9月 25 13:32 redis-server

[root@VM-8-15-centos bin]#

redis-server:Redis服务端的启动程序

redis-cli:Redis命令行客户端操作工具

redis-benchmark:性能测试工具(服务启动起来后执行)

redis-check-aof:修复有问题的AOF文件

redis-check-rdb:修复有问题的dump.rdb文件

redis-sentinel:Redis集群使用,用来监控各个Redis服务

4:Redis配置文件的基本介绍

上面我们学会了如何安装Redis并简单的用客户端连接服务端,但是我们发现连接没有密码,也不知道如何在远程连接;但实现这些我们就得修改Redis的配置文件,也就是我们之前从解压目录里拷贝的 redis.conf 配置文件到安装目录里的conf目录下;我们要修改conf安装目录下的文件

注:在启动执行 redis-server 服务启动命令时 最好后面携带配置文件目录,否则默认官方配置!如:> redis-server ../redis.conf

bind:接口(IP地址)绑定

默认配置文件设置的是127.0.0.1 -::1,也就是说只能本机访问;那么未指定bind配置则代表任何计算机都可以访问连接

注:localhost/127.0.0.1 IPv4格式:127.0.0.1 IPv6格式:0:0:0:0:0:0:0:1 简写 ::1 '::'代表前面都是0

假设当前Redis服务器地址是119.29.68.209;我又租了两台服务器分别为

A服务器地址:119.29.68.222 B服务器地址:119.29.68.223

设置A服务器可以访问Redis bind 119.29.68.222

设置A、B服务器都可以访问Redis bind 119.29.68.222 -119.29.68.223 (多个IP之间可以用 '-' 分割)

设置任何服务器(IPv4、IPv6形式)都可以访问Redis bind * -::* 或注释bind配置 老版本设置 bind * 或者 bind 0.0.0.0

port:端口设置指定Redis端口,默认6379

requirepass:设置连接Redis需要验证的密码,默认空

5:客户端远程连接Redis

在设置好bind后我们就可以远程Redis服务器服务了(前提设置指定IP或者 * 任何都可连接)

第三方Redis客户端连接工具

①:使用自带的redis-cli连接远程Redis

具体的redis-cli使用操作可以执行 ./redis-cli --help

用法: redis-cli [OPTIONS] [cmd [arg [arg ...]]]

-h 设置要连接的Redis服务IP

-p 设置要连接的Redis服务端口

-a 设置要连接的Redis认证密码

连接时直接认证密码:

[root@VM-8-15-centos bin]# ./redis-cli -h 119.29.68.209 -p 6379 -a 1234

Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.

# 警告:在命令行界面上使用带有“-a”或“-u”选项的密码可能不安全

连接后认证密码:推荐√

[root@VM-8-15-centos bin]# ./redis-cli -h 119.29.68.209 -p 6379

119.29.68.209:6379> set name zhangsan

(error) NOAUTH Authentication required. # (错误)需要 NOAUTH 身份验证。

119.29.68.209:6379>AUTH 1234 # 设置密码

②:使用第三方RedisGUI工具

三:windows下安装Redis单机版部署

话说在windows下安装就可以理解为小白安装,解压就可以使用,最多运行是携带一个 redis.windows.conf 配置文件;这里我就以现在最新版的redis6.2.5版本讲解;安装包在第二节已经给出,大家可以去下载并解压,里面的大部分不认识的文件后面会介绍,我们只要掌握其中的几个文件即可

养成一个好习惯,启动redis是必须携带自己的配置文件!!

解压后里面的文件和在redis安装时的差不多,redis里有的它都有,我们最终搞懂那几个文件即可!

①:在windows下运行redis

打开cmd进入redis解压目录

cd C:\Users\xiaof\Desktop\redis\installRedis\redis-x64-6.2.5

注:如果cd不是当前盘符可以执行完cd后 再切换盘符 执行 如 D: 回车

运行服务端

redis-server.exe redis.windows.conf

运行客户端

redis-cli.exe -h 127.0.0.1 -p 6379

②:创建windows下运行redis的.bat脚本

①:创建一个Redis.bat文件,并把下面的脚本粘上去后修改路径即可

@echo off

rem cmd窗口名称

title redis-server

rem 路径变量

set ENV_HOME="C:\Users\xiaof\Desktop\redis\installRedis\redis-x64-6.2.5"

color 03

cd %ENV_HOME%

redis-server redis.windows.conf

②:使用本地客户端连接脚本,创建一个RedisClient.bat文件;如果没密码则需要把脚本的 -a xxxx 剔除

@echo off

rem 设置我们redis目录位置 开启客户端

set redis_home=C:\Users\xiaof\Desktop\redis\installRedis\redis-x64-6.2.5

start cmd /k "cd /d %redis_home%\ && redis-cli.exe -h 127.0.0.1 -p 6379 -a 1234"

exit

此地说明一下,在接下来的章节和续章都是以本地windows下的redis来讲解基本命名!!

四:Redis性能测试

官方已经为我们准备好性能测试工具 redis-benchmark ,它可以测试我们redis各个类型及场景下的读写性能

1:基本属性说明

Usage: redis-benchmark [-h ] [-p ] [-c ] [-n ] [-k ]

-h 设置redis服务端 IP (default 127.0.0.1)

-p 设置redis服务端 端口 (default 6379)

-a 设置redis服务端 密码

-c 设置多少个redis客户端并发连接redis服务端 (default 50)

-d 设置每次SET/GET值的数据大小,默认3字节 “VXK” (default 3)

-n 设置请求总数,若默认50个客户端,每个客户端只需要请求2000次 (default 100000)

-q 只显示每种类型测试 读/写 的秒数(不会输出大片测试过程)

-l 闭环模式,测试完后,循环上一次测试,(就是命令永远循环)

-r 设置指定数量的键;对SET/GET/INCR使用随机键,对SADD使用随机值,ZADD的随机成员和分数。

注:-r会被应用到key和counter键,并且拼接12位后缀标识为多少个;如 "key:000000000008" 代表生成的第八个键

-P 选项代表每个请求pipeline的数据量. Default 1 (no pipeline).

-t 设置选择性的测试操作,它只会对我们指定的命令测试 如 -t SET,SPOP,LPUSH (低版本只能测试17个命令) 注:看下面的性能测试方式,选择其中指定方式测试

-I 空闲模式。只需打开N个空闲连接并等待

-s Server socket (覆盖主机和端口)

-k 1=保持活动状态 0=重新连接 (default 1)

注:默认测试是一旦第一次连接,后面就不会断开,直到测试完成,

注:若是 1 则代表每次请求完成则断开连接,下次请求再重新连接

--csv 以CSV格式输出,方便我们统计Excel等处理

--user 用于发送ACL样式的“验证用户名密码”。需要 -a。

--dbnum 选择指定的数据库号进行测试,redis默认数据库为(0~16) (default 0)

--threads 启动多线程模式来测试

--cluster 启用集群模式来测试

--enable-tracking 启动测试之前发送客户端跟踪

--help 帮助文档

--version 显示版本号

2:性能测试默认方式

如果未指定 -t xxx,xxx,... 则从头至尾20个性能测试方案依次进行

(1) PING_INLINE

(2) PING_MBULK

(3) SET:将字符串值value关联到key;

(4) GET:返回key所关联的字符串值,如果key存储的值不是字符串类型,返回一个错误;

(5) INCR:将key中存储的数字值增一。不能转换为数字则报错;

(6) LPUSH:将一个或多个值value插入到列表key的表头;

(7) RPUSH:将一个或多个值value插入到列表key的表尾;

(8) LPOP:移除并返回列表key的头元素;

(9) RPOP:移除并返回列表key的尾元素;

(10) SADD:将一个或多个member元素加入到集合set当中,已经存在于集合的member元素将被忽略;

(11) HSET:将字符串值value关联到hash里的key中

(12) SPOP:移除并返回集合中的一个随机元素;

(13) ZADD:测试sortedSet有序集合的添加时间性能

(14) ZPOPMIN:测试sortedSet有序集合,删除多个值并把删除的值排序,从小到大

(15) LPUSH:将一个或多个value插入到列表key的表头;

(16) LRANGE_100:返回列表key中指定区间内的元素,前100条元素;

(17) LRANGE_300:返回列表key中指定区间内的元素,前300条元素;

(18) LRANGE_500:返回列表key中指定区间内的元素,前500条元素;

(19) LRANGE_600:返回列表key中指定区间内的元素,前600条元素;

(20) MSET:同时设置一个或多个key-value对,value为字符串。

如果未指定 -r 则以下面的四个键来测试

redis-benchmark性能测试默认情况下只会以如下类型key来测试(低版本应该是三个)

1) "myhash"

此类型为“hash”,每次请求则会向“hash”key为 “element:__rand_int__” 添加“VXK”,后者覆盖前者

2) "counter:__rand_int__"

此类型为“string”,每次请求则会累加 1 ,后个请求会对前一个值+1操作

3) "key:__rand_int__"

此类型为“string”,每次请求则会存放“VXK”,后个请求覆盖上一个值

4) "mylist"

此类型为“list”,往集合内部存储“VXK”,每请求一次则插入一次

3:常用测试方案及说明

①:连接redis服务器并测试以50个客户端并发(平分每个客户端2000次)访问100000次

./redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 100000

②:不输出测试过程,只显示当前测试案例结束的时间

./redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 100000 -q

④:总和上面,并设置每个请求的请求值的大小字节

./redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 100000 -q -d 5

④:综合上面,并设置指定的测试案例

./redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 100000 -q -d 5 -t SET,SADD,ZADD,GET

测试输出的格式说明:(以 SET 测试案例说明)

./redis-benchmark -h 127.0.0.1 -p 6379 -c 1000 -n 1000000 -t SET

====== SET ======

1000000 requests completed in 18.75 seconds

-- 1000000 请求用时 18.75 秒

1000 parallel clients

-- 每个客户端请求次数1000

3 bytes payload

-- 每次测试请求字节大小为 3byte

keep alive: 1

-- 保持活力模式 1 一直连接 (0则代表每次请求从新连接)

host configuration "save": 3600 1 300 100 60 10000

host configuration "appendonly": no

-- 上面两个主机配置 持久化方式关闭

multi-thread: no

-- 不是多线程测试

Latency by percentile distribution:

0.000% <= 6.159 milliseconds (cumulative count 1)

50.000% <= 13.191 milliseconds (cumulative count 500348)

75.000% <= 15.687 milliseconds (cumulative count 750168)

98.438% <= 26.799 milliseconds (cumulative count 984415)

100.000% <= 49.151 milliseconds (cumulative count 1000000)

100.000% <= 49.151 milliseconds (cumulative count 1000000)

Cumulative distribution of latencies:

0.000% <= 0.103 milliseconds (cumulative count 0)

1.234% <= 7.103 milliseconds (cumulative count 12338)

12.473% <= 9.103 milliseconds (cumulative count 124733)

49.156% <= 13.103 milliseconds (cumulative count 491559)

100.000% <= 48.127 milliseconds (cumulative count 999999)

100.000% <= 50.111 milliseconds (cumulative count 1000000)

Summary:

throughput summary: 53339.02 requests per second

-- 吞吐量摘要:每秒53339.02个请求

latency summary (msec):

--延迟摘要(毫秒)

avg min p50 p95 p99 max

13.639 6.152 13.191 21.663 28.351 49.151

4:其它参数介绍

①:-P

在上面的性能测试中,每个客户端都是在一个请求完成之后才发送下一个请求,如指定-c 50 -n 10000

则服务器几乎是按照顺序依次读取每个客户端的请求

Redis 支持 /topics/pipelining;就是说Redis可以一次性执行多条命令(把命令打包一次性发送服务器)

所以Redis pipelining 可以提高服务器的 TPS。

总结:减少网络的请求,把请求汇总处理

./redis-benchmark -c 20 -n 1000000 -t set,get -P 16 -q

五:Redis单线程为什么这么快

1.Redis基于内存,内存的读写速度非常快;

2.Redis是单线程的,为上下文切换线程节省了大量时间;

3.Redis使用多路复用技术来处理并发连接。非阻塞IO的内部实现使用epoll,以及由epoll +本身实现的简单事件框架。

epoll中的读取,写入,关闭和连接都被转换为事件,然后使用epoll的多路复用功能从不浪费任何时间在io上。

由于Redis是基于内存的操作,因此CPU并不是Redis的瓶颈。Redis的瓶颈很可能是机器内存或网络带宽的大小。既然单线程易

于实现并且CPU不会成为瓶颈,那采用单线程解决方案是合乎逻辑的了。

详细原因:

不需要各种锁的性能消耗:

Redis的数据结构并非全都是简单的键值(Key-Value),而是有复杂的结构,如列表(list)和哈希(hash)。这些结构

可以执行细粒度的操作,例如在长列表后面添加元素并将其添加到哈希(hash)或删除对象。

这些操作可能需要非常大量的锁,从而导致同步开销显着增加。简而言之,在单线程的情况下,不需要考虑各种锁,没有

锁释放操作,也没有由于可能的死锁而导致的性能消耗。

单线程多进程集群方案:

单线程的功能实际上非常强大,每个内核的效率也很高。与单线程相比,多线程自然可以具有更高的性能限制。但是,

在当今的计算环境中,甚至单机多线程限制通常也无法满足。需要进一步探索的是多服务器群集方案,并且这些方案中

的多线程技术仍然是不可用的。

所以单线程、多进程的集群不失为一个时髦的解决方案。

CPU消耗:

单线程用于避免不必要的上下文切换和竞争条件,并且不存在消耗CPU的多进程或多线程切换。但是,如果CPU成为Redis的

瓶颈,或者不想让服务器的其他CPU内核闲置怎么办?您可以考虑几个Redis进程。Redis是一个键值(key-value)数据库,

而不是关系数据库,并且数据之间没有约束。只要客户端能够区分哪个键位于哪个Redis进程上,就可以完成了。

.

🌟 相关推荐

野生动物斑鸠随处可见,为啥不能吃?吃的食物各种各样,有毒
我该如何面对你的私生子?
365世界杯

我该如何面对你的私生子?

📅 07-09 👀 4424
咽字组词,包含咽字的词语
如何打开mobile365

咽字组词,包含咽字的词语

📅 07-30 👀 3633