GO Cache
golang
49
caching
1
White

Thuc Le viết ngày 22/12/2017

alt text

Go cache là library hỗ trợ việc lưu trữ data dưới dạng key-value trên bộ nhớ máy tính.

Cách sử dụng

Các bước để cài đặt go-cache để sử dụng đơn giản như bao thư viện khác. Bạn chỉ cần chạy câu lệnh thông qua commandline:

go get github.com/patrickmn/go-cache

Và thế là xong bước cài library :)

Cách sử dụng go-cache trong code cũng khá là đơn giản và dễ hiểu, ví dụ phía dưới sẽ khởi tạo go-cache, thêm dữ liệu vào và lấy ra ra xử lý:

import "github.com/patrickmn/go-cache"

c := cache.New(5*time.Minute, 10*time.Minute)
c.Set("foo", "bar", cache.DefaultExpiration)
foo, found := c.Get("foo")
if found {
    // Do somthing
}

Khá đơn giản đúng không nào các bạn :)

Để khởi tạo cho việc sử dụng, go-cache cung cấp 2 phương thức:

func New(defaultExpiration, cleanupInterval time.Duration) *Cache
func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]Item) *Cache

c1 := cache.New(5*time.Minute, 10*time.Minute)
c 2:= cache.New(5*time.Minute, 10*time.Minute, aMap)

Phương thức New() đã được sử dụng trong ví dụ đầu tiên, sẽ yêu cầu chúng ta định nghĩa 2 tham số đầu vào, đó là defaultExpiration, thời gian mặc định mà dữ liệu trong go-cache bị hết hạn, và cleanupInterval, thời gian bộ dọn dẹp dữ liệu hết hạn chạy.

Phương thức NewFrom() cũng tương tự với New() và cho phép chung ta định nghĩa trước tập dự liệu ban đầu thông qua tham số đầu vào là map. NewFrom thông dụng trong việc clone hoặc khôi phục dựa trên nguồn dữ liệu có sẵn.

Trong ví dụ phía trên, chúng ta thấy thời gian thời gian hết hạn mặc định của dữ liệu là năm phút. Khi hàm New tạo đối tượng Cache, nó đồng thời khởi động 1 ticker chạy ngầm. Ticker này sẽ được kích hoạt mỗi 10 phút để đi kiểm tra dữ liệu được lưu và dọn dẹp những dữ liệu đã hết hạn của go-cache object.

Lưu ý 1: Với mỗi lần bạn New một object của go-cache, library sẽ start 1 cái ticker ở background để dọn dẹp, vì thế, càng nhiều go-cache object được tạo, càng nhiều goroutine được tạo và chạy ở background

Lưu ý 2: Khi object của go-cache bị garbage collector dọn dẹp, gorountine tương ứng dùng để check và dọn dẹp dữ liệu hết hạn cũng sẽ được stop lại

Lưu ý 3: Bạn có thể khởi tạo trước map với capacity và sử dụng nó như param khởi tạo go-cache để improve performance: make(map[string]Item, 200)

Go-cache cung cấp const NoExpiration cho phép bỏ đi thời gian hết hạn của dữ liệu được lưu trữ trong go-cache. Do đó, việc xoá dự liệu trong trường hợp này sẽ được thực hiện một cách thủ công.

c1:= cache.New(cache.NoExpiration, 10*time.Minute)
c1.Set("foo", "bar", cache.DefaultExpiration)
c1.Delete("foo")

NoExpiration cũng cho phép khởi tạo go-cache nhưng không khởi tạo bộ dọn dẹp định kì kiểm tra dữ liệu hết hạn. Do đó, bạn phải tự gọi phương thức DeleteExpired() để chủ động xoá dữ liệu hết hạn đang nằm trên bộ nhớ trong trường hợp này.

c2:= cache.New(5*time.Minute, cache.DefaultExpiration)
c2.Set("foo", "bar", cache.DefaultExpiration)
c2. DeleteExpired()

Lưu ý 4: Khi bạn c2.Get("foo") dữ liệu đã bị expired, bạn sẽ nhận được kết quả là dữ liệu này không tồn tại trong go-cache object. Nhưng thật sự, nếu hàm DeleteExpired() chưa được gọi, dữ liệu này vẫn còn tồn tại trên bộ nhớ và chưa bị xoá.

Những phương thức go-cache hỗ trợ

Chỉnh sửa data

Go cache cung cấp cho chúng ta 3 cách thức riêng biệt để thay đổi dữ liệu trong go-cache

func (c Cache) Add(k string, x interface{}, d time.Duration) error
func (c Cache) Replace(k string, x interface{}, d time.Duration) error
func (c Cache) Set(k string, x interface{}, d time.Duration)
func (c Cache) SetDefault(k string, x interface{})

