I,Cache Overview
Since the cached data actually comes from the database, how can the cached data be synchronized with the database? In general, the cache should store data that is not changed much or does not require much real-time data. In this way, we only need to regularly update the cache. On the contrary, if the cache is updated too frequently, the significance of using the cache is not great. Therefore, when updating the cache, you need to read a large amount of data from the database at a time, updating the cache too frequently increases the burden on the database.
So what cache expiration policies does the cache in ASP. NET provide?
· Never expire. Like the application, the cache never expires.
· The absolute time expires. The cache expires at a certain time, for example, 5 minutes later.
· The change time expires (smooth expiration ). If the cache is not accessed within a certain period of time, the timeout expires. This is a bit similar to the session. For example, we can set the cache to expire if no one accesses are cached for 5 minutes.
· Dependency expiration. The cache depends on the data in the database or the content in the file. Once the data of some tables in the database changes or the file content changes, the cache automatically expires.
When the cache expires, We need to update the cache. ASP. NET provides two update policies.
· Passive update. The cache is updated manually after expiration.
· Active updates. The cache is updated in the callback method after it expires.
II,Cache performance and expiration policies
First, add two buttons on the page, and double-click the button to implement the click event processing method.
<Asp: button id = "btn_getdatafromcache" runat = "server" onclick = "btn_getdata_click"
TEXT = "read data from cache"/>
<Asp: button id = "btn_getdatafromdb" runat = "server" onclick = "btn_getdatafromdb_click"
TEXT = "read data from database"/>
The first button reads data from the cache.
Note: In this example, the following namespace needs to be using.
Using system. diagnostics; // used to accurately determine the time interval
Using system. Web. caching; // Cache Policy
Using system. IO; // used for file operations
Protected void btn_getdata_click (Object sender, eventargs E)
{
Insertrecord ();
Stopwatch Sw = new stopwatch ();
Sw. Start ();
If (Cache ["data"] = NULL)
{
Response. Write ("invalid cache <br/> ");
}
Else
{
Dataset DS = cache ["data"] As dataset;
Response. Write (string. Format ("query result: {0} <br/>", DS. Tables [0]. Rows [0] [0]);
Response. Write (string. Format ("Time consumed: {0} <br/>", SW. elapsedticks ));
}
}
Here are some notes to explain.
· The insertrecord () method was created by ourselves to insert a record into the database. In this way, we can see whether the data is read from the cache.
The insertrecord () method is as follows:
Private void insertrecord ()
{
Using (sqlconnection conn = new sqlconnection (@ "Server = (local) \ sqlexpress;
Database = Forum; trusted_connection = true "))
{
Conn. open ();
Using (sqlcommand cmd = new sqlcommand ("insert into cachetest (TEST) Values
('Test') ", Conn ))
{
Cmd. executenonquery ();
}
}
}
· If the cache exists, the query results and query time are output. If the cache does not exist, the "invalid cache" is output ".
· The stopwatch class is used to accurately determine the elapsed time. The elapsedticks attribute returns the interval counter scale. The so-called counter scale is the number of times the system counter has passed. Of course, stopwatch and elapsedmilliseconds can return the total number of milliseconds of the interval. Elapsedticks is used because it is a smaller unit of time.
The second button reads data directly from the database.
Protected void btn_getdatafromdb_click (Object sender, eventargs E)
{
Insertrecord ();
Stopwatch Sw = new stopwatch ();
Sw. Start ();
Dataset DS = getdata ();
Response. Write (string. Format ("query result: {0} <br/>", DS. Tables [0]. Rows [0] [0]);
Response. Write (string. Format ("Time consumed: {0} <br/>", SW. elapsedticks ));
}
Here, we encapsulate the Data Reading operation using a getdata () method, which is implemented as follows:
Private dataset getdata ()
{
Dataset DS = new dataset ();
Using (sqlconnection conn = new sqlconnection (@ "Server = (local) \ sqlexpress;
Database = Forum; trusted_connection = true "))
{
Sqldataadapter da = new sqldataadapter ("select count (*) from cachetest", Conn );
Da. Fill (DS );
}
Return Ds;
}
To reflect the cache efficiency, a new cachetest data table is created in the Forum database. The table structure is very simple, as shown in Figure 4-1.
Figure 4-1 cachetest table structure
We have inserted more than 0.1 million records in the table, so that the table size is about MB.
Run the program and click "read data from database", as shown in Figure 4-2,
Figure 4-2 it takes a lot of time to read data from the database
We can see that this operation takes a lot of time.
Because we directly read count (*) from the database, the number displayed in the query result is + 1 each time you click the button. Now, when you click "read data from cache", it will definitely show "invalid cache" because we have not added any cache.
Then, we add three buttons on the page and double-click the button to create an event handling method. The three buttons use different expiration policies to add a cache.
<Asp: button id = "btn_insertnoexpirationcache" runat = "server" text = "insert cache Never Expires"
Onclick = "btn_insertnoexpirationcache_click"/>
<Asp: button id = "btn_insertabsoluteexpirationcache" runat = "server" text = "insert absolute time
Expired cache "onclick =" btn_insertabsoluteexpirationcache_click "/>
<Asp: button id = "btn_insertslidingexpirationcache" runat = "server" text = "insert change time
Expired cache "onclick =" btn_insertslidingexpirationcache_click "/>
The three button click events are handled as follows:
Protected void btn_insertnoexpirationcache_click (Object sender, eventargs E)
{
Dataset DS = getdata ();
Cache. insert ("data", DS );
}
Protected void btn_insertabsoluteexpirationcache_click (Object sender, eventargs E)
{
Dataset DS = getdata ();
Cache. insert ("data", DS, null, datetime. Now. addseconds (10), timespan. Zero );
}
Protected void btn_insertslidingexpirationcache_click (Object sender, eventargs E)
{
Dataset DS = getdata ();
Cache. insert ("data", DS, null, datetime. maxvalue, timespan. fromseconds (10 ));
}
Let's analyze the three expiration policies.
· Never expire. Directly assign the cached key and value.
· The absolute time expires. Datetime. Now. addseconds (10) indicates that the cache expires after 10 seconds, and timespan. Zero indicates that the smooth expiration policy is not used.
· The change time expires (smooth expiration ). Datetime. maxvalue indicates that the absolute time expiration policy is not used, and timespan. fromseconds (10) indicates that the cache expires after 10 seconds of no access.
Here, we use the insert () method to add cache. In fact, the cache also has an add () method to add items to the cache. The difference is that the add () method can only add items not in the cache. If an existing item in the cache is added, it will fail (but will not throw an exception), while insert () the method can overwrite the original items.
Note: Unlike the application, you do not need to use the lock operation when inserting the cache. The cache will process the concurrency by itself.
Now, we can open the page to test the three expiration policies.
1. Click the "read data from cache" button to prompt "invalid cache ".
2. Click "read data from database". The query result shows that the total number of records is 100646.
3. click "insert never expire cache" and then click the "read data from cache" button consecutively. You can find that the cache has never expired no matter how long it has been, in addition, the query results of the record show that the value remains unchanged. The difference is that the efficiency of reading data from the cache is several orders of magnitude higher than that of reading data from the database. As shown in Figure 4-3, you can compare it with Figure 4-2.
Figure 4-3 time spent reading data from the cache
4. click "insert Absolute Time Expiration cache", and then click the "read data from cache" button consecutively. After about 10 seconds, the page prompts "invalid cache", indicating that the cache has expired.
5. click "insert Change Time Expiration cache", and then click the "read data from cache" button consecutively. The cache never expires. If we click the button again after 10 seconds, the page prompts "invalid cache", indicating that the cache has expired.
Let's take a look at the dependency expiration policy. The so-called dependency expiration means that the cache becomes invalid after the content of the cached dependency item (such as a file) changes. Due to the length, only file dependencies are described here. We add two buttons on the page and double-click the button to add the click event processing method.
<Asp: button id = "btn_modifyfile" runat = "server" text = "Modify file" onclick = "btn_modifyfile _
Click "/>
<Asp: button id = "btn_addfiledependencycache" runat = "server" text = "insert file dependency cache"
Onclick = "btn_addfiledependencycache_click"/>
In this example, the cache is dependent on a TXT text file. For this reason, a test.txt text file is first created in the project. Click Modify file to modify the file.
Protected void btn_modifyfile_click (Object sender, eventargs E)
{
Filestream FS = new filestream (server. mappath ("test.txt"), filemode. append,
Fileaccess. Write );
Streamwriter Sw = new streamwriter (FS );
Sw. writeline (datetime. Now. tostring ());
Sw. Close ();
FS. Close ();
}
We modify the file by writing the current time at the end of the file. The insert file dependency cache button event processing method is as follows:
Protected void btn_addfiledependencycache_click (Object sender, eventargs E)
{
Cachedependency Cd = new cachedependency (server. mappath ("test.txt "));
Dataset DS = getdata ();
Cache. insert ("data", DS, Cd );
}
It is also easy to add a file dependency cache. A file dependency is associated through cachedependency.
Now you can open the page for testing.
1. Click the "read data from cache" button to prompt "invalid cache ".
2. Click "read data from database". The query result shows that the total number of records is 100710.
3. click the insert file dependency cache button, and then click the read data from cache button consecutively. You can find that the cache has never expired no matter how long it has been, in addition, the query results of the record show that the value remains unchanged.
4. Click "Modify file" and then click "read data from cache" to prompt "invalid cache ". Because the file has been modified, the cache of the dependent file immediately becomes invalid.
III,Cache update policy
Finally, we will discuss the cache update policy. Passive update is usually used in web programs. Passive update determines whether the cache is empty when calling data. If it is empty, the cache is updated first and then the data is read from the cache, if it is not null, data is directly read from the cache. You can modify the Click Event Processing Method of the "read data from cache" button to the following to implement passive update.
Protected void btn_getdata_click (Object sender, eventargs E)
{
Insertrecord ();
Dataset DS = new dataset ();
Stopwatch Sw = new stopwatch ();
Sw. Start ();
If (Cache ["data"] = NULL)
{
DS = getdata ();
Cache. insert ("data", DS, null, datetime. Now. addseconds (10), timespan. Zero );
}
Else
{
DS = cache ["data"] As dataset;
}
Response. Write (string. Format ("query result: {0} <br/>", DS. Tables [0]. Rows [0] [0]);
Response. Write (string. Format ("Time consumed: {0} <br/>", SW. elapsedticks ));
}
We can see that if no one accesses the data cache, it will not be updated. It will be updated only when the cache is accessed and it is found that the cache is invalid. The obvious drawback is that it takes a long time to query the update operation when the cache expires. We can use the cache callback function to automatically update the cache after expiration.
Protected void btn_insertactiveupdatecache_click (Object sender, eventargs E)
{
Dataset DS = getdata ();
Cache. insert ("data", DS, null, datetime. Now. addseconds (10), timespan. Zero,
Cacheitempriority. Default, cacheremovedcallback );
}
The last parameter indicates that the cacheremovedcallback () method is automatically called after the cache is removed. The method is implemented as follows.
Private void cacheremovedcallback (string key, object value, cacheitemremovedreason
Removedreason)
{
Dataset DS = getdata ();
Cache. insert (Key, DS, null, datetime. Now. addseconds (10), timespan. Zero, cacheitempriority.
Default, cacheremovedcallback );
}
In the callback method, we insert a cache that supports callback again. In this way, the cache can be automatically updated after it is removed. After talking about so many cache creation methods, the reader may ask how to manually remove the cache? For example, to remove the key = "data" cache, you only need:
Cache. Remove ("data ");
You may immediately think of using the cache. removeall () method to remove all the caches. However, the cache does not provide such a method. We can only remove all the caches through traversal.
Idictionaryenumerator cacheenum = httpruntime. cache. getenumerator ();
While (cacheenum. movenext ())
{
Cache. Remove (cacheenum. Key. tostring ());
}
IV,Cache Summary
Similarly, we end the discussion of cache with several questions in section 1.
· Physical storage location. Server Memory.
· Storage type restrictions. Any type.
· Range of status usage. The current request context, which is shared by all users.
· Storage size limit. Any size.
· Lifecycle. Multiple expiration policies are available to control cache destruction.
· Security and performance. Data is always stored on the server, which is highly secure but not easy to store too much data.
· Advantages and disadvantages and precautions. Quick data retrieval and rich expiration policies. Do not put data that requires high real-time performance into the cache. Constantly updating the cache will put pressure on the database.