Ich selbst nutze unter anderem PowerDNS als DNS-Server. Im letzten
Monat betrachtete ich mir dessen MySQL-Backend, da ich über zwei
Kanäle darüber informiert wurde, dass PowerDNS mit dem
Backend nicht skaliert.
Die DNS-Records werden in PowerDNS in zwei Tabellen abgelegt. Eine
für die Domains:
create table domains (
id INT auto_increment,
name VARCHAR(255) NOT NULL,
master VARCHAR(128) DEFAULT NULL,
last_check INT DEFAULT NULL,
type VARCHAR(6) NOT NULL,
notified_serial INT DEFAULT
NULL,
account VARCHAR(40) DEFAULT NULL,
primary key (id)
) Engine=InnoDB;
Und eine weitere für die Records:
CREATE
TABLE records (
id int(11) NOT NULL auto_increment,
domain_id int(11) NOT NULL,
name varchar(255) NOT NULL,
type varchar(10) NOT NULL,
content varchar(255) NOT NULL,
ttl int(11) NOT NULL,
prio int(11) default NULL,
change_date int(11) default NULL,
PRIMARY KEY (id),
KEY name_index(name),
KEY nametype_index(name,type),
KEY domainid_index(domain_id)
);
Zudem existiert noch ein FK-Constraint, aber auch der tut hier nichts
zur Sache.
Die Tabelle domains wurde mit 6.000.000 Datensätzen bestückt.
Die Tabelle records wurde mit 46.195.356 Datensätzen
bestückt. Ich denke damit wird schon ein größerer DNS-Server simuliert :D
Zwar schien es, als sollte man sich mal Gedanken über die
Normalisierung ansich machen, doch das war nicht
mein Skope. Es galt mit etwas Mikrotuning schon Erfolge zu erzielen. PowerDNS pdns-3.0-rc2 und folgende MySQL-Version kam zum Einsatz:
[pdns]>
SELECT VERSION();
+-------------------+
| VERSION() |
+-------------------+
| 5.2.5-MariaDB-log |
+-------------------+
1 row in set (0.00 sec)
Die Datenbank meinte die
Tabellen würden folgenden Platzverbrauch haben.
>
SELECT TABLE_NAME,INDEX_LENGTH,DATA_LENGTH from
information_schema.TABLES where TABLE_NAME IN('records','domains');
+------------+--------------+-------------+
| TABLE_NAME | INDEX_LENGTH | DATA_LENGTH |
+------------+--------------+-------------+
| domains | 475004928 |
431898624 |
| records | 11372855296 | 5813305344 |
+------------+--------------+-------------+
Bei PowerDNS wurden alle Caches
abgeschaltet (es galt die Datenbank zu testen!) und 494969 disjunkte
DNS-Abfragen gestellt. Diese waren in 48.9 Sekunden durchgelaufen, was
ca. 10114 qps entspricht. (Für jeden Test wurde die Datenbank
restartet und der zweite Lauf genommen.)
Da Abfragen gegen die Tabelle records gehen, nur diese Tabelle "optimiert"- Einige Änderungen sind analog in der Tabelle domains möglich.
Als erste Optimierung wurde ein redundanter Index entfernt.
drop
index `rec_name_index` on records;
Hiernach wurden 10822 qps gemessen. Dies ist wohl nicht die Welt. Beim
Platzverbrauch sieht es schon besser aus:
+------------+--------------+-------------+
| TABLE_NAME | INDEX_LENGTH | DATA_LENGTH |
+------------+--------------+-------------+
| domains | 475004928 |
431898624 |
| records | 6116343808 | 5813305344
|
+------------+--------------+-------------+
Die Spalte type speichert die
Recordtypen. Aus dem VARCHAR wird im Index ein CHAR. Da die
Menge der Werte begrenzt ist bietet sich hier ein ENUM an. Welches den
Vorteil hat ein INT zu sein und zum anderen sicherstellt, dass nicht
andere als die definierten Werte in die Tabelle kommen.
ALTER
TABLE records MODIFY `type`
enum('A','AAAA','SOA','NS','MX','CNAME','PTR','TXT');
Zugegeben, dies ist nur ein
Subset der nötigen Recordtypen. Am Ergebnis wird dies nichts
ändern. Nach dieser Änderung haben wir nun 10918 qps.
Angenehmer ist die weitere Reduktion der Datengröße. Diesmal
auch nicht nur bei den Indexdaten.
+------------+--------------+-------------+
| TABLE_NAME | INDEX_LENGTH | DATA_LENGTH |
+------------+--------------+-------------+
| domains | 475004928 |
431898624 |
| records | 5816451072 | 5696913408
|
+------------+--------------+-------------+
All diese Tests liefen
mit distributor-threads=32. Das ist eine
Konfigurationseinstellung (im PowerDNS) für die Anzahl der
Verbindungen, die PowerDNS zur Datenbank öffnet. Der Default liegt
bei 3. Mit distributor-threads=3
erreichte ich lediglich 5656 qps.
Zu guter Letzt ändern wir noch den Index nametype_index. Die
wenigsten FQDN nutzen die im RFC ermöglichten 255 Zeichen aus.
Sprich hier lohnt sich ein prefix-Index. (Der alte wurde gedropt)
CREATE
INDEX `nametype_index` on records(name(100),type);
Nun waren wir bei 10923
qps angelangt und was sagt der Platzverbrauch?
+------------+--------------+-------------+
| TABLE_NAME | INDEX_LENGTH |
DATA_LENGTH |
+------------+--------------+-------------+
| domains
| 475004928 | 431898624 |
| records
| 3547332608 | 5696913408 |
+------------+--------------+-------------+
Sweet! Halten wir fest die Index_legth ist von
11372855296 Bytes auf 3547332608 Bytes reduziert worden.
Damit wurden hier etwas über 7GB gespart \o/
An diesem Punkt angelangt wurden die records noch in PBXT
geändert. Hierbei wurden 12375 qps erreicht:) Wobei der
Platzverbrauch immens anstieg:
| records
| 5684629504 | 12380356432 |
Später wurde ich in #powerdns darauf hingewiesen, dass das
verwendete Benchmarktool (dnsperf) auch mit einer längeren Queue
ausgeführt werden kann. So wurden mit
./dnsperf -d
/var/tmp/pdns.list -q 2000 -s localhost schnell 22994 qps
erreicht.
Das ist selbstredend nur ein Anfang. Aber zeigt es doch, dass in vielen
Projekten noch Steigerungspotential steckt. Von nicht skalieren kann aber nicht gesprochen werden. :)
Viel Spaß
Erkan
Aktuelle Kommentare