不奢望岁月静好 只希望点滴积累

0%

lsof(list open files) 查看当前系统文件、Linux下任何事物都以文件的形式存在、通过文件不仅可以访问常规数据库、还可以访问网络连接(tcp、udp等)和硬件

lsof 打开文件可以是:

  • 普通文件
  • 目录
  • 网络文件系统的文件
  • 字符或设备文件
  • 共享库
  • 管道、命名管道
  • 符号连接
  • 网络文件 (eg. NFS file、网络socket、unix 域名socket等)
  • 其它类型的文件

命令参数

-a 列出打开文件存在的进程
-c<进程名>

选项

  • -i any 监听所有的网卡接口、用来查看是否有网络流量
  • -i eth0 只监听eth0网卡流量
  • -D 显示可用的接口列表
  • -n 不解析主机名、直接使用ip
  • -nn 显示端口
  • -q 显示简化输出
  • -t 显示可读的时间戳
  • -XhexASCII两种形式显示包内容
  • -v -vv -vvv 显示更加多的包信息
  • -XX-X 类似、增加以太网header的显示
  • -c 只读取x个包, 然后停止
  • -s 指定每个包捕获的长度、单位是 byte, 可以使用 -s0 捕获整个包
  • -S 输出绝对的序列号
  • -e 获取以太网 header
  • -w 将捕获的数据包信息写入文件
  • -r 加载之前保存的文件

表达式
tcpdump中可以使用表达式过滤指定类型的流量:

  • 类型type选项包含:host net port
  • 方向dir包含:src dst
  • 协议proto选项包含:tcp udp ah

示例

捕获所有流量
tcpdump -i any

指定网卡接口、查看指定网卡发生了什么
tcpdump -i eth0

原生输出、不解析主机、端口、显示绝对序列号、可读的时间戳
tcpdump -ttttnnvvS

查看指定ip的流量
tcpdump host {ip}

使用目的过滤
tcpdump src {source ip}
tcpdump dst {dest ip}

过滤某个子网的数据包
tcpdump net 1.2.3.0/24

过滤指定端口相关的流量
tcpdump port {port}
tcpdump src port {port} -> 只显示发出

过滤指定协议的流量
tcpdump tcp

只显示ipv6流量
tcpdump ip6

基于包大小过滤流量
tcpdump less 32
tcpdump grater 64
tcpdump <=128

使用端口范围过滤
tcpdump portrange 21-23

保存到指定文件
tcpdump port 80 -w file

加载之前保存的文件
tcpdump -r file

高级使用

  • AND: and or &&
  • OR: or or ||
  • except: not or !

    过滤指定源和目的端口
    tcpdump -n src 1.1.1.2 and dst port 8080

过滤指定网络方向
tcpdump -n src net 1.1.1.2/16 and dst net 1.1.1.3/16

过滤到指定ip的非icmp报文
tcpdump dst 1.1.1.2 and src net and not icmp

构建规则过于复杂的时候、可以使用单引号将规则放到一起
tcpdump ‘src 1.1.1.1 and (dst port 80 or 22)’

隔离指定的TCP标识
tcp[13]表示在tcp header中的偏移位置13开始、后边代表的是匹配的字节数

显示所有的urgent包(URG)
tcpdump ‘tcp[13] & 32!=0’

显示所有的ACK包
tcpdump ‘tcp[13] & 16!=0’

显示所有的push包
tcpdump ‘tcp[13] & 8!=0’

显示所有的reset包
tcpdump ‘[tcp13] & 4!=0’

显示所有的SYN包
tcpdump ‘[tcp13] & 2!=0’

显示所有的FIN包
tcpdump ‘[tcp13] & 1!=0’

显示所有的SYN/ACK包
tcpdump ‘tcp[13]=18’

识别重要流量

过滤同时设置SYN和RST标识的包(这在正常情况下不应该发生)
tcpdump ‘tcp[13] = 6’

过滤明文的HTTP GET请求
tcpdump ‘tcp[32:4] = 0x47455420’

通过横幅文本过滤任意端口的SSH连接
tcpdump ‘tcp[(tcp[12]>>2):4] = 0x5353482D’

过滤TTL小于10的包(通常情况下是存在问题或者在使用traceroute)
tcpdump ‘ip[8] < 10’

过滤恶意的包
tcpdump ‘ip[6] & 128 != 0’

理解报文内容

1
08:41:13.729687 IP 192.168.64.28.22 > 192.168.64.1.41916: Flags [P.], seq 196:568, ack 1, win 309, options [nop,nop,TS val 117964079 ecr 816509256], length 372

