C# MongoDB vs Couchbase

Hustengutzel

Ensign
Registriert
Juni 2008
Beiträge
214
Hallo zusammen,

ich wollte mal in die Runde fragen, ob jemand schon mit den oben genannten No-SQL Datenbanken Erfahrung gemacht hat oder sogar einen direkten Vergleich hat.

Was ich damit machen möchte:
Daten wie Benzinpreis(Diesel,Benzin und Super) und Ort(Ortsname) als Json (MognoDB als Bson) abspeichern.
Danach möchte ich wie folgt suchen:
Code:
from t in collection.AsQueryable<Tankstelle>()
			              where (t.Adresse.Hauptort.StartsWith(searchString) && t.Benzinpreise.Diesel.Preis <= price)
			              select t

Das wichtigste ist die Performance.:rolleyes:

Aktuell habe ich 16k Datensätze. Diese durchsucht MongoDB innerhalb von 70ms und liefert mir Ergebnisse zurück.
Eine Liste(.Net) liefert mir bereits das Ergebniss innerhalb von 6ms zurück.


Aktuell habe ich jedoch keinen vergleich zu Couchbase oder anderen No-SQL Datenbanken die C# unterstützen.




Grüße
Matias
 
Ich hätte eine Frage an dich :)
Woher hast du die Datensätze? War letztens selbst auf der Suche nach Benzinpreisdaten, aber ohne Erfolg :(
 
Da hast du ja super viele Infos geliefert...

Hast du Indizes gesetzt?
In welchem Format speicherst du die Preise? int, long, double, string?
Wie schaut die Datenstruktur überhaupt aus?
 
70ms bei 16k Datensätzen? Das geht auf jedenfall schbeller mit MongoDB!
Um zu sagen wie du das optimieren kannst, müsstest aber mehr Infos liefern.
 
Morgen :)

@benneque und Fonce

So sieht aktuell meine Datenstruktur aus:

Code:
         public class Tankstelle
	{
		[BsonId]
		public ObjectId ID { get; set; }
		public BsonInt32 EbiNr { get; set; }
		public string Firmenname { get; set; }
		public Adresse Adresse { get; set; }
		public Benzinpreise Benzinpreise { get; set; }
		public BsonDateTime LetzeAenderung { get; set; }
	}

        public class Adresse
	{
		public string Land { get; set; }
		public string Strasse { get; set; }
		public string Hausnummer { get; set; }
		public BsonInt32 Postleitzahl { get; set; }
		public string Hauptort { get; set; }
		public Koordinaten Koordinaten { get; set; }		
	}

        public class Koordinaten
	{
		public BsonInt32 XKoordinaten { get; set; }
		public BsonInt32 YKoordinaten { get; set; }
		public COORD_FORMAT KoordinatenFormat { get; set; }
	}

        public enum COORD_FORMAT
	{
		NONE,
		SUPERKONFORM,
		MERCATOR,
		GEODECIMAL,
		GEODECIMAL_POINT
	}

        public class Benzinpreise
	{		
		public BenzinFormat Diesel { get; set; }
		public BenzinFormat Benzin { get; set; }
		public BenzinFormat Super { get; set; }
	}	

	public class BenzinFormat
	{
		public BsonDouble Preis { get; set; }
		public BsonDateTime Stand { get; set; }
	}

Diese werden anshlißend befüllt und in die MongoDB abgespeichert:

Code:
          collection.InsertBatch(bsonlistOfTankstellen);

und ausgelesen werden die Daten so:
Code:
var result = (from t in collection.AsQueryable<Tankstelle>()
			              where (t.Adresse.Hauptort.StartsWith(searchString) && t.Benzinpreise.Diesel.Preis <= price)
			              select t);

Dieser abschnitt dauert ca 1-2ms.
Jedoch sind im result noch keine Ergebnisse, bzw. eine richtige Abfrage wird erst an MongoDB verschickt wenn man .toList(),.toDictionary(), .toBson() oder sowas aufruft. das dauert dann ca 70ms.
Klar er muss das erstmal eine "Liste" ersellen die er befüllt. Dauert aber bei einer Liste auch nur 7ms.

Wäre glücklich über jeden Tipp. Ich vermute dass entweder meine Bson Objekte, oder Abfrage nicht Performant sind, oder alles :D

Wichtig zu erwähnen wäre auch, das aktuell nur eine MongoDB Instanz läuft ohne besondere Konfigurationen.

@xSeppelx
http://mdm-portal.de/
Ergänzung ()

Habe soeben mir das "Indexing" angeschaut. Bin gerade am testen.

Code:
collection.CreateIndex(IndexKeys<Tankstelle>.Ascending(t => t.Adresse.Hauptort));
			collection.CreateIndex(IndexKeys<Tankstelle>.Ascending(t => t.Benzinpreise));