Phương thức Add() sẽ add dữ liệu vào vào go-cache với key tương ứng. Nếu key này đã tồn tại trong go-cache object và chưa bị hết hạn, thì go-cache sẽ trả ra error. Hàm Add chỉ thích hợp cho trong trường hợp bạn muốn add dữ liệu với key mới hoặc key tồn tại nhưng bạn biết chắc là hết hạn:

c:= cache.New(5*time.Minute, 10*time.Minute)
c.Add("foo", "bar", cache.DefaultExpiration)
c.Add("foo2", "bar2", cache.DefaultExpiration)

err := c.Add("foo", "another bar", cache.DefaultExpiration) // got error

Phương thức Replace() sẽ đè dữ liệu lên key có sẵn. Nếu key chưa từng tồn tại hoặc đã hết hạn trong go-cache object, phương thức Replace() sẽ trả ra error báo rằng không thể đè dữ liệu lên key chưa tồn tại trong go-cache được:

c:= cache.New(5*time.Minute, 10*time.Minute)
c.Add("foo", "bar", cache.DefaultExpiration)
c.Replace("foo", "another bar", cache.DefaultExpiration)

err :=  c.Replace("foo2", "bar2", cache.DefaultExpiration) // got error

Để tiện dụng hơn, go-cache còn cung cấp chung ta 2 phương thức SetSetDefault, cho phép chúng ta thay đổi dữ liệu trong go-cache object. Thông qua sử dụng SetSetDefault, nếu key truyền vào chưa từng tồn tại thì chúng sẽ được tạo mới, nếu key đã tồn tại rồi thì dữ liệu sẽ được đè lên key cũ:

c:= cache.New(5*time.Minute, 10*time.Minute)
c.Set("foo", "bar", cache.DefaultExpiration)
c.Set("foo2", "bar2", cache.DefaultExpiration)
c.Set("foo", "another bar", cache.DefaultExpiration)

err := c.Replace("foo3", "another bar", cache.DefaultExpiration) // got error
c:= cache.New(5*time.Minute, 10*time.Minute)
c.SetDefault("foo", "bar")
c.SetDefault("foo2", "bar2")
c.SetDefault("foo", "another bar")

Phương thức Set() sẽ cho chúng ta tuỳ chỉnhthời gian hết hạn của từng key được set vào. SetDefault() sẽ dùng thời gian hết hạn mặc định khi tạo go-cache thông qua New() hoặc NewFrom().

Bên cạnh việc thêm và chỉnh sửa dữ liệu, go-cache còn cung cấp 3 cách thức để xoá dữ liệu:

func (c Cache) Delete(k string)
func (c Cache) DeleteExpired()
func (c Cache) Flush()

Phương thức Delete() là cách để xoá dữ liệu trong go-cache căn bản nhất. Dựa trên key được truyền vào, go-cache object sẽ xoá thông tin về key và dữ liệu của nó. Nếu key truyền vào chưa từng tồn tại trong go-cache object, phương thức sẽ không làm gì cả.

c:= cache.New(5*time.Minute, 10*time.Minute)
c.SetDefault("foo", "bar")
c.Delete("foo")

Để yêu cầu go-cache tường minh xoá những dữ liệu đã hết hạn ( thay vì chờ đến thời gian dọn dẹp ), go-cache cung cấp ta method method DeleteExpired để chúng ta có thể tự làm:

c:= cache.New(5*time.Minute, 10*time.Minute)
...
c.DeleteExpired()

Và nếu muốn dọn dẹp sạch sẽ mọi thứ luôn thì chúng ta sẽ sử dụng Flush:

c:= cache.New(5*time.Minute, 10*time.Minute)
...
c.Flush()

Truy xuất dữ liệu

Chúng ta đã thấy một cách để truy xuất dữ liệu trong go-cache ở ví dụ đầu tiên, đó chính là phương thứcGet()

func (c Cache) Get(k string) (interface{}, bool)

Phương thức Get() sẽ trả về giá trị của key tương ứng và một biến bool để xác định key có tồn tại hay không trong go-cache object. Giá trị trả về sẽ được định nghĩa là interface{}, nên chúng ta cần chuyển giá trị này sang kiểu dữ liệu xác định mà chúng ta dự đoán trước trước khi sử dụng.

c:= cache.New(5*time.Minute, 10*time.Minute)
c.SetDefault("foo", "bar")
value, existed := c.Get("foo")
if !existed {
    doSomething(value.(string))
}

Go-cache cũng cung cấp phương thức GetWithExpiration() để lấy dữ liệu cùng với thông tin hết hạn của key. Cách sử dụng GetWithExpiration() tương tự với Get nhưng sẽ có thêm giá trị time.Time trả về để biểu thị thời điểm key hết hạn.

func (c Cache) GetWithExpiration(k string) (interface{}, time.Time, bool)

Linh tinh

Ngoài những phương thức thông dụng để chỉnh sửa, xoá và lấy dữ liệu, Go-cache cũng còn những phương thức thú vị khác :)

func (c Cache) Items() map[string]Item

