Oracle查看parameter

别人问你数据库的某个参数是多少时,一般我们都是会直接通过show parameter,或者select value from v$parameter的方式,但是这种查询只是查到的当前会话里的参数值,而如果这个会话对参数进行过修改的情况下,查出来的值与数据库实际的值其实是不一样的。

通过字典可以查到好多带有parameter的系统视图,比如V$PARAMETER,V$SPPARAMETER,V$SYSTEM_PARAMETER,以及V$PARAMETER2和V$SYSTEM_PARAMETER2等等,那这些视图之间到底有什么区别呢?

V$PARAMETER

表示对于当前会话生效或正在起作用的参数值

V$SPPARAMETER

它显示spfile内容的信息。 如果未使用spfile启动实例,则ISSPECIFIED列为FALSE。

V$SYSTEM_PARAMETER

表示实例级别的参数信息,每开启一个新会话的时候,会话使用所有的参数都从这里进行继承。

V$PARAMETER2

V$PARAMETER一样表示对于当前会话生效或正在起作用的参数值,稍有区别的是对于那些在V$PARAMETER里的一个参数值里面有列表的情况,在本视图里就会显示成多行。

阅读更多

Automatic Memory Management简析

从11g开始引入了AMM(Automatic Memory Management)的概念,AMM管理了SGA+PGA的内存分配,它允许将内存在SGA和PGAs之间进行转移,你只需要指定MEMORY_TARGET一个参数即可,剩下的事情全部交给oracle自己来做。

这里首先解释几个名词:

  • System global area (SGA)

    SGA是一组共享内存结构,作为SGA组件,包含了一个oracle实例中的数据和控制信息。所有server和后台进程都共享SGA,SGA的数据包括缓存数据块和共享SQL区。

  • Program global area (PGA)

    PGA是一块非共享的内存区域,单独存放每个oracle进程的数据和控制信息。当进程开始运行时oracle就会自动为这个进程创建pga,每个服务进程和后台进程都有一个PGA,而所有PGA的集合就构成了整个实例的PGA,数据库的初始化参数设置了整个实例的PGA大小。

  • User global area (UGA)

    UGA是每个用户会话的内存

阅读更多

Zabbix Logrt Monitoring Windows Logs

有些应用程序会每天生成一个当天日期命名的日志文件,对应日志文件里面出现的报错信息要进行实时监控,实际的过程中遇到了很多坑,总结记录下来,供以后参考。

监控设备是多台win机器,而每个win机器上有多个路径不一的日志目录,目录都含有中文。每个目录下每天都会生成一个新的日期命名的log文件,而目前所要做的是对每个日志文件中的多个关键字进行监控告警。

有几个需要思考的地方:

  • 多个机器多个目录肯定是需要通过模板和自动发现的方式
  • 涉及到win机器并且包含中文,涉及到字符集转码的问题
  • 多个关键字正则表达式处理
  • 如何最简化部署的工作

配置自动发现

这里选择了一种比较简单的方式,通过自带的system.run进行文件路径的获取,将每台设备的目录手动记录在一个文本文件当中,然后通过system.run的方式去读取到,接着通过preprocessing的js进行处理生成json格式的返回结果,然后将每个目录路径作为变量来交给后面的步骤。这样就不用在每台机器上去部署脚本和新增userparameter,简化操作。

基于system.run key创建发现规则:

阅读更多

Zabbix监控GBK字符集的oracle

监控oracle有很多方式,目前主要使用的是通过第三方软件orabbix,它是通过jdbc连到各个oracle数据库上去执行sql,效率还可以,只是目前有个新需求,有个业务监控返回结果必须含有中文,而orabbix中无法配置字符集 ,导致存放到zabbix中时就会乱码,所以只能换一种方式,采用自带的database monitor来监控。整个配置过程很简单,主要是记录下字符集的转换配置。

Linux安装oracle客户端

下载linux客户端rpm包

1
2
3
4
5
6
[root@localhost oracle linux client]# ll
total 54436
-rw-r--r-- 1 root root 54172532 Jul 23 16:30 oracle-instantclient19.8-basic-19.8.0.0.0-1.x86_64.rpm
-rw-r--r-- 1 root root 612416 Jul 23 16:29 oracle-instantclient19.8-devel-19.8.0.0.0-1.x86_64.rpm
-rw-r--r-- 1 root root 246760 Jul 23 16:29 oracle-instantclient19.8-odbc-19.8.0.0.0-1.x86_64.rpm
-rw-r--r-- 1 root root 702872 Jul 23 16:29 oracle-instantclient19.8-sqlplus-19.8.0.0.0-1.x86_64.rpm

安装rpm包

1
rpm -ivh oracle-instantclient19.8-*.rpm

安装完以后会生成对应的oracle目录/usr/lib/oracle/19.8/client64

新建tns目录

1
2
3
[root@zabbix-wh-proxy client64]# mkdir -p network/admin

touch tnsnames.ora

设置环境变量

1
2
3
4
export ORACLE_HOME=/usr/lib/oracle/19.8/client64
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib64:$LD_LIBRARY_PATH
export TNS_ADMIN=$ORACLE_HOME/network/admin
export PATH=$PATH:$ORACLE_HOME/bin:$HOME/bin

Linux安装odbc

1
yum -y install unixODBC unixODBC-devel

配置文件主要为两个,/etc/odbc.ini/etc/odbcinst.ini,后者配置驱动,前者配置数据库参数

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
cat /etc/odbcinst.ini

[PostgreSQL]
Description=ODBC for PostgreSQL
Driver=/usr/lib/psqlodbcw.so
Setup=/usr/lib/libodbcpsqlS.so
Driver64=/usr/lib64/psqlodbcw.so
Setup64=/usr/lib64/libodbcpsqlS.so
FileUsage=1

[MySQL]
Description=ODBC for MySQL
Driver=/usr/lib/libmyodbc5.so
Setup=/usr/lib/libodbcmyS.so
Driver64=/usr/lib64/libmyodbc5.so
Setup64=/usr/lib64/libodbcmyS.so
FileUsage=1

[MySQL ODBC 8.0 Unicode Driver]
Driver=/usr/lib64/libmyodbc8w.so
UsageCount=1

[MySQL ODBC 8.0 ANSI Driver]
Driver=/usr/lib64/libmyodbc8a.so
UsageCount=1


[FreeTDS]
Driver = /usr/lib64/libtdsodbc.so.0

[OracleDriver]
Description= ODBC for Oracle
Driver= /usr/lib/oracle/19.8/client64/lib/libsqora.so.19.1

添加oracle的驱动到末尾,driver指向lib路径

1
2
3
4
5
6
7
cat /etc/odbc.ini

[test]
Driver = OracleDriver
ServerName = 192.168.2.112:1521/pdb12c
UserID = zabbix
Password = zabbix

测试配置

1
2
3
4
5
6
7
8
9
10
isql test

+---------------------------------------+
| Connected! |
| |
| sql-statement |
| help [tablename] |
| quit |
| |
+---------------------------------------+

这里很简单就配置完了,只需要在web端添加database monitor就可以了,odbc.ini里设置的test就是唯一DSN

字符集问题

zabbix后台存放数据的库是mysql,是utf8字符集,在监控字符集为gbk的oracle时,会碰到一个问题,就是当sql返回结果含有中文时就会乱码,由于我之前所有的oracle监控都是通过orabbix来完成,对于这个问题始终无法解决。而通过odbc监控时,则可以通过修改odbc的配置来实现字符集的转换。

在odbc.ini里添加环境变量参数

1
DMEnvAttr = SQL_ATTR_UNIXODBC_ENVATTR={NLS_LANG=SIMPLIFIED CHINESE.AL32UTF8}

然后重新执行sql,就可以得到正确的中文返回结果。

ssh登录非常慢

