MongoDB 连接

标准 URI 连接语法:

1
mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
  • mongodb:// 这是固定的格式,必须要指定。

  • username:password@ 可选项,如果设置,在连接数据库服务器之后,驱动都会尝试登录这个数据库

  • host1 必须的指定至少一个host, host1 是这个URI唯一要填写的。它指定了要连接服务器的地址。如果要连接复制集,请指定多个主机地址。

  • portX 可选的指定端口,如果不填,默认为27017

  • /database 如果指定username:password@,连接并验证登录指定数据库。若不指定,默认打开 test 数据库。

  • ?options 是连接选项。如果不使用 /database,则前面需要加上/。所有连接选项都是键值对name=value,键值对之间通过&或;(分号)隔开

标准的连接格式包含了多个选项(options):

微信截图_20200922163649.png

创建数据库

语法格式

MongoDB 创建数据库的语法格式如下:

1
use DATABASE_NAME

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> use test1
switched to db test1
> db
test1
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
> db.test1.insert({"name":"test1"})
WriteResult({ "nInserted" : 1 })
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
test1 0.000GB

如果数据库不存在,则创建数据库,否则切换到指定数据库。

ongoDB 中默认的数据库为 test,如果你没有创建新的数据库,集合将存放在 test 数据库中。

注意: 在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。

删除数据库

语法格式

MongoDB 删除数据库的语法格式如下:

1
db.dropDatabase()

删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名。

实例

1
2
3
4
5
6
7
8
> use test1
switched to db test1
> db.dropDatabase()
{ "dropped" : "test1", "ok" : 1 }
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB

创建集合

语法格式

1
db.createCollection(name, options)

参数说明:

  • name: 要创建的集合名称
  • options: 可选参数, 指定有关内存大小及索引的选项

options 可以是如下参数

微信截图_20200922165116.png

在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。

在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。

可以使用 show collectionsshow tables 命令查看已有集合。

删除集合

语法格式:

1
db.collection.drop()

如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。

1
2
3
4
> db.createCollection("test1")
{ "ok" : 1 }
> db.test1.drop()
true

插入文档

所有存储在集合中的数据都是 BSON 格式。

BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。

1
2
3
4
5
# 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。
db.COLLECTION_NAME.insert(document)

# 该方法新版本中已废弃
db.COLLECTION_NAME.save(document)

3.2 版本之后新增insertOne,insertMany

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 参数说明:
# document:要写入的文档。
# writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
# ordered:指定是否按顺序写入,默认 true,按顺序写入。

# 向集合插入一个新文档
db.collection.insertOne(
<document>,
{
writeConcern: <document>
}
)

# 向集合插入一个多个文档
db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)

异常级别

  • WriteConcern.NONE:没有异常抛出
  • WriteConcern.NORMAL:仅抛出网络错误异常,没有服务器错误异常
  • WriteConcern.SAFE:抛出网络错误异常、服务器错误异常;并等待服务器完成写操作。
  • WriteConcern.MAJORITY: 抛出网络错误异常、服务器错误异常;并等待一个主服务器完成写操作。
  • WriteConcern.FSYNC_SAFE: 抛出网络错误异常、服务器错误异常;写操作等待服务器将数据刷新到磁盘。
  • WriteConcern.JOURNAL_SAFE:抛出网络错误异常、服务器错误异常;写操作等待服务器提交到磁盘的日志文件。
  • WriteConcern.REPLICAS_SAFE:抛出网络错误异常、服务器错误异常;等待至少2台服务器完成写操作。

更新文档

update() 方法

语法格式

1
2
3
4
5
6
7
8
9
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)

参数说明:

  • query : update的查询条件,类似sql update查询内where后面的。
  • update : update的对象和一些更新的操作符(如$,$inc…)等,也可以理解为sql update查询内set后面的
  • upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
  • multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
  • writeConcern :可选,抛出异常的级别。

实例

1
2
3
4
5
6
db.test1.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}})
db.test1.find().pretty()

# 以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,
# 则需要设置 multi 参数为 true。
db.test1.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}},{multi:true})

save() 方法

save() 方法通过传入的文档来替换已有文档,_id 主键存在就更新,不存在就插入。

语法格式如下:

1
2
3
4
5
6
db.collection.save(
<document>,
{
writeConcern: <document>
}
)

参数说明:

  • document : 文档数据。
  • writeConcern :可选,抛出异常的级别。

在3.2版本开始,MongoDB提供以下更新集合文档的方法:

1
2
3
4
5
6
7
8
9
10
11
12
# 向指定集合更新单个文档
db.collection.updateOne()

# 向指定集合更新多个文档
db.collection.updateMany()

# 移除集合中的键值对,使用的 $unset 操作符:
db.col.update({"_id":"56064f89ade2f21f36b03136"}, {$set:{ "test2" : "OK"}})

db.col.update({"_id":"56064f89ade2f21f36b03136"}, {$unset:{ "test2" : "OK"}})


删除文档

MongoDB 最早删除文档使用的是 remove() 方法,后来官方把它移除了因为 remove() 并不会真正释放空间。需要继续执行 db.repairDatabase() 来回收磁盘空间。

建议在执行删除文档函数前先执行 find() 命令来判断执行的条件是否正确 db.test1.find()

1
2
3
db.repairDatabase()
# 或者
db.runCommand({repairDatabase: 1})

官方推荐使用 deleteOne()deleteMany() 方法。

语法

deleteOne() 语法:

1
2
3
4
5
6
7
db.collection.deleteOne(
<filter>,
{
writeConcern: <document>,
collation: <document>
}
)

deleteMany() 语法:

1
2
3
4
5
6
7
db.collection.deleteMany(
<filter>,
{
writeConcern: <document>,
collation: <document>
}
)
1
2
3
4
5
# 删除集合下全部文档
db.inventory.deleteMany({})

# 删除 status 等于 A 的全部文档
db.inventory.deleteMany({ status : "A" })

查询文档

MongoDB 查询文档使用 find(), findOne() 方法。

find() 方法以非结构化的方式来显示所有文档。

语法格式

