更多优质内容
请关注公众号

深入学习mongodb(八) 应用管理之开启和关闭mongodb、建立和删除索引、mongodb系统分析器、压缩数据、mongodb持久性和安全性-张柏沛IT博客

正文内容

深入学习mongodb(八) 应用管理之开启和关闭mongodb、建立和删除索引、mongodb系统分析器、压缩数据、mongodb持久性和安全性

栏目:数据库 系列:深入学习mongdb 发布时间:2022-11-26 18:30 浏览量:1471

一、启动和关闭mongodb

 

1. 启动mongodb

执行mongod命令即可启动MongoDB服务器,mongod在启动时可使用许多可配置选项,在命令行中运行mongod --help可列出这些选项。

 

下列选项十分常用,需着重注意。

 

--dbpath

使用此选项可指定一个目录为数据目录。其默认值为/data/db/(在Windows中则为MongoDB可执行文件所在磁盘卷中的./data/db目录)。

机器上的每个mongod进程都需要属于自己的数据目录,即若在同一机器上运行三个mongod实例,则需三个独立的数据目录。mongod启动时,会在其数据目录中创建一个mongod.lock文件,以阻止其他mongod进程使用此数据目录。

 

若尝试启动另一个使用相同数据目录的MongoDB服务器,则会出现错误提示:

"Unable to acquire lock for lockfilepath: /data/db/mongod.lock."

 

--port

此选项用以指定服务器监听的端口号。mongod默认占用27017端口,除其他mongod进程外,其余程序不会使用此端口。若要在同一机器上运行多个mongod进程,则需为它们指定不同的端口。若尝试在已被占用的端口启动mongod,则会出现错误提示:"Address already in use for socket: 0.0.0.0:27017"

 

--fork

启用此选项以调用fork创建子进程,在后台运行MongoDB。

首次启动mongod而数据目录为空时,文件系统需几分钟时间分配数据库文件。预分配结束,mongod可接收连接后,父进程才会继续运行。因此,fork可能会发生挂起。可查看日志中的最新记录得知正在进行的操作。启用--fork选项时,必须同时启用--logpath选项。

 

--logpath

此选项指定mongodb日志文件路径,所有输出信息会被发送至指定文件,而非在命令行上输出。假设指定的日志文件不存在,启用该选项后则会自动生成一个日志文件。若指定日志文件已存在,选项启用后则会覆盖掉已有日志文件。如需保留旧日志,除--logpath选项外,强烈建议使用--logappend选项。

 

--directoryperdb

启用该选项可将每个数据库存放在单独的目录中。我们可由此按需将不同的数据库挂载到不同的磁盘上。该选项一般用于将本地数据库或副本放置于单独的磁盘上,或在磁盘空间不足时将数据库移动至其他磁盘。也可将频繁操作的数据库挂载到速度较快的磁盘上,而将不常用的数据库放到较慢的磁盘上。

 

--config 指定使用的配置文件

启动mongodb时指定要加载的配置文件,未在命令行中指定的选项将使用--config所指定的配置文件中的参数。该选项通常用于确保每次重新启动时的选项都是一样的。

例如,要在后台启动一个服务器,监听5586端口,并将所有输出信息发送至mongodb.log文件中,可运行如下命令:

./mongod  --port  5586  --fork  --logpath  mongodb.log  --logappend

 

然而当使用的选项很多,或自动化启动任务时,启动mongodb时指定使用配置文件就十分实用。使用-f或--config标记,告知服务器使用配置文件。

例如,运行以下命令从而使用~/.mongodb.conf作为配置文件。

mongod --config  ~/.mongodb.conf 

 

配置文件中支持的参数和在命令行中的参数完全相同。以下是一个配置文件的例子:

#  Start  MongoDB  as  a  daemon  on  port  5586
port  =  5586
fork  =  true  #  daemonize  it!
logpath  =  /var/log/mongodb.log
logappend  =  true

# 等价于./mongod  --port  5586  --fork  --logpath  mongodb.log  --logappend

该配置文件指定的参数与之前启动时在命令行中指定的参数相同。

 

 

2. 停止MongoDB

安全停止运行中的MongoDB服务器,与安全启动该服务器一样重要。有若干不同选项可有效地完成这一操作。

