Cache cache (car cleaning)

When I first came into contact with Cache, it was in WebForm. When I first came into contact with Cache, I never forgot that cache was the Pinyin for car cleaning.

Client Browser Cache https://blog.csdn.net/y874961524/article/details/61419716

CDN Caching Principle https://www.cnblogs.com/shijingxiang/articles/5179032.html

Aliyun CDN Open Settings https://jingyan.baidu.com/article/948f5924f1d642d80ff5f980.html

There is a saying that the first step in system performance optimization is to use caching, so caching is really important.

Cache:

In fact, it is an effect-the goal is to find a place to store the data points after the first acquisition and use them directly after the first acquisition, so as to improve the efficiency of each subsequent acquisition of data. When reading the configuration file, put the information in the static field, which is the cache. Caching is ubiquitous.

 

 

Let's ask for a website to open developer tools

 

 

The benefits of client caching:

1. Shortening the network path and speeding up the response speed

2. Reduce requests and server pressure

How does browser caching actually work?

Open a web page, browser - - - Request - - - server - - - processing request will respond - - - browser display

Http protocol, data transmission format (protocol, like communication between two people, in what language)

Whether the information is cached or not must be controlled by the server. ResponseHeader -- Cache -- Control specifies the caching strategy. Browsers see this and store it.

First request server:

 

 

Request the server again

 

 

 

DNS is the first jump of the Internet, DNS cache is CDN, content distribution network, CDN is accelerated cache

No request with CDN:

 

 

Using CDN caching

 

 

Reverse proxy:

1. Isolate the network and protect the server (save public IP)

2. Network Acceleration, Reverse Proxy Dual Network Card

3. Load Balancing

4. Caching (with CDN, also identify header, compress to a physical path/memory)

Why is it called reverse agent? Because he is a proxy, the general proxy, is between the client and the server, there is a proxy to deal with. But the agent is installed on the server side.

 

 

 

Several cache routines are the same, but their locations and impact ranges are different.

Client caching: affects only current users

CDN caching: for a group of users

Reverse proxy cache: for all users.

Client cache, memory or hard disk, next time directly. Cookie, with memory or hard disk, browsers carry information with them every time they request a server.

When to use caching?

1. Repeated requests, 100 people visit the home page, everyone actually does the same thing, not repetition.

2. Good time-consuming resources

3. The result remains unchanged

There is a third-party data storage and acquisition area below:

 /// <summary>
 /// Places for third party data storage and acquisition
 /// </summary>
 public class CustomCache
 {
     /// <summary>
     /// private:Private Data Container, Security
     /// static: Not be GC
     ///   Dictionary: High efficiency in reading and writing
     /// </summary>
     private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>();

     public static void Add(string key, object oVaule)
     {
         CustomCacheDictionary.Add(key, oVaule);
     }

     /// <summary>
     /// Demand in Get Do before Exists Testing
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="key"></param>
     /// <returns></returns>
     public static T Get<T>(string key)
     {
         return (T)CustomCacheDictionary[key];
     }

     public static bool Exists(string key)
     {
         return CustomCacheDictionary.ContainsKey(key);
     }

     public static T GetT<T>(string key, Func<T> func)
     {
         T t = default(T);
         if (!CustomCache.Exists(key))
         {
             t = func.Invoke();
             CustomCache.Add(key, t);
         }
         else
         {
             t = CustomCache.Get<T>(key);
         }
         return t;
     }
 }

Unique Identification of Access Data: 1 Unique and 2 Reproducible

for (int i = 0; i < 5; i++)
{
    Console.WriteLine($"Obtain{nameof(DBHelper)} {i}second {DateTime.Now.ToString("yyyyMMdd HHmmss.fff")}");
    //List<Program> programList = DBHelper.Query<Program>(123);
    List<Program> programList = null;
    string key = $"{nameof(DBHelper)}_Query_{123}";
    //Unique Identification of Access Data:1 The only 2 can reproduce
    //if (!CustomCache.Exists(key))
    //{
    //    programList = DBHelper.Query<Program>(123);
    //    CustomCache.Add(key, programList);
    //}
    //else
    //{
    //    programList = CustomCache.Get<List<Program>>(key);
    //}
    programList = CustomCache.GetT<List<Program>>(key, () => DBHelper.Query<Program>(123));
}

 

 /// <summary>
 /// Database query
 /// </summary>
 public class DBHelper
 {
     /// <summary>
     /// 1 Time-consuming and resource-consuming
     /// 2 When the parameters are fixed, the results remain unchanged.
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="index"></param>
     /// <returns></returns>
     public static List<T> Query<T>(int index)
     {
         Console.WriteLine("This is {0} Query", typeof(DBHelper));
         long lResult = 0;
         for (int i = index; i < 1000000000; i++)
         {
             lResult += i;
         }
         List<T> tList = new List<T>();
         for (int i = 0; i < index % 3; i++)
         {
             tList.Add(default(T));
         }

         return tList;
     }

 }

