Ich schreibe eine sehr einfache Webanwendung, die als Endpunkt für das Hochladen von Geldtransaktionen von Kunden dient und speichert sie in SQL Server DB. Es akzeptiert Anfragen mit nur 2 Parametern: userid: 'xxx', balancechange: -19.99
. Wenn die Benutzer-ID in der App-Datenbank vorhanden ist, wird der Kontostand geändert. Wenn nicht, wird eine neue Zeile für diese ID erstellt.Korrigieren Sie Gleichzeitigkeitskontrolle in ASP.NET WebAPI + SQL
Der schwierige Teil in all dem ist, dass die Anzahl der Anfragen enorm ist und ich die App so implementieren muss, dass sie so schnell wie möglich funktioniert und Nebenläufigkeitsprobleme behebt (wenn zwei Anfragen für dieselbe ID gleichzeitig ankommen)).
Die App ist eine ASP.NET MVC WebAPI. Ich entschied mich für das gute alte ADO.NET für Geschwindigkeit zu verwenden, und das ist, was ich derzeit haben:
private static readonly object syncLock = new object();
public void UpdateBalance(string userId, decimal balance)
{
lock (syncLock)
{
using (var sqlConnection = new SqlConnection(this.connectionString))
{
var command = new SqlCommand($"SELECT COUNT(*) FROM Users WHERE Id = '{userId}'", sqlConnection);
if ((int)command.ExecuteScalar() == 0)
{
command = new SqlCommand($"INSERT INTO Users (Id, Balance) VALUES ('{userId}', 0)", sqlConnection);
command.ExecuteNonQuery();
}
command = new SqlCommand($"UPDATE Users SET Balance = Balance + {balance} WHERE Id = {userId}", sqlConnection);
command.ExecuteNonQuery();
}
}
}
von einem Controller wie folgt aufgerufen:
[HttpPost]
public IHttpActionResult UpdateBalance(string id, decimal balanceChange)
{
UpdateBalance(id, balanceChange);
return Ok();
}
Das, was ich mit Concurrency concernred bin ist Steuerung mit lock (syncLock)
. Dies würde die App bei hoher Auslastung verlangsamen und es nicht zulassen, dass mehrere Instanzen der App auf verschiedenen Servern bereitgestellt werden. Wie kann man die Steuerung des Nebenläufers hier richtig implementieren?
Hinweis: Ich möchte eine schnelle und DB-unabhängige Art der Implementierung der Steuerung des gemeinsamen Zugriffs verwenden, da sich der aktuelle Speichermechanismus (SQL Server) in der Zukunft ändern kann.
Addition ist assoziativ, also warum sind Sie besorgt über eine Änderung des Gleichgewichts in Reihenfolge? Und wenn Sie mehr Geschwindigkeit wünschen, sollten Sie dies in einer gespeicherten Prozedur anstelle von Inline-Strings (injection-anfällig) einkapseln. – Crowcoder
Ihr SQL-Code ist für SQL-Injektionen geöffnet, da die Variable 'userId' eine Zeichenfolge ist. Verwenden Sie parametrisierte Abfragen. – jgauffin
Ihre Sperren müssen pro Zeile und verteilt sein: Sie benötigen eine DB-Transaktion. Ich verstehe nicht, warum Sie versuchen, es mit einem so brüchigen Ansatz wie diesem umzusetzen. –