Indexing
 
Zuletzt bearbeitet:
Sieht auf den ersten blick so aus als wenn ein Index über "Tankstelle.Adresse.Hauptort" fehlen würde.
Kannst du mal zeigen wie das ganze dann in MongoDB gespeichert wird?(Arbeite mit MongoDB nur in verbindung mit Java ohne einen ODM Mapper. Habe dort Queries über 1M Datensätze in 10ms)
Und die Collection vor allem "system.indexes".
 
Zuletzt bearbeitet:
Datensicht in MongoDB

Code:
 _id: ObjectId("542cfc61ecb6e92ed0350ea3")
 Datetime: 10/02/2014 09:18:57 AM (+0200)
 Adresse:
    Hauptort: "Testheim"
    Hausnummer: "2"
    Koordinaten:
       KoordinatenFormat: "GEODECIMAL"
       XKoordinaten: 5069845
       YKoordinaten: 3734279
    Land: "D"
    Postleitzahl: 12345
    Strasse: "Test Str."
 Benzinpreise:
    Benzin:
       Preis: 1.82
       Stand: 06/09/2014 12:00:00 AM (+0200)
    Diesel:
       Preis: 1.62
       Stand: 06/09/2014 12:00:00 AM (+0200)
    Super:
       Preis: 1.92
       Stand: 06/09/2014 12:00:00 AM (+0200)
 EbiNr: 111221391
 Firmenname: "Firmenname"
 LetzeAenderung: 01/26/2010 05:03:00 PM (+0100)
Ergänzung ()

Struktur update

Code:
                [BsonElement("Hauptort")]
		public string Hauptort { get; set; }

                [BsonElement("Benzinpreise")]
		public Benzinpreise Benzinpreise { get; set; }
 
Ok, das sieht aus wie ich es auch erwartet habe. Dann setz mal einen Index auf "Adresse.Hauptort" der Collection.
Wenn ich das grade auf die schnelle richtig gefunden habe müsste das bei C# so klappen.
Code:
var Tankstelle= db.GetCollection<Tankstelle>("Tankstelle");
Tankstelle.EnsureIndex(new IndexKeysBuilder()
    .Ascending("Adresse.Hauptort"));
 
Erste abfrage dauert nun knapp 91ms, dienächsten dafür zwischen 14 - 30ms
Mit ca 19 Ergebnissen aus 30802 Einträgen <- allerdings schon als Array abgespeichert.
Ergänzung ()

nachdem ich nun noch über die Diesel Preise einen Index gelegt habe
Code:
collection.CreateIndex(new IndexKeysBuilder().Ascending("Benzinpreise.Diesel.Preis"));
Dauert die erste abfrage ca 80ms dafür die zweite(die selbe abfrage) nur noch 4ms...
Wenn ich die Suche abändere(andere Stadt,anderer Preis) dauert es im schnitt 22ms.
Ergänzung ()

@ Fonce

Tankstelle.EnsureIndex(new IndexKeysBuilder()
.Ascending("Adresse.Hauptort"));

EnsureIndex <- ist obsolete
Ergänzung ()

Macht es eigt. einen Unterschied WIE man die Objecte speichert? also als Tankstelle oder BsonDocument?
Aktuell sieht es so aus:

Code:
public BsonDocument GetTankstelleFromSplitedLine(string[] splittedLine)
		{
			var newTankstelle = new BsonDocument();
			newTankstelle.Add("Adresse",GetAdresseFromSplittedLine(splittedLine));
			newTankstelle.Add(_benzinpreisGenerator.CreateRandomBenzinpreise());
			newTankstelle.Add("EbiNr",new BsonInt32( GetEbinr(splittedLine)));
			newTankstelle.Add("Firmenname",GetFirmenname(splittedLine));
			newTankstelle.Add("LetzeAenderung", new BsonDateTime(GetLetzteaenderung(splittedLine)));
			return newTankstelle;
		}

//bsonlistOfTankstellen == BsonArray of BsonDocuments
collection.InsertBatch(bsonlistOfTankstellen);
Ergänzung ()

Wie ich gerade feststelle, je größer die Datenmenge wird, desto langsamer wird die Abfrage :/
Irgendetwas läuft da noch nicht rund :D
 
Zuletzt bearbeitet:
Wenn du auf beiden Felden in _einem_ Query suchst, solltest du einen Compound Index benutzen.
MongoDB kann pro Query nur _einen_ Index benutzen - MySQL z.B. kann auch mehrere einzel-Indizes verwenden.

Also erstelle einen Compound Index in dem alle Felder enthalten sind, auf denen du suchst. Dann ist natürlich noch immer gut zu wissen, dass Text-Indizes nur mit "equals" und "startsWith" funktionieren (ist bei dir ja schon der Fall), aber niemals mit Wildcards an Anfang _und_ Ende.