Cache optimization performance, the core is result reuse, the next request or the last result. If there is a change in the database, isn't it using an incorrect data? Yes, caching is inevitable, caching will inevitably have dirty data, of course, we will also categorize to minimize dirty data.

User-role-menu, multi-user privilege check + more resource-consuming + relatively stable, very suitable for caching, caching method should be user id as key, menu list as value.

   string name = "bingle";
   List<string> menu = new List<string>();
   if (!CustomCache.Exists(name))
   {
       menu = new List<string>() { "123", "125553", "143", "123456" };
       CustomCache.Add(name, menu);
   }
   else
   {
       menu = CustomCache.Get<List<string>>(name);
   }

If bingle's permissions change, the cache should fail. Data updates affect single-handed caching. The conventional approach is Remove rather than update, because caching is only used to improve efficiency, not data preservation, so there is no need to update, just delete, if it is really used next time, then initialize it.

The CustomCache class adds the method of deleting the cache:
 public static void Remove(string key)
 {
     CustomCacheDictionary.Remove(key);
 }
 string name = "bingle";
 CustomCache.Remove(name);

 List<string> menu = new List<string>();
 if (!CustomCache.Exists(name))
 {
     menu = new List<string>() { "123", "125553", "143" };
     CustomCache.Add(name, menu);
 }
 else
 {
     menu = CustomCache.Get<List<string>>(name);
 }

A menu was deleted, affecting a large number of users. According to the menu - Zhaojue Temple - find the user - assemble each key and go to Remove. But this method is not good. In order to increase the task of database caching, the biggest problem is the amount of data. Caching is the 28 principle. Only 20% of the hot users can cache, which is too expensive.

You can choose to add a RemoveAll method

 public static void RemoveAll()
 {
     CustomCacheDictionary.Clear();
 }

Or, if the menu is deleted, can it affect only part of the cached data?

1. When adding caches, key s take rules, such as permissions containing _menu_

2. When cleaning up, just delete key s containing _menu_

 /// <summary>
 /// Delete by condition
 /// </summary>
 /// <param name="func"></param>
 public static void RemoveCondition(Func<string, bool> func)
 {
     List<string> keyList = new List<string>();
     lock (CustomCache_Lock)
         foreach (var key in CustomCacheDictionary.Keys)
         {
             if (func.Invoke(key))
             {
                 keyList.Add(key);
             }
         }
     keyList.ForEach(s => Remove(s));
 }

The third party modifies the data and the cache doesn't know, so there's no way to do that.

a can call the interface to clean up the cache, b system to modify the data, and call the cache update under the notification of cxiyong. b can only tolerate dirty data, but it can add time restrictions to reduce the impact time.

Time, expiration strategy:

Permanent validity - right now.

Absolute expiration:

There's a time point when it's overdue.

Sliding expiration:

How long will it expire and how long will it last if the period update/query/check exists?

 /// <summary>
 /// Active clean-up
 /// </summary>
 static CustomCache()
 {
     Task.Run(() =>
     {
         while (true)
         {
             try
             {
                 List<string> keyList = new List<string>();
                 lock (CustomCache_Lock)
                 {
                     foreach (var key in CustomCacheDictionary.Keys)
                     {
                         DataModel model = (DataModel)CustomCacheDictionary[key];
                         if (model.ObsloteType != ObsloteType.Never && model.DeadLine < DateTime.Now)
                         {
                             keyList.Add(key);
                         }
                     }
                     keyList.ForEach(s => Remove(s));
                 }
                 Thread.Sleep(1000 * 60 * 10);
             }
             catch (Exception ex)
             {
                 Console.WriteLine(ex.Message);
                 continue;
             }
         }
     });
 }

