MySQL应用实践

《老男孩Linux运维》笔记
MySQL-Documentation




概述

MySQL介绍

MySQL属于传统关系型数据库产品,它开放式的架构使得用户选择性很强,同时社区开发与维护人数众多。
MySQL是一种关系型数据库管理系统,关系型数据库的特点是将数据保存在不同的表中,再将这些表放入不同的数据库中,而不是将所有数据统一放在一个大仓库里,这样的设计增加了MySQL的读取速度,而且灵活性和可管理型也得到了很大提高。
访问和管理MySQL数据库的最常用标准化语言为SQL结构化查询语言。


MariaDB介绍

自从甲骨文公司收购了MySQL之后,为了避免Oracle将MySQL闭源,MySQL社区采用分支的方式来避开这个风险。MariaDB就这样诞生了。
MariaDB是一个向后兼容,可能在以后替代MySQL的数据库产品。


MySQL多实例介绍

什么是MySQL多实例

MySQL多实例就是在一台服务器上同时开启多个不同的服务器端口(如3306, 3307),同时运行多个MySQL服务进程,这些服务进程通过不同的socket监听不同的服务器端口来提供服务。

这些MySQL多实例公用一套MySQL安装程序,使用不同的 my.cnf(也可以相同)和数据文件。在提供服务时,多实例MySQL在逻辑上看起来是各自独立的,他们根据配置文件的对应设定值,获得服务器相应数量的硬件资源。

其实很多网络服务都是可以配置多实例的,如 Nginx,Apache,Mongodb,Redis等。

MySQL多实例的作用与问题

MySQL多实例作用:

  1. 有效利用服务器资源;
  2. 节约服务器资源;

MySQL多实例有它的好处,也有其弊端。比如,会存在资源互相抢占的问题


MySQL多实例的应用场景

  1. 资金紧张型公司的选择;
  2. 并发访问不是特别大的业务;
  3. 门户网站应用MySQL多实例场景;


MySQL多实例常见的配置方案

单一配置文件、单一启动程序的多实例部署方案

单一配置文件、单一启动程序实施方案。
vim /etc/my.cnf

[mysqld_multi]
mysqld = /bin/mysqld_safe
mysqladmin = /bin/mysqladmin
user = mysql

[mysqld1]
socket = /dir/path/mysql.sock
port = 3306
pid-file = /dir/path/mysql.pid
datedir = /dir/path/mysql
user = mysql

[mysql2]
socket = /path/mysql.sock
port = 3307
pid-file = /path/mysql.pid
datedir = /path/mysql
user = mysql

启动命令:
mysqld_multi --config-file=/path/my.cnf start 1,2

不建议使用单一配置文件和单一启动程序部署多实例方案



安装并配置多实例MySQL数据库

安装MySQL多实例

安装MySQL依赖包
yum install -y ncurses-devel libaio-devel

安装MySQL

  1. 源码安装:
useradd -s /sbin/nologin -M mysql
wget  mysql源码包
tar mysql压缩包
cd mysql-xxx
./configure
make&&make install
  1. rpm包安装:
wget http://repo.mysql.com/xxx 选择匹配的版本
rpm -ivh mysql.xxx.rpm
yum install mysql-server


创建MySQL多实例的数据文件目录

mkdir -p /var/mysql/{3306,3307,3308}/db


创建MySQL多实例的配置文件

MySQL数据库默认为用户提供了多个配置文件模板,用户可以根据服务器硬件配置的大小来选择。

vi /var/mysql/3306/my06.cnf
vi /var/mysql/3307/my07.cnf
vi /var/mysql/3308/my08.cnf

为了让MySQL多实例之间彼此独立,要为每一个实例建立一个 my.cnf 配置文件和一个启动文件MySQL,让它们分别对应自己的数据文件目录 db。


3306实例 3307实例 3308实例
[ client ]
port = 3306
socket = /var/mysql/3306/mysql.sock

[ mysql ]
no-auto-rehash

[ mysqld ]
user = mysql
port = 3306
socket = /var/mysql/3306/mysql.sock
basedir = /bin/mysql
datadir = /var/mysql/3306/db
以及其他优化信息

[ mysql_safe ]
pid-file = /var/mysql/3306/mysql.pid
log-error = /var/log/mysql/mysql06.log
[ client ]
port = 3307
socket = /var/mysql/3307/mysql.sock