db.collection.find(query, projection)

  • query :可选,使用查询操作符指定查询条件
  • projection :可选,使用投影操作符指定返回的键。若不指定 projection,则默认返回所有键(默认省略)。
1
2
3
# projection入参格式为{columnA : 0/1,columnB : 0/1}
# columnA 和 columnB 表示你要查询的表中的字段,0/1 表示取或不取。
db.collection.find(query, projection)

如:{title:1},表示查询出的每条记录中只显示 title 字段内容。{description:0},表示查询出的每条记录中不显示 description 字段内容(其他字段都展示)。

注:_id(主键)字段默认为 1,可指定 {_id:0} 来不输出 _id 字段值。

指定 projection 格式如下,有两种模式

1
2
db.collection.find(query, {title: 1, by: 1}) // inclusion模式 指定返回的键,不返回其他键
db.collection.find(query, {title: 0, by: 0}) // exclusion模式 指定不返回的键,返回其他键

两种模式不可混用(因为这样的话无法推断其他键是否应返回)

1
2
3
4
db.collection.find(query, {title: 1, by: 0}) # 错误

# 只能全1或全0,除了在inclusion模式时可以指定_id为0
db.collection.find(query, {_id:0, title: 1, by: 1}) # 正确

若不想指定查询条件参数 query 可以 用 {} 代替,但是需要指定 projection 参数:

1
querydb.collection.find({}, {title: 1})

AND 条件

1
db.test1.find({key1:value1, key2:value2}).pretty()

OR 条件

使用关键字 $or,语法格式如下:

1
2
3
4
5
6
7
db.test1.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()

AND 和 OR 联合使用

where likes>50 AND (by = 'test1' OR title = 'MongoDB')

1
db.test1.find({"likes": {$gt:50}, $or: [{"by": "test1"},{"title": "MongoDB"}]}).pretty()

模糊查询

1
2
3
4
5
6
7
8
# 查询 title 包含"教"字的文档:
db.col.find({title:/教/})

# 查询 title 字段以"教"字开头的文档:
db.col.find({title:/^教/})

# 查询 titl e字段以"教"字结尾的文档:
db.col.find({title:/教$/})

条件操作符

结合查询语句使用。

  • $gt — greater than >
  • $gte — gt equal >=
  • $lt — less than <
  • $lte — lt equal <=
  • $ne — not equal !=
  • $eq — equal =

$type 操作符

$type 操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。

获取 test1 集合中 title 为 String 的数据

1
2
3
db.test1.find({"title" : {$type : 2}})
# 或
db.test1.find({"title" : {$type : 'string'}})

Limit与Skip方法

1
2
3
4
5
6
7
8
9
# Limit() 方法
db.COLLECTION_NAME.find().limit(NUMBER)

# 如果你们没有指定limit()方法中的参数则显示集合中的所有数据。
db.test1.find({},{"title":1,_id:0}).limit(2)

# Skip() 方法
# skip()方法默认参数为 0
db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

skip 和limit方法只适合小数据量分页,如果是百万级效率就会非常低,因为skip方法是一条条数据数过去的,建议使用 where limit

排序

sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。

语法:

1
2
3
4
5
6
# 语法
db.COLLECTION_NAME.find().sort({KEY:1})

# 实例
# 按字段 likes 的降序排列
db.test1.find({},{"title":1,_id:0}).sort({"likes":-1})

索引

3.0.0 版本前创建索引方法为 db.collection.ensureIndex() ,之后的版本使用了 db.collection.createIndex() 方法,ensureIndex() 还能用,但只是 createIndex() 的别名。

语法

1
2
3
4
5
6
7
db.collection.createIndex(keys, options)

# 实例
db.test1.createIndex({"title":1})

# 可以设置使用多个字段创建索引(关系型数据库中称作复合索引)
db.test1.createIndex({"title":1,"description":-1})

语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引 -1 按降序来创建索引。

可选参数:

微信截图_20200924134809.png

1
2
3
4
5
6
7
8
9
10
11
# 查看集合索引
db.test1.getIndexes()

# 查看集合索引大小
db.test1.totalIndexSize()

# 删除集合所有索引
db.test1.dropIndexes()

# 删除集合指定索引
db.test1.dropIndex("索引名称")

TTL索引

保存最近三个月的文档(单位秒),当中途修改了createdAt的值时,
则不会删除文档(指定的时间是字段与当前时间的差值)。

db.user.createIndex({"createdAt": 1},{expireAfterSeconds: 60*60*24*3});

若需求变动,需要将三个月修改为一个月可以使用collMod,如下:

db.runCommand({collMod: 'user', index: {keyPattern:{"createdAt": 1}, expireAfterSeconds:60*60*24*1}});

TTL索引限制:

  • TTL索引是单字段索引,不能使用在聚合索引上
  • 不能在普通索引上再创建TTL索引,只能删除再建
  • _id主键上不能建立TTL索引
  • 一个集合上可以建立多个TTL索引
  • 索引不能包含多个字段
  • TTL索引可以用于普通索引一样进行排序和查询
  • 如果定义的字段不存在,则永不过期
  • 不能对capped集合创建TTL索引
  • TTL索引会每分钟检查超时文档,并进行删除操作。需要注意删除时候的并发问题(不要影响线上业务)

聚合

MongoDB中聚合( aggregate )主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)

aggregate() 方法

db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)

聚合的表达式:

微信截图_20200924142403.png

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
db.test1.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : 1}}}])
# 类似:
select by_user, count(*) from mycol group by by_user

# $project实例
# 只展示_id,tilte和author三个字段
db.article.aggregate(
{ $project : {
title : 1 ,
author : 1 ,
}}
);

# $match实例
db.article.aggregate(
{ $project : {
_id : 0 ,
title : 1 ,
author : 1
}});

