Zabbix优化: 数据库表分区

在说数据库分表之前,先随便聊聊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的数量:

要注意的是,例如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的性能

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

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

二、导入存储过程:

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 ;
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 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 @[email protected]__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 ;
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 ;

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

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

三、使用存储过程:

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

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

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个分区

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

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 ;

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

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

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

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

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

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

参考文章:

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

时间: 2024-10-17 16:23:44

Zabbix优化: 数据库表分区的相关文章

千万级SQL Server数据库表分区的实现

千万级SQL Server数据库表分区的实现 2010-09-10 13:37 佚名 数据库 字号:T | T 一般在千万级的数据压力下,分区是一种比较好的提升性能方法.本文将介绍SQL Server数据库表分区的实现. AD:51CTO 网+ 第十二期沙龙:大话数据之美_如何用数据驱动用户体验 最近使用SQL SERVER一个的缓存,数据量一天100w的速度增长,同时接受客户查询,速度由于数据量越来越大越来越慢,这里感谢  KillKill 和 邀约, 最近读了一套书不错,感兴趣的同学可以读读

数据库表分区实例总结

什么是数据库表分区 数据库表分区(Partitioning),即将一个大的数据表(Table)及其索引(Index)切分更小的部分.这些分区可以有不同的名字,甚至是存储方式. 为什么要做分区 存储均衡:可以分摊大量数据到不同存储介质中. 方便管理:方便DBA管理数据表,进行各种操作,比如删除陈旧的数据. 直接定位查询快:根据分区策略查询数据时,可直接定位到目标分区,减少查询时间. 并行查询提高效率:做聚合查询时,多个分区(磁盘)并行查询,可以提高效率. 怎么做分区 分区策略 Range:分区规则

Zabbix优化:数据库表分区

参考:https://www.zabbix.org/wiki/Docs/howto/mysql_partition(官网) 当zabbix监控的服务器数量越来越多,就会报"Zabbix housekeeper processes more than 75% busy"这个错误 机器数量多的时候,mysql里面的history表就会越来越大,虽然zabbix本身有删除功能(就是那个housekeeper的功能),但这东西太影响性能,所以网上的做法都是关闭这个东西,用mysql的表分区功能

数据库优化-oracle表分区的创建和分类

当表中的数据量不断增大,查询数据的速度就会变慢,应用程序的性能就会下降,这时就应该考虑对表进行分区.表进行分区后,逻辑上表仍然是一张完整的表,只是将表中的数据在物理上存放到多个表空间(物理文件上),这样查询数据时,有可能不需要每次都扫描整张表. Oracle的表分区功能通过改善可管理性.性能和可用性,从而为各式应用程序带来了极大的好处.通常,分区可以使某些查询以及维护操作的性能大大提高.此外,分区还可以极大简化常见的管理任务,分区是构建千兆字节数据系统或超高可用性系统的关键工具. 分区功能能够将

Mysql数据库表分区深入详解

0.mysql数据库分区的由来? 1)传统不分区数据库痛点 mysql数据库中的数据是以文件的形势存在磁盘上的,默认放在/mysql/data下面(可以通过my.cnf中的datadir来查看), 一张表主要对应着三个文件,一个是frm存放表结构的,一个是myd存放表数据的,一个是myi存表索引的. [[email protected] test]# ls -al 总用量 1811444 drwx------ 2 mysql mysql 4096 10月 17 15:12 . drwxr-xr-

合理优化数据库表结构提高项目执行效率[数据库设计]

数据库表设计优化: 有时为了提高数据库效率,可适当考虑反三范式,适当添加冗余字段,减少多表去关联查询. 使用索引: 2.1 数据库表设计时要合理的去使用普通索引.主键索引.唯一索引.全文索引以及复合(组合)索引. 2.2 何时创建索引: (1) 较频繁的作为查询条件的字段; (2) 唯一性太差的字段(如性别/状态字段等),即使我们频繁作为查询的条件,匀不适合创建索引; (3) 数据更新非常频繁的字段(如统计平台当前的在线人数字段/商品的销售数量字段等)是不适合创建索引. 2.3 具体索引的使用请

Innodb IO优化 — 数据库表设计 转

数据库表设计这块学问比较多,我这里单从互联网角度出发同时结合Innodb的特性给出一些设计方法供大家参考.本文构建大概分两分部分:Innodb的特性及设计中如何利用这种特性. Innodb特性: Innodb是索引聚集表, 存储结构是BTREE Innodb的表的数据存储是有顺序的,默认是以主建排序,主建即是数据本身,不单独存放. 如果没有主建,Innodb以第一个唯一索引排序,如果连唯一索引也没,Innodb内部会产生一个6字节的字段排序(这个也是性能杀手,所以对这块如果不想花太多时间去想这个

zabbix监控数据库表空间大小

1.通过脚本生成日志文件 2.添加自定义脚本 oracle_discovery.sh 通过该脚本取出表空间名,并进行JSON格式化输出(因为zabbix的自动发现功能获取的数据类型是JSON格式的) #!/bin/bash TABLESPACE=`cat /home/patrol/tablespace.log |awk '{print$2}'|awk 'NR>3{print}'` COUNT=`echo "$TABLESPACE" |wc -l` INDEX=0 echo '{&

hibernate动态创建数据库表名几种方式

数据库中数据量很大, 但又不可以删除时同时又要优化程序检索数据时间. 答:方式有很多比如 创建数据库表分区,创建索引, 存储过程等; 我这里采用动态创建数据库表的方式. 完全可以在不创建表分区情况下实行分表管理 例如 日志记录表 将日期(yyyy-MM)作为默认表后缀动态追加, 例如 文章发布表 将用户名作为后缀名进行动态追加 ; 动态创建数据库表的方式要具体问题具体分析, 比如JDBC中直接使用create table 表名_dynamicStr(...); 文章发布系统 dynamicStr