在说数据库分表之前,先随便聊聊Zabbix的一些参数的优化吧,其实我也不是很懂,只是机器上了1500+,楞着头皮去调一下参数

首先是几个Poller的调整:

### Option: StartPollers (处理监控项的东西)

### Option: StartIPMIPollers (母鸡干什么用的,暂时没用,设为0)

### Option: StartPollersUnreachable (获取数据遇到不可到达时,交给这些进程处理)

### Option: StartHTTPPollers (监控HTTP,WEB那些东西用的,没用,设为0)

### Option: StartJavaPollers  (监控JAVA专用进程,为毛就只有JAVA)

### Option: StartProxyPollers (处理代理的进程,暂时没用,设为0)

### Option: StartDiscoverers   (处理自动发现的,没用,设为0)

### Option: StartPingers  (如果用了ICMP PING那个模板的,这个值要调大一些)

把几个没用到的先关掉:StartHTTPPollers StartJavaPollers  StartProxyPollers,设为0就可以了,然后可以观察ZabbixServer的图形来调整StartPollers和StartPollersUnreachable的数量:

wKiom1Q0vJyw73UPAAGDcswK5dg629.jpg要注意的是,例如StartPollers,虽然配置文件写是可用范围是0-1000,但这个1000是所有的进程的值,也就是说上面那些进程的总数不能超过1000,设的时候要注意一下这点。


关于Cache的调整:

### Option: CacheSize

### Option: CacheUpdateFrequency

### Option: HistoryCacheSize

### Option: TrendCacheSize

### Option: HistoryTextCacheSize

### Option: ValueCacheSize

关于内存的参数有这么多,都是要根据机器数量和item数量的增加而增加,这些内存的值不能大于系统内核的kernel.shmall这个值,否则申请不了内存程序启动不了


如果日志出现连续一大片地出现这些信息:

Zabbix agent item "vfs.fs.size[/boot,free]" on host "192.168.1.100" failed: first network error, wait for 15 seconds

 resuming Zabbix agent checks on host "192.168.1.100": connection restored

说明你的poller不够,需要再加大poller,如果加大poller还是这样则需要减少监控的item,或者用proxy来做分布式监控了


机器数量多的时候,mysql里面的history表就会越来越大,虽然zabbix本身有删除功能(就是那个housekeeper的功能),但这东西太影响性能,所以网上的做法都是关闭这个东西,用mysql的表分区功能来实现清理历史数据还可以提升mysql的性能


一、先修改两张表的结构:

1
Alter  table  history_text  drop  primary  key add  index  (id),  drop  index  history_text_2,  add  index  history_text_2 (itemid, id)
1
Alter  table  history_log  drop  primary  key add  index  (id),  drop  index  history_log_2,  add  index  history_log_2 (itemid, id);