# $match实例
db.articles.aggregate( [
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );

管道操作

MongoDB 的聚合管道将 MongoDB 文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • $match:用于过滤数据,只输出符合条件的文档。$match 使用 MongoDB 的标准查询操作。
  • $limit:用来限制 MongoDB 聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。

注意:当 match 条件和 group 同时存在时,顺序会影响检索结果,必须先写 match 在前面。

时间关键字如下:

  • $dayOfYear: 返回该日期是这一年的第几天(全年 366 天)。
  • $dayOfMonth: 返回该日期是这一个月的第几天(1到31)。
  • $dayOfWeek: 返回的是这个周的星期几(1:星期日,7:星期六)。
  • $year: 返回该日期的年份部分。
  • $month: 返回该日期的月份部分( 1 到 12)。
  • $week: 返回该日期是所在年的第几个星期( 0 到 53)。
  • $hour: 返回该日期的小时部分。
  • $minute: 返回该日期的分钟部分。
  • $second: 返回该日期的秒部分(以0到59之间的数字形式返回日期的第二部分,但可以是60来计算闰秒)。
  • $millisecond:返回该日期的毫秒部分( 0 到 999)。
  • $dateToString: { $dateToString: { format: , date: } }。

实例:

1
2
3
4
5
6
7
8
9
10
db.getCollection('m_msg_tb').aggregate(
[
{$match:{m_id:10001,mark_time:{$gt:new Date(2017,8,0)}}},
{$group: {
_id: {$dayOfMonth:'$mark_time'},
pv: {$sum: 1}
}
},
{$sort: {"_id": 1}}
])

参考:

MongoDB 教程

utf8mb4 的最低mysql版本支持版本为5.5.3+,若不是,请升级到较新版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> SHOW VARIABLES WHERE Variable_name LIKE 'character_set_%' OR Variable_name LIKE 'collation%';
+--------------------------+----------------------------------+
| Variable_name | Value |
+--------------------------+----------------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | /usr/local/mysql/share/charsets/ |
| collation_connection | utf8_general_ci |
| collation_database | utf8_general_ci |
| collation_server | utf8_general_ci |
+--------------------------+----------------------------------+
11 rows in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
# 修改database默认的字符集
# 虽然修改了database的字符集为utf8mb4,但是实际只是修改了database新创建的表,默认使用utf8mb4,
# 原来已经存在的表,字符集并没有跟着改变,需要手动为每张表设置字符集
ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci

# 修改表默认的字符集
ALTER TABLE table_name DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

# 修改表默认的字符集和所有字符列的字符集
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

修改MySQL配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
# 对本地的mysql客户端的配置
[client]
default-character-set = utf8mb4

# 对其他远程连接的mysql客户端的配置
[mysql]
default-character-set = utf8mb4

# 本地mysql服务的配置
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 必须保证一下系统变量是 utf8mb4

# 客户端来源数据使用的字符集
character_set_client

# 连接层字符集
character_set_connection

# 当前选中数据库的默认字符集
character_set_database

# 查询结果字符集
character_set_results

# 默认的内部操作字符集
character_set_server

参考:

MySQL中的 utf8 并不是真正的UTF-8编码 ! !

mysql 修改字符集为utf8mb4

更改MySQL数据库的编码为utf8mb4

MongoDB基本概念

微信截图_20200921170214.png

1
2
3
4
5
6
7
8
9
# 可以显示所有数据的列表
show dbs

# db 显示当前数据库对象或集合
> db
test

# use 可以连接到一个指定的数据库
use local

数据库也通过名字来标识。数据库名可以是满足以下条件的任意 UTF-8 字符串。

  • 不能是空字符串(””)。
  • 不得含有’ ‘(空格)、.、$、/、\和\0 (空字符)。
  • 应全部小写。
  • 最多64字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。

  • admin: 从权限的角度来看,这是 root 数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
  • local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
  • config: 当Mongo用于分片设置时,config 数据库在内部使用,用于保存分片的相关信息。

文档(Document)

RDBMS (关系数据库管理系统:Relational Database Management System) 与 MongoDB 对应的术语:

微信截图_20200921170852.png

需要注意的是:

  • 文档中的键/值对是有序的。
  • 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
  • MongoDB区分类型和大小写。
  • MongoDB的文档不能有重复的键。
  • 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:

  • 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
  • .和$有特别的意义,只有在特定环境下才能使用。
  • 以下划线”_”开头的键是保留的(不是严格要求的)。

集合

当第一个文档插入时,集合就会被创建。

合法的集合名

  • 集合名不能是空字符串””。
  • 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以”system.”开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。

capped collections

名词解释:

capped:用…覆盖顶部(或端部)

Capped collections 就是固定大小的 collection

它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 “RRD” 概念类似(round robin database,即环形数据库)。

Capped collections 是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能和标准的 collection 不同,你必须要显式的创建一个 capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。

Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新 Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。

由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。

要注意的是指定的存储大小包含了数据库的头信息。

db.createCollection("mycoll", {capped:true, size:100000})

size 是整个集合空间大小,单位为【KB】

max 是集合文档个数上线,单位是【个】

如果空间大小到达上限,则插入下一个文档时,会覆盖第一个文档;如果文档个数到达上限,同样插入下一个文档时,会覆盖第一个文档。
两个参数上限判断取的是【与】的逻辑。

  • 在 capped collection 中,你能添加新的对象。
  • 能进行更新,然而,对象不会增加存储空间。如果增加,更新就会失败 。
  • 使用 Capped Collection 不能删除一个文档,可以使用 drop() 方法删除 collection 所有的行。
  • 删除之后,你必须显式的重新创建这个 collection
  • 在 32位机器中,capped collection 最大存储为 1e9( 1X109)个字节,约为482.5M,64位上只受系统文件大小的限制。。
  • 对固定集合进行插入速度极快
  • 按照插入顺序的查询输出速度极快
  • 能够在插入最新数据时,淘汰最早的数据

用法

用法1:储存日志信息
用法2:缓存一些少量的文档

元数据

数据库的信息是存储在集合中。它们使用了系统的命名空间:

dbname.system.*

在MongoDB数据库中名字空间 <dbname>.system.* 是包含多种系统信息的特殊集合 (Collection),如下:

微信截图_20200921174139.png

对于修改系统集合中的对象有如下限制。

1
2
3
4
在 {{system.indexes}} 插入数据,可以创建索引。
但除此之外该表信息是不可变的(特殊的 `drop index` 命令将自动更新相关信息)。

{{system.users}} 是可修改的。 {{system.profile}} 是可删除的。

MongoDB 数据类型

微信截图_20200921174631.png

ObjectId

ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:

  • 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
  • 接下来的 3 个字节是机器标识码
  • 紧接的两个字节由进程 id 组成 PID
  • 最后三个字节是随机数

MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId对象

由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:

1
2
3
4
5
6
7
> var newObject = ObjectId()
> newObject.getTimestamp()
ISODate("2017-11-25T07:21:10Z")
ObjectId 转为字符串

> newObject.str
5a1919e63df83ce79df8b38f

字符串

BSON 字符串都是 UTF-8 编码。

时间戳

BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的 日期 类型不相关。 时间戳值是一个 64 位的值。其中:

前32位是一个 time_t 值(与Unix新纪元相差的秒数)
后32位是在某秒中操作的一个递增的序数
在单个 mongod 实例中,时间戳值通常是唯一的。

在复制集中, oplog 有一个 ts 字段。这个字段中的值使用BSON时间戳表示了操作时间。

BSON 时间戳类型主要用于 MongoDB 内部使用。在大多数情况下的应用开发中,你可以使用 BSON 日期类型。

日期

表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期。

1
2
3
4
5
6
7
8
9
10
> var mydate1 = new Date()     //格林尼治时间
> mydate1
ISODate("2020-09-21T09:49:56.273Z")
> typeof mydate1
object
> var mydate2 = ISODate() //格林尼治时间
> mydate2
ISODate("2020-09-21T09:50:29.386Z")
> typeof mydate2
object

这样创建的时间是日期类型,可以使用 JS 中的 Date 类型的方法。

返回一个时间类型的字符串:

1
2
3
4
5
6
7
> var mydate1str = mydate1.toString()
> mydate1str
Mon Sep 21 2020 17:49:56 GMT+0800
> typeof mydate1str
string
> Date()
Mon Sep 21 2020 17:51:15 GMT+0800

参考:

MongoDB 概念解析

下载安装包,安装

https://www.mongodb.com/try/download/community

默认安装位置:C:\Program Files\MongoDB\Server\4.4\bin

运行 MongoDB 服务器

命令行运行

1
2
cd C:\Program Files\MongoDB\Server\4.4\bin\
mongod --dbpath D:\MongoDBData

微信截图_20200921162755.png

连接服务

1
2
cd C:\Program Files\MongoDB\Server\4.4\bin\
mongo.exe

微信截图_20200921162957.png

配置 MongoDB 服务

mongod 默认配置文件:

微信截图_20200921161709.png

根据实际情况,修改日志路径,和文档路径。

如:dbPath: D:\MongoDBData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# mongod.conf

# for documentation of all options, see:
# http://docs.mongodb.org/manual/reference/configuration-options/

# Where and how to store data.
storage:
dbPath: C:\Program Files\MongoDB\Server\4.4\data
journal:
enabled: true
# engine:
# mmapv1:
# wiredTiger:

# where to write logging data.
systemLog:
destination: file
logAppend: true
path: C:\Program Files\MongoDB\Server\4.4\log\mongod.log

# network interfaces
net:
port: 27017
bindIp: 127.0.0.1


#processManagement:

#security:

#operationProfiling:

#replication:

#sharding:

## Enterprise-Only Options:

#auditLog:

#snmp:

安装 MongoDB服务

1
mongod.exe --config "C:\Program Files\MongoDB\Server\4.4\bin\mongod.cfg" --install

dbpath,可以在配置文件(例如:C:\mongodb\mongod.cfg)或命令行中通过 --dbpath 选项指定。

可以安装 mongod.exemongos.exe 的多个实例的服务。只需要通过使用 --serviceName--serviceDisplayName 指定不同的实例名。

以管理员身份启动 cmd:

1
2
3
4
5
6
7
8
9
# 启动MongoDB服务
net start MongoDB

# 关闭MongoDB服务
net stop MongoDB

# 移除 MongoDB 服务
cd C:\Program Files\MongoDB\Server\4.4\bin
mongod.exe --remove

MongoDB 后台管理 Shell

打开 mongodb 装目录的下的bin目录,然后执行 mongo.exe 文件,MongoDB ShellMongoDB 自带的交互式 Javascript shell ,用来对MongoDB进行操作和管理的交互式环境。

当进入mongoDB后台后,它默认会链接到 test 文档(数据库):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
C:\Program Files\MongoDB\Server\4.4\bin>mongo
MongoDB shell version v4.4.1
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("7aa2a8eb-097a-4433-a348-314868079dff") }
MongoDB server version: 4.4.1
---
The server generated these startup warnings when booting:
2020-09-21T16:38:42.622+08:00: ***** SERVER RESTARTED *****
2020-09-21T16:38:43.487+08:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
---
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).