通过ssh登陆一台linux主机非常慢,基本上每次都在10s以上

1
2
3
4
5
[root@localhost ~]# time ssh 192.168.146.104

real 0m13.730s
user 0m0.014s
sys 0m0.009s

通过ssh -vvv查看建立链接的过程中详细日志,找出最慢的地方出现在哪里

[root@localhost ~]# ssh -vvv 192.168.146.104
OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Applying options for *
debug2: ssh_connect: needpriv 0
debug1: Connecting to 192.168.146.104 [192.168.146.104] port 22.
debug1: Connection established.             <<== 说明发起的连接已经建立成功  
debug1: permanently_set_uid: 0/0
debug1: identity file /root/.ssh/identity type -1
debug1: identity file /root/.ssh/identity-cert type -1
...
debug1: Local version string SSH-2.0-OpenSSH_5.3
debug2: fd 3 setting O_NONBLOCK
debug1: SSH2_MSG_KEXINIT sent
debug3: Wrote 960 bytes for a total of 981
debug1: SSH2_MSG_KEXINIT received
...
debug2: mac_setup: found hmac-sha1
debug1: kex: server->client aes128-ctr hmac-sha1 none
debug2: mac_setup: found hmac-sha1
debug1: kex: client->server aes128-ctr hmac-sha1 none
debug1: SSH2_MSG_KEX_DH_GEX_REQUEST(1024<2048<8192) sent
debug1: expecting SSH2_MSG_KEX_DH_GEX_GROUP
debug3: Wrote 24 bytes for a total of 1005
debug2: dh_gen_key: priv key bits set: 170/320
debug2: bits set: 1026/2048
debug1: SSH2_MSG_KEX_DH_GEX_INIT sent
debug1: expecting SSH2_MSG_KEX_DH_GEX_REPLY
debug3: Wrote 272 bytes for a total of 1277
debug3: check_host_in_hostfile: host 192.168.146.104 filename /root/.ssh/known_hosts
debug3: check_host_in_hostfile: host 192.168.146.104 filename /root/.ssh/known_hosts
debug3: check_host_in_hostfile: match line 6
debug1: Host '192.168.146.104' is known and matches the RSA host key.
debug1: Found key in /root/.ssh/known_hosts:6
debug2: bits set: 1032/2048
debug1: ssh_rsa_verify: signature correct
debug2: kex_derive_keys
debug2: set_newkeys: mode 1
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug3: Wrote 16 bytes for a total of 1293
debug2: set_newkeys: mode 0
debug1: SSH2_MSG_NEWKEYS received
debug1: SSH2_MSG_SERVICE_REQUEST sent
debug3: Wrote 52 bytes for a total of 1345
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug2: key: /root/.ssh/identity ((nil))
debug2: key: /root/.ssh/id_rsa ((nil))
debug2: key: /root/.ssh/id_dsa ((nil))
debug2: key: /root/.ssh/id_ecdsa ((nil))
debug3: Wrote 68 bytes for a total of 1413    <<==这一步完后大概等了10s        


debug1: Authentications that can continue: publickey,password
debug3: start over, passed a different list publickey,password
debug3: preferred gssapi-keyex,gssapi-with-mic,publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
...

所以通过上面的ssh详细日志可以得出

  • tcp连接是成功建立的
  • ssh的握手连接是持续进行send和received的
  • SSH2_MSG_SERVICE_REQUEST在发送一个68bytes的包时等待了10s

所以在建立链接成功以后,等待了服务端返回某种响应,看起来不像是网络问题,而更可能是服务端的sshd服务的问题。

查看服务端的进程

1
2
3
4
5
[root@testyum .ssh]# ps -ef --sort start_time | grep [s]shd
root 45251 1 0 08:48 ? 00:00:00 sshd: root@pts/1
root 46417 1 0 08:57 ? 00:00:00 sshd: root@pts/2
root 46596 1 0 08:58 ? 00:00:00 /usr/sbin/sshd -D
root 48669 46596 0 09:27 ? 00:00:00 sshd: root@pts/3

这里服务端sshd进程是正常运行的,监听端口是22,而当客户端发起ssh连接的过程中,新增了两个进程accepted和net

1
2
3
4
5
6
[root@testyum .ssh]# ps -ef --sort start_time | grep [s]shd
root 45251 1 0 08:48 ? 00:00:00 sshd: root@pts/1
root 46417 1 0 08:57 ? 00:00:00 sshd: root@pts/2
root 46596 1 0 08:58 ? 00:00:00 /usr/sbin/sshd -D
root 48994 46596 0 09:30 ? 00:00:00 sshd: [accepted]
sshd 48995 48994 0 09:30 ? 00:00:00 sshd: [net]

到这里可以判断服务端也确实建立了连接,置于为什么需要10s,可以用strace命令来进行进一步的分析。

  • -r: 打印每个系统调用开始的时间戳
  • -T: 打印每个系统调用花费的时间
  • -f: 跟踪和记录子进程
  • -p: 关联pid并跟踪
1
strace -o /tmp/1.trc -r -T -f -p 46596

将得到的结果根据消耗时间进行排序,找出消耗时间最长的系统调用

1
2
3
4
5
6
7
8
9
10
11
[root@testyum ~]# sort -nrk 2 /tmp/1.trc | head
46596 5.137961 accept(3, {sa_family=AF_INET, sin_port=htons(51889), sin_addr=inet_addr("192.168.146.43")}, [16]) = 5 <0.000030>
50382 5.005162 poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}]) <0.000017>
50382 5.004525 close(4) = 0 <0.000010>
50382 0.040266 rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0 <0.000016>
50383 0.039898 read(3, "\v\3239\0033~\260\220\223\4\230x\356\307d\204\302@\314\237\314\272o\261\7\371\3477{\352\3251"..., 8192) = 52 <0.000020>
50418 0.011046 recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{"l\4\1\0015\0\0\0\6\0\0\0\211\0\0\0", 16}], msg_controllen=0, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 16 <0.000020>
50383 0.005337 read(3, "\252g\25\232\306h\225\373\245\250\300\207\275\205\360Pm1\245\275r\204Q\324\224\5\331Nb\3556\311"..., 8192) = 644 <0.000006>
50383 0.002997 read(3, "\0\0\0\f\n\25\0\0\0\0\0\0\0\0\0\0", 8192) = 16 <0.000016>
50383 0.002341 read(3, "\0\0\1\f\5 \0\0\1\1\0\272\212\352\200\242\213`\207\27dF\2648mo\213$\275t\264\353"..., 8192) = 272 <0.000016>
50383 0.002071 write(3, "\0\0\1\24\10\37\0\0\1\1\0\3600\305\23\325\306iO\260\2259\354\371\330)\6\10\251b\200\355"..., 280) = 280 <0.000053>

poll([{fd=4, events=POLLOUT}], 1, 0) = 1这个系统调用等待了5s,这里可以知道它是在读取文件4fd=4

转过头去从原始trace里去找到descriptor #4是指向的什么,这里通过关键字socket,connect,= 4 <等去搜索

1
2
50382      0.000081 socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, IPPROTO_IP) = 4 <0.000017>
50382 0.000038 connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.0.1")}, 16) = 0 <0.000032>

这个socket()已经创建了一个internet socket(AF_INET),根据SOCK_DGRAM判断是一个UDP socket,下面的conect ()根据建立的socket去访问192.168.0.1的53端口,因为udp的无状态性质,所以并不会去检查目标主机是否存在和是否响应,所以connect()调用很快就完成了。

我去ping 192.168.0.1这个地址发现无法ping通,通过搜索知道53端口是DNS服务

1
2
3
[root@testyum ~]# grep " 53/" /etc/services
domain 53/tcp # name-domain server
domain 53/udp

当发起ssh连接时,sshd服务会去访问DNS服务器,通过nslookup这个ip

