Major problems facing Cache Management
As a data center, the cache can be used to add, update, or delete data. Therefore, similar to databases, the cache has issues such as data consistency in transactional and concurrent situations.
The following code describes typical caching methods: Database db = new Database ();
Transaction tx = db. BeginTransaction ();
Try
{
// Read from the cache
MyEntity1 entity1 = cache. Get <MyEntity1> ("pk of entity1 ");
// The cache does not sometimes read data from the database
If (entity1 = null) entity1 = db. Get <MyEntity1> ("pk of entity1 ");
// Process entity1
Updated = db. Update (entity1); // Save the Update of entity1 to the database.
If (updated) cache. Put (entity1); // if the database is updated successfully, the cache is updated.
// Other operations in the transaction
Tx. Commit ();
}
Catch
{
Tx. Rollback ();
Throw;
}
Upper
The following sample code uses the cache in a transactional environment and involves update operations (non-read-only cache). If this is a shared cache, there are many problems with this usage method, for example:
If other processing in the transaction causes an exception, the update to entity1 in the database can be rolled back, But entity1 in the cache has been updated. If this problem is not solved
The entity1 read from the cache is an incorrect data.
Therefore, to use the cache correctly, you must have a complete solution that fully considers transactions and concurrency to ensure data correctness and consistency.
Two levels of cache mechanism for nhib.pdf
Compared with the session, the first-level cache is private and the second-level cache is shared.
The session Object loading search sequence is as follows: 1. Search from the first-level cache; 2. Search from the second-level cache; 3. Search from the database
A level-1 cache acts as an isolated area between transactions. All new, modified, and deleted object in the transaction are invisible to other sessions before the transaction is committed, after the transaction is committed successfully, these updates are applied to the second-level cache in batches.
This level-2 cache mechanism ensures data correctness to a large extent (for example, if the transaction in the previous Sample Code fails, the data will not be updated to the level-2 cache, prevents errors in the second-level cache) and other transaction consistency problems such as ReadUncommited.
In terms of internal implementation, it is easy to manage a level-1 cache. All loaded objects (and objects that have created a proxy but not loaded) are cached in the persistent context (nhib.pdf. engine. statefulPersistenceContext)
Waiting
Newly Added, updated, and deleted entities are cached using three lists. When a transaction is committed, they are applied to the database and the second-level cache (Flush called or because of queries, etc.
The Flush operations automatically executed by NHibernate will also be applied to the database, but will not be applied to the second-level cache. The second-level cache will be updated only after the transaction is committed successfully)
The three lists in NH1.2 are maintained in SessionImpl. There are many new features and code refactoring actions added after NH2.0. The three lists are maintained in nhib.pdf. Engine. ActionQueue.
Because the second-level cache is a shared cache, concurrent update conflicts exist, but the correctness of the second-level cache data must be ensured. Therefore, the processing mechanism is much more complicated. Below are detailed second-level cache processing mechanisms
Primary Structure of level 2 Cache
Main Interface:
Interface responsibilities:
ICache: Unified cache access interface
ICacheProvider: factory class and initialization class. It is used to create an ICache object, initialize the cache server or component at startup, and exit the cache server or component at exit.
Processing Process:
1. Specify the implementation class of ICacheProvider in the configuration file
2. Create an ICacheProvider object at SessionFactory startup, execute the ICacheProvider. Start () method, and create an ICache object for each cache region
3. During the entire operation, nhibache can use the ICache created by SessionFactory to access the cache.
4. Call the ICacheProvider. Stop () method when SessionFactory is disabled.
Object State Conversion:
Taking memcached as an example, the status transition during entity cache is as follows:
1. CacheEntry indicates an object to be stored in the cache or returned from the cache.
CacheEntry contains the disassembled object attribute values (DisassembledState, object [] type, which is the value of each attribute in the array), object version (optimistic
Used when locking), type name. In this way, the domain object we define does not need to implement the Serializable interface, and can be serialized and stored in the cache.
Medium
For primitive
There is no special processing for the entity attributes, disassembly and assembly of type; for composite
Collection and Other Object Attributes of component, one-to-one, and one-to-one are decomposed and then stored in DisassembledState.
Stores the id value of the owner (that is, the cached object, during the assembly process, the relevant objects are retrieved based on this id value and set to this attribute (from level 1 cache, level 2 cache, or data ).
Library Loading depends on the specific settings and runtime status)
2. cacheItem is used to solve the data consistency problem during the concurrent update of the second-level cache (Save the CacheEntry directly to the cache if this problem is not considered), mainly for processing the soft lock mechanism, detailed introduction
3. The processing of converting CacheItem into DictionaryEntry is done by nhib.pdf. Caches. Memcache, which is completely redundant.
Nhib.pdf uses the rule [complete class name # id value] to generate a cache
Key, nhib.pdf. Caches. Memcache will be added before the key generated by nhib.pdf
[Region name @] (if the region name is not set in the hbm file of the class, the region is the complete class name by default, so that the complete class name will be in the cache
Key appears twice)
Memcached has a maximum key length of 250 characters.
When the key exceeds 250 characters, take the hash value of the key as the new memcached
Key value, because there will be hash conflicts, so nhib.pdf. Caches. Memcache constructs a DictionaryEntry object (the original
The MD5 value of the key value is used as the key value of DictionaryEntry, And the cached object is used as the value.
The DictionaryEntry is stored in memcached. When a get object is cached
The key value of DictionaryEntry is compared again to eliminate hash conflicts.
Using memcached in this way is a waste of efficiency. If you are not careful, the complete class name will appear four times in the cache data!
You can consider using the cache based on the mechanism of nhib.pdf and the characteristics of memcached.
Region is used to distinguish different memcached clusters, such as A and B.
The two servers serve as the read-only cache, and the region name is readonly_region; C, D, and E
The three servers serve as the read/write cache. The region name is readwrite_region.
4.
From DictionaryEntry to Memcached
Memcached. ClientLibrary is used to process this section of the Server. For Analysis on Memcached. ClientLibrary, see
Test memcached client-memcacheddotnet
(Memcached. ClientLibrary)
Resolve concurrent update conflicts
Nhib.pdf defines three cache policies: read-only (useage = "read-only") and non-strict read/write (useage = "nonstrict-read-write ") and read/write policy (useage = "read-write ")
Structure for processing concurrent updates
ICacheConcurrencyStrategy
When an ICache object is merged, NHibernate does not directly use the ICache object when operating the cache, but uses ICacheConcurrencyStrategy
This ensures that the system performs second-level cache operations under specific cache policies.
The semantics of ICacheConcurrencyStrategy and ICache interfaces are different. ICache is a pure cache operation interface, while ICacheConcurrencyStrategy is related to the state change of the object.
Semantics of ICacheConcurrencyStrategy
Evict: invalidates cache items
Get, Put, Remove, Clear: Same as ICache, pure cache read, storage, and other operations
Insert, AfterInsert: the method used to add an object. After the object is added to the database, the Insert method is executed. After the transaction is committed, the AfterInsert method is executed. The specific cache policy determines how the second-level cache is processed in these methods.
Update, AfterUpdate: method used to update an object. After the object is updated to the database, the Update method is executed. After the transaction is committed, the AfterUpdate method is executed. The specific cache policy determines how the second-level cache is processed in these methods.
Lock and Release: These two methods respectively Lock and unlock the cache items. In terms of semantics, the Lock method is executed on the cache items when the transaction starts to update the entity, and the Release method is executed on the cache items after the transaction is committed. In these methods, the specific cache policy determines how to process the second-level cache.
In
In the preceding figure, the conversion from CacheEntry to CacheItem is completed by the ICacheConcurrencyStrategy interface.
Yes, CacheItem is used only by ICacheConcurrencyStrategy, and other places inside nhibitem that need to interact with the cache are used
CacheEntry and ICacheConcurrencyStrategy Interfaces
ReadOnly Policy
In this scenario, data is not updated, and NHibernate does not update second-level cache data. Objects using the read-only policy cannot perform the update operation. Otherwise, an exception is thrown and new or delete operations can be performed. The read-only policy is only written to the cache after the entity is loaded from the database.
UnstrictReadWrite Policy
The application scenario is that data is updated, but the frequency is not high, and the number of concurrent storage is small.
The entity that uses this policy will not operate on the level-2 Cache when it is added. When updating, it simply deletes the level-2 cache data (the level-2 cache data will be deleted in the Update and AfterUpdate methods ), in this case, data will be loaded from the database and cached again in the future or subsequent requests.
Because no lock is used for cache data during the update process and no version check is performed during reading, Data Consistency cannot be guaranteed during concurrent access. The following is an example scenario:
1, 2: Request 1 executes updates in the transaction. NH updates the database and deletes the data from the second-level cache.
3: some operations (such as ISession. Evict) cause the data to be invalid in the first-level cache of request 1.
4, 5: Request 2 loads the data from the database and puts it into the second-level cache. Because request 2 is in another transaction context, the loaded data does not include the update of request 1.
6: Request 1 needs to re-load the data, because the first-level cache does not exist, so read from the second-level cache, the result will be read as an error data
ReadWrite Policy
The application scenario is that data may be updated frequently concurrently. nhibted ensures the transaction isolation level of ReadCommitted. If the database isolation level is RepeatableRead, this policy can also basically ensure that the secondary cache meets the isolation level of RepeatableRead.
NHibernate achieves this goal through version, timestamp check, soft lock, and other mechanisms.
Soft
The principle of lock is relatively simple. If the transaction needs to update data whose key is 839, first create a soft
The lock object is saved to the cache with the 839 key. (If the 839 key is used to cache the data, the soft
Lock overwrite), and then update the database to complete other processing of the transaction. After the transaction is committed, the entity object with id 839 will be re-stored in the cache. All other reads from the second-level cache during the transaction
839 of requests will return the soft lock Object, indicating that the data in the second-level cache has been locked, so it is switched to the database to read
ReadWriteCache. ILockable is the soft lock interface. The CacheItem and CacheLock classes implement this interface.
Data update process
1: Lock the second-level cache data before the update operation
2, 3: Retrieves data from the second-level cache. If the returned data is null or CacheItem, a new CacheLock is created and saved to the second-level cache. If the returned data is a CacheLock, it indicates that another transaction has locked this value, increasing the number of concurrent lock Counters by 1 and updating it back to the second-level cache.
4: return the lock Object To EntityAction.
5, 6, 7: update the database, complete other transaction processing, and submit the transaction. Update of ReadWriteCache is not processed.
8: Execute the AfterUpdate method of ReadWriteCache after the transaction is committed.
Read the CacheLock object from the second-level cache first. If null is returned, the lock has expired (caused by too long transaction time)
If the lock has expired, or the returned CacheLock is not the one returned when the lock is applied (the lock has expired and is re-locked by other threads), create a new CacheLock and set it
The unlock status is put back to the second-level cache to end the entire update process.
If CacheLock is in the concurrent lock status, the CacheLock concurrent lock counter will be reduced by one and updated back to the second-level cache to end the entire update process.
If this is not the case above, it indicates that no concurrent updates are made during the period, and the new entity state is updated to the second-level cache (the lock is naturally removed)
I
Once a concurrent update occurs, after the last concurrent transaction is committed, NHibernate will not re-store the entity into the second-level cache. At this time, the second-level cache stores an unlock status.
CacheLock object. After this CacheLock expires, the object may be cached in the second-level cache again. This processing method is used because concurrent transactions occur.
Nhib.pdf does not know which transaction is executed first and which is then executed in the database. To ensure the semantics of the ReadWrite policy, the second-level cache is disabled during this period of time.
Effect
The Get method of ReadWriteCache compares the timestamp of the cache item with the transaction time of the Request thread in addition to the null value returned when the data in the second-level cache is locked, the return value may also be null, so that the request is redirected to the database query, and the database ensures the transaction isolation level.
The put method also compares the version of the entity (when optimistic locks are used)
When you look at the source code, the Timestamper class is a product of combining timestamps and counters. The time is accurate to milliseconds, and a counter ranging from 1 to is used per millisecond for incremental distribution. Nhib.pdf. Caches. MemCache sets ReadWriteCache's second-level cache lock timeout time to 0xea60000, which is 1 minute.