The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.

To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
> db
test
> 2 + 2
4
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB

参考:

Windows 平台安装 MongoDB

一个应用服务(service)中,配置多个端口号

一个service配置多个端口,项目可以通过多个端口访问。

修改 vim /usr/local/apache-tomcat-8.5.40/conf/server.xml,在Service下配置多个 <Connector> 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<Service name="Catalina"> 
<Connector connectionTimeout="20000" port="8013" protocol="HTTP/1.1" redirectPort="8443"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>

<Connector port="8014" protocol="HTTP/1.1" maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000" URIEncoding="utf-8"
disableUploadTimeout="true" />

<Engine defaultHost="localhost" name="Catalina">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
<Context path="/" reloadable="true" docBase="/test/webapp"></Context>
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>
</Engine>
</Service>

在这个应用里,可以用8080端口号访问服务,也可以用8099端口号来访问服务; 服务放置的路径由host决定,上例中服务放在 webapps 下。
以下两种方式访问同一个项目:

1
2
http://localhost:8013/项目名称
http://localhost:8014/项目名称

在一个Tomcat下配置多个服务,用不同的端口号。

配置多个service,每个service可以配置多个端口。

修改 vim /usr/local/apache-tomcat-8.5.40/conf/server.xml ,添加多个 Service 即可。

注意 Service nameEngine nameappBase端口号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<Service name="Catalina">
<Connector connectionTimeout="20000" port="8013" protocol="HTTP/1.1" redirectPort="8443"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
<Engine defaultHost="localhost" name="Catalina">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
</Host>
</Engine>
</Service>

