zabbix mysql 数据库调优

zabbix 默认使用的是mysql数据库,当时zabbxi历史数据大的时候,会出现 cpu iowait的报警。
可以使用 mysql的分区表来解决,使用方法如下;

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
##zabbix
innodb_file_per_table=1
sync_binlog=0
query_cache_size=0
query_cache_type=0
innodb_flush_method = O_DIRECT
innodb_io_capacity = 2000
innodb_old_blocks_time = 1000
innodb_buffer_pool_size=10G
innodb_flush_log_at_trx_commit=0
innodb_log_file_size = 512M
innodb_log_buffer_size = 128M
tmpdir = /dev/shm/mysql
##zabbix -end
## zabbix 清空 所有历史数据
use zabbix;
truncate table history;
optimize table history;
truncate table history_log;
optimize table history_log;
truncate table history_str;
optimize table history_str;
truncate table history_text;
optimize table history_text;
truncate table history_uint;
optimize table history_uint;
truncate table trends;
optimize table trends;
truncate table trends_uint;
optimize table trends_uint;

use zabbix

#关闭housekeeper
ALTER TABLE housekeeper ENGINE = BLACKHOLE;

#删除主键
mysql> Alter table history_text drop primary key, add index (id), drop index history_text_2, add index history_text_2 (itemid, id);
Query OK, 0 rows affected (0.49 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> Alter table history_log drop primary key, add index (id), drop index history_log_2, add index history_log_2 (itemid, id);
Query OK, 0 rows affected (2.71 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql>

使用分区表

  • 创建存储过程
    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
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    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_description >= CLOCK;
    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 ;
    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
    a "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 ;
    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 OLD_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'));
    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');
    IF(PARTITION_NAME != OLD_PARTITION_NAME) THEN
    CALL partition_create(SCHEMA_NAME, TABLE_NAME, PARTITION_NAME, LESS_THAN_TIMESTAMP);
    END IF;
    SET @__interval=@__interval+1;
    SET OLD_PARTITION_NAME = PARTITION_NAME;
    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 ;
    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 a 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 ;

##
设置上面后 存储过程调用方法是
CALL partition_maintenance(‘zabbix’, ‘history’, 28, 24, 14);
CALL partition_maintenance(‘zabbix’, ‘history_log’, 28, 24, 14);

优化存储过程调用

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 ;

##使用上面这个存储过程之后
mysql> CALL partition_maintenance_all(‘zabbix’);
+—————-+——————–+
| table | partitions_deleted |
+—————-+——————–+
| zabbix.history | N/A |
+—————-+——————–+
1 row in set (0.01 sec)

….
….
….

+——————–+——————–+
| table | partitions_deleted |
+——————–+——————–+
| zabbix.trends_uint | N/A |
+——————–+——————–+
1 row in set (22.85 sec)

Query OK, 0 rows affected, 1 warning (22.85 sec)

mysql>

创建计划任务 每天定时 生成新的分区表

zabbix tunning 分区表

30 04 * mysql zabbix -e “CALL partition_maintenance_all(‘zabbix’);”

end zabbix

在zabbix server上关闭 Housekeeper

登录zabbix server的web管理
在上面菜单栏 –>管理–>一般 右边选管家

历史记录
开启内部管家 去除勾选
覆盖监控项历史期间 勾选
数据存储时间(天)28

趋势
开启内部管家 去除勾选
覆盖监控项趋势期间 勾选
数据存储时间(天) 730

注释:
mysql> CALL partition_maintenance(‘zabbix’, ‘history’, 28, 24, 14);
表明:历史数据保留28天,24小时,14代表一次创建14个表

参考

howto/mysql partition
如何对zabbix mysql做分区表
zabbix进行数据库备份以及表分区