关闭运行中的服务器,最简洁的方法是使用 shutdown 命令。这是一个管理员命令,必须运行在admin数据库上。shell提供了一个辅助函数,用以简单地执行shutdown命令:

 

>  use  admin
switched  to  db  admin

>  db.shutdownServer()
server  should  be  down...

 

在主节点(primary)上运行shutdown命令时,服务器在关闭前,会先等待备份节点(secondary)追赶(catch up)主节点以保持同步。这将回滚的可能性降至最低,但shutdown操作有失败的可能性。如几秒钟内没有备份节点成功同步,则shutdown操作失败,主节点亦不会停止运行

>  db.shutdownServer()   
{
    "closest"  :  NumberLong(1349465327),
    "difference"  :  NumberLong(20),
    "errmsg"  :  "no  secondaries  within  10  seconds  of  my  optime",     "ok"  :  0
}

 

可使用force选项,强制关闭mongodb主节点

db.adminCommand({"shutdown"  :  1,  "force"  :  true})

 

这相当于向mongodb发送了一个SIGINT或SIGTERM信号(三种做法都能使MongoDB安全地停止运行,但可能会有数据未能完成同步)。如服务器正在终端中作为前台进程运行,那么按下Ctrl-C快捷键也能发送一个SIGINT信号。

另外,kill之类的命令也可用于发送这些信号。相应的命令就是kill -2 10014(发送 SIGINT信号)或kill 10014(发送SIGTERM信号)。

请不要用kill -9或者pkill关闭mongod服务,这样可能会导致数据损坏而导致下次启动mongod服务失败(这种情况下只能删除整个数据目录,再用备份的数据恢复才能启动mongod)

 

mongod收到SIGINT或SIGTERM信号后,会安全地停止运行。这意味着mongod会等当前正在进行的操作完成后再关闭所有打开的连接,将缓存写入磁盘,继而结束运行。

 

 

二、建立和删除索引

 

 

1. 单机建立索引

建立索引期间,MongoDB会查找集合中每一个文档内要被索引的字段然后对查找到的字段值进行排序。不出所料的话,随着集合体积的增长,该操作消耗非常大。而且索引建立期间会锁定数据库,从而导致其他操作无法进行数据读写。

因此,建立索引时应使用对生产服务器影响最小的方式,要么在服务空闲的时候执行建立索引,要么在后台建立索引(background:true参数)。

 

后台建立索引的命令如下:

db.foo.createIndex({"someField"  :  1},  {"background"  :  true})

 

在前台建立索引要比在后台建立索引耗时少, 因为前者是在放弃执行其他操作的前提下全力在建立索引,还会锁住数据库而无法接受读写请求。后台在建立索引期间,则会定期释放写锁,从而保证其他操作的运行。

 

2. 副本集上建立索引

在副本集上建立索引最简单的方式,是在主节点中建立索引,然后等待其被复制到其他备份节点。在较小的集合中,这一操作不会造成太大的影响。

如集合较大,则会出现所有备份节点同时开始建立索引的情况。所有备份节点都无法处理客户端请求,同时可能也无法及时进行同步复制。

 

推荐采用的方式是先不对主节点建立索引,而是先将一个备份节点关闭再作为独立的节点启动然后对它手动建立索引,然后再重新将它加到副本集。对每个备份节点都进行同样操作。

最后还有一种方式是对主节点使用后台建立索引的方式建索引。

 

 

3. 在分片集群上建立索引

首先关闭平衡器。然后按照上一节中的步骤,依次在每一个分片中进行操作,即把每个分片当作一个单独的副本集。最后,通过mongos运行createIndex,并重新启动平衡器。只有在现存分片中添加索引时才需这样做,新添加的分片会在开始接收集合数据块时抓取集合中的索引。

 

4. 删除索引

如不再需要某索引,可使用dropIndexes命令并指定索引名来删除索引。查询system.indexes集合找出索引名,即使是自动生成的索引名,在不同驱动器间也会存在些许差异:

>  db.runCommand({"dropIndexes"  :  "foo",  "index"  :  "alphabet"})

 

将"*"作为index的值,即可删除一个集合中的所有索引:

>  db.runCommand({"dropIndexes"  :  "foo",  "index"  :  "*"})

 

但这种方法无法删除_id索引。

 

 

5. 小心内存溢出杀手杀死mongodb