Zum beoabachteten Geschwindigkeitszuwachs: Das könnte schlicht und ergreifend an der Festplatte liegen ;) Nach dem ersten Query hat er die Collection im Cache und kann dementsprechend alles direkt im RAM bearbeiten. Davor muss er natürlich erst mal alle Daten von der Festplatte holen was um einige Größenordnungen langsamer ist (auch bei einer SSD!).
 
Ah ok hatte übersehen das "Benzinpreise.Diesel.Preis" in dem Query vorkommt. Ja dann musst du, wie benneque schon sagte, einen Compound Index setzen über "Benzinpreise.Diesel.Preis" und "Adresse.Hauptort" setzen.
Code:
var keys = new IndexKeysBuilder();
keys.Ascending("Adresse.Hauptort");
keys.Ascending("Benzinpreise.Diesel.Preis");
collection.CreateIndex(keys);

EDIT:
Ergänzung vom 02.10.2014 10:58 Uhr: Wie ich gerade feststelle, je größer die Datenmenge wird, desto langsamer wird die Abfrage :/
Irgendetwas läuft da noch nicht rund
Gedanken würde ich mir machen wenn es anders rum wäre. :lol:
Wenn du die richtigen Indezies gesetzt hast, solltest bei deiner aktuellen Datenmenge keinen(bzw. vernachlässigbaren) unterschied zwischen 1k und 16k Datensätzen merken. Das dürften dann wohl frühstens bei so >1M-5M anfangen.
 
Zuletzt bearbeitet:
Erstmal vielen Dank für eure Hilfe und eure Zeit :)

Aktuell sieht mein code so aus:
Code:
	var mongo = new MongoClient("mongodb://localhost:27017");
	var server = mongo.GetServer();
	var database = server.GetDatabase("TestTankstellen");
	var collection = database.GetCollection<Tankstelle>("TankstellenCollection");
        collection.InsertBatch(bsonlistOfTankstellen);
	var keys = new IndexKeysBuilder();
	keys.Ascending("Adresse.Hauptort");
	keys.Ascending("Benzinpreise.Diesel.Preis");
	collection.CreateIndex(keys);

	//Suche
	var regex = new Regex(searchString);
	var tankst =
			collection.AsQueryable()
			.Where(t => regex.IsMatch(t.Adresse.Hauptort) && t.Benzinpreise.Diesel.Preis <= price)
			.ToArray();

Datenbestand: 120k
Dauer: ca. 40ms
Ergebnisse: ca 40

Ist das ein gutes Ergebniss? Oder kann man da noch was machen? Dieses .toArray(), kann man das durch was schnellers ersetzten?
 
Wie genau misst du die Zeit? Über den kompletten dort angegebenen Code? Ich hoffe ja nur über den Teil vom Eigentlichen Query. Und daran denken, das du den Index natürlich nicht bei jedem Query erstellen musst, sondern nur einmal!
 
eh ja, natürlich nur über den Query :D

Code:
stopwatch.Reset();
			stopwatch.Start();
			var regex = new Regex(searchString);
			var tankst =
				collection.AsQueryable()
					.Where(t => regex.IsMatch(t.Adresse.Hauptort) && t.Benzinpreise.Diesel.Preis <= price)
					.ToArray();		
			
			stopwatch.Stop();

Index wird nach dem Import der Daten ausgeführt.
 
Und du musst das Query mehrfach ausführen. Wenn die Daten nicht im Cache sind, ist das Ergebnis nicht sonderlich repräsentativ.

Das ist wie, wenn du sagst: Mein Auto braucht 30 Sekunden von 0 auf 100. ... weil du vorher noch einsteigen musst, den Motor starten, anschnallen, Sitz einstellen, etc. pp.
Deshalb misst man diese Zeit, wenn der Wagen läuft und der Motor schon seine Betriebstemperatur erreicht hat.
 
Die Ergebnisse sind auch ein "Durchschnittswert", also immer nach dem ersten Query gemessen ;)
 
Und ich sehe gerade:

Im ersten Post hast du das benutzt: "t.Adresse.Hauptort.StartsWith"
und nun: "regex.IsMatch(t.Adresse.Hauptort)"

Ich bezweifle, dass bei einem Regulären Ausdruck überhaupt der Index genutzt werden kann. Vergleich das noch mal mit dem StartsWith.

EDIT: MongoDB _kann_ einen Index bei RegExp Suchen benutzen, aber es hängt sehr stark vom Regulären Ausdruck ab, ob die Suche durch den Index beschleunigt werden kann oder nicht. Also nichts desto trotz: Versuchs noch mal mit StartsWith.
 
@benneque,

Zeit ist ca. die selbe. da verändert sich nicht ganz so viel :)
 
Zurück
Oben