1. 前言

由于线上的mongodb 数据体量越来越大,如果没有完善的备份方案,发生故障势必造成业务很长时间的暂停。参考了网上方案,写出以下总结和备份方案:

备份方案分为两种:全备和增量备份,二者结合起来使用。

参考链接:https://www.cnblogs.com/xuliuzai/p/9917137.html  感谢作者的分享。

2. 构建mongodb 副本集测试环境

首先在测试环境进行测试,过程如下:

  测试机:192.168.118.16  系统:CentOS 7

首先搭建mongodb 副本集(为了和线上环境保持一致)这里使用 mongodb 3.6 的版本,建议和生产环境相同的版本。

Mongdb 没啥安装的, 开箱即用。副本集参考链接:https://www.cnblogs.com/hukey/p/5769548.html

rs0:PRIMARY> rs.isMaster();
{
"hosts" : [
"192.168.118.16:27017",
"192.168.118.16:27018",
"192.168.118.16:27019"
],

副本集创建成功。

接下来,向集群里写入数据:

rs0:PRIMARY> for(var i=1;i<=10000;i++) db.users.insert({id:i, name:"hukey",city:"xi'an"});
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY> show dbs;
admin 0.000GB
config 0.000GB
local 0.000GB
test 0.000GB
rs0:PRIMARY> use test
switched to db test
rs0:PRIMARY> db.users.count();
10000

写入了 1W 条数据。

准备工作完成。

3. mongodb 全量备份及恢复

全备脚本 [ mongodb_back_all.sh ] 如下:

#!/bin/bash
# Author:hukey host='192.168.118.16'
port='27017'
sourcepath='/mongodb/bin'
targetpath='/mongodb/backup'
nowtime=$(date "+%Y%m%d") start(){
$sourcepath/mongodump --host $host --port $port --oplog --gzip --out ${targetpath}/${nowtime}
} execute(){
echo "=========================$(date) backup all mongodb back start ${nowtime}========="
start
if [ $? -eq 0 ];then
echo "The MongoDB BackUp Successfully!"
else
echo "The MongoDB BackUp Failure"
fi
} if [ ! -d "${targetpath}/${nowtime}" ];then
mkdir -p "${targetpath}/${nowtime}"
fi execute backtime=$(date -d '-7 days' "+%Y%m%d")
if [ -d "${targetpath}/${backtime}/" ];then
rm -rf "${targetpath}/${backtime}/"
echo "=======${targetpath}/${backtime}/===删除完毕=="
fi echo "========================= $(date) backup all mongodb back end ${nowtime}========="

全库还原脚本 [ mongodb_restore_all.sh ] 如下:

#!/bin/bash
# Author:hukey echo -e "\033[31;1m*****[ Mongodb ] 全库恢复脚本*****\033[0m"
host=192.168.118.16
mongo_bin=/mongodb/bin/
backpath='/mongodb/backup' echo -e "\033[32;1m[ 选择要恢复全库的日期 ] \033[0m"
for backfile in `ls $backpath`; do
echo $backfile
done read -p ">>>" date_bak if [[ $date_bak == "" ]] || [[ $date_bak == '.' ]] || [[ $date_bak == '..' ]]; then
echo -e "\033[31;1m输入不能为特殊字符.\033[0m"
exit 1
fi if [ -d $backpath/$date_bak ];then
read -p "请确认是否恢复全库备份[y/n]:" choice if [ "$choice" == "y" ];then
echo -e "\033[32;1m正在恢复全库备份,请稍后...\033[0m"
$mongo_bin/mongorestore --host $host --port 27017 --oplogReplay --gzip $backpath/$date_bak/
if [ $? -eq 0 ];then
echo -e "\033[32;1m--------全库恢复成功.--------\033[0m"
else
echo -e "\033[31;1m恢复失败,请手动检查!\033[0m"
exit 3
fi
else
exit 2
fi
else
echo "\033[31;1m输入信息错误.\033[0m"
exit 1
fi

执行全量备份脚本:

[root@192.168.118.16 /mongodb/script]#sh mongodb_back_all.sh
=========================Thu Sep 12 11:57:32 CST 2019 backup all mongodb back start 20190912=========
2019-09-12T11:57:32.863+0800 writing admin.system.version to
2019-09-12T11:57:32.866+0800 done dumping admin.system.version (1 document)
2019-09-12T11:57:32.867+0800 writing test.users to
2019-09-12T11:57:32.955+0800 done dumping test.users (10000 documents)
2019-09-12T11:57:32.956+0800 writing captured oplog to
2019-09-12T11:57:32.975+0800 dumped 1 oplog entry
The MongoDB BackUp Successfully!
========================= Thu Sep 12 11:57:32 CST 2019 backup all mongodb back end 20190912=========

查看备份数据:

[root@192.168.118.16 /mongodb/script]#ls /mongodb/backup/20190912/
admin oplog.bson test

在测试全库还原之前,首先需要清库数据(注意:本次操作是在测试环境)

rs0:PRIMARY> use test;
switched to db test
rs0:PRIMARY> db.users.count();
10000
rs0:PRIMARY> db.dropDatabase();
{
"dropped" : "test",
"ok" : 1,
"operationTime" : Timestamp(1568264437, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1568264437, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}

执行全库恢复脚本:

[root@192.168.118.16 /mongodb/script]#sh mongodb_restore_all.sh
*****[ Mongodb ] 全库恢复脚本*****
[ 选择要恢复全库的日期 ]
20190912
>>>20190912
请确认是否恢复全库备份[y/n]:y
正在恢复全库备份,请稍后...
2019-09-12T13:43:58.956+0800 preparing collections to restore from
2019-09-12T13:43:58.959+0800 reading metadata for test.users from /mongodb/backup/20190912/test/users.metadata.json.gz
2019-09-12T13:43:59.019+0800 restoring test.users from /mongodb/backup/20190912/test/users.bson.gz
2019-09-12T13:44:01.950+0800 [#################.......] test.users 28.0KB/37.9KB (73.9%)
2019-09-12T13:44:04.083+0800 [########################] test.users 37.9KB/37.9KB (100.0%)
2019-09-12T13:44:04.083+0800 no indexes to restore
2019-09-12T13:44:04.084+0800 finished restoring test.users (10000 documents)
2019-09-12T13:44:04.084+0800 replaying oplog
2019-09-12T13:44:04.084+0800 done
--------全库恢复成功.--------

全库的备份和还原已经实现,可以通过 crontab 来制定计划任务触发。

4. mongodb 增量备份及恢复

增量备份的思路是通过 oplog 来实现的,大家可以通过文档搜索了解。
直接上脚本:

增量备份 [ mongodb_backup_incremental.sh ] 脚本

#!/bin/bash
# Author:hukey
command_linebin='/mongodb/bin/mongo'
port=27017 if [ ! -d "/mongodb/backup/mongodbOplog_bak/mongo-$port" ];then
mkdir -p /mongodb/backup/mongodbOplog_bak/mongo-$port
fi if [ ! -d "/mongodb/backup/mongodbOplog_bak/log-$port" ];then
mkdir -p /mongodb/backup/mongodbOplog_bak/log-$port
fi bkdatapath=/mongodb/backup/mongodbOplog_bak/mongo-$port
bklogpath=/mongodb/backup/mongodbOplog_bak/log-$port logfilename=$(date +"%Y%m%d") echo "===MongoDB 端口为" $port "的差异备份开始,开始时间为" $(date -d today +"%Y%m%d%H%M%S") paramBakEndDate=$(date +%s)
echo "===本次备份时间参数中的结束时间为:" $paramBakEndDate diffTime=$(expr 65 \* 60)
echo "===备份设置的间隔时间为:" $diffTime paramBakStartDate=$(expr $paramBakEndDate - $diffTime)
echo "===本次备份时间参数中的开始时间为:" $paramBakStartDate diffTime=$(expr 61 \* 60)
paramAfterBakRequestStartDate=$(expr $paramBakEndDate - $diffTime)
echo "===为保证备份的连续性,本次备份后,oplog中的开始时间需小于:" $paramAfterBakRequestStartDate bkfilename=$(date -d today +"%Y%m%d%H%M%S") command_line="${command_linebin} 192.168.118.16:27017" opmes=$(/bin/echo "db.printReplicationInfo()" | $command_line --quiet) echo $opmes > /tmp/opdoctime$port.tmplog
opbktmplogfile=/tmp/opdoctime$port.tmplog
opstartmes=$(grep "oplog first event time" $opbktmplogfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}' )
oplogRecordFirst=$(date -d "$opstartmes" +%s)
echo "===oplog集合记录的开始时间为[格式化]:" $oplogRecordFirst
if [ $oplogRecordFirst -le $paramBakStartDate ]; then
echo "Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。"
else
echo "Fatal Error --检查设置的备份时间不合理合理。备份参数的开始时间不在oplog记录的时间范围内。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。"
fi /mongodb/bin/mongodump -h 192.168.118.16 --port $port -d local -c oplog.rs --query '{ts:{$gte:Timestamp('$paramBakStartDate',1),$lte:Timestamp('$paramBakEndDate',9999)}}' -o $bkdatapath/mongodboplog$bkfilename opmes=$(/bin/echo "db.printReplicationInfo()" | $command_line --quiet)
echo $opmes > /tmp/opdoctime$port.tmplog
opbktmplogfile=/tmp/opdoctime$port.tmplog
opstartmes=$(grep "oplog first event time" $opbktmplogfile | awk -F 'CST' '{print $1}' | awk -F 'oplog first event time: ' '{print $2}' | awk -F ' GMT' '{print $1}' )
oplogRecordFirst=$(date -d "$opstartmes" +%s)
echo "===执行备份后,oplog集合记录的开始时间为[时间格式化]:" $oplogRecordFirst if [ $oplogRecordFirst -le $paramAfterBakRequestStartDate ]; then
echo "Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 paramAfterBakRequestStartDate)。这样可以保证每个增量备份含有最近一个小时的全部op操作,满足文件的持续完整性,逐个还原无丢失数据风险。"
else
echo "Fatal Error --备份后,检查oplog集合的涵盖的时间范围过小(小于61min)。设置的备份时间不合理合理,备份后的文件不能完全涵盖最近60分钟的数据。请调整oplog size或调整备份频率。本次备份可以持续进行,但还原时数据完整性丢失。"
fi if [ -d "$bkdatapath/mongodboplog$bkfilename" ]
then
echo "Message --检查此次备份文件已经产生.文件信息为:" $bkdatapath/mongodboplog$bkfilename >> $bklogpath/$logfilename.log
else
echo "Fatal Error --备份过程已执行,但是未检测到备份产生的文件,请检查!" >> $bklogpath/$logfilename.log
fi keepbaktime=$(date -d '-3 days' "+%Y%m%d%H")*
if [ -d $bkdatapath/mongodboplog$keepbaktime ]; then
rm -rf $bkdatapath/mongodboplog$keepbaktime
echo "Message -- $bkdatapath/mongodboplog$keepbaktime 删除完毕" >> $bklogpath/$logfilename.log
fi echo "===MongoDB 端口为" $port "的差异备份结束,结束时间为:" $(date -d today +"%Y%m%d%H%M%S")

这个脚本比较长,需要注意一个变量: diffTime

第一次定义这个变量的时候,是为了定义备份的时长,从此刻到之前 65 * 60ms 之前的时间,也就是备份从现在到之前 1小时5分这段时间的增量;

第二次定义这个变量的时候,是为了避免数据增长过快,覆盖了还未备份的数据的,比较的依据是 mongodb db.printReplicationInfo(); 的 oplog first event time 时间。

这两个定义的时间可灵活调整。

增量备份还原 [ mongodb_backup_incremental.sh ] 脚本

#!/bin/bash
# Author:hukey host=192.168.118.16
port=27017
mongo_bin=/mongodb/bin/
backpath='/mongodb/backup/mongodbOplog_bak/mongo-27017' echo -e "\033[31;1m*****[ Mongodb ] 增量恢复脚本*****\033[0m" echo -e "\033[32;1m[ 选择要恢复增量的日期(格式:年月日时分秒) ] \033[0m"
for time_file in `ls $backpath`; do
echo $time_file
done read -p ">>>" date_bak
if [[ $date_bak == "" ]] || [[ $date_bak == '.' ]] || [[ $date_bak == '..' ]]; then
echo -e "\033[31;1m输入不能为特殊字符.\033[0m"
exit 1
fi
if [ -d $backpath/$date_bak ]; then
read -p "请确认是否恢复[$date_bak]增量备份[y/n]:" choice
if [ "$choice" == "y" ];then
mkdir -p /tmp/mongodb/ && cp -a $backpath/$date_bak/local/oplog.rs.bson /tmp/mongodb/oplog.bson
$mongo_bin/mongorestore --host $host --port $port --oplogReplay /tmp/mongodb/ && rm -rf /tmp/mongodb/
if [ $? -eq 0 ];then
echo -e "\033[32;1m--------[$date_bak]增量恢复成功.--------\033[0m"
else
echo -e "\033[31;1m恢复失败,请手动检查!\033[0m"
exit 3
fi
else
exit 2
fi
else
echo -e "\033[31;1m输入信息错误.\033[0m"
exit 1
fi

测试下增量备份脚本和还原增量脚本

(1)首先写入一批数据到 mongodb

rs0:PRIMARY> for(var i=1;i<=100;i++) db.users.insert({id:i,name:"hukey"});
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY> use test;
switched to db test
rs0:PRIMARY> db.users.count();
100

写入了 100 条数据成功。

(2)执行增量备份脚本

[root@192.168.118.16 /mongodb/script]#sh mongodb_backup_incremental.sh
===MongoDB 端口为 27017 的差异备份开始,开始时间为 20190912141545
===本次备份时间参数中的结束时间为: 1568268945
===备份设置的间隔时间为: 3900
===本次备份时间参数中的开始时间为: 1568265045
===为保证备份的连续性,本次备份后,oplog中的开始时间需小于: 1568265285
===oplog集合记录的开始时间为[格式化]: 1568260252
Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。
2019-09-12T14:15:48.648+0800 Failed: error connecting to db server: no reachable servers
===执行备份后,oplog集合记录的开始时间为[时间格式化]: 1568260252
Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 paramAfterBakRequestStartDate)。这样可以保证每个增量备份含有最近一个小时的全部op操作,满足文件的持续完整性,逐个还原无丢失数据风险。
===MongoDB 端口为 27017 的差异备份结束,结束时间为: 20190912141548

(3)再次新增 100 条数据到 mongodb

rs0:PRIMARY> use test
switched to db test
rs0:PRIMARY> for(var i=1;i<=100;i++) db.users.insert({id:i,name:"xiaofei"});
WriteResult({ "nInserted" : 1 })
rs0:PRIMARY> db.users.count();
200

(4)再次执行增量备份

[root@192.168.118.16 /mongodb/script]#sh mongodb_backup_incremental.sh
===MongoDB 端口为 27017 的差异备份开始,开始时间为 20190912141728
===本次备份时间参数中的结束时间为: 1568269048
===备份设置的间隔时间为: 3900
===本次备份时间参数中的开始时间为: 1568265148
===为保证备份的连续性,本次备份后,oplog中的开始时间需小于: 1568265388
===oplog集合记录的开始时间为[格式化]: 1568260252
Message --检查设置备份时间合理。备份参数的开始时间在oplog记录的时间范围内。
2019-09-12T14:17:31.696+0800 Failed: error connecting to db server: no reachable servers
===执行备份后,oplog集合记录的开始时间为[时间格式化]: 1568260252
Message --备份后,检查oplog集合中数据的开始时间,即集合中最早的一笔数据,时间不小于61分钟的时间(即参数 paramAfterBakRequestStartDate)。这样可以保证每个增量备份含有最近一个小时的全部op操作,满足文件的持续完整性,逐个还原无丢失数据风险。
===MongoDB 端口为 27017 的差异备份结束,结束时间为: 20190912141731

到此,已知:

第一次增量备份前,数据是 100 条

第二次增量备份前,数据是 200 条

(5)删除 mongodb 数据,恢复第一次增量备份数据

rs0:PRIMARY> db.users.count();
200
rs0:PRIMARY> db.dropDatabase();
{
"dropped" : "test",
"ok" : 1,
"operationTime" : Timestamp(1568270110, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1568270110, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
rs0:PRIMARY>
rs0:PRIMARY> show dbs;
admin 0.000GB
config 0.000GB
local 0.001GB

执行恢复增量备份的脚本:

[root@192.168.118.16 /mongodb/script]#sh mongodb_restore_incremental.sh
*****[ Mongodb ] 增量恢复脚本*****
[ 选择要恢复增量的日期(格式:年月日时分秒) ]
mongodboplog20190912144121
mongodboplog20190912144403
>>>mongodboplog20190912144121
请确认是否恢复[mongodboplog20190912144121]增量备份[y/n]:y
2019-09-12T14:44:34.534+0800 preparing collections to restore from
2019-09-12T14:44:34.534+0800 replaying oplog
2019-09-12T14:44:37.531+0800 oplog 1.12MB
2019-09-12T14:44:40.021+0800 oplog 1.79MB
2019-09-12T14:44:40.021+0800 done
--------[mongodboplog20190912144121]增量恢复成功.--------

此时,mongodb 应该是 100 条数据才对,验证下:

rs0:PRIMARY> use test;
switched to db test
rs0:PRIMARY> db.users.count();
100

数据正确,执行第二次增量备份的还原:

[root@192.168.118.16 /mongodb/script]#sh mongodb_restore_incremental.sh
*****[ Mongodb ] 增量恢复脚本*****
[ 选择要恢复增量的日期(格式:年月日时分秒) ]
mongodboplog20190912144121
mongodboplog20190912144403
>>>mongodboplog20190912144403
请确认是否恢复[mongodboplog20190912144403]增量备份[y/n]:y
2019-09-12T14:45:41.886+0800 preparing collections to restore from
2019-09-12T14:45:41.887+0800 replaying oplog
2019-09-12T14:45:44.882+0800 oplog 1.04MB
2019-09-12T14:45:47.882+0800 oplog 1.75MB
2019-09-12T14:45:50.341+0800 oplog 1.81MB
2019-09-12T14:45:50.341+0800 done
--------[mongodboplog20190912144403]增量恢复成功.--------

此时,mongodb 应该是 200 条数据才对,验证下:

rs0:PRIMARY> use test;
switched to db test
rs0:PRIMARY> db.users.count();
200

正确,数据恢复没有问题。增量备份完成。

5. 后记

对于线上生产环境,目前的备份解决方案是:

  全量备份 1 周执行一次,增量备份每天执行一次。

  后期准备在建立一个备用的副本集,每天都会将备份数据还原到 新建的副本集中,进行数据备份的校验,准备还是通过脚本来实现。后期在编写。

[ Mongodb ] 全量备份和增量备份的更多相关文章

  1. Centos 6.9 安装xtrabackup-2.4.8 通用包,yum安装,全量备份,增量备份

    xtrabackup-2.4.8的安装及使用 Xtrabackup是由percona提供的mysql数据库备份工具,据官方介绍,这也是世界上惟一一款开源的能够对innodb和xtradb数据库进行热备 ...

  2. xtrabackup实现全量备份和增量备份

    mysql增量和完全备份innobackupex2.1.9版本1 yum安装: 官网地址:https://www.percona.com/doc/percona-xtrabackup/LATEST/i ...

  3. 【运维实战】利用tar -g 实现简单全量备份和增量备份(带演示)

    备份产生 全量备份指完全备份,增量备份指针对上次至今的修改进行备份.linux提供tar -g可实现备份功能. 第一次运行 tar -g 备份存放目录/snapshot -czvf  备份存放目录/备 ...

  4. mysql的全量备份与增量备份

    mysql的全量备份与增量备份 全量备份:可以使用mysqldump直接备份整个库或者是备份其中某一个库或者一个库中的某个表. 备份所有数据库:[root@my ~]# mysqldump -uroo ...

  5. VMware 虚拟化编程(11) — VMware 虚拟机的全量备份与增量备份方案

    目录 目录 前文列表 全量备份数据的获取方式 增量备份数据的获取过程 前文列表 VMware 虚拟化编程(1) - VMDK/VDDK/VixDiskLib/VADP 概念简析 VMware 虚拟化编 ...

  6. mysql全量备份与增量备份

    mysql全量备份与增量备份   1.全量备份 全量备份就是把数据库中所有的数据进行备份. 备份所有库: mysqldump -uroot -p456 -S /data/3306/mysql.sock ...

  7. Percona Xtrabackup备份mysql全库及指定数据库(完整备份与增量备份)

    原文地址:http://www.tuicool.com/articles/RZRnq2 Xtrabackup简介 Percona XtraBackup是开源免费的MySQL数据库热备份软件,它能对In ...

  8. Percona备份mysql全库及指定数据库(完整备份与增量备份)

    Percona Xtrabackup备份mysql全库及指定数据库(完整备份与增量备份) Xtrabackup简介 Percona XtraBackup是开源免费的MySQL数据库热备份软件,它能对I ...

  9. 阿里云 如何减少备份使用量? mysql数据库的完整备份、差异备份、增量备份

    RDS for MySQL备份.SQL审计容量相关问题_MYSQL使用_技术运维问题_云数据库 RDS 版-阿里云 https://help.aliyun.com/knowledge_detail/4 ...

随机推荐

  1. redis发布订阅实现各类定时业务(优惠券过期,商品不支付自动撤单,自动收货等)

    修改redis配置文件找到机器上redis配置文件conf/redis.conf,新增一行  notify-keyspace-events Ex 最后的Ex代表 监听失效的键值 修改后效果如下图 代码 ...

  2. 在本地调用hadoop的api

    第一次在本地运行Java代码,调用hadoop的hdfs的api接口,遇到下面的问题: 1.HADOOP_HOME and hadoop.home.dir are unset 解决办法:在本地安装配置 ...

  3. poj3268 Silver Cow Party(最短路)

    非常感谢kuangbin专题啊,这道题一开始模拟邻接表做的,反向边不好处理,邻接矩阵的话舒服多了. 题意:给n头牛和m条有向边,每头牛1~n编号,求所有牛中到x编号去的最短路+回来的最短路的最大值. ...

  4. Map遍历效率比较

    1.由来 上次博客提到了Map的四种遍历方法,其中有的只是获取了key值或者是value值,但我们应该在什么时刻选择什么样的遍历方式呢,必须通过实践的比较才能看到效率. 也看了很多文章,大家建议使用e ...

  5. Navicat 的使用 —— 快捷键

    名称 功能 备注 Ctrl+Q 打开查询窗口   Ctrl+/  注释sql语句   Ctrl+R  运行查询窗口的sql语句   Ctrl+Shift+R 只运行选中的sql语句   F6  打开一 ...

  6. Laravel —— 多模块开发

    Laravel 框架比较庞大,更适用于比较大的项目. 为了整个项目文件结构清晰,不同部分分为不同模块很有必要. 一.安装扩展包 1.根据不同 Laravel 版本,选择扩展包版本. packagest ...

  7. application内置对象

    application 实现用户间的数据共享,可存放全局变量 setAttribute() getAttribute() getServerInfo(); //获取引擎名和版本号,如:Apache T ...

  8. 分享STM32 FLASH 擦除(以及防止误擦除程序代码)、写入

    编译环境:我用的是(Keil)MDK4.7.2   stm32库版本:我用的是3.5.0一.本文不对FLASH的基础知识做详细的介绍,不懂得地方请查阅有关资料. 对STM32 内部FLASH进行编程操 ...

  9. shell脚本awk的基本用法

    AWK 1 AWK 2 3 linux取IP地址 4 5 ifconfig | grep -w inet | sed -n '1p' | awk '{print $2}' 6 7 eg: 8 9 aw ...

  10. js 做留言提交

    如下是留言提交源码 功能:点击按钮或点击enter键可以提交内容扩展:ctrlKey\shiftKey\altKye 可以将如上三个参数与oEvent.keyCode == 13 进行与操作可以得到 ...