Linux的内存溢出杀手(OOM Killer,out-of-memory killer)负责终止使用过多内存的进程。考虑到MongoDB使用内存的方式,除了在建立索引的情况下,它通常不会遇到这种问题(也就是说除了建立索引之外,mongodb不会造成内存溢出)如果在建立索引时,mongod进程突然消失,就有可能是建立索引时占用内存过多而被OOM Killer终止进程,请检查/var/log/messages文件,其中记录了OOM Killer的输出信息。在后台建立索引或增加交换(swap)空间可避免此类情况。如拥有机器的管理员权限,可将MongoDB设置为不可被OOM Killer终止。

 

 

三、mongodb操作管理

 

1. 查看正在进行的操作

db.currentOp()

 

以下返回字段比较重要

opid 操作的唯一标识符(identifier),可通过它来终止一个操作

active表示该操作是否正在运行。如这一字段的值是false,意味着此操作已交出或正等待其他操作交出锁。

secs_running 表示该操作已经执行的时间。可通过它来判断是哪些查询耗时过长,或者占用了过多的数据库资源。

op 表示操作的类型。通常是查询、插入、更新、删除中的一种。注意,数据库命令也被作为查询操作来处理。

desc 该值可与日志(log)信息联系起来。日志中与此连接相关的每一条记录都会以[conn3]为前缀,因此可以此来筛选相关的日志信息。

locks 描述该操作使用的锁的类型。其中“^”表示全局锁。

waitingForLock 表示该操作是否因正在等待其他操作交出锁而处于阻塞状态。

lockstats.timeAcquiringMicros 表示该操作需要多长时间才能取得所需的锁。

 

在执行currentOp()时,可添加过滤条件,从而只显示符合条件的结果。把查询条件作为参数传入函数来进行过滤:

>  db.currentOp({"ns"  :  "prod.users"}) // 只查prod.users集合的正在执行的命令

对于currentOp中的任何字段都可以进行查询,使用普通的查询语句即可。

 

 

2. 终止操作的执行

db.killOp(123) // 123是opid

并非所有操作都能被终止。一般来讲,只有释放了锁的操作才能被终止(当该操作发生查询的时候该操作会释放锁,更新和删除也会发生查询),因此更新(update)、查找(find)、删除(remove)操作都可被终止。正在占用锁,或正在等待其他操作交出锁的操作则通常无法被终止。

 

在查找哪些操作耗时过长时,可能会发现一些长时间运行的内部操作。最常见的是用于复制(replication)的线程(它会持续向同步源请求更多的操作)和分片中用于回写(writeback)的监听器(listener)。所有local.oplog.rs中的长时间运行请求,以及所有回写监听命令,都可以被忽略掉。

 

 

四、mongodb系统分析器(性能分析器)

 

1. 系统分析器开启和关闭

可利用系统分析器来查找耗时过长的操作。系统分析器会将curd命令记录到 固定集合system.profile 中,并提供大量有关耗时过长的操作信息,但相应的,开启了分析器的情况下,mongod的整体性能也会有所下降。因此,我们可能只需定期打开分析器来获取信息即可。

db.setProfilingLevel(2) // 开启分析器,级别为2,此时分析器会记录所有操作

 

设置级别为2会使mongod性能下降较大,可以设置为级别1,级别为1的分析器会默认记录耗时大于100 ms的操作。

也可以自定义“耗时过长”的标准,通过第二参传递:

db.setProfilingLevel(1,  500) // 记录所有耗时超过500 ms的操作

 

将分析级别设为0可关闭分析器。

db.setProfilingLevel(0)

 

但是即使将分析器关闭,slowms(setProfilingLevel()的第二参)也会对mongod有所影响:假如设为2 ms,那么哪怕分析器是关闭着的,每个耗时超过2 ms的操作也都会出现在日志里。所以应该在关闭分析器之前将第二参的时间设长一些。

通过db.getProfilingLevel()来查看当前的分析级别。分析级别的设定值会在重启数据库后被清除。

更改分析级别通常只是在调试时作为一种临时措施,而不应该将其长期地加入配置中。

 

 

2. 计算空间消耗 

Object.bsonsize(db.users.findOne()) // 计算单条文档的空间占用,这里也可以用find搭配各种条件

db.users.stats() // 查看集合的状态,包含了集合的空间占用大小, 这里的大小不包括文档间的间隔以及索引的占用空间

