Introduction
go-cache is widely used in Go language programming, and is suitable for memory caches in the form of key-value pairs stored on a stand-alone machine.
On github, the address is/patrickmn/go-cacheWhen it is concurrency, thread-safe (read-write lock) + map[string]interface{} + expiration time is used as the localized storage of go.
These are also his three major characteristics:
- Thread-safe, supports multiple coroutine concurrent access through read and write locks
- No need for serialization, key-value pair form, any value type map[string]interface{}
- Customize the expiration time of each key
Data structure
There are mainly cache, and its two structures consisting of cache and Item.
type Cache struct { *cache // If this is confusing, see the comment at the bottom of New() // If this is confusing, see the comments at the bottom of New().} type cache struct { defaultExpiration items map[string]Item //Storage key-value pairs mu // Read and write lock, concurrency security onEvicted func(string, interface{}) // Callback function when cleared janitor *janitor // Scripts, regularly clean out expired data} type Item struct { Object interface{} //Stored value Expiration int64 // Expiry time}
Create a Cache object
Use the New (defaultExpiration default expiration time, cleanupInterval time) function to initialize.
Pass to two parameters: the expiration time of the key, and the time when the time script cleans out expiration data.
// Return a new cache with a given default expiration duration and cleanup interval. // Return new cache with given default expiration and clear time// If the expiration duration is less than one (or NoExpiration), // the items in the cache never expire (by default), // If the expiration time is -1, it will never expire// and must be deleted manually. // And can only be deleted manually.// If the cleanup interval is less than one, expired items are not // If the clearing time is less than 1, the expired item will not be deleted before the call.// deleted from the cache before calling (). func New(defaultExpiration, cleanupInterval ) *Cache { items := make(map[string]Item) return newCacheWithJanitor(defaultExpiration, cleanupInterval, items) } func newCacheWithJanitor(de , ci , m map[string]Item) *Cache { c := newCache(de, m) // This trick ensures that the janitor goroutine (which--granted it // This code confirms whether to start janitor gatekeeper goroutine // was enabled--is running DeleteExpired on c forever) does not keep // the returned C object from being garbage collected. When it is // garbage collected, the finalizer stops the janitor goroutine, after // which c can be collected. C := &Cache{c} if ci > 0 { runJanitor(c, ci) // Call the guard process to clean out expired data (C, stopJanitor) // (C, stopJanitor) will specify that the call function stops the background goroutine, // When GC is ready to release the object, the stopJanitor method will be called. // The channel in the Run function will output a signal, thereby exiting the coroutine. } return C } func runJanitor(c *cache, ci ) { j := &janitor{ Interval: ci, stop: make(chan bool), } = j go (c) // Call the guard process to clean out expired data} // Turn on a timer to clean up data regularly.func (j *janitor) Run(c *cache) { // Create a timer. When the time comes, the channel will output a value. Call the DeleteExpired() function // This function will traverse the expiration time of the map[string]Item in cache, and delete it directly from the map if it expires. // If the value has a callback function, the callback function will be executed after deletion. ticker := () for { select { case <-: () case <-: () return } } } // Delete all expired items from the cache. // Delete all expired items in cache// Lock will be added at this time. If the time cleaning time is longer and there are more keys,// It will cause the cleanup coroutine to be locked all the time. Other coroutines cannot be written.func (c *cache) DeleteExpired() { var evictedItems []keyAndValue now := ().UnixNano() () // When deletion expires, lock needs to be locked. for k, v := range { // "Inlining" of expired if > 0 && now > { ov, evicted := (k) if evicted { evictedItems = append(evictedItems, keyAndValue{k, ov}) } } } () // Unlock after deletion for _, v := range evictedItems { (, ) } } func (c *cache) delete(k string) (interface{}, bool) { if != nil { if v, found := [k]; found { delete(, k) return , true } } delete(, k) return nil, false }
Get the data
// Get an item from the cache. Returns the item or nil, and a bool indicating // whether the key was found. // Get an item from cache. Returns item or nil, and a bool value.func (c *cache) Get(k string) (interface{}, bool) { () // "Inlining" of get and Expired item, found := [k] if !found { () return nil, false } // Get the item, and whether it can be returned still needs to determine the expiration time if > 0 { if ().UnixNano() > { () return nil, false } } () return , true }
Set save data
Set saves data, and there are three situations for the expression of d:
- To be 0, use the default expiration time
- To be -1, never expires
- A normal value greater than 0 is the expiration time
// Add an item to the cache, replacing any existing item. If the duration is 0 // Add item to cache to replace any existing item. If the duration is 0// (DefaultExpiration), the cache's default expiration time is used. If it is -1 // (DefaultExpiration), use the cached default expiration time. If it is -1// (NoExpiration), the item never expires. // (No development), the project will never expire.func (c *cache) Set(k string, x interface{}, d ) { // "Inlining" of set var e int64 if d == DefaultExpiration { d = } if d > 0 { e = ().Add(d).UnixNano() } () [k] = Item{ Object: x, Expiration: e, } // TODO: Calls to are currently not deferred because defer // adds ~200 ns (as of go1.) () }
Frequently Asked Questions
1. High concurrency
Because it is locked on the entire cache, the concurrency will be lower than the other caches locked on the shard.
2. About memory overflow
If the cleaning time is set to 0, it means that it will never be cleaned, or the time is too long, which may lead to more and more caches.
Because there is no active cleaning, the cache occupied becomes larger and larger.
3. About timed cleaning
If the time is too long, the cleaning is too large, and the entire cache is locked, other coroutines may not be able to be written.
4. The value stored in map[string]interface{} may change.
interface{}, if the array, or pointer, when extracted and used, modifying the value will cause the original value in the cache to change.
The above is the detailed content of the reading and analysis of the source code of patrickmn/go-cache. For more information on the reading and analysis of the source code of patrickmn/go-cache, please pay attention to my other related articles!