二、导入存储过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DELIMITER $$ CREATE  PROCEDURE  `partition_create`(SCHEMANAME  VARCHAR (64), TABLENAME  VARCHAR (64), PARTITIONNAME  VARCHAR (64), CLOCK  INT ) BEGIN
         /*
            SCHEMANAME = The DB  schema  in  which  to  make changes
            TABLENAME = The  table  with  partitions  to  potentially  delete
            PARTITIONNAME = The  name  of  the partition  to  create
         */
         /*
            Verify that the partition does  not  already exist
         */          DECLARE  RETROWS  INT ;         SELECT  COUNT (1)  INTO  RETROWS         FROM  information_schema.partitions         WHERE  table_schema = SCHEMANAME  AND  TABLE_NAME = TABLENAME  AND  partition_name = PARTITIONNAME;
          IF RETROWS = 0  THEN
                 /*
                    1. Print a message indicating that a partition was created.
                    2.  Create  the SQL  to  create  the partition.
                    3.  Execute  the SQL  from  #2.
                 */
                 SELECT  CONCAT(  "partition_create(" , SCHEMANAME,  "," , TABLENAME,  "," , PARTITIONNAME,  "," , CLOCK,  ")"  AS  msg;                 SET  @SQL = CONCAT(  'ALTER TABLE ' , SCHEMANAME,  '.' , TABLENAME,  ' ADD PARTITION (PARTITION ' , PARTITIONNAME,  ' VALUES LESS THAN (' , CLOCK,  '));'  );                 PREPARE  STMT  FROM  @SQL;                 EXECUTE  STMT;
                 DEALLOCATE  PREPARE  STMT;         END  IF; END $$
DELIMITER ;
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
DELIMITER $$ CREATE  PROCEDURE  `partition_drop`(SCHEMANAME  VARCHAR (64), TABLENAME  VARCHAR (64), DELETE_BELOW_PARTITION_DATE  BIGINT ) BEGIN
         /*
            SCHEMANAME = The DB  schema  in  which  to  make changes
            TABLENAME = The  table  with  partitions  to  potentially  delete
            DELETE_BELOW_PARTITION_DATE =  Delete  any  partitions  with  names that are dates older than this one (yyyy-mm-dd)
         */
         DECLARE  done  INT  DEFAULT  FALSE ;         DECLARE  drop_part_name  VARCHAR (16);
          /*
            Get a list  of  all  the partitions that are older than the  date
            in  DELETE_BELOW_PARTITION_DATE.   All  partitions are prefixed  with
            "p" , so use  SUBSTRING  TO  get rid  of  that  character .
         */
         DECLARE  myCursor  CURSOR  FOR
                 SELECT  partition_name                 FROM  information_schema.partitions                 WHERE  table_schema = SCHEMANAME  AND  TABLE_NAME = TABLENAME  AND  CAST ( SUBSTRING (partition_name  FROM  2)  AS  UNSIGNED) < DELETE_BELOW_PARTITION_DATE;         DECLARE  CONTINUE  HANDLER  FOR  NOT  FOUND  SET  done =  TRUE ;
          /*
            Create  the basics  for  when  we need  to  drop  the partition.  Also,  create
            @drop_partitions  to  hold a comma-delimited list  of  all  partitions that
            should be deleted.
         */
         SET  @alter_header = CONCAT( "ALTER TABLE " , SCHEMANAME,  "." , TABLENAME,  " DROP PARTITION " );         SET  @drop_partitions =  "" ;
          /*
            Start looping through  all  the partitions that are too old.
         */
         OPEN  myCursor;
         read_loop: LOOP
                 FETCH  myCursor  INTO  drop_part_name;                IF done  THEN
                         LEAVE read_loop;                 END  IF;                 SET  @drop_partitions = IF(@drop_partitions =  "" , drop_part_name, CONCAT(@drop_partitions,  "," , drop_part_name));         END  LOOP;        IF @drop_partitions !=  ""  THEN
                 /*
                    1. Build the SQL  to  drop  all  the necessary partitions.
                    2. Run the SQL  to  drop  the partitions.
                    3. Print  out  the  table  partitions that were deleted.
                 */
                 SET  @full_sql = CONCAT(@alter_header, @drop_partitions,  ";" );                 PREPARE  STMT  FROM  @full_sql;                 EXECUTE  STMT;
                 DEALLOCATE  PREPARE  STMT;
                  SELECT  CONCAT(SCHEMANAME,  "." , TABLENAME)  AS  ` table `, @drop_partitions  AS  `partitions_deleted`;         ELSE
                 /*
                    No  partitions are being deleted, so print  out  "N/A"  ( Not  applicable)  to  indicate
                    that  no  changes were made.
                 */
                 SELECT  CONCAT(SCHEMANAME,  "." , TABLENAME)  AS  ` table `,  "N/A"  AS  `partitions_deleted`;         END  IF; END $$
DELIMITER ;
1
2
3
4
5
6
7
8
9
10
DELIMITER $$ CREATE  PROCEDURE  `partition_maintenance`(SCHEMA_NAME  VARCHAR (32), TABLE_NAME  VARCHAR (32), KEEP_DATA_DAYS  INT , HOURLY_INTERVAL  INT , CREATE_NEXT_INTERVALS  INT ) BEGIN
         DECLARE  OLDER_THAN_PARTITION_DATE  VARCHAR (16);         DECLARE  PARTITION_NAME  VARCHAR (16);         DECLARE  LESS_THAN_TIMESTAMP  INT ;         DECLARE  CUR_TIME  INT ;
          CALL partition_verify(SCHEMA_NAME, TABLE_NAME, HOURLY_INTERVAL);         SET  CUR_TIME = UNIX_TIMESTAMP(DATE_FORMAT(NOW(),  '%Y-%m-%d 00:00:00' ));        IF  DATE (NOW()) =  '2014-04-01'  THEN
                 SET  CUR_TIME = UNIX_TIMESTAMP(DATE_FORMAT(DATE_ADD(NOW(), INTERVAL 1  DAY ),  '%Y-%m-%d 00:00:00' ));         END  IF;         SET  @__interval = 1;
         create_loop: LOOP                IF @__interval > CREATE_NEXT_INTERVALS  THEN
                         LEAVE create_loop;                 END  IF;
                  SET  LESS_THAN_TIMESTAMP = CUR_TIME + (HOURLY_INTERVAL * @__interval * 3600);                 SET  PARTITION_NAME = FROM_UNIXTIME(CUR_TIME + HOURLY_INTERVAL * (@__interval - 1) * 3600,  'p%Y%m%d%H00' );                CALL partition_create(SCHEMA_NAME, TABLE_NAME, PARTITION_NAME, LESS_THAN_TIMESTAMP);                 SET  @__interval=@__interval+1;         END  LOOP;
          SET  OLDER_THAN_PARTITION_DATE=DATE_FORMAT(DATE_SUB(NOW(), INTERVAL KEEP_DATA_DAYS  DAY ),  '%Y%m%d0000' );        CALL partition_drop(SCHEMA_NAME, TABLE_NAME, OLDER_THAN_PARTITION_DATE);
  END $$
DELIMITER ;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
DELIMITER $$ CREATE  PROCEDURE  `partition_verify`(SCHEMANAME  VARCHAR (64), TABLENAME  VARCHAR (64), HOURLYINTERVAL  INT (11)) BEGIN
         DECLARE  PARTITION_NAME  VARCHAR (16);         DECLARE  RETROWS  INT (11);         DECLARE  FUTURE_TIMESTAMP  TIMESTAMP ;
          /*
          Check  if  any  partitions exist  for  the given SCHEMANAME.TABLENAME.
          */
         SELECT  COUNT (1)  INTO  RETROWS         FROM  information_schema.partitions         WHERE  table_schema = SCHEMANAME  AND  TABLE_NAME = TABLENAME  AND  partition_name  IS  NULL ;
          /*
          * If partitions do  not  exist, go ahead  and  partition the  table
          */
         IF RETROWS = 1  THEN
                 /*
                  * Take the  current  date  at  00:00:00  and  add  HOURLYINTERVAL  to  it.  This  is  the  timestamp  below which we will store  values .
                  * We  begin  partitioning based  on  the beginning  of  day .  This  is  because we don 't want to generate a random partition
                  * that won' t necessarily fall  in  line  with  the desired partition naming (ie: if the  hour  interval  is  24 hours, we could
                  end  up creating a partition now named  "p201403270600"  when  all  other partitions will be  like  "p201403280000" ).
                  */
                 SET  FUTURE_TIMESTAMP = TIMESTAMPADD( HOUR , HOURLYINTERVAL, CONCAT(CURDATE(),  " " '00:00:00' ));                 SET  PARTITION_NAME = DATE_FORMAT(CURDATE(),  'p%Y%m%d%H00' );
                  -- Create the partitioning query
                 SET  @__PARTITION_SQL = CONCAT( "ALTER TABLE " , SCHEMANAME,  "." , TABLENAME,  " PARTITION BY RANGE(`clock`)" );                 SET  @__PARTITION_SQL = CONCAT(@__PARTITION_SQL,  "(PARTITION " , PARTITION_NAME,  " VALUES LESS THAN (" , UNIX_TIMESTAMP(FUTURE_TIMESTAMP),  "));" );
                  -- Run the partitioning query
                 PREPARE  STMT  FROM  @__PARTITION_SQL;                 EXECUTE  STMT;
                 DEALLOCATE  PREPARE  STMT;         END  IF; END $$
DELIMITER ;

可以将这四个存储过程写成一个SQL文件直接导入:

1
/usr/local/mysql/bin/mysql   -uroot -p '123456'  zabbix < partition_call.sql

三、使用存储过程:

1
mysql> CALL partition_maintenance( '<zabbix_db_name>' '<table_name>' , <days_to_keep_data>, <hourly_interval>, <num_future_intervals_to_create>)

例(不要急着直接执行,后面统一运行):

1
mysql> CALL partition_maintenance( 'zabbix' 'history' , 7, 24, 7);

zabbix_db_name:库名

table_name:表名

days_to_keep_data:保存多少天的数据

hourly_interval:每隔多久生成一个分区

num_future_intervals_to_create:本次一共生成多少个分区

这个例子就是history表最多保存7天的数据,每隔24小时生成一个分区,这次一共生成7个分区

下面这个存储过程就是统一调用:

1
2
3
4
5
6
7
8
9
10
11
12
DELIMITER $$
CREATE  PROCEDURE  `partition_maintenance_all`(SCHEMA_NAME  VARCHAR (32))
BEGIN
                 CALL partition_maintenance(SCHEMA_NAME,  'history' , 28, 24, 14);
                 CALL partition_maintenance(SCHEMA_NAME,  'history_log' , 28, 24, 14);
                 CALL partition_maintenance(SCHEMA_NAME,  'history_str' , 28, 24, 14);
                 CALL partition_maintenance(SCHEMA_NAME,  'history_text' , 28, 24, 14);
                 CALL partition_maintenance(SCHEMA_NAME,  'history_uint' , 28, 24, 14);
                 CALL partition_maintenance(SCHEMA_NAME,  'trends' , 730, 24, 14);
                 CALL partition_maintenance(SCHEMA_NAME,  'trends_uint' , 730, 24, 14);
END $$
DELIMITER ;

保存成文件,再次导入存储过程:

1
/usr/local/mysql/bin/mysql   -uroot -p '123456'  zabbix < partition_all.sql

以后只需要调用这个存储过程就可以了,每天执行一次:

1
mysql -uzabbix -pzabbix zabbix -e  "CALL partition_maintenance_all('zabbix');"

四、最后关掉Zabbix的HouseKeeper功能:

wKiom1Q03xez_w4_AATfqHMVm04179.jpg

最后附上两个存储过程的文件~



参考文章:

https://www.zabbix.org/wiki/Docs/howto/mysql_partition