08:41:13.729687 本地时间戳
ip 协议是ipv4、若是ipv6会显示为 ip6
192.168.64.28.22 源ip和端口
192.168.64.1.41916 目的ip和端口
Flags [P.] 报文标记段
seq 196:568 代表该数据包包含该数据流的第 196 到 568 字节
ack 1 该数据包是数据发送方,ack 值为 1。在数据接收方,该字段代表数据流上的下一个预期字节数据,例如,该数据流中下一个数据包的 ack 值应该是 568
win 309 表示接收缓冲区中可用的字节数,后跟 TCP 选项如 MSS(最大段大小)或者窗口比例值
length 372 代表数据包有效载荷字节长度

flag描述
S SYN Connection Start
F FIN Connection Finish
P PUSH Data Push
. ACK Acknowledment

平均负载 loadavg

1
2
当系统变慢时、我们想到的很可能是 uptime 或者 top
它每一列的含义是什么 ?

uptime

so. mark
10:13:59 是当前时间
up 303 days, 22:27 是系统允许时间
1 user 是正在登录用户数
load average: 4.61, 4.37, 3.73 这三列呢 ?
依次是 过去1min5min15min的平均负载

什么是平均负载 ???

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
知道了uptime是如何看的、那么平均负载多少算是合适 ?它又是代表什么含义呢 ?

简单来说平均负载就是单位时间内、系统处于可运行状态和不可中断状态的平均进程数、也就是平均活跃进程数,
之前、我以为是cpu的使用率来着的~~~xxxxx


可运行状态的进程:指正在使用CPU或者等待CPU的进程、ps看到的、处于R状态(Runnable/Running)的进程

不可中断进程:正处于内核关键流程中的进程、并且这些进程是不可打断的、eg. 等待硬件设备的io响应、也就是ps命令中的D状态(uninterrutible sleep)的进程
不可中断状态其实是系统对进程包含和硬件设备的一种保护机制

既然平均负载就是平均的活跃进程数、那么最理想的状态就是每个CPU上都刚好运行着一个进程(每个cpu充分利用、又不会过载)

eg. loadavg为2、意味着什么呢 ?
1) 假如系统有2CPU、则cpu刚好都被占用
2) 在4CPU的系统上、意味着50%的CPU空闲
3) 在只有1CPU的系统上、则会有一半的进程竞争不到CPU

最理想的状态是、刚好等于CPU的核心数
那么如何查询系统有几个CPU ??
1) 使用top
2) 从/proc/cpuinfo 获取

cpuinfo.png

知道了cpu的核数和衡量指标、如何分析负载趋势?

1
2
3
4
5
6
7
8
9
1.1min、5min、15min的值基本相同或者相差不大、说明系统负载很平稳
2.1min内的值远小于15min的值、说明最近1min的负载在减小、而过去15min则负载很高
3. 反过来、若最近1imn的值远高于过去15min的负载、说明负载在明显升高、需要持续观察一下

eg. 单核cpu的 loadavg为:1.73 0.60 7.98
说明过去1min有73%的超载、过去15min有698%的超载
总体来看、负载在降低

那么实际生产环境、负载达到多少的时候、我们应该开始关注呢 ? 一般 超过70%就要持续观察下、看看系统负载的平均趋势

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1. 命令
stress 压力测试工具
mpstat 多核CPU性能分析工具、可实时查看每个CPU的平均指标
pidstat 实时查看进程的cpu、内存、io及上下文切换等指标
watch 监控进程

2. 模拟cpu密集型(需要准备3个终端、每个终端执行一个命令)
stress --cpu 1 --timeout 600

watch -d uptime (-d代表高亮显示变化的区域)

mpstat -P ALL 5 (-p ALL代表监控所有CPU 5代表间隔5s输出1次)

3. 模拟IO密集型
stress -i 1 --timeout 600

4. 大量进程的场景
stress -c 8 --timeout 600

附:安装stress
sudo yum -y install epel-release
sudo yum -y install stress

stress.png

uptime.png

mpstat.png

cat /proc/cpuinfo | grep 'physical id' | sort| uniq| wc -l 查看物理CPU的个数
cat /proc/cpuinfo| grep "cpu cores"| uniq 查看每个物理CPU中cores的个数
cat /proc/cpuinfo| grep "processor"| wc -l 查看逻辑CPU的个数