<Service name="Catalina1">
<Connector connectionTimeout="20000" port="8014" protocol="HTTP/1.1" redirectPort="8443"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
<Engine defaultHost="localhost" name="Catalina1">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
<Host appBase="webapps1" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
</Host>
</Engine>
</Service>

<Service name="Catalina2">
<Connector connectionTimeout="20000" port="8015" protocol="HTTP/1.1" redirectPort="8443"/>
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>
<Engine defaultHost="localhost" name="Catalina2">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
<Host appBase="webapps2" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">
</Host>
</Engine>
</Service>

以上三个service,发布的路径不同,项目分别发布在 webapps、webapps1、webapps2下,

访问不同的项目的方法:

1
2
3
http://localhost:8013/项目名称1
http://localhost:8014/项目名称2
http://localhost:8015/项目名称3

参考:

Tomcat 配置多个端口号或多个应用

Tomcat配置为系统服务

创建Tomcat8服务文件

1
2
touch /usr/lib/systemd/system/tomcat8.service
vim /usr/lib/systemd/system/tomcat8.service

tomcat8.service文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=Tomcat8
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking

Environment="JAVA_HOME=/usr/local/jdk1.8.0_91"
ExecStart=/usr/local/apache-tomcat-8.5.40/bin/startup.sh
ExecReload=/usr/local/apache-tomcat-8.5.40/bin/startup.sh
ExecStop=/usr/local/apache-tomcat-8.5.40/bin/shutdown.sh

[Install]
WantedBy=multi-user.target
1
2
3
4
5
systemctl enable tomcat8.service
systemctl is-enabled tomcat8
systemctl daemon-reload
systemctl status tomcat8
systemctl restart tomcat8

验证

1
2
3
# 查看监听的端口
# Tomcat默认8080端口
netstat -lnpt

参考:

centos7配置tomcat开机自启动

CentOS 7下Tomcat 安装与配置(Tomcat开机启动)

Linux 文件打开最大数

1
2
3
4
5
6
7
8
vi /etc/security/limits.conf
* soft nproc 65535
* hard nproc 65535
* soft nofile 65535
* hard nofile 65535

# 查看当前打开文件数
lsof | wc -l

参考:Linux最大文件打开数

内核优化

方法一:修改 /proc 下内核参数文件内容,不能使用编辑器来修改内核参数文件,理由是由于内核随时可能更改这些文件中的任意一个,另外,这些内核参数文件都是虚拟文件,实际中不存在,因此不能使用编辑器进行编辑,而是使用echo命令,然后从命令行将输出重定向至 /proc 下所选定的文件中。如:将 timeout_timewait 参数设置为30秒:

echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout

参数修改后立即生效,但是重启系统后,该参数又恢复成默认值。因此,想永久更改内核参数,需要修改/etc/sysctl.conf文件

方法二.修改 /etc/sysctl.conf 文件

优化适合apachenginxsquid 多种等web应用。

文件:/etc/sysctl.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 表示套接字由本端要求关闭,这个参数决定了它保持在FIN-wAIT-2状态的时间,默认值是60秒
# server端主动发起断开连接后保持在FIN-WAIT-2状态的时间(建议30s)
# 该参数对应系统路径为:/proc/sys/net/ipv4/tcp_fin_timeout 60
net.ipv4.tcp_fin_timeout = 30

## reuse和recycle这俩个参数是为防止生产环境下 web,nginx 等业务服务器 time_wait 网络状态数量过多设置的

# 表示开启重用,允许TIME-wAIT sockets重新用于新的TCP链接,默认值为0
# 表示关闭,该参数对应系统路径为:/proc/sys/net/ipv4/tcp_tw_reuse 0
net.ipv4.tcp_tw_reuse = 1

# 表示开启TCP链接中TIME_WAIT sockets的快速回收,
# 该参数对应系统路径为:/proc/sys/net/ipv4/tcp_tw_recycle
# 默认为0 表示关闭,建议为0
net.ipv4.tcp_tw_recycle = 0
reuse/recycle注解:

因为TCP连接是双向的,所以在关闭连接的时候,两个方向各自都需要关闭。先发 FIN 包的一方执行的是 主动关闭;后发 FIN 包的一方执行的是 被动关闭。主动关闭的一方会进入TIME_WAIT 状态,并且在此状态停留两倍的 MSL maximum segment lifetime(最大分节生命期最大报文存活时间,这是一个IP数据包能在互联网上生存的最长时间,超过这个时间IP数据包将在网络中消失 。MSL在RFC 1122上建议是2分钟,而源自berkeley的TCP实现传统上使用30秒。一般Linux内核设置30秒 )时长。TIME_WAIT 状态维持时间是两个MSL时间长度,也就是在1-4分钟。Windows操作系统就是4分钟。

等待 2MSL 原因:

TCP目的是可靠传输,主动关闭的一方发出 FIN,被动方回复ACK,接着被动方发送 FIN ,主动方收到被动关闭的一方发出的 FIN 包后,回应 ACK 包,同时进入 TIME_WAIT 状态。但是因为网络原因,主动关闭的一方发送的这个 ACK 包很可能延迟,从而触发被动连接一方重传 FIN 包。极端情况下,这一去( ACK 去被动方)一回(重传 FIN 回来),就是两倍的 MSL 时长。

如果主动关闭的一方跳过 TIME_WAIT 直接进入 CLOSED ,或者在 TIME_WAIT 停留的时长不足两倍的 MSL ,那么当被动关闭的一方早先发出的 FIN 延迟包到达或者重传 FIN 包到达后,就可能出现类似下面的问题:

  • 主动方旧的TCP连接已经不存在了,主动方只能返回RST包
  • 主动方新的TCP连接被建立起来了,延迟包可能干扰新的连接

减少TIME_WAIT