1
2
3
[root@testyum ~]# nslookup 192.168.0.1
;; connection timed out; trying next origin
;; connection timed out; no servers could be reached

超过10s后,直接报错了,检查服务端的dns配置

1
2
3
[root@testyum ~]# cat /etc/resolv.conf 
search localdomain
nameserver 192.168.0.1

这个ip设置的有问题,修改成正确的以后重试

1
2
3
4
[root@localhost ~]# time ssh 192.168.146.104
real 0m0.397s
user 0m0.017s
sys 0m0.014s

另外也可以直接修改sshd_config的配置,注释掉UseDNS yes

Zabbix监控ogg延迟情况

最近ogg出现了一点问题,没有及时发现,于是考虑将ogg的监控也纳入zabbix当中来。对于这一类监控,考虑的地方不单单在于如何监控,而是善用zabbix的模板、自动发现等功能来实现,这样会方便配置以及后期的可扩展性。

首先对ogg的运行情况查看通常是通过info all命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MANAGER     RUNNING                                           
EXTRACT RUNNING EPRISK 00:00:03 00:00:08
EXTRACT RUNNING ERDMEDW 00:00:04 00:00:06
EXTRACT RUNNING ESHDW 00:00:00 00:00:01
EXTRACT RUNNING PCIF 00:00:00 00:00:08
EXTRACT RUNNING PEIF 00:00:00 00:00:08
EXTRACT RUNNING PHG 00:00:00 00:00:08
EXTRACT RUNNING PQH 00:00:00 00:00:06
EXTRACT RUNNING PRISKMGR 00:00:00 00:00:08
EXTRACT RUNNING PSHDW 00:00:00 00:00:06
REPLICAT RUNNING RCIF 00:00:10 00:00:06
REPLICAT RUNNING REDMDB1 00:00:00 00:00:01
REPLICAT RUNNING REDQDB1 00:00:00 00:00:07
REPLICAT RUNNING REIF 00:00:00 00:00:00
REPLICAT RUNNING REPWIND 00:00:00 00:00:06
REPLICAT RUNNING REPWIND2 00:00:00 00:00:00
REPLICAT RUNNING REPWIND3 00:00:00 00:00:05
REPLICAT RUNNING REPWIND4 00:00:00 00:00:04
REPLICAT RUNNING RZNMGR 00:00:00 00:00:00
REPLICAT RUNNING RZNMGRC 00:00:00 00:00:01

这里总共有19个抽取、传输进程,根据实际需求只需要做到分钟级别监控即可,所以筛选出4列,分别为运行状态、进程名、lag时间、time列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
echo "info all"|ggsci|egrep 'EXTRACT|REPLICAT'|awk -F"[ ]+|:" '{print $2,$3,$4*60+$5,$7*60+$8}'

RUNNING EPRISK 0 0
RUNNING ERDMEDW 0 0
RUNNING ESHDW 0 0
RUNNING PCIF 0 0
RUNNING PEIF 0 0
RUNNING PHG 0 0
RUNNING PQH 0 0
RUNNING PRISKMGR 0 0
RUNNING PSHDW 0 0
RUNNING RCIF 0 0
RUNNING REDMDB1 0 0
RUNNING REDQDB1 0 0
RUNNING REIF 0 0
RUNNING REPWIND 0 0
RUNNING REPWIND2 0 0
RUNNING REPWIND3 0 0
RUNNING REPWIND4 0 0
RUNNING RZNMGR 0 0
RUNNING RZNMGRC 0 0

通过shell将获取到的数据写入中间文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
all()
{
source /home/oracle/.bash_profile;echo "info all"|ggsci|egrep 'EXTRACT|REPLICAT'|awk -F"[ ]+|:" '{print $2,$3,$4*60+$5,$7*60+$8}'
}

name()
{
source /home/oracle/.bash_profile;echo "info all"|ggsci|egrep 'EXTRACT|REPLICAT'|awk '{print $3}'
}

a=$1
case "$a" in
all)
all
;;
name)
name
;;
*)
echo -e "Usage: ./`basename $0` [all|name]"
esac

为了满足zabbix自动发现的要求,返回值必须要是json格式,所以可以对上面的数据进行格式化

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
#!/usr/bin/env python  
#coding:utf-8
import os, json, sys
f = open("/etc/zabbix/scripts/ogg.cfg","r")
lines = f.readlines()
ogg_list=[]
ogg_dict={"data":None}
for line in lines:
if line.startswith('#')or not line.split():
continue
line=line.strip('\n')
status=line.split()[0]
name=line.split()[1]
lag=line.split()[2]
time=line.split()[3]
pdict={}
pdict["{#NAME}"]=name
pdict["{#STATUS}"]=status
pdict["{#LAG}"]=lag
pdict["{#TIME}"]=time
ogg_list.append(pdict)
f.close()
ogg_dict["data"]=ogg_list
var = sys.argv[1]
var2 = sys.argv[2]
jsonStr = json.dumps(ogg_dict, sort_keys=True, indent=4)
#print jsonStr

def get_lag(name):
l = [x.get("{#LAG}") for t, x in enumerate(ogg_list) if x.get("{#NAME}") == name]
print l[0]

def get_time(name):
l = [x.get("{#TIME}") for t, x in enumerate(ogg_list) if x.get("{#NAME}") == name]
print l[0]

def get_status(name):
l = [x.get("{#STATUS}") for t, x in enumerate(ogg_list) if x.get("{#NAME}") == name]
print l[0]

def get_json():
jsonStr = json.dumps(ogg_dict, sort_keys=True, indent=4)
print jsonStr

def ogg(var, var2):
if var == 'json':
get_json()
else:
if var2 == 'lag':
get_lag(var)
elif var2 == 'status':
get_status(var)
else:
get_time(var)
ogg(var, var2)

最后得到的数据

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
{
"data": [
{
"{#LAG}": "0",
"{#NAME}": "EPRISK",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "ERDMEDW",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "ESHDW",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "PCIF",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "PEIF",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "PHG",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "PQH",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "PRISKMGR",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "PSHDW",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "RCIF",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "REDMDB1",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "REDQDB1",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "REIF",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "REPWIND",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "REPWIND2",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "REPWIND3",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "REPWIND4",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "RZNMGR",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
},
{
"{#LAG}": "0",
"{#NAME}": "RZNMGRC",
"{#STATUS}": "RUNNING",
"{#TIME}": "0"
}
]
}

数据都已经获取到,到这里脚本就准备完毕,需要添加两个自定义key

1
2
UserParameter=ogg.discovery,/etc/zabbix/scripts/ogg_delay.py json test
UserParameter=ogg_delay[*],/etc/zabbix/scripts/ogg_delay.py $1 $2

在zabbix上新建模板,配置宏参数以及新增监控项和触发器等,最后只需要将模板添加到对应监控的主机即可



ORA-00445: background process W003 did not start after 120 seconds

一个11g的数据库出现报错,根据字面判断可能是某个slave进程启动失败。这种ORA-00455错误通常表示在操作系统层面为了响应某种请求而去生成一个新的进程时因为某种原因导致失败,最有可能的原因一般是由于操作系统资源不足或者配置错误,所以这个错误的解决途径通常是从操作系统层面入手,但是也有部分情况是与oracle有关的。

这里显示的120s超时可以通过设置event事件来进行动态修改

1
2
3
4
$ sqlplus / as sysdba
alter system set events '10281 trace name context forever, level xxx';
-- where xxxxxx is the number of seconds to timeout at.
eg: alter system set events '10281 trace name context forever, level 300';

检查相关日志

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
Memory (Avail / Total) = 221.27M / 32181.19M
Swap (Avail / Total) = 47847.41M / 49151.99M
F S UID PID PPID C PRI NI ADDR SZ WCHAN STIME TTY TIME CMD
0 S oracle 6755 1 0 80 0 - 3206692 semtim 10:21 ? 00:00:00 ora_w003_xxxxx