1
2
3
uptime:
输出: 11:39:32 up 13 days, 1:51, 18 users, load average: 6.21, 5.86, 4.91
说明: 当前时间、系统运行时间、正在登陆用户数、最近1min、5min、15min的平均负载 load average

平均负载: 是单位时间内、系统处于可运行状态和不可中断状态的平均进程数, 即: 平均活跃进程数、和CPU的使用率无直接关系

可运行状态的进程: 正在使用CPU或者等待CPU的进程、ps的R(Runnning / Runable) 状态

不可中断状态: 正处于内核关键流程中的进程、且这些流程是不可打断的、eg. 等待硬件设备的IO响应、ps的D状态(Uninterruptible Sleep,即: Disk Sleep)

eg. 当一个进程向磁盘写数据时, 为了保证数据一致性、在得到磁盘回复前、是不能被打断的、若被打断, 容易出现磁盘数据和进程数据不一致

其实: 不可中断状态其实是系统堆进程和硬件设备的一种保护机制

平均负载合理性评估

平均负载为2怎么解读 ?

1
2
3
1. 在只有2个CPU的系统上、意味着CPU刚好可以全部占用
2.4个CPU的系统上、意味着有50%的空闲
3.1个CPU的系统上、意外着会有一半的进程竞争不到CPU

CPU核数查看

1
2
cat /proc/cpuinfo | grep 'model name' | wc -l
lscpu

经验值: cpu 的负载到达70%的时候、就需要关注了

平均负载与CPU使用率

1
2
3
4
5
1. 平均负载: 单位时间内处于可运行状态和不可中断状态的进程数
包含 正在使用CPU的进程 + 等待CPU的进程 + 等待io的进程
2. CPU使用率: 单位时间内CPU繁忙情况的统计、不一定与平均负载对应
eg. cpu密集型、使用大量CPU会导致load升高、 - 变化趋势一致
io密集型、等待io也导致load升高、CPU使用率不一定很高

案例分析

一、CPU密集型进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
终端一:
stress --cpu 1 --timeout 600
--cpu CPU压力测试

终端二:
watch -d uptime
-d: 高亮显示变化区域

终端三:
mpstat -P ALL 5
-P ALL: 表示监控所有CPU
5表示间隔5s输出一组数据

终端四:
pidstat -u 5 1 间隔5s输出一组数据、查找CPU高的线程

stress.png

二、IO密集型进程

查看方式同上

开机加电 -> main 函数执行

分3步完成、实现从启动盘加载操作系统、完成 main 函数加载所需要的准备工作

启动BIOS、完成实模式下的中断向量表和中断服务程序

Q: RAM ?
A: Random Access Memory. 随机存取存储器、eg. 内存条
特点是 在加电状态下、可随意读写、断电消失

Q: 加电瞬间、RAM中无任何程序、谁来完成 操作系统从软盘的加载 ?
A: BIOS

Q: 为什么必须把操作系统从软盘加载到RAM ?
A: CPU的逻辑电路被设计为只能从内存中运行、无法直接从软盘运行

Q: 为什么CPU的逻辑电路被设计为只能从内存运行 ?
A: … 暂未清楚

Q: BIOS本身是如何启动的 ?
A: 固定地址: 0xFFFF0、
CPU逻辑上被设计为、在加电的瞬间、强行将 CS设为 0xF000, IP 设为 0xFFF0
这样、CS:IP 就指向 0xFFFF0(BIOS程序的入口地址、BIOS程序就开始运行)

BIOS.jpg

从启动盘加载操作系统到内存、加载操作系统的工作就是利用 中断服务程序来完成的

开始加载操作系统….

对Linux 0.11而言、是分3步、
1: 有BIOS中断 0x19 将第一扇区bootsect的内容加载到内存
2: 在bootsect的引导下、将后边的4个扇区和240个扇区的内容依次加载到内存中

BIOS程序加载.jpg

Q: 操作系统如何加载 ?
A: BIOS代码执行完毕、硬件完成开机自检、会和BIOS联手、让CPU收到 int 0x19中断、CPU在中断向量表中找到中断程序的位置(0x0E6F2)
即: 启动记载服务程序的入口地址、
so. in 0x19中断程序的作用就是把软盘第一扇区的代码512b加载到内存指定位置(BIOS设计、与操作系统无关) - 内存(0x07C00)处
第一扇区的代码作用就是将软盘中的操作系统程序陆续加载到内存、所以称为引导程序(bootsect)
第一扇区的载入、标记着操作系统代码要开始发挥作用
响应int0x19中断.jpg

