Regel 1: keinen Code aus "nettigkeit" wegkürzen, nur weil man denkt, da gibt's keinen Fehler.
"load from DB"
Wenn die DB jetzt in einen Lock fährt - aus welchen Gründen auch immer - ist das Verhalten des Codes außenrum nicht mehr definiert. Wir hatten genau das gleichzeitig auf der DB. Also hat der Code zwar macken, war aber nicht schuld.
Ich hoffe das dient wenigstens als gutes Beispiel für schlechtes Fragen nach Hilfe bei einem Fehler...
hab mich hier lange nicht gemeldet, aber jetzt ein Problem, dass ich logisch nicht erklären kann:
Der Code ist ~4 Jahre alt und benutzt ein Dictionary für ein internes Caching.
private static Dictionary<int, Tuple<DateTime, object>> myDict = new ..
void foo(key int)
{
if (myDict .ContainsKey(key))
{
var result = myDict[key];
if ((DateTime.Now - result.Item1) > TimeSpan.FromMinutes(5)) //5 Minutes Cache
{
myDict.Remove(key);
}
else
{
return result.Item2;
}
}
[..] load from DB
if (myDict .ContainsKey(key))
myDict[key] = new Tuple<DateTime,object>(DateTime.Now, data);
else
myDict.Add(key, new Tuple<DateTime, object>(DateTime.Now, data));
return data;
}
Code ist gekürzt und anonymisiert.
Das ganze ist nicht durch ein lock geschützt, noch wird z.B. ein ConcurrentDictionary verwendet. Wie gesagt, alter, funktionierender Code.
Meine Frage ist also - außer dass das jetzt etwas korrigiert wird - was sorgt dafür, dass das ganze nicht nur manchmal wegen einem threading Problem (in der Routine) auf die Schnauze fällt, sondern mehrere Stunden eine IndexOutOfRangeException beim Insert (Add) in das Dictionary erzeugt?
Threading-Problem hat das ganze sicher. Ein static überlebt keinen Prozesswechsel (AppPool Recycle)
* Stage und Prod haben dieselben Bindings, aber terminiert auf unterschiedliche IP'S
* Stage und Prod benutzen dieselbe Session & DB
* IP's im LB sind je nach Anwendung getrennt
* Im LB von Prod auf Stage (rein Server basierend, auf IP) umschalten lassen (und das andere zurück)
* Prod und Stage "logisch" tauschen
Das hängt ein wenig von eurem LB ab. Am einfachsten wäre es wohl, wenn man Regeln für die URL zu den physischen Servern angeben könnte oder wenn ihr mehrere öffentliche IP's habt.
stage.example.com -> 141.1.1.1 -> Real 192.168.1.1
live.example.com -> 141.1.1.2 -> Real 192.168.1.2, Real 192.168.1.3
Dann im LB die Real-Server tauschen (lassen). Die User merken nix, außer die Session wird gekillt.
Ansonsten ist ein Ausfall für die User wohl nicht unbemerkt. Sanft kann man das noch mit einem ändern des DNS auf ne andere LB-IP erledigen, dann wandert die Last je nach TTL.
Macht ihr den Umstieg programmatisch über System.Web.Administration oder so? (die 2,3 Sekunden deuten darauf hin)
Ich hab den Thread jetzt nicht ganz nachvollzogen, da ich mit Farmbetrieb weniger Erfahrungen habe, wir regeln alles über LB.
hast du noch ein Backup? DBCC war immer mein Freund in Notfällen. Ehrlicherweise hab ich noch nie eine DB wirklich verloren - es war alles immer irgendwo vorhanden und wiederherstellbar.
Wenn deine DB tatsächlich "eine unerwartet kleine größe" hat, dann sollte man noch nach dem Transaktionslog schauen. Auch sein anzumerken: wenn man in einer 1000Mb Datenbank die Tabellen alle löscht, so hat die DB immernoch 1000 Mb. Der SQL Server gibt nur auf expliziten Befehl (Autoshrink, dbcc) den Speicher an das Betriebssystem zurück. Die änderungen sind wenn dann auch noch im Log, außer es wurde ein komplettes Backup gemacht oder das Log war ausgeschalten (Log = simple).
Hi!
..Windowsstart (also z.b. eine Verknüpfung im Autostartordner) ... dabei scheint der SQLServer-Dienst etwas zu spät verfügbar zu sein, so dass meine Anwendung denk es sein keine Verbindung zum Server möglich.
scheint ja beides auf dem selben Rechner zu liegen. Windows versucht seit geraumer Zeit schneller beim Start zu werden. Der Effekt ist allerdings, dass Windows "schnell" startet, aber die Dienste (mssqlserver) erst später hochfahren. Es sieht schnell aus, aber es läuft noch lange nicht alles. Wenn der Rechner nicht sooo besonders fix ist, dann kann es schon ein paar Minuten dauern. Man merkt das auch gerne daran, dass 2-3 Minuten nach dem Start plötzlich der (endlich gestartete) Virenscanner sich seine Updates holt und nochmal alles ausbremst.
Tips, was man machen kann gabs schon, wollte nur das "Problem" nochmal beleuchten.
nimm die ganze "schreiben" logik in einen seperaten Thread, dann behält das auch das gewünschte sequenzielle Verhalten.
Im Endeffekt wird nur dein Hauptthread und der 2. Thread vertauscht. Deine Gui im Hauptthread läuft weiter, die Sequenz im Worker bleibt.
Je nachdem mit welcher Datenbank und mit welchem Isolationlevel ich arbeite haben die anderen User längere Wartezeiten. Wenn Dirty Reads ausgeschlossen sind müssen die anderen Benutzer warten, bis die Transaktion abgeschlossen sind, bevor sie Daten lesen können. Aus diesem Grunde sind meine Transaktionen immer so kurz wie möglich, maximal so 2 Sekunden.
Handhabe ich genauso. Wenn es wirklich viele Daten sind, dann einzelne Transaktionen a 10.000 Stück (je nach Anwendung variierend) mit einem View zum löschen (der ist tatsächlich performanter).
Zum Teil auch gerne einfach in einer StoredProc mit nem Cursor und Schleife in einzelne Transaktionen zerhackt. Dann müssen die anderen Transaktionen nicht unnötig warten.
Zitat von buyden
Die Tabellen befinden sich in der selben DB, ich verschiebe sie nur zum verarbeiten in eine work-tabelle und danach in eine history, um den Datenbestand beim Verarbeiten so klein wie möglich zu halten da sich in der Work-Tabelle nur die Datensätze zum aktuellen Vorgang befinden.
In diesen Fällen bevorzuge ich Temporäre Tabellen oder - wenn es die performance zulässt - Tabellenvariablen. Das erspart die Aufräumarbeit und man kann das ganze später auch von unterschiedlichen Stellen (Threads) anwerfen, ohne dass die "Worktabelle" von allen belegt wird.
vielen dank für den Link, der ist sehr informativ. Allerdings auch schon etwas älter, hat sich da nichts getan?
Naja, Grundlegend muss doch alles beim alten bleiben, oder?
Von ODBC würde ich generell abraten: alt, langsam, existiert nur noch aus Kompatibilitätsgründen.
Dass die Zieldatenbank nicht bekannt ist, ist extrem ärgerlich, denn man muss trotz allem die eigentlichen DB-Statements (Insert, Update etc.) Datenbankspezifisch halten. Hier kommt man also nie um eine Anpassung auf eine bestimmte DB herum.
Dafür gibt es aber OR-Mapper:
Zitat von BerndFfm
Wenn Du Datenbank-unabhängig programmieren willst dann schau Dir die O/R-Mapper an, z.B. Entity Framework.
naja, die Libs sind erstmal nicht im CE-Framework vorhanden, also geht es erstmal nicht.
Ein Problem - warum es vermutlich nicht wirklich funktioniert - ist, dass alle diese "kleinen" Geräte Applikationen abschalten, welche "subjektiv" keine Rechenzeit brauchen. Demzufolge ergibt die Implementation eines Services, welcher immer mal wieder Rechenzeit braucht aber keine Foreground Anwendung ist, keinen Sinn. Auch wenn der Server (Service) eine Anfrage bekommt, hat er keine Rechenzeit, Interrupts etc. um darüber überhaut eine Information zu bekommen.
Vielleicht kannst du auf andere Libs ausweichen oder ein Polling des "Servers" einbauen.
ein paar Sachen schliesst Du bereits aus: mangelnde Rechte lösen eine Exception aus, Windows Anmeldung bzw. SQL-Server geht auch. Fehlende Rechte bei SP lösen auch ne Exception aus.
Ich finde jetzt nur 4 Erklärungen:
a) Es gibt eine zweite Instanz von SQL-Server mit den DB's und eine hat die Datenstruktur aber keine Daten. (oder tatsächlich ein ganz anderer Rechner/Server?) Im SSMS verbindest und testest du auf die andere DB mit Daten.
b) Es gibt eine Daten-Einschränkung auf den Benutzer (ja, man kann unter SQl-Server angeben welcher Benutzer welche Daten selectieren darf) - wenn das aber nicht explizit eingestellt wird, kommt es nicht vor.
c) Abfrage über SP's mit expliziter Authetifizierung (selst gebaut) welche dann nichts oder "-1" zurückgibt.
d) der DB- oder BI-Layer hält die Daten zurück und/oder filtert die aus.
Lösung: mal SQL-Profiler anwerfen und gucken was da an Anfragen wirklich reinkommt. wenn nicht kommt, dann ist es der falsche Server, wenn der Server Daten rausgibt ist es das Programm.
:-)
Xynratron
PS: wo wird denn "-1" zurückgegeben? kein SQl-Server macht das freiwillig.
definitiv subjektiv :-) Ja, die Contentfarmen sind weg. Aber jetzt gehts mir bei Google fast wie bei Bing - Verweise auf die MSDN rangieren weiter hinten. (hab ich bei Bing eh noch nie verstanden)
heute hatte ich mal nach - aufgrund einer Frage hier im Forum - mal nach "sqldatareader wrong special chars" gesucht (und alternativen). Naja, grad nochmal versucht, aktuell sind die Treffer deutlich besser. Heute Mittag gab es nur _einen_ wirklich guten Treffer.
Hab in einem Forum gelesen dass man erst ab 78 auslesen muss. Alles davor sei nur header.
ok, denjenigen fordere ich gerne auf dem Oktoberfest heraus. Vielleicht wirds mir dann klar. Aktuell behaute ich aber: absoluter Blödsinn. Keine DB "verschwendet" mal kurz XX Bytes.
Wenn Deine Datenbank dir Daten liefert, dann lies bitte immer alles. Wenn es Probleme gibt, dann kann mann immer noch den Stream analysieren.
als erste mal - hier gibts c# und sql tags - dann kann man das ganze besser lesen.
Aber zu deinem Problem. Was bedeutet ineffizient? 6 Queries stören eine DB im allgemeinen nicht wirklich. Du kannst mal den Profiler laufen lassen, um die ineffiziez zu überprüfen (alles gen > 200ms von der DB ist definitv ineffizient).
Ok, wen du das getestet hast, dann bemüh mal die Stopwatch (sieht bei Dir nach Winforms aus) um nen Zeitfresser zu finden - alternativ nen Profiler dazwischenschalten.
Deinn Code an sich sieht nicht übel aus. Ich bin mir jetzt abe nicht sichre ob das ganze nicht in nem Dataset am Client passiert.
Linq und Konsorten sind nur eine Möglichkeit "eleganter" zu programmieren, in der Laufzeit hilft es nur, wenn man extrem grobe Schnitzer macht. Ansonsten sind diese Bibliotheken im allgemeinen etwas langsamer als selbst programmiert. Allerdings so wenig, dass sich es definitiv lohnt, diese zu verwenden (aufgrund der persöhnlich gesparten Arbeit).
klar geht es auch ohne "IIOP" (ich kenns nicht mal) - oder sonstige Bibliotheken. Die Kapseln auch nur "Dein" Problem.
Prinzipiell würde ich mir aber definitiv mal WCF angucken. Das kapselt auch remoting (zusätzlich zu anderen sachen) und kann alles was du brauchst. Ausserdem guckt es erstmal komplex aus, ist aber eigentlich ganz einfach.
naja, wenigstens reagierst du Fix auf Fragen und Antworten. Andere schaffen nichtmal das. Aber leider bist Du wiedermal ein etwas schlechtes Beispiel. Ich persönlich schimpfe meine (IT-)Lehrlinge immer kräftig, wenn sie mit einer Frage/Problem kommen, ohne beschreiben zu können, was sie eigentlich dazu bewegt hat, zu mir zu kommen.
Für die Zukunft, guck Dir bitte immer genau an, in welcher Umgebung "Der Fehler" auftritt. Dann findet man deutlich schneller Hilfe - sei es nun im Forum oder nur "per Google" - immer Hilfe. Und, je besser ein eigenes Wissen um die Materie ist, desto besser findest du auch die Ansatzpunkte.
HappyCoding
Xynratron
PS: und benutze doch Doppelpunkte statt Ausrufezeichen.
Wie bekomme ich denn raus wo die Zeichen, die ja noch richtig in der DBZelle stehen verädert werden?
Die Daten in der "DBZelle" werden nur im Managementstudio richtig angezeigt. Geändert werden sie nirgends, nur die Anzeige ist falsch.
Zitat
Überprüf bitte, ob die Daten vom Webservice richtig kommen (z.B. per Fiddler) und der Client das ruiniert oder ob am Webservice eine Änderung vorgenommen werden muss.
naja, deine Fragen sind ihmo nicht so abwegig wie es gerade erscheint. Genau das passiert ja in jedem P2P Program: Daten werden verteilt und jeder vertraut darauf, dass der (später) resultierende Hash die Originaldaten wiederspiegelt. Das Funktioniert bekanntermaßen auch sehr gut und verlässlich.
Was nicht funktioniert, sind Daten, welche (potentiell) nur einem Benutzer zugute komme sollen. Denn diese daten muss man übertragen - womit die Idee der Netzwerklast schon wegfällt - und das immer nur zu "dem einen Client".
Ein Hash ist immer nur bedingt gut. Wenn die Datenmenge sehr klein wird, dann reduziert sich die Menge der möglichen Hashes auf das maximum dieser Datenmenge. Also z.B. Daten sind nur ein Buchstabe ("A-Z") - dann gibts auch nur 26 mögliche Hashes. Bei sehr grossen Datenmengen reduziert sich wiederum die Nachprüfbarkeit. Hash-Algorithmen sind nur möglichst gut mathematisch analysiert - aber es garantiert niemand, dass sie nicht "versehentlich" bei großen Datenmengen eine kollision produzieren. (kleine Mengen lassen sich berechnen, große nicht mehr) Aber: ja, es funktioniert auch bei großen Datenmengen (aktuell)^^
Crythographie ist übrigens bei deinen Überlegungen nochmal ein anderer Punkt. Verschlüsselung kostet immer Serverkapazität (CPU, RAM, Nertzwerklast) - womit ja deine anderen Punkte angesprochen sind. Ok, hier gibt es sehr gute Lösungen dafür (eine extra Crytobox für SSL vor den eigentlichen Servern) aber es ist imho nicht das was du meintest.
Wenn Du dich für das Thema interessierts, kann ich leider kein Buch empfehlen, nur Richtung. schau Dir an, wie https (ssl) funktioniert, Unterschiede zwischen synchroner und asynchroner Verschlüsselung, Unterschiede zwischen (crypto)Hashes; da reicht ja auch schon mal Codierungen auf Netzwerkkabeln als Grundlage anzugucken. Man muss ja immer davon ausgehen, das _viel clevere Leute_ sich da schonwas dabei gedacht haben.
hab durch Zufall mitbekommen (heise) dass Google seinen Suchindex "verbessert" hat.
Naja, ich hab schon von Tagen festgestellt, dass _meine_ Suchergebnisse schlechter geworden sind. Ich such auch fast auschschliesslich nach Programierer-Problemen - da helfen Seiten, welche nur andere Seiten wiedergeben und deswegen von google entfernt wurden - aber meine Suche ist subjektiv gute 100% langsamer geworden.
Hab vor längerer Zeit mal ein System so implemetiert (Addin wird anhand von Job in Db geladen) und habe - in derselben AppDomain - recht gute Erfahrungen bzgl. Geschwindigkeit. Das hat aber auch seine Grenzen, ca. 1000 Transaktionen/Sekunde macht das Ding, allerdings sind die DB-Aufgaben auch nicht ganz trivial.
Damals hatte ich einen Artikel von den Machern des AddIn-System gefunden, der die Unterschiede bei der Geschwindigkeit angibt. Imho war damals der Übergang von unterschiedliche AppDomains bei ca. 3000 / Sekunde.
Den Artikel (Blog Eintrag) müsste man mal wieder suchen / finden.
Wenn ich getdate() als ComputedColumnSpecification eintrage dann schreibt er mir für jede neue Zeile das Datum an die entsprechende Stelle.
Die Annahme ist falsch. Du wirst bei jedem Select den aktuellen Wert von getdate() zurückbekommen (also jetzt den 07.03.2011, morgenden 08.03.2011). Computed Columns werden nur (maximal) solange gespeichert, wie sich ihr Wert nicht ändert. getdate() ändert sich dauernd.
Das Beispiel von FZelle zeigt das korrekte Verwenden von ComputedColumns.
muss nicht, kann aber. In der Tat ist nvarchar unicode und varchar abhängig von der Einstellung in Server, Datenbank, Tabelle, Feld. Lässt sich bei allem per Collate einstellen.
Überprüf bitte, ob die Daten vom Webservice richtig kommen (z.B. per Fiddler) und der Client das ruiniert oder ob am Webservice eine Änderung vorgenommen werden muss.
Prinzipiell sollte man die "String-Spalten" am DB-Server auf Unicode umstellen, sonst wirds auch nix mit griechisch, türkisch oder gar chinesisch.
Das "Gruppiert" bei den Indizes ist ein "Clustered Index" - nur zur Erklärung.
Wie ist die Maschine ausgestattet und kannst du das mal auf eine aktuelle Workstation portieren? (SQL-Server installieren, Backup einspielen, Abfrage 5 mal ausführen)
FieldAttributes ist ja "nur" ein Int. Kannst du mal eine "Select top 10" gegen einen select top "1000" testen - und lass mal den Profiler nebenbei laufen; zusätzlich im Query Analyser die Kosten und Dauer mitloggen lassen. (Profiler gabs auch schon bei 2000; vielleicht gabs hier auch schon die Duration, ansonsten Start und End protokollieren und den Unterschied nehmen)
2-5% CPU Auslastung zeigt der Taskmanager während der Ausführung der Query an.
Der Einwand von MrSparkle ist berechtigt
Wenn du nen Index auf der varchar-Spalte hast, dann macht das keinen Unterschied. Da du nur sehr wenig CPU-Last hast, bedeutet das, dass wenig zu rechnen ist (=keine Stringvergleiche) denn ohne Index würde hier deine CPU-Last schnell auf 100% steigen.
Ich tippe jetzt wirklich drauf, dass dein Flaschenhals der Datendurchsatz zur Festplatte ist. Vermutlich hat der Server deutlich zu wenig Ram, und muss sich den Index und dann die Daten immer häppchenweise von der Platte laden.
Im günstigsten Fall ist der Durchsatz von/zur Festplatte sehr gering und die DB (oder wenigstens die Indizes) liegt praktisch vollständig im Arbeitsspeicher. Dann merkt man auch, dass die CPU-Last hochgeht und der SQL-Server das arbeiten anfängt.
Nachdem es im SQL Query Analyzer genauso langsam ist, liegt es auch definitiv nicht an deiner App.
Zum Clustered Index: Man kann (und sollte unbedingt) einen Index pro Tablle als Clustered definieren. Im Normalfall ist das dann automatisch der Primary Key. Der Clustered Index spiegelt die physikalische sortierung der Tabelle wieder. Manchmal kann es hier sinnvoll sein, das zu ändern, aber meistens nicht.
Im Prinzip könntest du die Projektbeschreibung aber auch in eine andere Tabelle auslagern, dann hast du in der großen Tabelle nur einen Fremdschlüssel auf das jeweilige Projekt. Damit ersparst du dir den Stringvergleich bei jedem Datensatz, und stattdessen wird nur ein Integer verglichen. Das sollte schon eine Menge Performance mit sich bringen.
Das wäre die richtige Richtung zur Normalisierung, aber bei der Performace dürfte sich hier nicht viel tun. Ein string als Index wir nicht als Liste von Strings abgelegt, sondern als Baum.
Ein count(*) ist immer schneller, weil er keine Daten "lesen" muss. wobei hier 5-10 sek. schon langsam sind.
die Daten im Management-Studio werden auch einfach mit einem DataReader geholt, hier interessiert eigentlich die Zeit, wenn alle Daten gelesen wurden, also die Query als "Fertiggestellt" gilt. (unten rechts in der Statusleiste). Eventuell auch mal einfach den SQLProfiler mitlaufen lassen und gucken was der sagt.
Der DataReader kann ja schon gelesen werden, bevor alle Daten da sind. Du bekommst einfach, sobald die Query die ersten Treffer hat, auch schon die ersten Daten zurück. Deswegen hängt dein Programm auch nicht bei "ExecuteReader()" sondern erst in der Read()-Schleife. Das bedeutet, dass die Daten sehr langsam in dein Programm fliessen.