Multithreading:

List<Task> taskList = new List<Task>();
for (int i = 0; i < 110000; i++)
{
    int k = i;
    taskList.Add(Task.Run(() => CustomCache.Add($"TestKey_{k}", $"TestValue_{k}", 10)));
}
for (int i = 0; i < 100; i++)
{
    int k = i;
    taskList.Add(Task.Run(() => CustomCache.Remove($"TestKey_{k}")));
}
for (int i = 0; i < 100; i++)
{
    int k = i;
    taskList.Add(Task.Run(() => CustomCache.Exists($"TestKey_{k}")));
}
//Thread.Sleep(10*1000);
Task.WaitAll(taskList.ToArray());

Multi-threaded operation of off-site safe containers can cause conflicts

1. Thread Safety Container Concurrent Dictionary

2. Using lock-Add/Remove/traversal can solve the problem, but what about performance?

How to reduce the impact and improve performance? Multiple data containers, multiple locks, and concurrency between containers

To solve the multithreading problem, the CustomCache class was finally modified as follows:

 public class CustomCache 
 {
     //ConcurrentDictionary

     private static readonly object CustomCache_Lock = new object();

     /// <summary>
     /// Active clean-up
     /// </summary>
     static CustomCache()
     {
         Task.Run(() =>
         {
             while (true)
             {
                 try
                 {
                     List<string> keyList = new List<string>();
                     lock (CustomCache_Lock)
                     {
                         foreach (var key in CustomCacheDictionary.Keys)
                         {
                             DataModel model = (DataModel)CustomCacheDictionary[key];
                             if (model.ObsloteType != ObsloteType.Never && model.DeadLine < DateTime.Now)
                             {
                                 keyList.Add(key);
                             }
                         }
                         keyList.ForEach(s => Remove(s));
                     }
                     Thread.Sleep(1000 * 60 * 10);
                 }
                 catch (Exception ex)
                 {
                     Console.WriteLine(ex.Message);
                     continue;
                 }
             }
         });
     }

     /// <summary>
     /// private:Private Data Container, Security
     /// static: Not be GC
     ///   Dictionary: High efficiency in reading and writing
     /// </summary>
     //private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>();

     private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>();

     public static void Add(string key, object oVaule)
     {
         lock (CustomCache_Lock)
             CustomCacheDictionary.Add(key, new DataModel()
             {
                 Value = oVaule,
                 ObsloteType = ObsloteType.Never,
             });
     }
     /// <summary>
     /// Absolute Expiration
     /// </summary>
     /// <param name="key"></param>
     /// <param name="oVaule"></param>
     /// <param name="timeOutSecond"></param>
     public static void Add(string key, object oVaule, int timeOutSecond)
     {
         lock (CustomCache_Lock)
             CustomCacheDictionary.Add(key, new DataModel()
             {
                 Value = oVaule,
                 ObsloteType = ObsloteType.Absolutely,
                 DeadLine = DateTime.Now.AddSeconds(timeOutSecond)
             });
     }
     /// <summary>
     /// Relative expiration date
     /// </summary>
     /// <param name="key"></param>
     /// <param name="oVaule"></param>
     /// <param name="duration"></param>
     public static void Add(string key, object oVaule, TimeSpan duration)
     {
         lock (CustomCache_Lock)
             CustomCacheDictionary.Add(key, new DataModel()
             {
                 Value = oVaule,
                 ObsloteType = ObsloteType.Relative,
                 DeadLine = DateTime.Now.Add(duration),
                 Duration = duration
             });
     }

     /// <summary>
     /// Demand in Get Do before Exists Testing
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="key"></param>
     /// <returns></returns>
     public static T Get<T>(string key)
     {
         return (T)(((DataModel)CustomCacheDictionary[key]).Value);
     }

     /// <summary>
     /// Passive cleaning, requesting data to clean up
     /// </summary>
     /// <param name="key"></param>
     /// <returns></returns>
     public static bool Exists(string key)
     {
         if (CustomCacheDictionary.ContainsKey(key))
         {
             DataModel model = (DataModel)CustomCacheDictionary[key];
             if (model.ObsloteType == ObsloteType.Never)
             {
                 return true;
             }
             else if (model.DeadLine < DateTime.Now)//It's over your last minute now.
             {
                 lock (CustomCache_Lock)
                     CustomCacheDictionary.Remove(key);
                 return false;
             }
             else
             {
                 if (model.ObsloteType == ObsloteType.Relative)//No expired&It's sliding, so it needs to be updated.
                 {
                     model.DeadLine = DateTime.Now.Add(model.Duration);
                 }
                 return true;
             }
         }
         else
         {
             return false;
         }
     }

     /// <summary>
     /// delete key
     /// </summary>
     /// <param name="key"></param>
     public static void Remove(string key)
     {
         lock (CustomCache_Lock)
             CustomCacheDictionary.Remove(key);
     }

     public static void RemoveAll()
     {
         lock (CustomCache_Lock)
             CustomCacheDictionary.Clear();
     }
     /// <summary>
     /// Delete by condition
     /// </summary>
     /// <param name="func"></param>
     public static void RemoveCondition(Func<string, bool> func)
     {
         List<string> keyList = new List<string>();
         lock (CustomCache_Lock)
             foreach (var key in CustomCacheDictionary.Keys)
             {
                 if (func.Invoke(key))
                 {
                     keyList.Add(key);
                 }
             }
         keyList.ForEach(s => Remove(s));
     }

     public static T GetT<T>(string key, Func<T> func)
     {
         T t = default(T);
         if (!CustomCache.Exists(key))
         {
             t = func.Invoke();
             CustomCache.Add(key, t);
         }
         else
         {
             t = CustomCache.Get<T>(key);
         }
         return t;
     }
 }