操作系统的内存规划

实模式下最大的寻址为1MB

//代码路径:boot/bootsect.s  
    …  
.globl begtext, begdata, begbss, endtext, enddata, endbss  
.text  
begtext:  
.data  
begdata:  
.bss  
begbss:  
.text  

SETUPLEN= 4    ! nr of setup-sectors  
BOOTSEG = 0x07c0    ! original address of boot-sector  
INITSEG = 0x9000    ! we move boot here-out of the way  
SETUPSEG= 0x9020    ! setup starts here  
SYSSEG  = 0x1000    ! system loaded at 0x10000 (65536).  
ENDSEG  = SYSSEG + SYSSIZE  ! where to stop loading  

! ROOT_DEV:0x000 - same type of floppy as boot.  
!  0x301 - first partition on first drive etc  
ROOT_DEV= 0x306 
… 
![实模式下的内存使用规划.jpg](https://upload-images.jianshu.io/upload_images/14027542-2fd3e1e6ef913630.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

SETUPLEN -> SETUPSEG 0x9020 (被加载到的位置)
BOOTSEG(0x07c0)(BIOS加载的位置) -> INITSEG 0x9000(被移动到的新的位置)
SYSSEG 0x1000 内核被加载的位置
ENDSEG 内核末尾位置
ROOT_DEV 0x306 根文件设备号
注意:
CPU的段寄存器(CS)指向 0x07C0 -> 原来 BOOTSEG 所在的位置

为执行32位的main函数做过度工作

tips

  1. 实模式: 兼容80286之后80x86的兼容CPU的模式、特性是一个20位的存储器地址空间(2^20 1MB的存储器空间可被寻址)
    可以直接软件访问BIOS及周边硬件、无硬件支持的分页机制和实时多任务概念
    80286 开始、所有的CPU开机都是 实模式 、之前的CPU只有一种模式、类似 实模式
  1. CPU: 计算机所有的计算操作都由它完成、可以理解为 一个有输入和输出功能的集成电路
  1. CPU寻址: 使用CS、IP两个寄存器
    CS: Code Segment Register 即代码段寄存器、指向CPU当前执行代码在内存中的位置(代码段的起始位置)、
    实模式为绝对地址、16位、保护模式为线性地址 需要结合GDT才能找到段基址
    IP/EIP: Intruction Pointer. 指令寄存器、存在于CPU中、记录将要执行的代码在代码段中的偏移地址
    实模式为绝对地址、指令指针为16位、即IP、保护模式为32位、即EIP、
    CS:IP 寻址规则: 段基址*16 + 偏移地址
  1. 中断向量表建立
    BIOS在最开始的位置(0x00000)用1k内存(0x00000 - 0x003FF)构建中断向量表、接下来256k(0x00400~0x004FF)构建BIOS数据区
    大概57k后的位置(0x0E05B)加载了大概8k左右的与中断向量表相关的中断服务程序
  1. 中断向量表
    记录所有中断向量对应中断程序的位置、实模式中断机制实现的重要部分
  1. 中断服务程序
    通过中断向量表的索引对中断程序进行响应、是一些具有特殊功能的代码
  1. 磁盘磁头
    利用特殊材料的电阻值会随磁场变化的原来来读写盘片数据、将硬盘盘片上的磁信号转化为电信号向外传输

聚合操作

$sum: 计算总和

1
2
3
4
5
db.mycol.aggregate([{$group : {_id:"$f1", num_tutorial:{$sum:"$f2"}}}])
-> select count(1) from table group by f1;

db.mycol.aggregate([{$group : {_id:"$f1", num_tutorial:{$sum:"$f2"}}}])
->select field,sum(f2) from table group by f1;

$avg: 计算平均值

1
2
db.test.aggregate([{$group:{_id:"$field", avg:{$avg:$f}}}])
-> select field,avg(f) from table group by field;

$min: 获取集合中所有文档对应的最小值

1
2
db.test.aggregate([{$group:{_id:"$field",min:{$min:"$f2"}}}])
-> select min(f2) from table group by field;

$max: 获取集合中所有文档对应的最大值

1
参考min

$push: 在结果文档中插入值到一个数组

1
db.t.aggregate([{$group:{_id:"$by_user",url:{$push:"$url"}}}])

$first: 根据资源文档的排序获取第一个文档数据

1
db.t.aggregate([{$group:{_id:"$by_user",first_url:{$first:"$url"}}}])

$last: 根据资源文档的排序获取最后一个文档数据

1
参考first

$project 修改输出文档的结构
$match 用于过滤数据、
$limit 限制返回条数
$skip 跳过指定条数的文档
$unwind 将某个数组类型字段拆分成多条
$group 将集合中的文档分组、可用于统计结果
$sort 将文档排序后输出

eg.

1
2
db.t.aggregate({$project:{_id:0,title:1,tags:1}})
db.t.aggregate({$project:{_id:0,title:1,tags:1}},[{$match:{score:{$gt:70,$lte:90}}},{$group:{_id:null,count:{$sum:1}}}])

mapReduce

1
2
3
4
5
6
map :映射函数 (生成键值对序列,作为 reduce 函数参数)。
reduce 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。
out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
query 一个筛选条件,只有满足条件的文档才会调用map函数。(querylimitsort可以随意组合)
sortlimit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)

