분산 캐시에서 Read-through 및 Write-through 사용

저자: 이크발 칸

트랜잭션이 매우 높은 웹 앱, SOA, 그리드 컴퓨팅 및 기타 서버 응용 프로그램의 폭발적인 증가로 인해 데이터 스토리지가 따라잡을 수 없습니다. 그 이유는 확장성이 뛰어난 애플리케이션 아키텍처와 달리 데이터 스토리지가 확장을 위해 더 많은 서버를 계속 추가할 수 없기 때문입니다.

이러한 상황에서 인메모리 분산 캐시는 데이터 스토리지 병목 현상에 대한 탁월한 솔루션을 제공합니다. 여러 서버(클러스터라고 함)에 걸쳐 있어 메모리를 함께 풀링하고 서버 간에 동기화된 모든 캐시를 유지합니다. 그리고 애플리케이션 서버와 마찬가지로 이 캐시 클러스터를 끝없이 확장할 수 있습니다. 이렇게 하면 데이터 스토리지에 대한 부담이 줄어들어 더 이상 확장성 병목 현상이 발생하지 않습니다.

사람들이 분산 캐시를 사용하는 두 가지 주요 방법이 있습니다.

  • 캐시 제외: 이것은 애플리케이션이 데이터베이스에서 읽고 쓰는 일을 담당하고 캐시가 데이터베이스와 전혀 상호 작용하지 않는 곳입니다. 캐시는 더 빠르고 확장 가능한 인메모리 데이터 저장소로 "보관"됩니다. 애플리케이션은 데이터베이스에서 아무것도 읽기 전에 캐시를 확인합니다. 그리고 애플리케이션은 데이터베이스를 업데이트한 후 캐시를 업데이트합니다. 이러한 방식으로 애플리케이션은 다음을 보장합니다. 캐시는 데이터베이스와 동기화된 상태로 유지됩니다..
  • 연속 읽기/쓰기(RT/WT): 여기서 애플리케이션은 캐시를 기본 데이터 저장소로 취급하고 데이터를 읽고 데이터를 씁니다.. 캐시는 이 데이터를 읽고 데이터베이스에 기록함으로써 이 책임의 적용을 완화합니다.
Read-Through/Write-Through 캐싱 아키텍처
그림 1: Read-Through/Write-Through 캐싱 아키텍처

Cache-aside에 대한 Read-through 및 Write-through의 이점

캐시 제외는 매우 강력한 기술이며 조인 및 중첩 쿼리와 관련된 복잡한 데이터베이스 쿼리를 실행하고 원하는 방식으로 데이터를 조작할 수 있습니다. 그럼에도 불구하고, 전체 읽기 / 연속 기입 아래에 언급된 것처럼 캐시 제외에 비해 다양한 이점이 있습니다.

  • 애플리케이션 코드 단순화: 캐시 제외 접근 방식에서는 애플리케이션 코드가 계속 복잡하고 데이터베이스에 직접 의존하며 여러 애플리케이션이 동일한 데이터를 처리하는 경우 코드 중복도 발생합니다. Read-through/Write-through는 일부 데이터 액세스 코드를 애플리케이션에서 캐싱 계층으로 이동합니다. 이는 애플리케이션을 극적으로 단순화하고 데이터베이스를 훨씬 더 명확하게 추상화합니다.
  • Read-through로 더 나은 읽기 확장성: 하는 상황이 많다. 캐시 항목 만료 여러 병렬 사용자 스레드가 결국 데이터베이스에 도달합니다. 여기에 수백만 개의 캐시 항목과 수천 개의 병렬 사용자 요청을 곱하면 데이터베이스의 로드가 눈에 띄게 높아집니다. 그러나 Read-through는 데이터베이스에서 최신 복사본을 가져오는 동안 캐시에 캐시 항목을 유지합니다. 그런 다음 캐시 항목을 업데이트합니다. 그 결과 애플리케이션은 이러한 캐시 항목에 대해 데이터베이스로 이동하지 않으며 데이터베이스 로드가 최소로 유지됩니다.
  • Write-behind로 쓰기 성능 향상: 캐시 배제에서는 애플리케이션이 데이터베이스를 동기식으로 직접 업데이트합니다. 반면, 뒤에 쓰기 애플리케이션이 캐시를 빠르게 업데이트하고 반환할 수 있습니다. 그런 다음 캐시가 백그라운드에서 데이터베이스를 업데이트하도록 합니다.
  • Write-behind로 데이터베이스 확장성 향상: Write-behind를 사용하면 데이터베이스 쓰기가 캐시 업데이트만큼 빠르게 수행되지 않으므로 데이터베이스에 대한 부담이 크지 않도록 조절 제한을 지정할 수 있습니다. 또한 데이터베이스 쓰기가 사용량이 적은 시간에 발생하도록 예약하여 다시 압력을 최소화할 수 있습니다.
  • 만료 시 캐시 자동 새로 고침: 전체 읽기 캐시가 만료되면 데이터베이스에서 개체를 자동으로 다시 로드할 수 있습니다. 이는 최신 데이터가 항상 캐시에 있기 때문에 애플리케이션이 피크 시간에 데이터베이스에 도달할 필요가 없음을 의미합니다.
  • 데이터베이스 변경 시 캐시 자동 새로 고침: Read-through를 사용하면 데이터베이스에서 해당 데이터가 변경될 때 캐시가 데이터베이스에서 개체를 자동으로 다시 로드할 수 있습니다. 이것은 캐시가 항상 최신 상태이며 최신 데이터가 항상 캐시에 있기 때문에 피크 시간에 애플리케이션이 데이터베이스에 도달할 필요가 없음을 의미합니다.

Read-Through/Write-through는 애플리케이션의 모든 데이터 액세스에 사용하기 위한 것이 아닙니다. 데이터베이스에서 개별 행을 읽거나 개별 캐시 항목에 직접 매핑할 수 있는 데이터를 읽는 상황에 가장 적합합니다. 또한 이 데이터가 주기적으로 변경되더라도 자주 읽을 수 있도록 캐시에 보관해야 하는 참조 데이터에 이상적입니다.

Read-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() 이러한 모든 할당을 재설정하기 위한 것입니다. 저장은 연속 기입 개체에 대한 캐시 호출 방법입니다.

애플리케이션에서 Read-Through 및 Write-Through 호출

다음 샘플 코드는 간단한 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);
        
        ...
    }
}

다음에 무엇을할지?

© 저작권 Alachisoft 2002 - . 판권 소유. NCache 는 Diyatech Corp.의 등록상표입니다.