*** 2020-07-02 10:23:53.079
Stack:
[Thread debugging using libthread_db enabled]
0x0000003595ceb197 in semop () from /lib64/libc.so.6
#0 0x0000003595ceb197 in semop () from /lib64/libc.so.6
#1 0x0000000009825453 in sskgpwwait ()
#2 0x00000000098241b0 in skgpwwait ()
#3 0x0000000009411825 in kslges ()
#4 0x00000000094111be in kslgetl ()
#5 0x000000000940e60a in ksfglt ()
#6 0x00000000098097e9 in kghalo ()
#7 0x0000000000b3f9f2 in ksp_param_handle_alloc ()
#8 0x0000000000b3eec0 in kspcrec ()
#9 0x0000000000ba8e2c in ksucre ()
#10 0x0000000000bdd2c4 in ksvrdp ()
#11 0x00000000025a57ea in opirip ()
#12 0x0000000001850509 in opidrv ()
#13 0x0000000001e27b37 in sou2o ()
#14 0x0000000000a298d6 in opimai_real ()
#15 0x0000000001e2de55 in ssthrdmain ()
#16 0x0000000000a297cd in main ()
A debugging session is active.
Inferior 1 [process 6755] will be detached.
Quit anyway? (y or n) [answered Y; input not from terminal]

-------------------------------------------------------------------------------
Process diagnostic dump actual duration=8.680000 sec
(max dump time=30.000000 sec)

*** 2020-07-02 10:23:53.079
Killing process (ospid 6755): (reason=KSOREQ_WAIT_CANCELLED error=0)
... and the process is still alive after kill!

通过这里可以看到是由于内存不足的情况导致创建子进程W003失败,于是经过了120s超时的上限以后,调度过程将子任务终止并报错。

这里的主进程是SMCO,引用官方说明

SMCO协调以下空间管理任务。 它执行主动的空间分配和空间回收。 它动态产生从属进程(Wnnn)来执行任务。

  • 表空间级空间(扩展)的预分配

    这里的预分配是指数据文件扩展,当通常通过插入/加载到段的空间请求(扩展分配)操作在表空间中找不到连续空间时发生数据文件扩展,会话将通过下一个extent来扩展数据文件,并将继续进行空间请求或范围分配。

    为了使SMCO自动扩展数据文件,应将数据文件的AUTOEXTEND设置为ON。 SMCO决定根据历史记录扩展表空间,扩展在表空间中的所有数据文件中平均分配,这些数据文件尚未达到其最大大小,并且在一小时的SMCO唤醒中仍限制为整个表空间大小的10%。

    (完整的表空间大小=任何给定时间实例的数据文件大小总和。)

除了上述任务外,SMCO流程还负责执行以下任务

  • 添加extent后,为本地管理的表空间更新SEG$中的块和extent计数(来自未发布的Bug 12940620)
  • 加密lob段预扩展
  • 加密lob段内存分配器空间的预分配.
  • 加密lob段空间回收
  • 临时段空间回收

这种好处是会话无需等待被动的空间分配,因为这是主动完成的,所以可以提高性能

启用和关闭SMCO,不是核心进程,可以通过这种方式进行重启

1
2
3
4
ALTER SYSTEM SET "_ENABLE_SPACE_PREALLOCATION" = 0;

# 默认值
ALTER SYSTEM SET "_ENABLE_SPACE_PREALLOCATION" = 3;

其他value

1
2
3
4
* 0 to turn off the tbs pre-extension feature.
* 1 To enable tablespace extension.
* 2 To enable segment growth.
* 4 To enable chunk allocation.

不同版本之间的EXPDP/IMPDP

故障现象

将一个18c版本的dmp导入到11.2.0.4当中时出现报错

1
2
ORA-39000: bad dump file specification
ORA-39142: incompatible version number 5.1 in dump file "/u01/schema_dump.dmp"

这是由于高版本导出的dmp文件在低版本数据库当中无法识别。

解决办法:

在导出语句里加上version=11.2即可

1
2
Example
expdp scott/tiger@orcl directory=EXPIMP schemas=scott Version=11.2 dumpfile=Exp_Scott.dmp logfile=Exp_Scott.log

跨版本DATAPUMP

兼容性

依据数据泵的兼容性有一些准则:

  1. 数据泵dmp文件的兼容级别是由源数据库的兼容级别决定的
  2. 使用与源数据库同版本的expdp客户端
  3. 使用与目标数据库同版本的impdp客户端
  4. 当目标库的compatibility级别比源库低时,使用expdp的VERSION参数
  5. 即使两边兼容级别不一致,也可以基于database link的数据传输
  6. impdp总是可以读取从更低版本库中导出的dmp文件
  7. imp只能读取exp的dmp,impdp只能读取expdp的dmp

Version

数据泵可以用来不同版本数据库之间的数据迁移,通常通过expdp当中的version参数来完成,带上这个参数后就会生成与指定版本兼容的数据dmp集。

1
VERSION=[COMPATIBLE | LATEST | version_string]

version总共有三种值

  • COMPATIBLE - 这是默认值,元数据的版本与数据库初始化参数COMPATIBLE相对应,这个参数必须被设成9.2或更新
  • LATEST - 元数据的版本和生成的SQL DDL对于数据库的发行版本,不用管兼容参数如何
  • version_string - 指定的数据库版本

如果数据库里的对象或者某些特性与指定的version参数有冲突,则冲突的对象不会被导出。比如12.2中表名的长度限制要比11.2中大,所以对于version=11.2的情况,12.2中对象名称过长的不会被导出。

数据泵的dmp兼容性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
源数据库兼容参数		目标库兼容参数

COMPATIBLE 10.1.0.x.y 10.2.0.x.y 11.1.0.x.y 11.2.0.x.y 12.1.0.x.y 12.2.0.x.y 18.x.y.z 19.x.y.z
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------
10.1.0.x.y - - - - - - - -
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------
10.2.0.x.y VERSION=10.1 - - - - - - -
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------
11.1.0.x.y VERSION=10.1 VERSION=10.2 - - - - - -
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------
11.2.0.x.y VERSION=10.1 VERSION=10.2 VERSION=11.1 - - - - -
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------
12.1.0.x.y VERSION=10.1 VERSION=10.2 VERSION=11.1 VERSION=11.2 - - - -
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------
12.2.0.x.y VERSION=10.1 VERSION=10.2 VERSION=11.1 VERSION=11.2 VERSION=12.1 - - -
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------
18.x.y.z VERSION=10.1 VERSION=10.2 VERSION=11.1 VERSION=11.2 VERSION=12.1 VERSION=12.2 - -
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------
19.x.y.z VERSION=10.1 VERSION=10.2 VERSION=11.1 VERSION=11.2 VERSION=12.1 VERSION=12.2 VERSION=18.x -
---------- ------------- ------------- ------------- ------------- ------------- ------------- ------------ ------------

数据泵client/server兼容性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Data Pump client compatibility.
===============================

连接数据库版本
数据泵客户端版本 10gR1 10gR2 11gR1 11gR2 12cR1 12cR2 18c 19c
version 10.1.0.x 10.2.0.x 11.1.0.x 11.2.0.x 12.1.0.x 12.2.0.x 18.x.y.z 19.x.y.z
----------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
10.1.0.x supported supported supported supported no no no no
10.2.0.x no supported supported supported supported no no no
11.1.0.x no supported supported supported supported no no no
11.2.0.x no no no supported supported supported supported supported
12.1.0.x no no no no supported supported supported supported
12.2.0.x no no no no no supported supported supported
18.x.y.z no no no no no supported supported supported
19.x.y.z no no no no no no no supported

