Warum C# Ihr geheimes Werkzeug für Big Data ist?
\n\nWenn es um die Verarbeitung großer Datenmengen geht, kommen einem sofort Python mit seinen Pandas, Scala mit Spark oder Java mit Hadoop in den Sinn. Aber seien wir ehrlich: C# bleibt oft zu Unrecht im Schatten. Und das zu Unrecht. Die .NET-Plattform bietet uns so leistungsstarke Werkzeuge wie PLINQ, MemoryMappedFile, Parallel.For und asynchrone Streams, mit denen wir Gigabytes an Daten auf normaler Hardware verarbeiten können, ohne großen Aufwand.
\n\nIn diesem Artikel werden wir keine Theorie verbreiten – wir kommen sofort zur Sache. Sie erfahren, wie Sie das Maximum aus Ihrer CPU bei der Big-Data-Verarbeitung mit C# herausholen, wie Sie einen OutOfMemoryException vermeiden und wie Sie Dateien mit einer Größe von Dutzenden Gigabytes verarbeiten, als wären sie nur ein großes Buch.
\n\nDas Hauptproblem: Wie vermeidet man Speicherüberlastung?
\n\nStellen Sie sich vor: Sie haben eine 50 GB große CSV-Datei mit Server-Logs. Sie öffnen sie mit einem normalen StreamReader und lesen sie zeilenweise. Alles gut? Nein. Wenn Sie versuchen, sie vollständig in eine List<string> zu laden – hallo, OutOfMemoryException. Selbst wenn Sie 64 GB RAM haben, kann .NET den Speicher so fragmentieren, dass ein großes Objekt einfach nicht in den Heap passt.
Lösung Nummer eins: Streaming und Chunking. Laden Sie nicht alles auf einmal – verarbeiten Sie es häppchenweise.
\n\npublic async IAsyncEnumerable<string[]> ReadCsvChunksAsync(string filePath, int chunkSize = 10000)\n{\n using var reader = new StreamReader(filePath);\n string? line;\n var buffer = new List<string>(chunkSize);\n\n while ((line = await reader.ReadLineAsync()) != null)\n {\n buffer.Add(line);\n if (buffer.Count >= chunkSize)\n {\n yield return buffer.ToArray();\n buffer.Clear();\n }\n }\n\n if (buffer.Count > 0)\n yield return buffer.ToArray();\n}\n\n\nJetzt können Sie Gigabytes verarbeiten, ohne an den Speicher zu denken:
\n\nawait foreach (var chunk in ReadCsvChunksAsync("logs.csv"))\n{\n ProcessChunk(chunk); // Ihre Logik\n}\n\n\nParallele Verarbeitung: Alle Prozessorkerne nutzen
\n\nGut, wir lesen die Daten in Portionen. Aber wenn Sie einen 16-Kern-Prozessor haben und die Daten sequenziell verarbeitet werden – nutzen Sie nur 6% seiner Leistung. Zeit, parallele Berechnungen mit C# einzuschalten.
\n\nPLINQ – Parallel LINQ
\n\nDie einfachste Magie ist das Hinzufügen von .AsParallel() zu Ihrer LINQ-Abfrage. Aber bei großen Datenmengen muss man vorsichtig sein: PLINQ entscheidet selbst, wann parallelisiert wird, was zu übermäßiger Fragmentierung führen kann.
var numbers = Enumerable.Range(1, 10_000_000);\n\n// Normales LINQ (langsam)\nvar sum = numbers.Where(n => n % 2 == 0).Sum();\n\n// PLINQ (schnell, aber vorsichtig)\nvar parallelSum = numbers\n .AsParallel()\n .WithDegreeOfParallelism(Environment.ProcessorCount)\n .Where(n => n % 2 == 0)\n .Sum();\n\nConsole.WriteLine($"Ergebnis: {parallelSum}");\n\n\nAber PLINQ ist kein Allheilmittel. Für komplexe Big-Data-Verarbeitung mit C# ist es besser, Parallel.ForEach mit manueller Aufteilungssteuerung zu verwenden.
Parallel.ForEach mit Bereichsaufteilung
\n\nvar allLines = File.ReadLines("huge_dataset.csv");\nvar processedCount = 0;\nvar lockObject = new object();\n\nParallel.ForEach(\n allLines,\n new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },\n line =>\n {\n var result = ProcessLine(line); // Ihre schwere Funktion\n lock (lockObject)\n {\n processedCount++;\n if (processedCount % 100_000 == 0)\n Console.WriteLine($"Verarbeitet: {processedCount}");\n }\n });\n\n\nMemoryMappedFile: Mit Dateien wie mit Arbeitsspeicher arbeiten
\n\nWenn die Datei zu groß zum Laden in den RAM ist, Sie aber dennoch