[ mysql ]
no-auto-rehash

[ mysqld ]
user = mysql
port = 3307
socket = /var/mysql/3307/mysql.sock
basedir = /bin/mysql
datadir = /var/mysql/3307/db
以及其他优化信息

[ mysqld_safe ]
pid-file = /var/mysql/3307/mysql.pid
log-error = /var/log/mysql/mysql07.log
[ client ]
port = 3308
socket = /var/mysql/3308/mysql.sock

[ mysql ]
no-auto-rehash

[ mysqld ]
user = mysql
port = 3308
socket = /var/mysql/3308/mysql.sock
basedir = /bin/mysql
datadir = /var/mysql/3308/db
以及其他优化信息

[ mysqld_safe ]
pid-file = /var/mysql/3308/mysql.pid
log-error = /var/log/mysql/mysql08.log


创建MySQL多实例的启动程序

创建MySQL启动文件

vim /var/mysql/3306/mysql.sh
vim /var/mysql/3307/mysql.sh
vim /var/mysql/3308/mysql.sh

这几个启动MySQL实例的脚本自己根据需要来写。
在多实例启动文件中,启动MySQL不同实例服务,所执行的命令实质是有区别的。

mysqld_sage --defaulte-files=/var/mysql/3306/my06.cnf >/dev/null 2>&1
mysqladmin -u root -p passwd -S /var/mysql/3306/mysql.sock shutdown


配置MySQL多实例文件权限

建议权限:700


MySQL相关命令加入全局路径的配置

配置MySQL全局路径

which mysql
echo 'export PATH=/path/xxx/mysql/bin:$PATH' >> /etc/profile
echo $PATH 查看



或者使用软连接的方法