数据泵dmp文件版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Data Pump file version.
=======================

Version Written by Can be imported into Target:
Data Pump database with 10gR1 10gR2 11gR1 11gR2 12cR1 12cR2 18c 19c
Dumpfile Set compatibility 10.1.0.x 10.2.0.x 11.1.0.x 11.2.0.x 12.1.0.x 12.2.0.x 18.x.y.z 19.x.y.z
------------ --------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
0.1 10.1.x supported supported supported supported supported supported supported supported
1.1 10.2.x no supported supported supported supported supported supported supported
2.1 11.1.x no no supported supported supported supported supported supported
3.1 11.2.x no no no supported supported supported supported supported
4.1 12.1.x no no no no supported supported supported supported
5.1 12.2.x no no no no no supported supported supported
5.1 18.x.y no no no no no no supported supported
5.1 19.x.y no no no no no no no supported

数据泵特性

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
63
64
65
Data Pump New Features.
=======================

Version New Feature Remark:
--------- --------------------------------- ----------------------------------------------
10.1.0.1 Data Pump Technology Introduction of high-speed data movement.
---------
10.2.0.1 COMPRESSION Compress metadata in export dumpfile.
10.2.0.1 ENCRYPTION_PASSWORD Allows encrypted column data in dumpfile.
10.2.0.1 SAMPLE Specify a percentage of data to be unloaded.
10.2.0.1 TRANSFORM Change DDL for OID's and space allocation.
10.2.0.1 VERSION Create file compatible with earlier release.
---------
11.1.0.1 COMPRESSION Both data and metadata can be compressed.
11.1.0.1 DATA_OPTIONS (expdp) Specify handling of XMLType columns.
11.1.0.1 DATA_OPTIONS (impdp) Specify handling of constraint violations.
11.1.0.1 ENCRYPTION Both data and metadata can be encrypted.
11.1.0.1 ENCRYPTION_ALGORITHM Specify a specific encryption algorithm.
11.1.0.1 ENCRYPTION_MODE Specify the type of security to be used.
11.1.0.1 PARTITION_OPTIONS Specify how to handle partitioned tables.
11.1.0.1 REMAP_DATA Change column values based on a function.
11.1.0.1 REMAP_TABLE Rename tables during an import operation.
11.1.0.1 REUSE_DUMPFILES Option to overwrite existing dumpfiles.
11.1.0.1 TRANSPORTABLE Transfer table data by copying datafiles.
---------
11.2.0.1 Legacy mode: accept exp and imp parameters.
11.2.0.1 ABORT_STEP Stop job after initializing to query master.
11.2.0.1 ACCESS_METHOD Specify a particular method to (un)load data.
11.2.0.1 CLUSTER Control whether workers use all RAC instances.
11.2.0.1 DATA_OPTIONS (impdp) Specify to disable the APPEND hint.
11.2.0.1 KEEP_MASTER Specify whether to retain master table.
11.2.0.1 MASTER_ONLY Only import the master table.
11.2.0.1 METRICS Report additional information in logfile.
11.2.0.1 SERVICE_NAME Use with CLUSTER to specify a service name.
11.2.0.1 SOURCE_EDITION (expdp) Specify the edition from which to exp objects.
11.2.0.1 TABLES Now specify tables in multiple schemas.
11.2.0.1 TABLES Now specify % for multiple tables and part.
11.2.0.1 TARGET_EDITION (impdp) Specify the edition from which to exp objects.
11.2.0.2 Default first segment now 8M for part. table.
11.2.0.2 TRANSFORM Specify how to handle SEGMENT CREATION.
---------
12.1.0.1 Support for CDB and Pluggable db's (PDB).
12.1.0.1 COMPRESSION_ALGORITHM Specify the algorithm when compressing data.
12.1.0.1 ENCRYPTION_PWD_PROMPT Specify whether Data to prompt for password.
12.1.0.1 FULL Can now be used together with TRANSPORTABLE.
12.1.0.1 LOGTIME Provide timestamp for messages in the logfile.
12.1.0.1 TRANSFORM Specify to disable logging during import.
12.1.0.1 TRANSFORM Specify to change the LOB storing on import.
12.1.0.1 TRANSFORM Specify to change table compression type.
12.1.0.1 VIEWS_AS_TABLES Export views as tables.
---------
12.2.0.1
12.2.0.1 REMAP_DIRECTORY Let you remap directories when you move databases between platforms.
12.2.0.1 TRUST_EXISTING_TABLE_PARTITIONS Enable data from multiple partitions to be loaded in parallel into a pre-existing table. This is a flag on Data Pump Import
DATA_OPTIONS
12.2.0.1 VALIDATE_TABLE_DATA Verify the format number and date data types in table data columns. This is a flag on Data Pump Import DATA_OPTIONS
12.2.0.1 ENABLE_NETWORK_COMPRESSION Tell Data Pump to compress data before sending it over the network. This is a flag on DATA_OPTIONS parameter.
12.2.0.1 GROUP_PARTITION_TABLE_DATA Enable data for all partitions to be loaded at once and in parallel. This is a flag on the Data Pump Export DATA_OPTIONS parameter.
12.2.0.1 VERIFY_STREAM_FORMAT Validate the format of a data stream before it is written to the Data Pump dump file. This is a flag on the Data Pump Export
DATA_OPTIONS parameter.
---------
18.1.X.X CONTINUE_LOAD_ON_FORMAT_ERROR This is a new value for the DATA_OPTIONS parameter for impdp. When it is set, Data Pump jumps ahead and continue loading from the
next granule when an inconsistency is found.
---------
19c Use Document 2457955.1 19c DataPump New Features are described in this document.

介绍

确定数据库的兼容版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
col value for a20
col description for a49
select * from database_compatible_level;

VALUE DESCRIPTION
-------------------- -------------------------------------------------
18.0.0 Database will be completely compatible with this
software version

-- 或者:
sys@> show parameter compatible

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
compatible string 18.0.0
noncdb_compatible boolean FALSE

检查数据库和数据泵的版本

1
2
3
4
5
6
7
8
9
10
11
12
sys@> select banner_full from v$version;

BANNER_FULL
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Oracle Database 18c Enterprise Edition Release 18.0.0.0.0 - Production
Version 18.5.0.0.0

-- client版本
[oracle@]$ expdp HELP=Y

Export: Release 18.0.0.0.0
Version 18.5.0.0.0

获取dmp文件的兼容版本

每个生成的dmp文件都会有一个头部信息(通常是4kb大小)包含了这个dmp文件的详细信息,从10.2版本开始,可以通过DBMS_DATAPUMP.GET_DUMPFILE_INFO过程来获取这些详细信息,但是这个过程生成的信息太过繁杂,为了便于阅读,通过一个自定义的存储过程SHOW_DUMPFILE_INFO.sql来调用然后输出

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
SET serveroutput on SIZE 1000000
exec show_dumpfile_info(p_dir=> 'xx', p_file=> 'xx.dmp');