/// <summary>
/// Cached information
/// </summary>
internal class DataModel
{
    public object Value { get; set; }
    public ObsloteType ObsloteType { get; set; }
    public DateTime DeadLine { get; set; }
    public TimeSpan Duration { get; set; }

    //Data Cleaning Start Event
    public event Action DataClearEvent;
}

public enum ObsloteType
{
    Never,
    Absolutely,
    Relative
}
 public class CustomCacheNew
 {
     //Dynamic initialization of multiple containers and locks
     private static int CPUNumer = 0;//Acquisition system CPU number
     private static List<Dictionary<string, object>> DictionaryList = new List<Dictionary<string, object>>();
     private static List<object> LockList = new List<object>();
     static CustomCacheNew()
     {
         CPUNumer = 4;
         for (int i = 0; i < CPUNumer; i++)
         {
             DictionaryList.Add(new Dictionary<string, object>());
             LockList.Add(new object());
         }

         Task.Run(() =>
         {
             while (true)
             {
                 Thread.Sleep(1000 * 60 * 10);
                 try
                 {
                     for (int i = 0; i < CPUNumer; i++)
                     {
                         List<string> keyList = new List<string>();
                         lock (LockList[i])//Reduce the impact of locks
                         {
                             foreach (var key in DictionaryList[i].Keys)
                             {
                                 DataModel model = (DataModel)DictionaryList[i][key];
                                 if (model.ObsloteType != ObsloteType.Never && model.DeadLine < DateTime.Now)
                                 {
                                     keyList.Add(key);
                                 }
                             }
                             keyList.ForEach(s => DictionaryList[i].Remove(s));
                         }
                     }


                 }
                 catch (Exception ex)
                 {
                     Console.WriteLine(ex.Message);
                     continue;
                 }
             }
         });
     }

Where exactly is the cache used? Which features are suitable for caching?

1. Frequent visits

2. Time-consuming and resource-consuming

3. Relative stability

4. Less bulky

It's not strictly satisfying. It depends on the situation. If you can check it three times at a time, it's worth caching.

Next, caching should be used

Dictionary data

2. Provincial and Urban Areas
Configuration files
4. Website bulletin information
5. Department authority, menu authority
6. Hot Search
7. Category List/Product List
8. Users, in fact, Session is also a manifestation of caching

Stock information price/lottery prize information, which can not be cached, requires high instantaneity. Pictures / videos, these are not good, too big. Commodity reviews, which can be cached, although comment compilation, but this is not important, we do not have to see the latest, and the first page is generally unchanged.

CustomCache can be tested for 100,000/million/10,000 insert/acquire/delete performance.



Tags: ASP.NET network Database DNS less

Posted on Sat, 21 Sep 2019 07:43:45 -0700 by notsleepy