ln -s /path/xxx/mysql/bin/* /usr/local/sbin/

务必把MySQL命令路径放在PATH路径中其他路径的前面,否则,可能会导致使用的 mysql 命令不是同一个,进而产生错误。


启动MySQL多实例数据库

/var/mysql/3306/mysql.sh start
/var/mysql/3307/mysql.sh start
/var/mysql/3308/mysql.sh start

如果发现没有显示MySQL对应实施的端口,请稍等几秒在检查,MySQL服务的启动比Web服务慢一些;
请查看错误日志


配置及管理MySQL多实例数据库

服务的开机自启动很关键!把MySQL多实例的启动命令加入 /etc/rc.local,实现开机自启动。

echo "/var/mysql/3306/mysql.sh start
echo "/var/mysql/3307/mysql.sh start
echo "/var/mysql/3308/mysql.sh start

socket connect
mysql -S /var/mysql/3306/mysql.sock

MySQL安全配置:

mysqladmin -uroot -S /var/mysql/3306/mysql.sock -ppasswd    #设置密码
mysql -uroot -S /var/mysql/3306/mysql.sock -p

禁止使用 kill -9 等命令强制杀死数据库,这会引起数据库无法启动等故障发生。


MySQL主从复制介绍

MySQL的主从复制并不是数据库磁盘上的文件直接拷贝,而是通过逻辑的 binlog 日志复制到要同步的服务器本地,然后由本地的线程读取日志里面的 SQL 语句,重新应用到MySQL数据库汇总。

MySQL数据库支持单向、双向、链式级联、环状等不同业务场景的复制。
在复制过程中,一台服务器充当 主服务器(Master), 接收来自用户的内容更新;
而一个或多个的其他服务器充当从服务器(Slave),接收来自主服务器 binlog 文件的日志内容,解析出SQL,重新更新到从服务器,使得主从服务器数据达到一致;
如果设置了链式级联,那么,从服务器(Slave)本身除了充当从服务器外,也会同时充当其下面从服务器的主服务器。链式级联复制类似 A-->B-->C 的复制形式。

一主一从逻辑图
一主多从逻辑图
双向主主复制逻辑图
线性级联单向双主复制逻辑图
环状级联单向多主同步逻辑图
MySQL常见复制架构图



MySQL主从复制都是异步的复制方式,既不是严格实时的数据同步,但是正常情况下给用户的体验是实时的。


MySQL主从复制的企业应用场景

MySQL主从复制集群功能使得MySQL数据库支持大规模高并发读写成为可能,同时有效保护了物理服务器宕机场景的数据备份。

应用场景1:从服务器作为主服务器的实时数据备份
主从服务器架构的设置可以大大加强MySQL数据库架构的健壮性。当主服务器出现问题时,可设置自动切换到从服务器继续提供服务,此时从服务器的数据与宕机时的主数据库几乎是一模一样的。
这类似 NFS 储存数据通过 inotify+rsync 同步到备份的 NFS服务器,只不过MySQL的复制方案是其自带的工具。
利用MySQL的复制功能进行数据备份时,在硬件故障、软件故障的场景下,该数据备份是有效的;但是对于人为地执行 drop , delete 等语句删除数据的情况,从库的备份功能就没用了,因为从服务器也会执行删除的语句。

应用场景2:主从服务器实现读写分离,从服务器实现负载均衡
主从服务器架构可通过程序(PHP,Java等)或代理软件实现对用户(客户端)的请求读写分离。即让重复服务器仅仅处理用户的 select 查询请求,降低用户查询响应时间,以及同事读写在主服务器上带来的访问压力; 对于更新的数据,如update, insert, delete等,仍交给主服务器处理。确保主服务器和从服务器保持实时同步。

百度、淘宝等绝大多数网站都是用户浏览页面多余用户发布内容,因此通过在从服务器上接受 只读请求,就可以很好地减轻主库的 读压力,且从服务器可以很容易地扩展为多台,使用LVS做负载均衡效果就非常帮棒了。
这就是传说中的数据库读写分离架构

应用场景3:把多个从服务器根据业务重要性进行拆分访问
可以把几个不同的从服务器,根据公司的业务进行拆分。如:

用来做查询服务的从服务器;
用来做数据备份的从服务器;
为公司人员提供访问的从服务器;
为开发人员使用的从服务器;

这样的拆分减轻了主服务器的压力外,还可以是数据库各个业务互不影响。


实现MySQL主从读写分离的方案

1. 通过程序实现读写分离(推荐)
PHP和Java等程序都可以通过设置多个连接文件轻松地实现对数据库的读写分离,即当语句关键字为 select 时,就去连接 读库 的连接文件,若为 update, insert, delete 时,则连接写库的连接文件。

MySQL主从复制根据业务拆分从库方案

2. 通过开源软件实现读写分离

3. 大型门户独立开发 DAL 层综合软件
像百度、阿里等大型门户都会自己开发适合自己业务的读写分离、负载均衡、监控报警、自动扩容等一系列功能的DAL层软件。

MySQL读写分离的基本逻辑图


MySQL主从复制原理介绍

MySQL的主从复制是一个异步的复制过程,虽然一般情况下感觉是实时的。数据将从一个MySQL数据库(Master)复制到另一个MySQL数据库(Slave)。
在Master与Slave之间实现整个主从复制的过程是由三个线程参与完成的。其中有两个线程(SQL线程和I/O线程)在Slave端,另外一个线程(I/O线程)在Master端。

要实现MySQL的主从复制,首先必须打开Master端的 binlog 记录功能,否则就无法实现。因为整个复制过程实际上就是Slave从Master端获取 binlog 日志,然后再在Slave上以相同顺序执行获取的 binlog 日志中所记录的各种 SQL 操作。

 打开MySQL的binlog功能
vim /etc/my.cnf
[ mysqld ]
log-bin = /path/mysql-bin

注意是放在 [mysqld]里,不要放错位置了!


MySQL主从复制原理过程详解

  1. 在Slave服务器上执行 start slave 命令开启主从复制开关,开始,开始进行主从复制;
  2. Slave服务器的I/O线程会通过在Master上已经授权的复制用户权限请求连接Master服务器,并请求从指定 binlog 日志文件的指定位置(日志文件名和位置就是在配置主从复制服务时执行 change master 命令指定的),之后开始发送 binlog 日志内容。
  3. Master服务器接收到来自Slave服务器的I/O线程请求后,其上负责复制的I/O线程会根据Slave服务器的I/O线程请求的信息分批读取指定 binlog 日志文件指定位置之后的 binlog 日志信息,然后返回给 Slave 端的I/O线程。返回的信息中除了 binlog 日志内容外,还有在Master服务器端记录的新的 binlog 文件名称,以及在新的 binlog 中的下一个指定更新位置。
  4. 当Slave服务器的I/O线程获取到Master服务器上I/O线程发送的日志内容、日志文件及位置点后,会将 binlog 日志内容依次写到Slave端自身的 Relay Log(中继文件)的最末端,并将新的 binlog 文件名和位置记录到 master-info 文件中,以便下一次读取Master端新 binlog 日志时能够告诉Master服务器从新binlog 日志的指定文件及位置开始请求新的 binlog 日志内容;
  5. Slave服务器端的SQL线程会实时监测本地的 Relay Log 中 I/O线程新增加的日志内容,然后及时地把Relay Log文件中的内容解析成SQL语句,并在自身Slave服务器上按解析SQL语句的位置顺序执行应用这些SQL语句,并在relay-log.info中记录当前应用中继日志的文件名和位置点。
MySQL主从复制基本原理逻辑图

小结:

  • 主从复制是异步逻辑的SQL语句级的复制;
  • 复制时,主库有一个I/O线程,从库有两个线程,即I/O和SQL线程;
  • 实现主从复制的必要条件是主库要开启记录的 binlog 功能;
  • 作为复制的所有MySQL节点的server-id都不能相同;
  • binlog文件只记录对数据库有更改的SQL语句,不记录任何查询语句。


MySQL主从复制实践

主从复制实践

MySQL主从复制实践对环境要求较简单,可以是单机单数据库多实例(3306,3307,3308)的环境;也可以是多台服务器,每个服务器一个独立数据库的环境。
建议使用多服务器实现数据库主从功能!

单机数据库多实例

1. 主从复制数据库单机多实例环境准备
使用前面配置的 3306,3307,3308三个实例

2. 定义主从复制需要的服务器角色
主库及从库名称、IP、Port信息:

Master,ip:3306;
Slave1,ip:3307;
Slave2,ip:3308;

3. 在主库Master上执行操作配置
设置server-id并开启binlog功能参数
vim ./3306/my.cnf

[mysqld]
server-id = 1
log-bin = /path/3306/mysql-bin

提示:

  • 这两个参数一定要放置于[mysqld]模块下,否则会出错;
  • server-id建议使用服务器ip最后一个点分十进制数,目的是避免不同机器或实例 ID 重复;
  • 参数不能重复;
  • 修改参数后重启数据库。

4. 在主库上建立用于主从复制的账号
登录主库3306:
mysql -uroot -ppasswd -S /path/3306/mysql.sock
件利用与从库复制的账号:

grant replication slave on *.* to 'rep'@'192.168.0.%' identified by 'passwd';

#登录之后
flush privilege;

#grant 权限1,权限2,…权限n on 数据库名.表名称 to 用户名@用户地址 identified by ‘连接口令’;
# 192.168.0.%代表此网段

select user,host form mysql.user where user='rep';

5. 实现对主数据库锁表只读
对主数据库锁表只读:

flush table with read lock;

在引擎不同的情况下,这个锁表命令的时间会受下面参数的控制。锁表时,如果超过设置时间不操作会自动解锁。

默认情况下自动解锁的时长参数

锁表后查看主库状态:
mysql> show master status;

锁表后导出数据库:
mysqldump -uusername -ppasswd -S /path/mysql.sock xxxxx

导出数据完毕后,解锁主库,恢复可写:
mysql> unlock tables;

6. 把主库导出的数据迁移到从库
这里常用的命令有 scprsync等,将备份的数据往异地拷贝。


在MySQL从库上执行的操作

1. 设置server-id并关闭binlog功能参数
数据库的server-id一般在一套主从复制体系内是唯一的,这里从库的 server-id 要与主库及其他从库不同,并且要注释掉从库的 binlog 参数配置。如果从库不做级联复制,并且不作为备份用,就不要开启 binlog,开启了反而会增加从库磁盘I/O等的压力。

这两种情况需要打开从库 binlog 记录功能,记录数据库更新的SQL语句:

级联同步 A-->B-->C中B时,需要开启;
在从库做数据备份,数据库备份必须要有全备和binlog日志,才是完整的备份。

vim /path/3307/my.cnf

[mysqld]
server-id = 2

2. 把主库的数据导入从库

mysql -uusername -ppasswd -S /path/mysql.sock < mysql_bak.sql

3. 登录从库,配置复制信息
从库连接主库配置信息:

mysql> CHANGE MASTER TO
MASTER_HOST='192.168.1.1',
MASTER_PORT='3306',
MASTER_USER='rep',
MASTER_PASSWORD='passwd',
MASTER_LOG_FILE='mysql-bin.log',
MASTER_LOG_POS=342;

#字符串用单引号' '括起来,数值不用引号,注意内容前后无空格,参数不能出错。

上述操作的原理实际上是把用户密码等信息写入从库新的 master.info 文件中。


启动从库同步开关,测试主从复制配置

1. 启动从库主从复制,并查看状态

mysql -uroot -ppasswd -S /path/mysql.sock -e "start slave;"
#等同于 mysql> start slave;

主从复制是否成功的3项关键参数:

  • Slave_IO_Running:Yes,这个是I/O线程状态。I/O线程复制从从库到主库读取binlog日志,并写入从库的中继日志;
  • Slave_SQL_Running:Yes,这个是SQL线程状态。SQL线程负责读取中继日志(relay-log)中的数据并转换为SQL语句应用到从数据库中;
  • Seconds_Behind_Master:0,这个是复制过程中从库比主库延迟的秒数,这个参数很重要。

测试主从复制:
在主库上随便新建数据,然后观察从库的数据状况。


MySQL主从复制配置小结

  1. 环境实例准备;
  2. 配置my.cnf文件;
  3. 登录主库并配置;
  4. 导出数据;
  5. 从库配置;
  6. 从库数据恢复;
  7. 从库开启复制;
  8. 检查同步状态。




MySQL主从复制应用技巧

工作中MySQL从库停止复制故障

模拟重现故障的能力是运维人员最重要的能力。
先在从库创建一个库,然后去主库创建一个同名的库来模拟数据冲突。
然后运行 show slave status 查看报错信息。

让从库记录binlog日志的方法

从库需要记录binlog的应用场景:当前从库还要作为其他从库的主库,如级联或互为主从的情况。

vim  /path/my.cnf

[mysqld]
log-slave-updates
log-bin = /path/mysql-bin

MySQL主从复制集群架构的数据备份策略

有了主从复制,还需要做定时 全量 + 增量 备份吗?答案是肯定的!
因为,如果主库有误操作(如:drop),从库也会执行,这样主从库都没有了该数据。

把从库作为数据库备份服务器时,备份策略如下:

根据业务重要性拆分从库

MySQL主从复制延迟问题的原因及解决方案

1. 主库的从库太多,导致复制延迟

从库数量尽量不要超过五个,要复制的节点数量过多,会导致复制延迟;

2. 从库硬件比主库差,导致复制延迟

查看主从系统配置,可能是因为配置不当;

3. 慢SQL语句过多

加入一条SQL语句执行时间是20s,那么从执行完毕到从库上能查到数据至少需要20s,这样就延迟了20s;
一般要把SQL语句的优化作为常规工作,不断地进行监控和优化。如果单个SQL的写入时间长,可以修改后分多次写入;
通过查看慢查询日志或 show full processlist 命令,找出执行时间长的慢查询语句或大的事务。

4. 主从复制的设计问题
如,主从复制单线程,如果主库写并发太大,来不及传送到从库,就会导致延迟;

5. 主从库之间的网络问题
主从库之间的网卡、线路、连接设备等都有可能称为复制的瓶颈,导致延迟;

6. 主库读写压力大,导致复制延迟
主机硬件搞好一点,增加buffer。

通过read-only参数让从库只读访问

read-only参数可以让从服务器只允许来自从服务器线程或具有SUPER权限的数据库用户进行更新,确保从服务器不接受来自用户端的非法用户更新。
方法一:直接带 --read-only 参数启动或重启数据库

mysql xxxxx --read-only

方法二:vim /path/my.cnf

[mysqld]
read-only

MySQL主从复制读写分离集群

读写分离账户设置
主从库,账户权限,访问IP等······

#Master
GRANT SELECT, INSERT, 权限 ON  'database'.'table' TO 'user'@'ip' identified by 'passwd';

#Slave
GRANT SELECT ON 'database'.'table' TO 'user'@'ip' identified by 'passwd';

flush privilege;




重点

  1. MySQL多实例的实现原理和实战部署;
  2. MySQL主从复制的原理;
  3. MySQL主从复制的实践;
  4. MySQL主从复制故障解决思路;
  5. MySQL主从复制延迟原因及解决思路;
  6. MySQL主从复制集群,从库备份的思想和思路;
  7. MySQL主从复制读写分离授权访问用户方案;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容