TIME_WAIT 期间,资源不会释放,现在都追求高性能高并发,快速释放资源是躲不掉的.对于客户端因为有端口 65535 问题,TIME_WAIT 过多直接影响处理能力. 对于服务器,无端口数量限制的问题,Linux优化也很给力,每个处于 TIME_WAIT 状态下连接内存消耗很少, 而且也能通过 tcp_max_tw_buckets = ${你要的阈值} 配置最大上限,但是对于短连接为主的web服务器,几十万的连接,基数很大,耗得内存也不小,快速释放总是好的。

tcp_tw_recycle:回收TIME_WAIT连接

对客户端和服务器同时起作用,开启后在 3.5*RTO 内回收,RTO 200ms~ 120s 具体时间视网络状况。RTO(Retransmission TimeOut)重传超时时间。内网状况比 tcp_tw_reuse 稍快,公网尤其移动网络大多要比 tcp_tw_reuse 慢,优点就是能够回收服务端的 TIME_WAIT 数量。

但是,需要注意的是:当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,也就是说对服务端而言这些客户端实际上等同于一个,可惜由于这些客户端的时间戳可能存在差异,于是乎从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。客户端处于NAT很常见,基本公司家庭网络都走NAT。

tcp_tw_reuse:复用 TIME_WAIT 连接 只对客户端起作用,1秒后才能复用,当创建新连接的时候,如果可能的话会考虑复用相应的 TIME_WAIT 连接。通常认为 tcp_tw_reuse tcp_tw_recycle 安全一些,这是因为一来 TIME_WAIT 创建时间必须超过一秒才可能会被复用;二来只有连接的时间戳是递增的时候才会被复用。

客户端请求服务器,服务器响应后主动关闭连接,TIME_WAIT 存在于服务器,服务器是被连接者,没有复用一说,所以只对客户端起作用.如果是客户端主动关闭, TIME_WAIT 存在于客户端,这个时候再次连接服务器,可以复用之前 TIME_WAIT 留下的半废品。

tcp_timestamps:以上两点,必须在客户端和服务端 timestamps 开启时才管用(默认开启) 需要根据 timestamp 的递增性来区分是否新连接

总结

客户端 tcp_tw_reuse 复用连接管用, tcp_tw_recycle 有用,但是客户端主要不是接受连接,用处不大
服务器 tcp_tw_recycle 回收连接管用,tcp_tw_reuse 复用无效.为了减少 TIME_WAIT 留在服务器,可以在服务器开启 KeepAlive ,尽量不让服务器主动关闭,而是客户端主动关闭,减少TIME_WAIT 产生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 表示开启SYN Cookies功能,当出现SYN等待队列溢出时,启用Cookies来处理,可防范少量SYN攻击,
# 该参数对应系统路径为:/proc/sys/net/ipv4/tcp_syncookies,默认为1,表示开启
net.ipv4.tcp_syncookies = 1

# 表示当keepalive启用时,TCP发送keepalive消息的频度,默认是2小时,建议更改为5分钟,
# 该参数对应系统路径为:/proc/sys/net/ipv4/tcp_keepalive_time,默认为7200秒
net.ipv4.tcp_keepalive_time = 300

# 该选项用来设定允许系统打开的端口范围,用于向外链接的端口范围,
# 每个TCP客户端连接都要占用一个唯一的本地端口号(此端口号在系统的本地端口号范围限制中),
# 如果现有的TCP客户端连接已将所有的本地端口号占满。将不能创建新的TCP连接。
# 该参数对应系统路径为:/proc/sys/net/ipv4/ip_local_port_range 默认,32768 60999
# 理论上单独一个进程最多可以同时建立60000多个TCP客户端连接。
# 影响 CLOSE_WAIT 数量
net.ipv4.ip_local_port_range = 1024 65000

# 表示SYN队列的长度,默认为1024,建议加大队列的长度,为8182或更多,这样可以容纳更多等待链接的网络连接数,
# 该参数为服务器端用于记录那些尚未收到客户端确认信息的链接请求的最大值,
# 该参数对应系统路径为:/proc/sys/net/ipv4/tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 262144

# 该选项默认值是128,这个参数用于调节系统同时发起的TCP连接数,
# 在高并发的请求中,默认的值可能会导致链接超时或重传,因此,需要结合并发请求数来调节此值,
# 该参数对应系统路径为:/proc/sys/net/ipv4/somaxconn 128
# 默认没有这个配置,需要自己生成,somaxconn值不应超过65535
net.core.somaxconn = 32768

listen方法指定的backlog是在用户态指定的,内核态的参数优先级高于用户态的参数,所以即使在listen方法里面指定backlog是一个大于somaxconn的值,socket在内核态运行时还会检查一次somaxconn,如果连接数超过somaxconn就会等待。

在没有调优的centOS7.4版本的服务器上,由于受到系统级别的限制,在该服务器上运行的服务端程序,在同一时间,最大只能接受128个客户端发起持久连接,并且只能处理128个客户端的数据通信。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 表示系统同时保持 TIME_WAIT 套接字的最大数量,如果超过这个数值,TIME_WAIT 套接字将立刻被清除并打印警告信息,
# 对于Aapache,Nginx等服务器来说可以将其调低一点,如改为 5000-30000,不用业务的服务器也可以给大一点,
# 比如LVS,Squid,该参数对应系统路径为:/proc/sys/net/ipv4/tcp_max_tw_buckets
# #1st低于此值,TCP没有内存压力,2nd进入内存压力阶段,3rdTCP拒绝分配socket(单位:内存页)
net.ipv4.tcp_max_tw_buckets = 30000

# 外向syn握手重试次数
# 该参数对应系统路径为:/proc/sys/net/ipv4/tcp_syn_retries,默认是6
net.ipv4.tcp_syn_retries = 1

# 为了打开对端的连接,内核需要发送一个 SYN 并附带一个回应前面一个 SYN 的 ACK。也就是所谓三次握手中的第二次握手。
# 该参数对应系统路径为:/proc/sys/net/ipv4/tcp_synack_retries,默认是5
# syn-ack握手状态重试次数,默认5,遭受SYN Flood [(SYN洪水) 是种典型的DoS (Denial of Service,拒绝服务)]攻击时改为1或2
net.ipv4.tcp_synack_retries = 1