----------------------------------------------------------------------------
Purpose..: Obtain details about export dumpfile. Version: 18-DEC-2013
Required.: RDBMS version: 10.2.0.1.0 or higher
. Export dumpfile version: 7.3.4.0.0 or higher
. Export Data Pump dumpfile version: 10.1.0.1.0 or higher
Usage....: execute show_dumfile_info('DIRECTORY', 'DUMPFILE');
Example..: exec show_dumfile_info('MY_DIR', 'expdp_s.dmp')
----------------------------------------------------------------------------
Filename.: xx.dmp
Directory: xx
Disk Path: /backup/xxxx
Filetype.: 1 (Export Data Pump dumpfile)
----------------------------------------------------------------------------
...Database Job Version..........: 18.00.00.00.00
...Internal Dump File Version....: 5.1
...Creation Date.................: Sun ---
...File Number (in dump file set): 1
...Master Present in dump file...: 1 (Yes)
...Master in how many dump files.: 1
...Master Piece Number in file...: 1
...Operating System of source db.: x86_64/Linux 2.4.xx
...Instance Name of source db....: ------
...Characterset ID of source db..: 852 (ZHS16GBK)
...Language Name of characterset.: ZHS16GBK
...Job Name......................: "SYSTEM"."SYS_EXPORT_SCHEMA_01"
...GUID (unique job identifier)..: A28977F60899EE30E053D326680AD984
...Block size dump file (bytes)..: 4096
...Metadata Compressed...........: 1 (Yes)
...Data Compressed...............: 1 (Yes)
...Compression Algorithm.........: 3 (Basic)
...Metadata Encrypted............: 0 (No)
...Table Data Encrypted..........: 0 (No)
...Column Data Encrypted.........: 0 (No)
...Encryption Mode...............: 2 (None)
...Internal Flag Values..........: 518
...Max Items Code (Info Items)...: 24
----------------------------------------------------------------------------

PL/SQL procedure successfully completed.

根据上面得到的结果可以看出这个生成的文件兼容参数为18.0(18.00.00.00.00),内部文件版本为5.1,这个文件只能导入到兼容版本高于或等于18.0的数据库。

Zabbix Preprocessing

介绍

监控结果预处理可以用来定义和根据相应的规则来对采集到的数据进行转换,通过预处理管理进程来管理这些预处理,而具体的步骤则是由多个work进程来完成。

Preprocessing页可以对接收到的数据定义转换规则,可以允许一个或多个规则同时存在,之后才将转换后的数据写入到数据库。如果有多个规则存在,则会按照定义的顺序来依次进行转换。Preprocessing可以运行在zabbix server或zabbix proxy上。

为什么需要Preprocessing,因为很多情况下我们获取到的数据是各种各样的格式,并不适合计算、合并或优化存储空间。

Preprocessing支持类型

从3.4版本开始支持preprocessing,原生只支持几种情况:

  • 自定义倍数
  • 数值型转换(布尔、hex等)
  • 简单改变(计算每秒变化等)

如果需要更复杂的转换,则需要自己通过其他工具(python、php)等来处理数据,而后来preprocessing也支持了更多的方法:

  • 正则表达式
  • 简单裁剪
  • XPath和JSONPath支持

随着时间推移,在4.2版本中逐步增加了更多的扩展

  • JavaScript和Prometheus支持
  • Validation-用于验证数据是否符合某些规则
  • Throttling-用于丢弃无用数据
  • 自定义错误处理-为了更好的可读性

zabbix4.4中同样进行了非常多的改进,带来了完全不一样的体验:

  • 通过XPath来处理XML数据
  • 通过JSONPath来抽取和分割JSON数据
  • 扩展自定义错误处理
  • CSV转换到JSON
  • 将WMI,JMX和ODBC数据收集到JSON数组,然后通过JSONPath进行处理

监控值处理

上面这个图示简单的描述了监控值处理过程中对应的步骤,包括进程、对象和动作等,并没有展示条件直接变化、错误处理。

  • 开始于从源获取原始数据,这时数据只包含ID,时间戳和值
  • 不管使用何种类型的数据,对于主动和被动模式或trapper等方式都是一样。它只改变数据的格式,原始数据被验证,并从配置缓存里获取item配置
  • 基于socket的IPC机制用来将数据从数据收集者传输给预处理管理进程。这时数据收集者不用等待预处理的响应,而是继续持续的去收集数据
  • 数据预处理时包含预处理步骤的执行和依赖监控项的运行
  • 预处理管理的本地缓存数据被刷新到历史缓存
  • 这时数据流会暂停直到下一次历史缓存的刷新
  • 同步进程开始于数据规范化存储在数据库中。数据规范化包括转换需要的格式,包括截取文本格式的数据为数据库表字段中可以支持的格式,在做完数据规范化后数据才会被发送到database中
  • 收集的数据也被处理-检查触发器,如果数据不可用则会修改item的状态

Examples

主要介绍几种我在实际工作中常用的几种,其他的可以参考官方文档详细说明。

Text preprocessing

使用正则表达式获取数据,然后从文本格式传输最后存储为number格式。

这里使用正则表达式的时候包含两个参数:

  • pattern-具体的表达式
  • output-输出格式模板,\N(N=1…9)转义会被替换成第N个匹配组,比如\0则会被整个匹配文本所替代

比如一段日志文本里记录的温度Temperature: 50C,作为监控需要提取其中的数字50作为最后的存储,于是这里则可以使用正则表达

如果想将字符格式的值转换成数字型,可以通过trim来清除文本中的字符。

JSON数据抽取

某个程序将进程所占用的资源情况全部记录在了日志当中,格式为

1
{"proc":"TEST","cpu":3.6,"mem":5559784,"vir_mem":14303532,"net_in":2,"net_out":0}

zabbix通过监控得到了这个json串,但是对于数据的存放、数据的二次分析以及触发器的配置都是很不方便的,所以需要将json串的里每个值都拆开作为单独的多个监控,这里就可以用到JSONPath和dependent item。

新建一个测试监控项,插入一条数据

新建dependent item

在preprocessing tab页,增加JSONPath处理

生成的结果

这样对于每次监控的JSON结果获取到以后,则会自动生成多个拆分出来的单项监控,便于后期处理。

jsonpath的详细用法参考:https://www.zabbix.com/documentation/current/manual/appendix/preprocessing/jsonpath_functionality

自定义倍数

custom multipliers很好理解,就是简单的乘以一个指定的倍数即可,常见的场景是在转换bytes到bits,或者得到的内存单位是KB,转换成B存储到zabbix里等。

throttling

这个功能主要是为了提高性能和节省数据库存储空间,特别是在一些高频的场景当中,对于那些监控得到的数据基本都是重复的情况下非常有用。

举个例子,当监控zabbix agent心跳网络时,我们需要比较高的频次,然后绝大部分情况下获取到的数据都是一模一样,通过throttling,则可以将重复的数据都过滤掉,只存储监控值产生变化的情况。

throttling也分为两种模式:

  • 丢弃未变化——如果监控值没有变化则丢弃掉,这个数据也不会保存到数据库,zabbix server也不知道获取到过这个值。触发器也不会生成相应的告警和恢复,因为触发器只会基于存储在数据库里的值起作用。trends也是基于存储在数据库里的值建立,每个item只能有一个throttling选项
  • 丢弃未变化(心跳模式)——在指定时间范围内丢弃没有变化的监控值,达到心跳时间时会仍然存储数据到数据库

下面用图示来表达,监控项不断的进行采集,获取到的数据分布为0和1

未使用throttling的情况:所有的数据都被存储到数据库里

使用throttling,重复的数据则被丢掉

增加心跳选项,可以避免nodata类型的触发器漏报

总结

通常有四大便利是你要考虑使用预处理的原因

  • 自动化
    1. 通过主监控项进行LLD
    2. 通过预处理发现指标
  • 提升
    1. 丢弃重复数据
    2. 4.4版本上直接在proxy层做预处理工作
  • 自定义化
    1. 数据验证
    2. 自定义高级预处理规则
  • 转换
    1. 转换数据
    2. 通过预处理进行数据合并和计算

JSON in Oracle

JSON(JavaScript Object Notation,JavaScript对象表示法,读作/ˈdʒeɪsən/)是一种由道格拉斯·克罗克福特构想和设计、轻量级的数据交换语言,该语言以易于让人阅读的文字为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是JavaScript的一个子集,但JSON是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯。

JSON 数据格式与语言无关。即便它源自JavaScript,但当前很多编程语言都支持 JSON 格式数据的生成和解析。