概念

1
2
3
4
5
6
database 数据库
collection 数据表
document 数据记录行/文档
filed 数据域
index 索引
primary key 主键、默认 _id

基本命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. show dbs 查看所有数据的列表
db 显示当前数据库对象或者集合
use local 切换到local这个db
2. use niuniu db不存在时会自动创建
3. db.dropDatabase() 删除一个db

4. db.createCollection("niu") # 先创建集合、类似db中的表
show tables/collections 查看所有的表
db.niu.drop() # 删除一个table

5. db.niu.insert({"name":"niu"}) 插入文档
6. db.niu.save() 若不指定id, 类似于insert、若指定、会执行更新
7. db.help() 库的帮助信息
8. db.table.help() 表的帮助文档

远程连接

1
mongo 192.168.1.100:27017/test

查看版本

1
2
3
db.version()
mongo --version
mongod --version

查看各节点的健康状态

1
2
3
4
复制集状态查看  rs.status()
查看oplog状态 rs.printReplicationInfo();
查看复制延迟 rs.printSlaveReplicationInfo();
查看服务状态详情 db.serverStatus();

查看数据库空间大小

1
2
3
4
5
6
7
db.stats() 默认返回字节
"dataSize" : 20, //所有数据的总大小
"storageSize" : 21, //所有数据占的磁盘大小
"fileSize" : 23, //预分配给数据库的文件大小

db.stats(1073741824) // 得到的单位是 G
这里的objects以及avgObjSize还是bytes为单位的,不受参数影响

查看总的记录数

1
2
3
db.dbname.find().count()
db.dbname.find().skip(n).limit(m).count(); 会返回所有的记录数
db.dbname.find().skip(n).limit(m).count(true); 会返回limit的记录数

查看数据库信息

1
db.getName 返回当前所在的db名称

允许副本集上的查询

1
rs.slaveOk()

参考:

1
2
https://www.cnblogs.com/kevingrace/p/8178549.html
https://stackoverflow.com/questions/30088833/strange-mongodb-and-mongoose-error-not-master-and-slaveok-false-error/30089063

查看sql执行计划

1
2
在查询语句后添加 explain 即可
eg. db.collection.find().explain()

返回信息解读

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
queryPlanner (查询计划): 查询优化选择的计划细节和被拒绝的计划
namespace 运行查询的指定命名空间
indexFilterSet Boolean值、表示mongodb在查询中是否使用索引过滤
winningPlan 由查询优化选择的计划文档
stage: 表示查询阶段的字符串
inputStage: 表示子查询的文档
inputStages: 表示子过程的文档数组
executionStats(执行状态): 被选中执行计划和被拒绝执行计划的详细说明
queryPlanner.nReturned-匹配查询条件的文档数
queryPlanner.executionTimeMillis-计划选择和查询执行所需的总时间(毫秒数)
queryPlanner.totalKeysExamined-扫描的索引总数
queryPlanner.totalDocsExamined-扫描的文档总数
queryPlanner.executionStages-显示执行成功细节的查询阶段树
executionStages.works-指定查询执行阶段执行的“工作单元”的数量
executionStages.advanced-返回的中间结果数
executionStages.needTime-未将中间结果推进到其父级的工作周期数
executionStages.needYield-存储层要求查询系统产生的锁的次数
executionStages.isEOF-指定执行阶段是否已到达流结束
queryPlanner.allPlansExecution-包含在计划选择阶段期间捕获的部分执行信息,包括选择计划和拒绝计划
serverInfo,(服务器信息):MongoDB实例的相关信息:
winningPlan-使用的执行计划
shards-包括每个访问片的queryPlannerserverInfo的文档数组