Mysql优化之Zabbix分区优化

吾爱主题 阅读:126 2024-04-05 13:59:05 评论:0

使用zabbix最大的瓶颈在于数据库,维护好zabbix的数据存储,告警,就能很好地应用zabbix去构建监控系统。目前zabbix的数据主要存储在history和trends的2个表中,随着时间的推移,这两个表变得非常大,性能会非常差,影响监控的使用。对MySQL进行调优,能够极大的提升Zabbix的性能,本文采用对MySQL进行分区的方法进行调优。

原理

对zabbix中的history和trends等表进行分区,按日期进行分区,每天一个,共保留90天分区。

操作详细步骤

操作影响: 可以在线操作,MySQL的读写变慢,Zabbix性能变慢,影响时间根据数据的小而变化,一般在2个小时左右。

第一步

登录zabbix server的数据库,统一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 cat > /etc/my.cnf<<EOF [mysqld] datadir=/data/mysql socket=/var/lib/mysql/mysql.sock default-storage-engine = innodb collation-server = utf8_general_ci init-connect = 'SET NAMES utf8' character-set-server = utf8 symbolic-links=0 max_connections=4096 innodb_buffer_pool_size=12G max_allowed_packet = 32M join_buffer_size=2M sort_buffer_size=2M query_cache_size = 64M  query_cache_limit = 4M  thread_concurrency = 8 table_open_cache=1024 innodb_flush_log_at_trx_commit = 0   long_query_time = 1 log-slow-queries =/data/mysql/mysql-slow.log   [mysqld_safe] log-error=/var/log/mariadb/mariadb.log pid-file=/var/run/mariadb/mariadb.pid   #[mysql] #socket=/data/mysql/mysql.sock # # include all files from the config directory # !includedir /etc/my.cnf.d EOF

注意:一定要修改innodb_buffer_pool_size=物理内存的1/3

第二步

先确认zabbix的版本,本操作zabbix的版本一定要大于3.2.0。小于3.2的版本不能安装此操作,线上默认是zabbix-3.2.6。

a、 导入存储过程

?
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 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 #cat partition.sql DELIMITER $$ CREATE PROCEDURE `partition_create`(SCHEMANAMEvarchar(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 ANDpartition_description >= CLOCK;        IF RETROWS = 0 THEN          /*            1. Print a messageindicating that a partition was created.            2. Create the SQL to createthe partition.            3. Execute the SQL from #2.          */          SELECT CONCAT( "partition_create(", SCHEMANAME, ",",TABLENAME, ",", PARTITIONNAME, ",", CLOCK, ")" )AS msg;          SET @sql = CONCAT( 'ALTER TABLE ', SCHEMANAME, '.', TABLENAME, ' ADDPARTITION (PARTITION ', PARTITIONNAME, ' VALUES LESS THAN (', CLOCK, '));' );          PREPARE STMT FROM @sql;          EXECUTE STMT;          DEALLOCATE PREPARE STMT;      END IF; END$$ DELIMITER ;   DELIMITER $$ CREATE PROCEDURE `partition_drop`(SCHEMANAMEVARCHAR(64), TABLENAME VARCHAR(64), DELETE_BELOW_PARTITION_DATE BIGINT) BEGIN      /*        SCHEMANAME = The DB schema in which tomake changes       TABLENAME = The table with partitions to potentially delete       DELETE_BELOW_PARTITION_DATE = Delete any partitions with names that aredates 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 TOget rid of that character.      */      DECLARE myCursor CURSOR FOR          SELECT partition_name          FROM information_schema.partitions          WHERE table_schema = SCHEMANAME AND table_name = TABLENAME ANDCAST(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 allthe necessary partitions.            2. Run the SQL to drop thepartitions.            3. Print out the tablepartitions 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 beingdeleted, 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              CALLpartition_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(), INTERVALKEEP_DATA_DAYS DAY), '%Y%m%d0000');      CALL partition_drop(SCHEMA_NAME, TABLE_NAME, OLDER_THAN_PARTITION_DATE);   END$$ DELIMITER ;   DELIMITER $$ CREATE PROCEDURE `partition_verify`(SCHEMANAMEVARCHAR(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 ANDpartition_name IS NULL;        /*      * If partitions do not exist, go ahead and partition the table      */      IFRETROWS = 1 THEN          /*          * Take the current date at 00:00:00 and add HOURLYINTERVAL to it. This is the timestamp below which we willstore values.          * We begin partitioning based on the beginning of a day. This is because we don't want to generate arandom 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" whenall 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 ;   DELIMITER $$ CREATE PROCEDURE`partition_maintenance_all`(SCHEMA_NAME VARCHAR(32)) BEGIN          CALL partition_maintenance(SCHEMA_NAME, 'history', 90, 24, 14);          CALL partition_maintenance(SCHEMA_NAME, 'history_log', 90, 24, 14);          CALL partition_maintenance(SCHEMA_NAME, 'history_str', 90, 24, 14);          CALL partition_maintenance(SCHEMA_NAME, 'history_text', 90, 24, 14);          CALLpartition_maintenance(SCHEMA_NAME, 'history_uint', 90, 24, 14);          CALL partition_maintenance(SCHEMA_NAME, 'trends', 730, 24, 14);          CALL partition_maintenance(SCHEMA_NAME, 'trends_uint', 730, 24, 14); END$$ DELIMITER ;

