Nach vielem Benchmarking mit sysbench komme ich zu folgendem Schluss:
Eine Situation zu überleben, in der
- Ein böser Kopiervorgang überflutet schmutzige Seiten
- und Hardware Write-Cache ist vorhanden (evtl. auch ohne das)
- und synchrone Lese- oder Schreibvorgänge pro Sekunde (IOPS) sind kritisch
Entleere einfach alle Aufzüge, Warteschlangen und schmutzigen Seiten-Caches. Der richtige Ort für fehlerhafte Seiten befindet sich im RAM dieses Hardware-Schreibcaches.
Passen Sie dirty_ratio (oder neue dirty_bytes) so niedrig wie möglich an, achten Sie jedoch auf den sequentiellen Durchsatz. In meinem speziellen Fall waren 15 MB optimal ( echo 15000000 > dirty_bytes
).
Dies ist eher ein Hack als eine Lösung, da Gigabyte RAM jetzt nur für das Lese-Caching anstelle von Dirty-Cache verwendet werden. Damit ein fehlerhafter Cache in dieser Situation gut funktioniert, muss der Linux-Kernel-Hintergrund-Flusher die durchschnittliche Geschwindigkeit ermitteln, mit der das zugrunde liegende Gerät Anforderungen akzeptiert, und das Hintergrund-Flushing entsprechend anpassen. Nicht einfach.
Spezifikationen und Benchmarks zum Vergleich:
Getestet, während dd
Nullen auf die Festplatte geschrieben wurden, zeigte sysbench einen enormen Erfolg : 10 Threads wurden mit 16 kB von 33 auf 700 IOPS (Leerlaufbegrenzung: 1500 IOPS) und ein einzelner Thread von 8 auf 400 IOPS erhöht.
Ohne Last waren IOPS nicht betroffen (~ 1500) und der Durchsatz leicht reduziert (von 251 MB / s auf 216 MB / s).
dd
Anruf:
dd if=/dev/zero of=dumpfile bs=1024 count=20485672
für sysbench wurde die test_datei.0 vorbereitet, um unsparse zu sein mit:
dd if=/dev/zero of=test_file.0 bs=1024 count=10485672
Sysbench-Aufruf für 10 Threads:
sysbench --test=fileio --file-num=1 --num-threads=10 --file-total-size=10G --file-fsync-all=on --file-test-mode=rndwr --max-time=30 --file-block-size=16384 --max-requests=0 run
Sysbench-Aufruf für einen Thread:
sysbench --test=fileio --file-num=1 --num-threads=1 --file-total-size=10G --file-fsync-all=on --file-test-mode=rndwr --max-time=30 --file-block-size=16384 --max-requests=0 run
Kleinere Blockgrößen zeigten noch drastischere Zahlen.
--file-block-size = 4096 mit 1 GB dirty_bytes:
sysbench 0.4.12: multi-threaded system evaluation benchmark
Running the test with following options:
Number of threads: 1
Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.
Operations performed: 0 Read, 30 Write, 30 Other = 60 Total
Read 0b Written 120Kb Total transferred 120Kb (3.939Kb/sec)
0.98 Requests/sec executed
Test execution summary:
total time: 30.4642s
total number of events: 30
total time taken by event execution: 30.4639
per-request statistics:
min: 94.36ms
avg: 1015.46ms
max: 1591.95ms
approx. 95 percentile: 1591.30ms
Threads fairness:
events (avg/stddev): 30.0000/0.00
execution time (avg/stddev): 30.4639/0.00
--file-block-size = 4096 mit 15 MB dirty_bytes:
sysbench 0.4.12: multi-threaded system evaluation benchmark
Running the test with following options:
Number of threads: 1
Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.
Operations performed: 0 Read, 13524 Write, 13524 Other = 27048 Total
Read 0b Written 52.828Mb Total transferred 52.828Mb (1.7608Mb/sec)
450.75 Requests/sec executed
Test execution summary:
total time: 30.0032s
total number of events: 13524
total time taken by event execution: 29.9921
per-request statistics:
min: 0.10ms
avg: 2.22ms
max: 145.75ms
approx. 95 percentile: 12.35ms
Threads fairness:
events (avg/stddev): 13524.0000/0.00
execution time (avg/stddev): 29.9921/0.00
--file-block-size = 4096 mit 15 MB dirty_bytes im Leerlauf:
sysbench 0.4.12: Multithread-Systembewertungsbenchmark
Running the test with following options:
Number of threads: 1
Extra file open flags: 0
1 files, 10Gb each
10Gb total file size
Block size 4Kb
Number of random requests for random IO: 0
Read/Write ratio for combined random IO test: 1.50
Calling fsync() after each write operation.
Using synchronous I/O mode
Doing random write test
Threads started!
Time limit exceeded, exiting...
Done.
Operations performed: 0 Read, 43801 Write, 43801 Other = 87602 Total
Read 0b Written 171.1Mb Total transferred 171.1Mb (5.7032Mb/sec)
1460.02 Requests/sec executed
Test execution summary:
total time: 30.0004s
total number of events: 43801
total time taken by event execution: 29.9662
per-request statistics:
min: 0.10ms
avg: 0.68ms
max: 275.50ms
approx. 95 percentile: 3.28ms
Threads fairness:
events (avg/stddev): 43801.0000/0.00
execution time (avg/stddev): 29.9662/0.00
Testsystem:
- Adaptec 5405Z (das sind 512 MB Schreib-Cache mit Schutz)
- Intel Xeon L5520
- 6 GiB RAM bei 1066 MHz
- Hauptplatine Supermicro X8DTN (5520 Chipsatz)
- 12 Seagate Barracuda 1 TB-Festplatten
- 10 in Linux-Software-RAID 10
- Kernel 2.6.32
- Dateisystem xfs
- Debian instabil
Zusammenfassend bin ich jetzt sicher, dass diese Konfiguration im Leerlauf, bei hoher Auslastung und sogar bei voller Auslastung für Datenbankverkehr, der ansonsten durch sequentiellen Verkehr ausgehungert worden wäre, eine gute Leistung erbringt. Der sequenzielle Durchsatz ist höher als zwei Gigabit-Verbindungen ohnehin liefern können, daher ist es kein Problem, ihn ein wenig zu reduzieren.