相对于传统的关系型数据库,一些基于文档存储的NoSQL非关系型数据库选择JSON作为其数据存储格式,比较出名的产品有:MongoDB。

从12.1版本开始,oracle开始支持JSON格式数据,包括事务、索引和视图等。

创建包含JSON数据的表

oracle并没有增加新的字段类型来专门存放JSON数据,而是将其存放在传统的VARCHAR2或CLOB字段中。另外IS JSON约束表明这个字段包含有效的JSON格式数据。

创建一张表,包含判断数据是否为JSON格式的约束

1
2
3
4
5
6
7
drop table json_test purge;

create table json_test (
id raw(16) not null,
document clob
CONSTRAINT ensure_json CHECK (document IS JSON)
);

这里的IS JSON约束默认情况下都代表弱约束,如果要做严格JSON语法约束,则换成document IS JSON(STRICT)

插入测试的JSON数据

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
INSERT INTO json_test
VALUES (SYS_GUID(),
'{ "PONumber" : 1600,
"Reference" : "ABULL-20140421",
"Requestor" : "Alexis Bull",
"User" : "ABULL",
"CostCenter" : "A50",
"ShippingInstructions" : { "name" : "Alexis Bull",
"Address": { "street" : "200 Sporting Green",
"city" : "South San Francisco",
"state" : "CA",
"zipCode" : 99236,
"country" : "United States of America" },
"Phone" : [ { "type" : "Office", "number" : "909-555-7307" },
{ "type" : "Mobile", "number" : "415-555-1234" } ] },
"Special Instructions" : null,
"AllowPartialShipment" : false,
"LineItems" : [ { "ItemNumber" : 1,
"Part" : { "Description" : "One Magic Christmas",
"UnitPrice" : 19.95,
"UPCCode" : 13131092899 },
"Quantity" : 9.0 },
{ "ItemNumber" : 2,
"Part" : { "Description" : "Lethal Weapon",
"UnitPrice" : 19.95,
"UPCCode" : 85391628927 },
"Quantity" : 5.0 } ] }');

如果更新数据为无效的JSON数据时,则会出现错误,这里假设去掉一个大括号

1
2
3
4
5
6
7
update json_test
set document = '{"User" : "xb"'
where document.User = 'ABULL';

*
ERROR at line 3:
ORA-01747: invalid user.table.column, table.column, or column specification

查询JSON数据

虽然例子中JSON数据是存放在CLOB字段当中,但是查询的返回值总是VARCAHR2类型

查询PONumber

1
2
3
4
5
6
SQL> col PONumber for a50
SQL> select a.id,a.document.PONumber from json_test a;

ID PONUMBER
-------------------------------- --------------------------------------------------
9AAB58F9BDD3DA0AE0536892A8C06893 1600

查询装运须知的Phone

1
2
3
4
5
6
SQL> col ShippingInstructions for a100
SQL> select a.document.ShippingInstructions.Phone from json_test a;

SHIPPINGINSTRUCTIONS
----------------------------------------------------------------------------------------------------
[{"type":"Office","number":"909-555-7307"},{"type":"Mobile","number":"415-555-1234"}]

查询phone的类型

1
2
3
4
5
SQL> select a.document.ShippingInstructions.Phone.type from json_test a;

SHIPPINGINSTRUCTIONS
----------------------------------------------------------------------------------------------------
["Office","Mobile"]

JSON字符集

文本化的JSON数据通常采用UNICODE字符集,从这个方面来说JSON数据比XML数据使用起来更简单,这是JSON数据内部交换格式的重要组成部分。对于Oracle中管理的JSON数据而言,所有需要进行字符集转换的操作都是自动进行。

Oracle内部使用UTF-8来操作JSON数据,包括解析、查询等。如果这些数据被传入或者被输出,必须要以非UTF-8的字符集时,则会自动做字符集之间的转换。

字符集自动转换会影响性能。并且在某些情况下会有信息丢失,将传入的数据转换成UTF-8这段过程是没有丢失的,但是转换成输出时如果在输出的字符集中无法显示字符的话则会丢失数据。

当数据库的字符集为Oracle建议的AL32UTF8时,如果文本化的JSON数据以UNICODE格式储存在数据库当中时则不需要做字符集的转换。

不以文本格式存储的JSON数据,永远不需要字符集转换:因为没有字符用来转换,这表示使用BLOB类型存放的JSON数据不需要经历字符集转换。

如果JSON数据是以非Unicode格式存放,使用非Unicode的VARCHAR2或CLOB字段,则需要考虑下面的方法去避免字符集的转换

  • 对于Oracle SQL函数返回的值使用NUMBER而不是VARCHAR2,例如json_value
  • 在输入时转义特定的Unicode字符

使用JSON的SQL函数和条件

Oracle提供了SQL函数和条件去创建、查询、操作JSON数据,其中一些将JSON路径表达式中作为参数,后面跟着RETURNING子句、WRAPPER子句或者error子句。

  • Oracle SQL Conditions IS JSON and IS NOT JSON 测试数据是否是符合语法的JSON数据,通常作为约束检查
  • Oracle SQL Condition JSON_EXISTS 测试JSON数据里是否存在特定的值
  • Oracle SQL Function JSON_VALUE 从JSON数据中选择一个标量值作为SQL值
  • Oracle SQL Function JSON_QUERY 从JSON数据中选择一个或多个值作为SQL串来表示JSON值,通常用来获取JSON文本的片段,特别是JSON对象或数组
  • Oracle SQL Function JSON_TABLE 将JSON数据映射成关系型的虚拟表,也可以认为是内联的关系型视图

JSON函数和条件中的子句

子句包含returningwrapper、和错误处理。每个子句都可以使用在一个或多个SQL函数和条件json_value, json_query, json_table, is json, is not json, and json_exists.

json_value和json_query允许可选的RETURNING子句,表示通过函数返回数据的类型。

对于json_value来说,可以允许VARCAHR2或者NUMBER类型返回值,而对于json_query,只能使用VARCHAR2。

json_value和json_query允许可选的WRAPPER子句,表示通过json_query返回值或json_table相关列的格式

  • WITH WRAPPER-用一个字符值表示包含所有JSON数据的数组,数组元素顺序不要求。
  • WITHOUT WRAPPER&#8211; 用一个字符表示单个JSON对象或者数组,如果路径表达式匹配上一个标量值或者多个值则会报错。
  • WITH CONDITIONAL WRAPPER-对于0个值、单个标量值或多个值与WITH WRAPPER一样,对于单个的JSON对象或数组值,则与WITHOUT WRAPPER一样。

默认是WITHOUT WRAPPER

JSON_QUERY Wrapper子句

JSON Values WITH WRAPPER WITHOUT WRAPPER WITH CONDITIONAL WRAPPER
{"id": 38327} (single object) [{"id": 38327}] {"id": 38327} {"id": 38327}
[42, "a", true] (single array) [[42, "a", true]] [42, "a", true] [42, "a", true]
42 [42] Error (scalar) [42]
42, "a", true [42, "a", true] Error (multiple values) [42, "a", true]
none [] Error (no values) []

IS JSON and IS NOT JSON

之前的例子中是将IS JSON写在了约束中,但同时也可以写在查询条件里

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
drop table json_test purge;

create table json_test
(
id raw(16) not null,
data clob,
constraint pk_json primary key(id));

insert into json_test values(sys_guid(), '{"name": "xb"}');
insert into json_test values(sys_guid(), 'dba');
commit;

# JSON_VALUE 使用NULL ON ERROR,对于非JSON数据返回NULL
col name for a10
select id,json_value(a.data, '$.name') as name from json_test a;

ID NAME
-------------------------------- ----------
9AFA0E321B112246E0536892A8C0BFD5 xb
9AFA0E321B122246E0536892A8C0BFD5

# 只返回是否包含JSON数据的行
select id,json_value(a.data, '$.name') as name
from json_test a
where data is json;