db.stats() // 查看数据库信息

 

3. mongotop和monogostat

mongotop 类似于UNIX中的top工具,可概述哪个集合最为繁忙(该命令不是在mongo客户端执行,而是直接在Linux命令行执行)。

mongotop-locks 命令可以得知每个数据库的锁状态。

 

mongostat提供有关服务器的信息,其字段如下

insert/query/update/delete/getmore/command:每种对应操作的发生次数。

flushes :mongod将数据刷新(flush)到磁盘的次数。

vsize :mongod正在使用的虚拟内存大小,通常为数据目录的2倍大小(一次用于映射的文件,一次用于日记系统)。

res:mongod正在使用的内存大小,通常该值应尽量接近机器的所有内存大小。

conn  此服务器打开的连接数,包括输入和输出连接。

 

 

五、mongodb压缩数据 和 移动集合

 

1. 压缩数据

MongoDB会占用大量的磁盘空间。有时,大量数据被删除或更新后,会在集合中产生碎片。如数据文件中有很多空闲空间,但由于这些独立的空闲区块过小,从而使得MongoDB无法对其进行重新利用时,就产生了碎片。在这种情况下,会在日志中看到类似如下信息:

Fri  Oct  7  06:15:03  [conn2]  info  DFM::findAll():  extent  0:3000  was  empty,  

        skipping  ahead.  ns:bar.foo

 

该信息本身是无害的。然而,这意味着某一整个区段(extent)中不包含任何文档。为消除空区段,并高效重整集合,可使用compact命令:

db.runCommand({"compact"  :  "collName"})

 

压缩操作会消耗大量资源,不应在mongod向客户端提供服务时计划压缩操作。推荐做法类似于建立索引时的做法,即在每个备份节点中对数据执行压缩操作,然后关闭主节点,进行最后的压缩操作。

 

压缩之后不意味着文档与文档之间就完全没有间隔,而是会让文档与文档间保持1的间隔。如需更大间隔可以使用paddingFactor参数:

db.runCommand({"compact"  :  "collName",  "paddingFactor"  :  1.5})

 

间隔参数最小为1,最大为4。对间隔参数的设定不会持续生效,只会影响压缩过程中文档之间的间隔。压缩完成后,间隔参数会逐渐重新回到之前的值。

压缩操作只是在磁盘空间不足时的临时措施,它不会减少MongoDB所使用的实际磁盘空间大小,但可使MongoDB不再需要分配新的空间。

 

可通过运行repair(修复)命令来回收磁盘空间。repair命令会对所有数据进行复制,所以必须拥有和当前数据文件一样大小的空余磁盘空间。

这时常是个大问题,因为需要运行repair的时候往往就是因为机器的磁盘空间不多了才要回收磁盘空间的。然而,如能挂载其他磁盘,则可指定一个修复路径,将数据复制到这个路径上。

由于repair操作会完全复制所有数据,因此可随时中断该操作而不影响原始数据集。如在repair操作的过程中遇到问题,可删除repair产生的临时文件而不会影响到数据文件。

在启动mongod时使用 --repair 选项(如需要,还可使用--repairpath选项)来进行修复。

 

  

2. 移动集合

可使用renameCollection命令来重命名集合。这一命令无法在数据库间移动集合,但可更改集合名。无论重命名的集合大小,该操作都基本上是瞬间完成的。

>  db.sourceColl.renameCollection("newName")

执行这一命令时可传入第二个参数,从而决定当名为newName的集合已经存在时应如何处理。该参数为true时,会删除名为newName的集合;为false时,则会抛出错误。后者是这一参数的默认值。

 

要想在数据库间移动集合,必须进行转储(dump)和恢复(restore)操作,或手动复制集合中的文档(使用find和insert命令,遍历集合中的所有文档,从而将其插入到新的数据库中)。

 

如需再mongod之间的集合移动,可执行以下命令:

db.runCommand({"cloneCollection"  :  "collName",  "from"  :  "hostname:27017"})

意思是从 hostname:27017这个节点中剪切名为 collName 的集合到当前的mongod服务。

 

 

六、Mongodb持久性

 

1. journal日志与持久性(PS:mongodb有系统日志,journal日志,oplog日志和慢查询日志。)

