Skip to content

Commit 1bf7bb0

Browse files
committed
Inital commit
1 parent c1126be commit 1bf7bb0

File tree

2 files changed

+248
-1
lines changed

2 files changed

+248
-1
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,28 @@
1-
# zabbix-mysql-partitioing-perl
1+
# Zabbix MySQL partitioning perl script
2+
3+
Disclaimer: This script isn't made by us, but the current author is unknown. We've added it to Github for ease of access. If you are the original creator of this script, please send us a private message. With that out of the way, let's move on.
4+
5+
This is a script written in Perl to partition the Zabbix database tables. We can use this script to replace the Zabbix housekeeper process with the use of fixed History and Trend storage periods for all items.
6+
7+
## How to use the script
8+
Make sure to partition the database first, if you do not know how. Check out this blog post:
9+
10+
Place the script in:
11+
```
12+
/usr/share/zabbix/
13+
```
14+
15+
Then make it executable with:
16+
```
17+
chmod +x /usr/share/zabbix/mysql_zbx_part.pl
18+
```
19+
20+
Then add a cronjob with:
21+
```
22+
crontab -e
23+
```
24+
25+
Last, add the following line:
26+
```
27+
0 23 * * * /usr/share/zabbix/mysql_zbx_part.pl >/dev/null 2>&1
28+
```