# 网卡设备将请求放入队列的长度
# 表示当每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许发送到队列的数据包最大数,
# 该参数对应系统路径为:/proc/sys/net/ipv4/netdev_max_backlog
# 默认没有这个配置,需要自己生成
net.core.netdev_max_backlog = 2000

# 用于设定系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上,
# 如果超过这个数值,孤立链接将立即被复位并打印出警号信息,这个限制只是为了防止简单的DoS攻击,不能过分依靠这个限制甚至
# 人为减小这个值,更多的情况是增加这个值,该参数对应系统路径为:/proc/sys/net/ipv4/tcp_max_orphans
net.ipv4.tcp_max_orphans = 3276800

#开启窗口缩放功能,要支持超过64KB的TCP窗口,必须启用该值,TCP连接双方都启用时才生效
net.ipv4.tcp_window_scaling = 1

# 表示套接字接收缓冲区的内存最大值
net.core.rmem_max = 5242880

# 表示套接字发送缓冲区大小的最大值,会覆盖 net.ipv4.tcp_wmem的MAX值
net.core.wmem_max = 5242880

# 接受缓冲的大小:MIN,DEFAULT,MAX
# 即TCP读取缓冲区,单位为字节byte
net.ipv4.tcp_rmem = 16384 32768 5242880

# socket的发送缓存区分配的MIN,DEFAULT,MAX
# 发送缓冲区,单位是字节byte
net.ipv4.tcp_wmem = 16384 32768 5242880

# 1低于此值,TCP没有内存压力,2在此值下,进入内存压力阶段,3高于此值,TCP拒绝分配socket.
# net.ipv4.tcp_mem[0]:低于此值,TCP没有内存压力。
# net.ipv4.tcp_mem[1]:在此值下,进入内存压力阶段。
# net.ipv4.tcp_mem[2]:高于此值,TCP拒绝分配socket。
# 内存单位是页,1页等于4096字节,1 Page = 4096 Bytes
# 32GB内存机器,最小值、压力值、最大值:8G 12G 16G
net.ipv4.tcp_mem = 2097152 3145728 4194304

# 禁用时间戳,时间戳可以避免序列号的卷绕
net.ipv4.tcp_timestamps = 0

# 定义SYN重试次数
# 避免重复发送数据包
net.ipv4.tcp_sack = 1

# 单独一个进程最多可以同时建立20000多个TCP客户端连接
# 不建议自行设置
net.ipv4.ip_conntrack_max = 20000

/etc/sysctl.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_max_syn_backlog = 262144
net.core.somaxconn = 65535
net.ipv4.tcp_max_tw_buckets = 30000
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_synack_retries = 1
net.core.netdev_max_backlog = 2000
net.ipv4.tcp_window_scaling = 1
net.core.rmem_max = 5242880
net.core.rmem_default = 256960
net.core.wmem_max = 5242880
net.core.wmem_default = 256960
net.ipv4.tcp_rmem = 16384 32768 5242880
net.ipv4.tcp_wmem = 16384 32768 5242880
net.ipv4.tcp_mem = 2097152 3145728 4194304
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_sack = 1

查看系统级文件句柄修改,是否生效

1
sysctl -p

备忘录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 获取操作系统参数的类型
sysctl -a|awk -F "." '{print $1}'|sort -k1|uniq

# 获取net类型下的子类型
sysctl -a|grep "^net."|awk -F "[.| ]" '{print $2}'|sort -k1|uniq

# 获取有关服务器套接字缓冲区的信息
sysctl -a|grep "^net."|grep "[r|w|_]mem[_| ]"

# 查看当前的连接数状况
# SYN_RECV表示正在等待处理的请求数;ESTABLISHED表示正常数据传输状态;TIME_WAIT表示处理完毕,等待超时结束的请求数
netstat -nat|awk '{print awk $NF}'|sort|uniq -c|sort -n

# 查看IP连接数状况
netstat -nat|grep ":8011"|awk '{print $5}' |awk -F: '{print $1}' | sort| uniq -c|sort -n

# 查看活动连接数
ss -n | grep ESTAB | wc -l

# 获取当前socket连接状态统计信息
cat /proc/net/sockstat

# 统计当前各种状态的连接的数量的命令
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

# 返回结果如下:
LAST_ACK 14
SYN_RECV 348
ESTABLISHED 70
FIN_WAIT1 229
FIN_WAIT2 30
CLOSING 33
TIME_WAIT 18122

# 对上述结果的解释:
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉

参考:

Linux内核 TCP/IP、Socket参数调优

Linux系统优化实现高并发

Linux内核优化

Linux 下高并发系统内核优化

centos7之系统优化方案

Linux 内核优化-调大TCP最大连接数

这个错是链接时报的错,要链接必须启动。修复的时候首先要启动mysql。

首先来了解一下 mysql.sock 的作用:

Mysql有两种连接方式:

(1)TCP/IP
(2)socket

mysql.sock来说,其作用是程序与 mysql server 处于同一台机器,发起本地连接时可用。
例如你无须定义连接 host 的具体IP,只要为空或 localhost 就可以。
在此种情况下,即使你改变 mysql 的外部 port 也是一样可能正常连接。
因为你在 my.ini 中或 my.cnf 中改变端口后,mysql.sock 是随每一次 mysql server启动生成的。已经根据你在更改完 my.cnf 后重启 mysql 时重新生成了一次,信息已跟着变更。

那么对于外部连接,必须是要变更port才能连接的。
mysql.sock 的作用是 serverclient 在同一台服务器,并且使用 localhost 进行链接的时候,就会使用socket来进行连接——仅此而已
也就是:为主机名为 localhost 建立的MySQL连接,该连接过程通过一个套接字文件 mysql.socket 实现的。所以该文件被删后,用 localhost 用户是连接不到MySQL服务器的。

必须建立一条 tcp/ip 连接,即使用 127.0.0.1 而不是 localhost 作为-h的参数去连接MySQL服务器,如:mysqladmin -h 127.0.0.1 -u root -p shutdown,强制地建立一条 tcp/ip 连接;
关闭MySQL服务器,再重新以 localhost 为主机名启动MySQL服务器,它就会重新创建一个套接字文件。

