저자: 이크발 칸
트랜잭션이 매우 높은 웹 앱, SOA, 그리드 컴퓨팅 및 기타 서버 응용 프로그램의 폭발적인 증가로 인해 데이터 스토리지가 따라잡을 수 없습니다. 그 이유는 확장성이 뛰어난 애플리케이션 아키텍처와 달리 데이터 스토리지가 확장을 위해 더 많은 서버를 계속 추가할 수 없기 때문입니다.
이러한 상황에서 인메모리 분산 캐시는 데이터 스토리지 병목 현상에 대한 탁월한 솔루션을 제공합니다. 여러 서버(클러스터라고 함)에 걸쳐 있어 메모리를 함께 풀링하고 서버 간에 동기화된 모든 캐시를 유지합니다. 그리고 애플리케이션 서버와 마찬가지로 이 캐시 클러스터를 끝없이 확장할 수 있습니다. 이렇게 하면 데이터 스토리지에 대한 부담이 줄어들어 더 이상 확장성 병목 현상이 발생하지 않습니다.
사람들이 분산 캐시를 사용하는 두 가지 주요 방법이 있습니다.
캐시 제외는 매우 강력한 기술이며 조인 및 중첩 쿼리와 관련된 복잡한 데이터베이스 쿼리를 실행하고 원하는 방식으로 데이터를 조작할 수 있습니다. 그럼에도 불구하고, 전체 읽기 / 연속 기입 아래에 언급된 것처럼 캐시 제외에 비해 다양한 이점이 있습니다.
Read-Through/Write-through는 애플리케이션의 모든 데이터 액세스에 사용하기 위한 것이 아닙니다. 데이터베이스에서 개별 행을 읽거나 개별 캐시 항목에 직접 매핑할 수 있는 데이터를 읽는 상황에 가장 적합합니다. 또한 이 데이터가 주기적으로 변경되더라도 자주 읽을 수 있도록 캐시에 보관해야 하는 참조 데이터에 이상적입니다.
read-through 핸들러는 캐시 서버에 등록되어 캐시가 데이터베이스에서 직접 데이터를 읽을 수 있도록 합니다. 그만큼 NCache 서버는 구현해야 하는 Read-through 핸들러 인터페이스를 제공합니다. 이를 통해 NCache Read-through 핸들러를 호출합니다.
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()
기본 데이터 소스에 대한 연결 설정과 같은 특정 리소스 할당 작업을 수행하는 반면 Dispose()
이러한 모든 할당을 재설정하기 위한 것입니다. LoadFromSource()
객체를 통해 읽기 위해 캐시가 호출하는 것입니다.
연속 기입 처리기는 캐시가 업데이트될 때 캐시가 데이터베이스에 기록해야 할 때 호출됩니다. 일반적으로 응용 프로그램은 추가, 삽입 또는 제거를 통해 캐시에 대한 업데이트를 발행합니다.
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()
데이터 소스에 대한 연결 설정과 같은 리소스 할당 작업을 수행하는 반면 Dispose()
이러한 모든 할당을 재설정하기 위한 것입니다. 저장은 연속 기입 개체에 대한 캐시 호출 방법입니다.
다음 샘플 코드는 간단한 Windows 애플리케이션에서 캐시의 read-through/write-through 기능을 사용하는 방법을 보여줍니다.
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);
...
}
}