Autor: Iqbal Khan
Mit der Explosion von Webanwendungen mit extrem hohen Transaktionsraten, SOA, Grid-Computing und anderen Serveranwendungen kann die Datenspeicherung nicht mithalten. Der Grund dafür ist, dass die Datenspeicherung im Gegensatz zu extrem skalierbaren Anwendungsarchitekturen nicht ständig weitere Server zum Aufskalieren hinzufügen kann.
In diesen Situationen bietet der verteilte In-Memory-Cache eine hervorragende Lösung für Engpässe bei der Datenspeicherung. Es umfasst mehrere Server (Cluster genannt), um deren Speicher zusammenzufassen und den gesamten Cache serverübergreifend zu synchronisieren. Und es kann diesen Cache-Cluster endlos erweitern, genau wie die Anwendungsserver. Dadurch wird der Druck auf die Datenspeicherung verringert, so dass sie kein Skalierbarkeitsengpass mehr darstellt.
Es gibt zwei Hauptmethoden, wie Benutzer einen verteilten Cache verwenden:
Cache-aside ist eine sehr leistungsfähige Technik, mit der Sie komplexe Datenbankabfragen mit Verknüpfungen und verschachtelten Abfragen ausführen und Daten beliebig manipulieren können. Trotzdem, Durchlesen / Durchschreiben hat verschiedene Vorteile gegenüber Cache-Aside, wie unten erwähnt:
Read-Through/Write-Through ist nicht für den gesamten Datenzugriff in Ihrer Anwendung vorgesehen. Es eignet sich am besten für Situationen, in denen Sie entweder einzelne Zeilen aus der Datenbank lesen oder Daten lesen, die direkt einem einzelnen Cache-Element zugeordnet werden können. Es ist auch ideal für Referenzdaten, die für häufiges Lesen im Cache gehalten werden sollen, obwohl sich diese Daten regelmäßig ändern.
Ein Read-Through-Handler ist beim Cache-Server registriert und ermöglicht dem Cache, Daten direkt aus der Datenbank zu lesen. Die NCache Server stellt eine Read-Through-Handler-Schnittstelle bereit, die Sie implementieren müssen. Das ermöglicht NCache um Ihren Read-Through-Handler aufzurufen.
public class SqlReadThruProvider : IReadThruProvider
{
private SqlConnection _connection;
// Called upon startup to initialize connection
public void Init(IDictionary parameters, string cacheId)
{
_connection = new SqlConnection(parameters["connstring"]);
_connection.Open();
}
// Called at the end to close connection
public void Dispose()
{
_connection.Close();
}
// Responsible for loading object from external data source
public ProviderCacheItem LoadFromSource(string key)
{
string sql = "SELECT * FROM Customers WHERE ";
sql += "CustomerID = @ID";
SqlCommand cmd = new SqlCommand(sql, _connection);
cmd.Parameters.Add("@ID", System.Data.SqlDbType.VarChar);
// Let's extract actual customerID from "key"
int keyFormatLen = "Customers:CustomerID:".Length;
string custId = key.Substring(keyFormatLen,
key.Length - keyFormatLen);
cmd.Parameters["@ID"].Value = custId;
// fetch the row in the table
SqlDataReader reader = cmd.ExecuteReader();
Customers customer = new Customers();
// copy data from "reader" to "cust" object
FillCustomers(reader, customer);
ProviderCacheItem cacheItem = new ProviderCacheItem(customer);
// specify a SqlCacheDependency for this object
CacheDependency dep = new SqlCacheDependency(_connection.ToString(), cmd.ToString());
cacheItem.Dependency = dep;
return cacheItem;
}
Init()
führt bestimmte Ressourcenzuweisungsaufgaben aus, z. B. das Herstellen von Verbindungen zur Hauptdatenquelle, während Dispose()
soll alle diese Zuweisungen zurücksetzen. LoadFromSource()
ruft der Cache auf, um die Objekte durchzulesen.
Write-Through-Handler wird aufgerufen, wenn der Cache in die Datenbank schreiben muss, während der Cache aktualisiert wird. Normalerweise gibt die Anwendung eine Aktualisierung für den Cache durch Hinzufügen, Einfügen oder Entfernen aus.
public class SqlWriteThruProvider : IWriteThruProvider
{
private SqlConnection _connection;
// Called upon startup to initialize connection
public void Init(IDictionary parameters, string cacheId)
{
_connection = new SqlConnection((string)parameters["connstring"]);
_connection.Open();
}
// Called at the end to close connection
public void Dispose()
{
_connection.Close();
}
public OperationResult WriteToDataSource(WriteOperation operation)
{
int rowsChanged = 0;
OperationResult result = new OperationResult(operation, OperationResult.Status.Failure);
ProviderCacheItem cacheItem = operation.ProviderItem;
Customers cust = cacheItem.GetValue<Customers>();
string[] customer = {cust.Id,cust.ContactName,cust.CompanyName,
cust.Address,cust.City, cust.Country,cust.PostalCode,
cust.Phone,cust.Fax};
SqlCommand cmd = _connection.CreateCommand();
cmd.CommandText = String.Format(CultureInfo.InvariantCulture,
"Update dbo.Customers " + "Set CustomerID='{0}'," +
"ContactName='{1}',CompanyName='{2}'," +
"Address='{3}',City='{4}'," +
"Country='{5}',PostalCode='{6}'," +
"Phone='{7}',Fax='{8}'" +
"Where CustomerID = '{0}'", customer);
rowsChanged = cmd.ExecuteNonQuery();
if (rowsChanged > 0)
{
result.OperationStatus = OperationResult.Status.Success;
return result;
}
return result;
}
}
Init()
führt Ressourcenzuweisungsaufgaben wie das Herstellen von Verbindungen zur Datenquelle durch, während Dispose()
soll alle diese Zuweisungen zurücksetzen. Save ist die Methode, die der Cache für Write-Through-Objekte aufruft.
Der folgende Beispielcode zeigt die Verwendung der Read-Through/Write-Through-Funktionen des Caches aus einer einfachen Windows-Anwendung
using Alachisoft.NCache.Client;
...
internal class MainForm : System.Windows.Forms.Form
{
/// Fetches record from the cache, which internally accesses the
/// datasource using read-thru provider
private void OnClickFind(object sender, System.EventArgs e)
{
Customer customer;
Cache cache = NCache.Caches[CacheName];
string key = cboCustomerID.Text.Trim();
string providerName = cboReadThruProvider.Text;
customer = (Customer) cache.Get(key,
providerName,
DSReadOption.ReadThru);
...
}
/// Updates the record using the cache, which internally accesses
/// the datasource using write-thru provider
private void OnClickUpdate(object sender, System.EventArgs e)
{
Cache cache = NCache.Caches[CacheName];
Customer customer = new Customer();
...
string key = customer.CustomerID;
string providerName = cboWriteThruProvider.Text;
cache.Insert(key, new CacheItem(customer), DSWriteOption.WriteThru, providerName, null);
...
}
}