ID NAME
-------------------------------- ----------
9AFA0E321B112246E0536892A8C0BFD5 xb

select id,json_value(a.data, '$.name') as name
from json_test a
where data is not json;

ID NAME
-------------------------------- ----------
9AFA0E321B122246E0536892A8C0BFD5

JSON_EXISTS

当通过点记法去查询JSON时,不可能分辨出缺失的元素和已有的元素之间的区别,除了一个NULL值以外。以前面第一个例子为例,用户ABULL有一个为NULL的 “Special Instructions”元素,并且没有ADDRESS元素,当通过点记法去查询json_test表时都是返回NULL,而无法判断ADDRESS是不存在的,语句并不会报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
col user for a10
col PONumber for a20
col Sins for a20
col address for a20
SELECT a.document."User",
a.document.PONumber,
a.document."Special Instructions" AS Sins,
a.document.address AS address
FROM json_test a
WHERE a.document."Special Instructions" IS NULL
AND a.document.address IS NULL;

User PONUMBER SINS ADDRESS
---------- -------------------- -------------------- --------------------
ABULL 1600

JSON_EXISTS可以分辨出空值和缺失对象的区别

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
# 查询"Special Instructions"元素存在,但是值为null

SELECT a.document."User",
a.document.PONumber,
a.document."Special Instructions" AS Sins
FROM json_test a
WHERE a.document."Special Instructions" IS NULL
and json_exists(a.document, '$."Special Instructions"' FALSE on error);

User PONUMBER SINS
---------- -------------------- --------------------
ABULL 1600

# 查询address元素不存在的数据

SELECT a.document."User",
a.document.PONumber,
a.document."Special Instructions" AS Sins
FROM json_test a
WHERE a.document."Special Instructions" IS NULL
and not json_exists(a.document, '$.address' FALSE on error)

User PONUMBER SINS
---------- -------------------- --------------------
ABULL 1600

默认错误控制为FALSE ON ERROR,另外包含TRUE ON ERROR和ERROR ON ERROR,后者顾名思义表示不会获取任何通过JSON_EXISTS函数生成的错误。

JSON_VALUE

JSON_VALUE函数返回基于JSON路径查询JSON文档的元素

1
2
3
4
5
6
7
8
col user for a30
col costcenter for a30

select json_value(a.document, '$."User"') as "user",json_value(a.document, '$.CostCenter') as costcenter from json_test a;

user COSTCENTER
------------------------------ ------------------------------
ABULL A50

json_value只会返回标量的值,并不会返回嵌套值或数组。默认的错误处理为NULL或ERROR,所以尝试返回非标量值时则返回NULL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
col ShippingInstructions for a100
select json_value(a.document, '$.ShippingInstructions') as ShippingInstructions from json_test a;

SHIPPINGINSTRUCTIONS
----------------------------------------------------------------------------------------------------


# 自定义错误控制
SQL> select json_value(a.document, '$.ShippingInstructions' ERROR ON ERROR) as ShippingInstructions from json_test a;
select json_value(a.document, '$.ShippingInstructions' ERROR ON ERROR) as ShippingInstructions from json_test a
*
ERROR at line 1:
ORA-40456: JSON_VALUE evaluated to non-scalar value


SQL> select a.document.ShippingInstructions from json_test a;

SHIPPINGINSTRUCTIONS
----------------------------------------------------------------------------------------------------
{"name":"Alexis Bull","Address":{"street":"200 Sporting Green","city":"South San Francisco","state":
"CA","zipCode":99236,"country":"United States of America"},"Phone":[{"type":"Office","number":"909-5
55-7307"},{"type":"Mobile","number":"415-555-1234"}]}

JSON支持布尔值,而SQL通常不支持。JSON_VALUE函数转换布尔值为字符串true、false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
insert into json_test 
values(sys_guid(), '{"active": false}');
insert into json_test
values(sys_guid(), '{"active": true}');
commit;

col active for a20
select id,json_value(a.document,'$.active') as active,json_value(a.document,'$.active' returning number) as activenum from json_test a;

ID ACTIVE ACTIVENUM
-------------------------------- -------------------- ----------
9B225A54605370F8E0536892A8C0616F false 0
9B225A54605470F8E0536892A8C0616F true 1
9AFA0E321B132246E0536892A8C0BFD5

正如前面所讲,返回值类型可以通过RETURNING来指定,如果没有指定返回或者只指定了VARCHAR2,则默认返回VARCHAR2(4000)

1
2
3
4
5
6
7
8
9
10
col REFERENCE for a20
col REQUESTOR for a50
select json_value(a.document,'$.Reference' returning varchar2) as Reference,
json_value(a.document,'$.Requestor' returning varchar2(50)) as Requestor from json_test a;

REFERENCE REQUESTOR
-------------------- --------------------------------------------------


ABULL-20140421 Alexis Bull

JSON_QUERY

JSON_QUERY用来返回一个JSON片段代表一个或多个值

1
2
3
4
5
6
7
8
col phone for a200
select json_query(a.document.ShippingInstructions,'$.Phone' WITH WRAPPER) as phone from json_test a;

PHONE
-------------------------------------------------------------------------------------------------


[[{"type":"Office","number":"909-555-7307"},{"type":"Mobile","number":"415-555-1234"}]]

这里的WITH WRAPPER表示用中括号将片段括起来。

JSON_TABLE

json_table函数合并了JSON_VALUE, JSON_EXISTS和JSON_QUERY的功能,相对来说json_table语法更加复杂些,但是也更加有效率些。

json_table能让json数据看起来更像关系型的数据,这对于基于JSON数据创建view来说更有帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
create or replace view v_json as
select jt.phone,jt.refrence,jt.name,jt.city,jt.quantity
from json_test,json_table(document,'$'
COLUMNS (phone varchar2(50 CHAR) PATH '$.PONumber',
refrence varchar2(50 CHAR) PATH '$.Reference',
name varchar2(50 CHAR) PATH '$.ShippingInstructions.name',
city varchar2(50 CHAR) PATH '$.ShippingInstructions.Address.city',
quantity varchar2(50 CHAR) PATH '$.LineItems.Quantity'
)) jt
where id='9AFA0E321B132246E0536892A8C0BFD5';

视图已创建。

col phone for a20
col refrence for a20
col name for a20
col city for a20
col quantity for a20

select * from v_json;

PHONE REFRENCE NAME CITY QUANTITY
-------------------- -------------------- -------------------- -------------------- --------------------
1600 ABULL-20140421 Alexis Bull South San Francisco

COLUMNS子句定义了每列数据的定义和展示。也可以在字段中显示JSON格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
col phone for a20
col refrence for a20
col name for a20
col city for a20
col items for a50

select jt.*
from json_test,json_table(document,'$'
COLUMNS (phone varchar2(50 CHAR) PATH '$.PONumber',
refrence varchar2(50 CHAR) PATH '$.Reference',
name varchar2(50 CHAR) PATH '$.ShippingInstructions.name',
city varchar2(50 CHAR) PATH '$.ShippingInstructions.Address.city',
items varchar2(2000 CHAR) format json with wrapper PATH '$.LineItems'
)) jt
where id='9AFA0E321B132246E0536892A8C0BFD5';

PHONE REFRENCE NAME CITY ITEMS
-------------------- -------------------- -------------------- -------------------- --------------------------------------------------
1600 ABULL-20140421 Alexis Bull South San Francisco [[{"ItemNumber":1,"Part":{"Description":"One Magic
Christmas","UnitPrice":19.95,"UPCCode":1313109289
9},"Quantity":9.0},{"ItemNumber":2,"Part":{"Descri
ption":"Lethal Weapon","UnitPrice":19.95,"UPCCode"
:85391628927},"Quantity":5.0}]]