MongoDB会在进行写入时建立一条日志(journal),日记中包含了此次写入操作具体更改的磁盘地址和字节。因此,一旦服务器突然停机,可在启动时对日记进行重放(replay),从而重新执行那些停机前没能够刷新(flush)到磁盘的写入操作。数据文件默认每60秒刷新到磁盘一次,因此日记文件只需记录约60秒的写入数据。日记系统为此预先分配了若干个空文件,这些文件存放在/data/db/journal目录中,文件名为_j.0、_j.1等。

 

长时间运行MongoDB后,日记目录中会出现类似 _j.6217、_j.6218和_j.6219的文件。这些是当前的日记文件。文件名中的数字会随着MongoDB运行时间的增长而增大。数据库正常关闭后,日记文件则会被清除(因为正常关闭后就不再需要这些文件了)。

 

如发生系统崩溃,或使用kill -9命令强制终止数据库的运行,mongod会在启动时重放日记文件

 

批量提交写入操作

MongoDB默认每隔100毫秒,或是写入数据达到若干兆字节时,便会将这些操作写入日志。这意味着MongoDB会成批量地提交更改,即每次写入不会立即刷新到磁盘。所以在默认设置下,系统发生崩溃时,不可能丢失间隔超过100毫秒的写入数据。

 

 

也就是说每隔60秒刷盘到数据集合中一次,每100毫秒刷盘到日志文件一次。

 

 

2. 设定提交时间间隔

另一个减少日记被干扰几率的选项是,可以调整两次提交间的时间间隔。运行setParameter命令,设定journalCommitInterval的值(最小为2毫秒,最大为500毫秒)。以下命令使得MongoDB每隔10毫秒便将数据提交到日记中一次:

>  db.adminCommand({"setParameter"  :  1,  "journalCommitInterval"  :  10})

也可使用命令行选项--journalCommitInterval来设定这一值。

如客户端的写入速度超过了日记的刷新速度,mongod则会限制写入操作,直到日记完成到磁盘的写入。这是mongod会限制写入的唯一情况。

 

 

3. 关闭日记系统

我们知道日记记录会降低数据写入的速度,如果写入数据的价值不及写入速度降低带来的损失,我们可能就会想要禁用日记系统。

禁用日记系统的缺陷在于,MongoDB无法保证发生崩溃后数据的完整性。

 

需要注意的是,当mongod因特殊原因崩溃或者被强制关闭服务时可能会导致数据损坏,此时重启mongod服务会失败。

 

此时我们可以有以下做法:

a. 删除数据目录的所有文件,然后重新启动mongod服务,再加载备份数据,或者同步副本集的数据。

b. 如果没有备份或者没有副本集,可以使用修复工具,修复实质上是删除所有受损数据。

 

mongod自带了两种修复工具,一种是mongod内置的,另一种是mongodump内置的。mongodump的修复更加接近底层,可能会找到更多的数据,但耗时要更长。因此,应根据愿意在数据恢复中消耗的时间长短来进行决定。

 

要使用mongod内置的修复工具,需附带--repair选项运行mongod:

mongod  --dbpath  /path/to/corrupt/data  --repair

进行修复时,MongoDB不会开启27017端口的监听,但我们可通过查看日志(log)的方式得知它正在做什么。

 

注意,修复过程会对数据进行一份完整的复制,所以如有80 GB的数据,则需80 GB的空闲磁盘空间。为尽量解决这一问题,修复工具提供了--repairpath选项。这一选项允许在主磁盘空间不足时挂载一个“紧急驱动器”,并使用它来进行修复操作。

 

--repairpath选项的用法如下:

mongod  --dbpath  /path/to/corrupt/data --repair  --repairpath  /media/external-hd/data/db

如果修复过程被强行终止,或者出现故障(如磁盘空间不足),至少不会使情况变得更糟。修复工具将所有的输出都写入新的文件中,不会对原有文件进行修改。因此原始数据文件不会比开始修复时变得更糟。

 

另一个选择是使用mongodump的--repair选项,就像这样:

mongodump  --repair

这些选择都不是特别好,但它们应该可以让mongod重新运行在一个干净的数据集上。

 

 

关于mongod.lock文件

数据目录中有一个名为mongod.lock的特殊文件。该文件在关闭日记系统运行时十分重要(如启用了日记系统,则这一文件不会出现)。

