Autor: Iqbal Kan
Con la explosión de las aplicaciones web de transacciones extremadamente altas, SOA, grid computing y otras aplicaciones de servidor, el almacenamiento de datos no puede mantenerse al día. La razón es que el almacenamiento de datos no puede seguir agregando más servidores para escalar, a diferencia de las arquitecturas de aplicaciones extremadamente escalables.
En estas situaciones, la caché distribuida en memoria ofrece una excelente solución para los cuellos de botella en el almacenamiento de datos. Abarca varios servidores (llamados un clúster) para agrupar su memoria y mantener todo el caché sincronizado entre servidores. Y puede seguir creciendo este clúster de caché sin cesar, al igual que los servidores de aplicaciones. Esto reduce la presión sobre el almacenamiento de datos para que ya no sea un cuello de botella de escalabilidad.
Hay dos formas principales en que las personas usan un caché distribuido:
Cache-aside es una técnica muy poderosa y le permite realizar consultas de base de datos complejas que involucran uniones y consultas anidadas y manipular datos de la forma que desee. A pesar de eso, Leer de parte a parte / Escriba por medio de tiene varias ventajas sobre el almacenamiento en caché, como se menciona a continuación:
La lectura/escritura simultánea no está diseñada para usarse para todos los accesos a datos en su aplicación. Es más adecuado para situaciones en las que está leyendo filas individuales de la base de datos o leyendo datos que pueden asignarse directamente a un elemento de caché individual. También es ideal para datos de referencia que deben guardarse en la memoria caché para lecturas frecuentes, aunque estos datos cambien periódicamente.
Un controlador de lectura directa se registra con el servidor de caché y permite que el caché lea directamente los datos de la base de datos. los NCache El servidor proporciona una interfaz de controlador de lectura directa que debe implementar. Esto permite NCache para llamar a su controlador de lectura completa.
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()
realiza ciertas tareas de asignación de recursos, como establecer conexiones con la fuente de datos principal, mientras que Dispose()
está destinado a restablecer todas esas asignaciones. LoadFromSource()
es lo que llama el caché para leer los objetos.
Se invoca el controlador de escritura simultánea cuando la memoria caché necesita escribir en la base de datos a medida que se actualiza la memoria caché. Normalmente, la aplicación emite una actualización del caché a través de agregar, insertar o eliminar.
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()
realiza tareas de asignación de recursos como establecer conexiones con la fuente de datos, mientras que Dispose()
está destinado a restablecer todas esas asignaciones. Guardar es el método que llama la memoria caché para escribir objetos.
El siguiente código de ejemplo muestra el uso de las capacidades de lectura/escritura simultáneas de la memoria caché desde una aplicación sencilla de Windows
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);
...
}
}