Utilisation de la lecture et de l'écriture dans le cache distribué

Auteur : Iqbal Khan

Avec l'explosion des applications Web extrêmement transactionnelles, de la SOA, de l'informatique en grille et d'autres applications de serveur, le stockage des données ne peut pas suivre. La raison en est que le stockage de données ne peut pas continuer à ajouter plus de serveurs pour évoluer, contrairement aux architectures d'applications extrêmement évolutives.

Dans ces situations, le cache distribué en mémoire offre une excellente solution aux goulots d'étranglement du stockage de données. Il s'étend sur plusieurs serveurs (appelés cluster) pour regrouper leur mémoire et synchroniser tout le cache entre les serveurs. Et, il peut continuer à développer ce cluster de cache à l'infini, tout comme les serveurs d'applications. Cela réduit la pression sur le stockage des données afin qu'il ne soit plus un goulot d'étranglement pour l'évolutivité.

Il existe deux manières principales d'utiliser un cache distribué :

  • Hors cache : C'est là que l'application est responsable de la lecture et de l'écriture à partir de la base de données et que le cache n'interagit pas du tout avec la base de données. Le cache est "mis de côté" en tant que magasin de données en mémoire plus rapide et plus évolutif. L'application vérifie le cache avant de lire quoi que ce soit dans la base de données. De plus, l'application met à jour le cache après avoir effectué des mises à jour de la base de données. De cette façon, l'application s'assure que le le cache est maintenu synchronisé avec la base de données.
  • Lecture/écriture immédiate (RT/WT) : C'est là que l'application traite le cache comme le magasin de données principal et lit des données à partir de celui-ci et y écrit des données. Le cache est responsable de la lecture et de l'écriture de ces données dans la base de données, déchargeant ainsi l'application de cette responsabilité.
Architecture de mise en cache en lecture/écriture
Figure 1 : Architecture de mise en cache en lecture/écriture

Avantages de la lecture et de l'écriture par rapport au cache

La mise en cache est une technique très puissante qui vous permet d'émettre des requêtes de base de données complexes impliquant des jointures et des requêtes imbriquées et de manipuler les données comme vous le souhaitez. Malgré que, Lire à travers / Ecriture présente divers avantages par rapport au cache, comme mentionné ci-dessous :

  • Simplifiez le code de l'application : Dans l'approche hors cache, votre code d'application continue d'avoir une complexité et une dépendance directe à la base de données et même une duplication de code si plusieurs applications traitent les mêmes données. La lecture/écriture directe déplace une partie du code d'accès aux données de vos applications vers le niveau de mise en cache. Cela simplifie considérablement vos applications et extrait encore plus clairement la base de données.
  • Meilleure évolutivité de la lecture avec Read-through : Il existe de nombreuses situations où un l'élément de cache expire et plusieurs threads utilisateur parallèles finissent par atteindre la base de données. En multipliant cela par des millions d'éléments mis en cache et des milliers de demandes d'utilisateurs parallèles, la charge sur la base de données devient sensiblement plus élevée. Mais, Read-through conserve l'élément de cache dans le cache pendant qu'il en récupère la dernière copie à partir de la base de données. Il met ensuite à jour l'élément de cache. Le résultat est que l'application n'accède jamais à la base de données pour ces éléments de cache et que la charge de la base de données est réduite au minimum.
  • Meilleures performances d'écriture avec Write-behind : En cache-côté, l'application met à jour la base de données directement de manière synchrone. Alors qu'un Écrire derrière permet à votre application de mettre à jour rapidement le cache et de revenir. Ensuite, il laisse le cache mettre à jour la base de données en arrière-plan.
  • Meilleure évolutivité de la base de données avec Write-behind : Avec Write-behind, vous pouvez spécifier des limites de limitation afin que les écritures de la base de données ne soient pas effectuées aussi rapidement que les mises à jour du cache et que la pression sur la base de données ne soit donc pas importante. De plus, vous pouvez programmer les écritures de la base de données pendant les heures creuses, là encore pour minimiser la pression.
  • Actualisation automatique du cache à l'expiration : Lire à travers permet au cache de recharger automatiquement un objet de la base de données lorsqu'il expire. Cela signifie que votre application n'a pas besoin d'accéder à la base de données aux heures de pointe, car les dernières données sont toujours dans le cache.
  • Actualisation automatique du cache lors des modifications de la base de données : La lecture directe permet au cache de recharger automatiquement un objet à partir de la base de données lorsque ses données correspondantes changent dans la base de données. Cela signifie que le cache est toujours frais et que votre application n'a pas besoin d'accéder à la base de données aux heures de pointe, car les dernières données sont toujours dans le cache.

La lecture/écriture immédiate n'est pas destinée à être utilisée pour tous les accès aux données dans votre application. Il convient mieux aux situations où vous lisez des lignes individuelles de la base de données ou lisez des données qui peuvent directement être mappées à un élément de cache individuel. Il est également idéal pour les données de référence destinées à être conservées dans le cache pour des lectures fréquentes, même si ces données changent périodiquement.

Développement d'un gestionnaire de lecture

Un gestionnaire de lecture directe est enregistré auprès du serveur de cache et permet au cache de lire directement les données de la base de données. le NCache Le serveur fournit une interface de gestionnaire de lecture que vous devez implémenter. Cela permet NCache pour appeler votre gestionnaire de lecture.

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() effectue certaines tâches d'allocation de ressources telles que l'établissement de connexions à la source de données principale, tandis que Dispose() est destiné à réinitialiser toutes ces allocations. LoadFromSource() est ce que le cache appelle pour parcourir les objets.

Développement d'un gestionnaire d'écriture immédiate

Le gestionnaire d'écriture immédiate est appelé lorsque le cache doit écrire dans la base de données lors de la mise à jour du cache. Normalement, l'application publie une mise à jour du cache via l'ajout, l'insertion ou la suppression.

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() effectue des tâches d'allocation de ressources telles que l'établissement de connexions à la source de données, tandis que Dispose() est destiné à réinitialiser toutes ces allocations. Save est la méthode que le cache appelle pour les objets en écriture immédiate.

Appel de la lecture et de l'écriture depuis l'application

L'exemple de code suivant illustre l'utilisation des capacités de lecture/écriture du cache à partir d'une simple application 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);
        
        ...
    }
}

Que faire ensuite?

© Copyright Alachisoft 2002 - . Tous droits réservés. NCache est une marque déposée de Diyatech Corp.