上面内容包含了创建分区的存储过程,将上面内容复制到partition.sql中,然后执行如下:

?
1 mysql -uzabbix -pzabbix zabbix < partition.sql

b、 添加crontable,每天执行01点01分执行,如下:

?
1 2 3 4 5 6 crontab -l > crontab.txt cat >> crontab.txt << EOF #zabbix partition_maintenance 01 01 * * * mysql -uzabbix -pzabbix zabbix -e"CALL partition_maintenance_all('zabbix')" &>/dev/null EOF cat crontab.txt |crontab

注意: mysql的zabbix用户的密码部分按照实际环境配置

c、首先执行一次(由于首次执行的时间较长,请使用nohup执行),如下:

?
1 nohup  mysql -uzabbix -pzabbix zabbix -e "CALLpartition_maintenance_all('zabbix')" &> /root/partition.log&

注意:观察/root/partition.log的输出

d、 查看结果

登录mysql,查看history等表, 如下:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 MariaDB [zabbix]> showcreate table history | history | CREATE TABLE `history` (   `itemid` bigint(20) unsigned NOT NULL,   `clock`int(11) NOT NULL DEFAULT '0',   `value`double(16,4) NOT NULL DEFAULT '0.0000',   `ns`int(11) NOT NULL DEFAULT '0',   KEY`history_1` (`itemid`,`clock`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 /*!50100 PARTITION BY RANGE (`clock`) (PARTITION p201708280000 VALUES LESS THAN(1503936000) ENGINE = InnoDB,   PARTITION p201708290000 VALUES LESS THAN(1504022400) ENGINE = InnoDB,   PARTITION p201708300000 VALUES LESS THAN(1504108800) ENGINE = InnoDB,   PARTITION p201708310000 VALUES LESS THAN(1504195200) ENGINE = InnoDB,   PARTITION p201709010000 VALUES LESS THAN(1504281600) ENGINE = InnoDB,   PARTITION p201709020000 VALUES LESS THAN(1504368000) ENGINE = InnoDB,   PARTITION p201709030000 VALUES LESS THAN(1504454400) ENGINE = InnoDB,   PARTITION p201709040000 VALUES LESS THAN(1504540800) ENGINE = InnoDB,   PARTITION p201709050000 VALUES LESS THAN(1504627200) ENGINE = InnoDB,   PARTITION p201709060000 VALUES LESS THAN(1504713600) ENGINE = InnoDB,   PARTITION p201709070000 VALUES LESS THAN(1504800000) ENGINE = InnoDB,   PARTITION p201709080000 VALUES LESS THAN(1504886400) ENGINE = InnoDB,   PARTITION p201709090000 VALUES LESS THAN(1504972800) ENGINE = InnoDB,   PARTITION p201709100000 VALUES LESS THAN(1505059200) ENGINE = InnoDB,   PARTITION p201709110000 VALUES LESS THAN(1505145600) ENGINE = InnoDB) */ |

发现了大量PARTITION字段,说明配置正确。注意观察Mysql的Slow Query,一般到执行操作的第二天,Slow Query几乎就会有了,此时Zabbix的Dashboard响应速度应该非常流畅了。

可以去百度分享获取分享代码输入这里。
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

【腾讯云】云服务器产品特惠热卖中
搜索
标签列表
    关注我们

    了解等多精彩内容