Items() sẽ trả về tất cả các key/value đã được lưu trữ và còn thời hạn trong go-cache tại thời điểm được gọi. Giá trị trả về từ Items có thể được dùng để xài trong phương thức NewFrom, thích hợp để lưu trữ và phục hồi lại go-cache. Hoặc đơn giản là để clone go-cache

func (c Cache) OnEvicted(f func(string, interface{}))

OnEvicted() là method dùng để đăng ký một phương thức vào trong go-cache object. Mỗi lần một key bị xoá khỏi go-cache object, phương thức được đăng ký sẽ được gọi.

Lưu ý 5: sự kiện OnEvicted chỉ sảy ra đối với trường hợp xoá key trong go-cache object (tự xoá hoặc do bị bộ dọn rác xoá). Với trường hợp Replace() hoặc Set() với key có sẵn, OnEvicted() sẽ không sảy ra.
Lưu ý 6: Nếu muốn không sử dụng OnEvicted nữa, chúng ta chỉ cần set nil cho nó

func (c Cache) Decrement(k string, n int64) error
func (c Cache) DecrementFloat(k string, n float64) error
func (c Cache) DecrementFloat32(k string, n float32) (float32, error)
func (c Cache) DecrementFloat64(k string, n float64) (float64, error)
func (c Cache) DecrementInt(k string, n int) (int, error)
func (c Cache) DecrementInt16(k string, n int16) (int16, error)
func (c Cache) DecrementInt32(k string, n int32) (int32, error)
func (c Cache) DecrementInt64(k string, n int64) (int64, error)
func (c Cache) DecrementInt8(k string, n int8) (int8, error)
func (c Cache) DecrementUint(k string, n uint) (uint, error)
func (c Cache) DecrementUint16(k string, n uint16) (uint16, error)
func (c Cache) DecrementUint32(k string, n uint32) (uint32, error)
func (c Cache) DecrementUint64(k string, n uint64) (uint64, error)
func (c Cache) DecrementUint8(k string, n uint8) (uint8, error)
func (c Cache) DecrementUintptr(k string, n uintptr) (uintptr, error)

func (c Cache) IncrementFloat(k string, n float64) error
func (c Cache) IncrementFloat32(k string, n float32) (float32, error)
func (c Cache) IncrementFloat64(k string, n float64) (float64, error)
func (c Cache) IncrementInt(k string, n int) (int, error)
func (c Cache) IncrementInt16(k string, n int16) (int16, error)
func (c Cache) IncrementInt32(k string, n int32) (int32, error)
func (c Cache) IncrementInt64(k string, n int64) (int64, error)
func (c Cache) IncrementInt8(k string, n int8) (int8, error)
func (c Cache) IncrementUint(k string, n uint) (uint, error)
func (c Cache) IncrementUint16(k string, n uint16) (uint16, error)
func (c Cache) IncrementUint32(k string, n uint32) (uint32, error)
func (c Cache) IncrementUint64(k string, n uint64) (uint64, error)
func (c Cache) IncrementUint8(k string, n uint8) (uint8, error)
func (c Cache) IncrementUintptr(k string, n uintptr) (uintptr, error)

Go-cache cung cấp một loạt phương thức dùng để tăng hoặc giảm giá trị được lưu với Decrement*Increment*. Error sẽ được trả về nếu giá trị không thể chuyển qua kiểu dữ liệu tương ứng

Thread safe

Hiện tại, mọi phương thức liên quan đến việc thay đổi thông tin lưu trữ (add/replace/delete) đều được thông qua một mutex lock để đảm bảo việc Thread safe

func (c *cache) Set(k string, x interface{}, d time.Duration) {
    var e int64
    if d == DefaultExpiration {
        d = c.defaultExpiration
    }
    if d > 0 {
        e = time.Now().Add(d).UnixNano()
    }
    c.mu.Lock()
    c.items[k] = Item{
        Object:     x,
        Expiration: e,
    }
    c.mu.Unlock()
}

Advantages

. Fit with simple caching/storing solution
. Thread-safe
. Don't take time for serialize and transmit stored data through network

Disadvantages

. Just support a application runs on single machine
. Simple solution, doesn't optimizing thread safe locking
. Doesn't separate read lock and write lock => phantom data

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Thuc Le

1 bài viết.
1 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Bài viết liên quan
White
16 0
Crawl dữ liệu Crawl là một vấn đề hay gặp trong quá trình làm software. Ví dụ lấy tin tức, tin giảm giá, vé xem phim... là những dạng của crawl. Mộ...
Thach Le viết hơn 2 năm trước
16 0
White
44 7
Là một người thường xuyên đọc Quora, có một điểm cá nhân tôi thấy rất ấn tượng ở quora chính là khả năng autocomplete với tốc độ ánh sáng. (Ảnh) ...
huydx viết 7 tháng trước
44 7
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
1 bài viết.
1 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!