mysql_zbx_part.pl

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#!/usr/bin/perl
2+
use strict;
3+
use Data::Dumper;
4+
use DBI;
5+
use Sys::Syslog qw(:standard :macros);
6+
use DateTime;
7+
use POSIX qw(strftime);
8+
9+
openlog("mysql_zbx_part", "ndelay,pid", LOG_LOCAL0);
10+
11+
my $db_schema = 'zabbix';
12+
my $dsn = 'DBI:mysql:'.$db_schema.':mysql_socket=/var/lib/mysql/mysql.sock';
13+
my $db_user_name = 'zabbix';
14+
my $db_password = 'zabbix';
15+
my $tables = { 'history' => { 'period' => 'day', 'keep_history' => '30'},
16+
'history_log' => { 'period' => 'day', 'keep_history' => '30'},
17+
'history_str' => { 'period' => 'day', 'keep_history' => '30'},
18+
'history_text' => { 'period' => 'day', 'keep_history' => '30'},
19+
'history_uint' => { 'period' => 'day', 'keep_history' => '30'},
20+
'trends' => { 'period' => 'month', 'keep_history' => '2'},
21+
'trends_uint' => { 'period' => 'month', 'keep_history' => '2'},
22+
23+
# comment next 5 lines if you partition zabbix database starting from 2.2
24+
# they usually used for zabbix database before 2.2
25+
26+
# 'acknowledges' => { 'period' => 'month', 'keep_history' => '23'},
27+
# 'alerts' => { 'period' => 'month', 'keep_history' => '6'},
28+
# 'auditlog' => { 'period' => 'month', 'keep_history' => '24'},
29+
# 'events' => { 'period' => 'month', 'keep_history' => '12'},
30+
# 'service_alarms' => { 'period' => 'month', 'keep_history' => '6'},
31+
};
32+
my $amount_partitions = 10;
33+
34+
my $curr_tz = 'Europe/Amsterdam';
35+
36+
my $part_tables;
37+
38+
my $dbh = DBI->connect($dsn, $db_user_name, $db_password);
39+
40+
unless ( check_have_partition() ) {
41+
print "Your installation of MySQL does not support table partitioning.\n";
42+
syslog(LOG_CRIT, 'Your installation of MySQL does not support table partitioning.');
43+
exit 1;
44+
}
45+
46+
my $sth = $dbh->prepare(qq{SELECT table_name, partition_name, lower(partition_method) as partition_method,
47+
rtrim(ltrim(partition_expression)) as partition_expression,
48+
partition_description, table_rows
49+
FROM information_schema.partitions
50+
WHERE partition_name IS NOT NULL AND table_schema = ?});
51+
$sth->execute($db_schema);
52+
53+
while (my $row = $sth->fetchrow_hashref()) {
54+
$part_tables->{$row->{'table_name'}}->{$row->{'partition_name'}} = $row;
55+
}
56+
57+
$sth->finish();
58+
59+
foreach my $key (sort keys %{$tables}) {
60+
unless (defined($part_tables->{$key})) {
61+
syslog(LOG_ERR, 'Partitioning for "'.$key.'" is not found! The table might be not partitioned.');
62+
next;
63+
}
64+
65+
create_next_partition($key, $part_tables->{$key}, $tables->{$key}->{'period'});
66+
remove_old_partitions($key, $part_tables->{$key}, $tables->{$key}->{'period'}, $tables->{$key}->{'keep_history'})
67+
}
68+
69+
delete_old_data();
70+
71+
$dbh->disconnect();
72+
73+
sub check_have_partition {
74+
my $result = 0;
75+
# MySQL 5.5
76+
#my $sth = $dbh->prepare(qq{SELECT variable_value FROM information_schema.global_variables WHERE variable_name = 'have_partitioning'});
77+
# MySQL 5.6 + MariaDB
78+
my $sth = $dbh->prepare(qq{SELECT plugin_status FROM information_schema.plugins WHERE plugin_name = 'partition'});
79+
80+
$sth->execute();
81+
82+
my $row = $sth->fetchrow_array();
83+
84+
$sth->finish();
85+
86+
# MySQL 5.5
87+
#return 1 if $row eq 'YES';
88+
# MySQL 5.6 + MariaDB
89+
return 1 if $row eq 'ACTIVE';
90+
}
91+
92+
sub create_next_partition {
93+
my $table_name = shift;
94+
my $table_part = shift;
95+
my $period = shift;
96+
97+
for (my $curr_part = 0; $curr_part < $amount_partitions; $curr_part++) {
98+
my $next_name = name_next_part($tables->{$table_name}->{'period'}, $curr_part);
99+
my $found = 0;
100+
101+
foreach my $partition (sort keys %{$table_part}) {
102+
if ($next_name eq $partition) {
103+
syslog(LOG_INFO, "Next partition for $table_name table has already been created. It is $next_name");
104+
$found = 1;
105+
}
106+
}
107+
108+
if ( $found == 0 ) {
109+
syslog(LOG_INFO, "Creating a partition for $table_name table ($next_name)");
110+
my $query = 'ALTER TABLE '."$db_schema.$table_name".' ADD PARTITION (PARTITION '.$next_name.
111+
' VALUES less than (UNIX_TIMESTAMP("'.date_next_part($tables->{$table_name}->{'period'}, $curr_part).'") div 1))';
112+
syslog(LOG_DEBUG, $query);
113+
$dbh->do($query);
114+
}
115+
}
116+
}
117+
118+
sub remove_old_partitions {
119+
my $table_name = shift;
120+
my $table_part = shift;
121+
my $period = shift;
122+
my $keep_history = shift;
123+
124+
my $curr_date = DateTime->now;
125+
$curr_date->set_time_zone( $curr_tz );
126+
127+
if ( $period eq 'day' ) {
128+
$curr_date->add(days => -$keep_history);
129+
$curr_date->add(hours => -$curr_date->strftime('%H'));
130+
$curr_date->add(minutes => -$curr_date->strftime('%M'));
131+
$curr_date->add(seconds => -$curr_date->strftime('%S'));
132+
}
133+
elsif ( $period eq 'week' ) {
134+
}
135+
elsif ( $period eq 'month' ) {
136+
$curr_date->add(months => -$keep_history);
137+
138+
$curr_date->add(days => -$curr_date->strftime('%d')+1);
139+
$curr_date->add(hours => -$curr_date->strftime('%H'));
140+
$curr_date->add(minutes => -$curr_date->strftime('%M'));
141+
$curr_date->add(seconds => -$curr_date->strftime('%S'));
142+
}
143+
144+
foreach my $partition (sort keys %{$table_part}) {
145+
if ($table_part->{$partition}->{'partition_description'} <= $curr_date->epoch) {
146+
syslog(LOG_INFO, "Removing old $partition partition from $table_name table");
147+
148+
my $query = "ALTER TABLE $db_schema.$table_name DROP PARTITION $partition";
149+
150+
syslog(LOG_DEBUG, $query);
151+
$dbh->do($query);
152+
}
153+
}
154+
}
155+
156+
sub name_next_part {
157+
my $period = shift;
158+
my $curr_part = shift;
159+
160+
my $name_template;
161+
162+
my $curr_date = DateTime->now;
163+
$curr_date->set_time_zone( $curr_tz );
164+
165+
if ( $period eq 'day' ) {
166+
my $curr_date = $curr_date->truncate( to => 'day' );
167+
$curr_date->add(days => 1 + $curr_part);
168+
169+
$name_template = $curr_date->strftime('p%Y_%m_%d');
170+
}
171+
elsif ($period eq 'week') {
172+
my $curr_date = $curr_date->truncate( to => 'week' );
173+
$curr_date->add(days => 7 * $curr_part);
174+
175+
$name_template = $curr_date->strftime('p%Y_%m_w%W');
176+
}
177+
elsif ($period eq 'month') {
178+
my $curr_date = $curr_date->truncate( to => 'month' );
179+
$curr_date->add(months => 1 + $curr_part);
180+
181+
$name_template = $curr_date->strftime('p%Y_%m');
182+
}
183+
184+
return $name_template;
185+
}
186+
187+
sub date_next_part {
188+
my $period = shift;
189+
my $curr_part = shift;
190+
191+
my $period_date;
192+
193+
my $curr_date = DateTime->now;
194+
$curr_date->set_time_zone( $curr_tz );
195+
196+
if ( $period eq 'day' ) {
197+
my $curr_date = $curr_date->truncate( to => 'day' );
198+
$curr_date->add(days => 2 + $curr_part);
199+
$period_date = $curr_date->strftime('%Y-%m-%d');
200+
}
201+
elsif ($period eq 'week') {
202+
my $curr_date = $curr_date->truncate( to => 'week' );
203+
$curr_date->add(days => 7 * $curr_part + 1);
204+
$period_date = $curr_date->strftime('%Y-%m-%d');
205+
}
206+
elsif ($period eq 'month') {
207+
my $curr_date = $curr_date->truncate( to => 'month' );
208+
$curr_date->add(months => 2 + $curr_part);
209+
210+
$period_date = $curr_date->strftime('%Y-%m-%d');
211+
}
212+
213+
return $period_date;
214+
}
215+
216+
sub delete_old_data {
217+
$dbh->do("DELETE FROM sessions WHERE lastaccess < UNIX_TIMESTAMP(NOW() - INTERVAL 1 MONTH)");
218+
$dbh->do("TRUNCATE housekeeper");
219+
$dbh->do("DELETE FROM auditlog_details WHERE NOT EXISTS (SELECT NULL FROM auditlog WHERE auditlog.auditid = auditlog_details.auditid)");
220+
}

0 commit comments

Comments
 (0)