注意:mysql.sock 是一个临时文件,启动mysql后才会生成。
报错的原因:MySQL将其放在 /tmp 目录,而linux将其放在 /var/mysql 目录。所以我们只需要创建一个软链接,输入以下两个命令即可:
如果 /var/ 下没有 mysql目录,则需创建

创建目录:sudo mkdir /var/mysql

创建软链接:sudo ln -s /var/mysql/mysql.sock /tmp/mysql.sock

如果提示: ln: creating symbolic link /var/lib/mysql/mysql.sock’: File exists删除之前的mysql.sock` 文件。

参考:

ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2)

MySql 报错ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/tmp/mysql.sock’ (2)

中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using Model.Base;
using Service.MonitorLog;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Diagnostics;
using System.IO;

namespace Test
{
public static class CustomLoggingMiddleware
{
public static void UseCustomLogging(this IApplicationBuilder app)
{
app.Use(async (context, next) =>
{
var requestContent = "";
var contentType = context.Request.ContentType;
context.Request.EnableBuffering();
// 只接受json格式参数,防止文件流等参数超长
if (!string.IsNullOrWhiteSpace(contentType) && contentType.Contains("json", StringComparison.OrdinalIgnoreCase))
{
var requestReader = new StreamReader(context.Request.Body);
requestContent = await requestReader.ReadToEndAsync();
}
context.Request.Body.Position = 0;

monitorlog ml = new monitorlog();

var dt = DateTime.Now;

ml.id = 0;
ml.jkmc = context.Request.Path;
ml.qqfs = context.Request.Method;
ml.qqcs_txt = requestContent;

Stream originalBody = context.Response.Body;
try
{
await using var memStream = new MemoryStream();
context.Response.Body = memStream;
await next();
memStream.Position = 0;
string responseBody = await (new StreamReader(memStream)).ReadToEndAsync();
ml.xyjg_txt = responseBody;

memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
finally
{
context.Response.Body = originalBody;
}

ml.statuscode = context.Response.StatusCode;
if (ml.statuscode == 200)
await MonitorLog.SaveMonitorLog(ml);
});
}
}
}

使用

Startup.csConfigure 方法添加如下代码:

1
app.UseCustomLogging();

支持IPV4端口

/conf/context.xml

1
2
3
4
<Connector port="80" maxHttpHeaderSize="8192" 
maxThreads="300" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" />

添加: address="0.0.0.0"

1
2
3
4
<Connector port="80" maxHttpHeaderSize="8192" address="0.0.0.0"
maxThreads="300" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" />

只支持IP4

linux平台

<tomcat>/bin 目录下 catalina.sh,添加如下内容:

vim catalina.sh,然后输入 / 搜索 JAVA_OPTS,

n 向下查找
N 向上查找

微信截图_20200831141600.png

1
2
# 添加以下内容
JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true "

windows

编辑 bin 目录下的 catalina.bat 文件,改成以下内容:

1
set "JAVA_OPTS=%JAVA_OPTS% -Djava.net.preferIPv4Stack=true %JSSE_OPTS%"

参考

如何让tomcat只支持ipv4

简介

Linux cp命令主要用于复制文件或目录。

语法

1
2
3
4
cp [options] source dest

# 或
cp [options] source... directory

参数说明:

  • -a:此选项通常在复制目录时使用,它保留链接、文件属性,并复制目录下的所有内容。其作用等于dpR参数组合。
  • -d:复制时保留链接。这里所说的链接相当于Windows系统中的快捷方式。
  • -f:覆盖已经存在的目标文件而不给出提示。
  • -i:与-f选项相反,在覆盖目标文件之前给出提示,要求用户确认是否覆盖,回答”y”时目标文件将被覆盖。
  • -p:除复制文件的内容外,还把修改时间和访问权限也复制到新文件中。
  • -r:若给出的源文件是一个目录文件,此时将复制该目录下所有的子目录和文件。
  • -l:不复制文件,只是生成链接文件。

实例

使用指令 cp 将当前目录 test/ 下的所有文件复制到新目录 newtest 下,输入如下命令:

cp –r test/ newtest

注意:用户使用该指令复制目录时,必须使用参数 -r 或者 -R

参考

Linux cp命令

下载

https://tomcat.apache.org/

安装

1
2
3
4
5
6
7
8
9
10
11
# 解压的 /usr/local
tar -zxvf apache-tomcat-8.5.40.tar.gz -C /usr/local

# 进入 目录
cd /usr/local/apache-tomcat-8.5.40/bin

# 启动
./startup.sh

# 停止
./shutdown.sh

验证

1
2
3
# 查看监听的端口
# Tomcat默认8080端口
netstat -lnpt

下载安装包

在官网下载jdk 安装包:

https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html

解压

1
2
# 解压 并输出到 /usr/local
tar -zxvf jdk-8u91-linux-x64.tar.gz -C /usr/local

配置

编辑 /etc/profile

vim /etc/profile

1
2
3
4
JAVA_HOME=/usr/local/jdk1.8.0_91
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=.:$JAVA_HOME/lib
export JAVA_HOME PATH CLASSPATH

使配置生效:

source /etc/profile

验证

1
2
3
java
java -version
javac

Linux 上的设备 (device)

Linux 操作系统中,各种设备驱动(device driver)通过设备控制器(device controller)来管理各种设备(device),其关系如下图所示:

697113-20160802132422028-1983172341.jpg

阅读全文 »

简介

Linux fdisk是一个创建和维护分区表的程序,它兼容DOS类型的分区表、BSD或者SUN类型的磁盘列表。

语法

fdisk [必要参数] [选择参数]

必要参数:

1
2
-l 列出素所有分区表
-u 与"-l"搭配使用,显示分区数目

选择参数:

1
2
-s<分区编号> 指定分区
-v 版本信息

菜单操作说明

1
2
3
4
5
6
7
8
9
10
11
m :显示菜单和帮助信息
a :活动分区标记/引导分区
d :删除分区
l :显示分区类型
n :新建分区
p :显示分区信息
q :退出不保存
t :设置分区号
v :进行分区检查
w :保存修改
x :扩展应用,高级功能

参考:

Linux fdisk命令

fdisk命令