当mongod正常退出时,会清除mongod.lock文件,这样在启动时,mongod就会得知上一次是正常退出的。相反,如果该文件没被清除,mongod就会得知上一次是异常退出的。

如果mongod监测到上一次是异常退出的(检测到mongod.lock文件存在),则会禁止再启动,这样我们就会意识到一份干净数据副本的需求。然而,有些人意识到可通过删除mongod.lock文件来启动mongod。请不要这么做。通常,在启动时删除这一文件,意味着我们不知道也不在乎数据是否受损。除非如此,否则请不要这么做。如果mongod.lock文件阻止了mongod的启动,请对数据进行修复,而非删除该文件。

 

 

七、mongodb安全性

不要将MongoDB服务器直接暴露在外网上。应尽可能地限制外部对MongoDB的访问。最好的方式是设置防火墙,只允许内部网络地址对MongoDB的访问。

 

除使用防火墙外,也可在配置文件中加入以下选项来增强安全性:

 

--bind_ip

指定MongoDB监听的接口。我们通常将其设置为一个内部IP地址,从而保证应用服务器和集群中其他成员的访问,同时拒绝外网的访问。如MongoDB与应用服务器运行于同一台机器上,则可将其设为localhost。但配置服务器和分片需要其他机器的访问,所以不应设为localhost

 

--nohttpinterface

MongoDB启动时,默认在端口1000启动一个微型的HTTP服务器。该服务器可提供一些系统信息,但这些信息均可在其他地方找到。对于一个可能只需通过SSH访问的机器,没有必要将这些信息暴露在外网上。

除非正在进行开发,否则请关闭此选项。

 

--nounixsocket

如不打算使用UNIX socket来进行连接,则可禁用此选项。只有在本地,即应用服务器和MongoDB运行在同一台机器上时,才能使用socket进行连接。

 

--noscripting

该选项完全禁止服务器端JavaScript脚本的运行。大多数报告的MongoDB安全问题都与 JavaScript有关。如程序允许的话,禁止JavaScript通常会更安全一些。

 

一些shell中的辅助函数依赖于服务器端的JavaScript,尤其是sh.status()。在一台禁止了JavaScript的服务器上运行这些辅助函数时,会出现错误提示。

 

 

八、mongodb系统日志

mongod默认将日志发送至stdout(标准输出,通常为终端)。大多初始化脚本会使用--logpath选项,将日志发送至文件。如在同一台机器上有多个MongoDB实例(比如说一个mongod和一个mongos),注意保证各实例的日志分别存放在单独的文件中。

 

MongoDB会输出大量日志消息,但请不要使用--quiet选项(该选项会隐藏部分日志消息)。保持日志级别为默认值通常不错,此时日志中有足够的信息进行基本调试(如耗时过长或启动异常的原因等),但日志占用的空间并不大。调试应用某特定问题时,可使用一些选项从日志中获取更多信息。

 

首先,在重启MongoDB时,可通过在参数中附加数目更多的“v”(即 -v、-vv、-vvv、-vvvv或 -vvvvv),或运行如下setParameter命令,完成日志级别(log level)的更改。

>  db.adminCommand({"setParameter"  :  1,  "logLevel"  :  3})

记得将日志级别重设为0,否则日志中会存在过多不必要的内容。可将日志级别调高至5,这时 mongod会在日志中记录几乎所有的操作,包括每一个请求所处理的内容。

由于mongod将所有内容都写入了日志文件,因此可产生大量的磁盘读写操作(IO),从而拖慢一个忙碌的系统。

 

最后,设置一个计划任务以便每天或每周分割(rotate)日志文件。如使用--logpath选项启动MongoDB,向进程发送一个SIGUSR1信号使其对日志进行分割。也可使用logRotate命令以达到相同目的:

>  db.adminCommand({"logRotate"  :  1})

如不是通过--logpath选项启动的MongoDB,则不能对日志进行分割。

 

 

 




更多内容请关注微信公众号
zbpblog微信公众号

如果您需要转载,可以点击下方按钮可以进行复制粘贴;本站博客文章为原创,请转载时注明以下信息

张柏沛IT技术博客 > 深入学习mongodb(八) 应用管理之开启和关闭mongodb、建立和删除索引、mongodb系统分析器、压缩数据、mongodb持久性和安全性

热门推荐
推荐新闻