Query Cache

gdb supports caching query results, which is commonly used in scenarios involving more reads and fewer writes. It also allows for manual cache clearing. Note that query caching only supports chained operations and is not available within a transaction.

Related methods:

  1. type CacheOption struct {
  2. // Duration is the TTL for the cache.
  3. // If the parameter `Duration` < 0, which means it clears the cache with given `Name`.
  4. // If the parameter `Duration` = 0, which means it never expires.
  5. // If the parameter `Duration` > 0, which means it expires after `Duration`.
  6. Duration time.Duration
  7. // Name is an optional unique name for the cache.
  8. // The Name is used to bind a name to the cache, which means you can later control the cache
  9. // like changing the `duration` or clearing the cache with specified Name.
  10. Name string
  11. // Force caches the query result whatever the result is nil or not.
  12. // It is used to avoid Cache Penetration.
  13. Force bool
  14. }
  15. // Cache sets the cache feature for the model. It caches the result of the sql, which means
  16. // if there's another same sql request, it just reads and returns the result from cache, it
  17. // but not committed and executed into the database.
  18. //
  19. // Note that, the cache feature is disabled if the model is performing select statement
  20. // on a transaction.
  21. func (m *Model) Cache(option CacheOption) *Model

Cache Management

Cache Object

By default, the ORM object provides a cache management object, and this cache object type is *gcache.Cache. This means it also supports all the features of *gcache.Cache. You can obtain the cache object through the GetCache() *gcache.Cache method and use the returned object to perform various custom cache operations, such as g.DB().GetCache().Keys().

Cache Adaptation (Redis Caching)

By default, the *gcache.Cache cache object of ORM provides single-process memory caching, which is highly efficient but can only be used in a single process. If the service is deployed on multiple nodes, caches between nodes may be inconsistent, so in most scenarios, we implement database query caching through a Redis server. The *gcache.Cache object adopts an adapter design pattern, allowing easy switching from single-process memory caching to distributed Redis caching. Example use:

  1. redisCache := gcache.NewAdapterRedis(g.Redis())
  2. g.DB().GetCache().SetAdapter(redisCache)

For more information, refer to: Caching - Redis

Management Methods

To simplify database query cache management, starting from version v2.2.0, two cache management methods are provided:

  1. // ClearCache removes cached sql result of certain table.
  2. func (c *Core) ClearCache(ctx context.Context, table string) (err error)
  3. // ClearCacheAll removes all cached sql result from cache
  4. func (c *Core) ClearCacheAll(ctx context.Context) (err error)

The methods are explained in the comments. These methods are mounted on the Core object, and the underlying Core object is already exposed through the DB interface, allowing us to obtain the Core object like this:

  1. g.DB().GetCore()

Usage Example

Table Structure

  1. CREATE TABLE `user` (
  2. `uid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  3. `name` varchar(30) NOT NULL DEFAULT '' COMMENT 'Nickname',
  4. `site` varchar(255) NOT NULL DEFAULT '' COMMENT 'Homepage',
  5. PRIMARY KEY (`uid`)
  6. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

Sample Code

  1. package main
  2. import (
  3. "time"
  4. "github.com/gogf/gf/v2/database/gdb"
  5. "github.com/gogf/gf/v2/frame/g"
  6. "github.com/gogf/gf/v2/os/gctx"
  7. )
  8. func main() {
  9. var (
  10. db = g.DB()
  11. ctx = gctx.New()
  12. )
  13. // Enable debug mode to record all executed SQL
  14. db.SetDebug(true)
  15. // Insert test data
  16. _, err := g.Model("user").Ctx(ctx).Data(g.Map{
  17. "name": "john",
  18. "site": "https://goframe.org",
  19. }).Insert()
  20. // Perform the query twice and cache the result for 1 hour with an optional cache name
  21. for i := 0; i < 2; i++ {
  22. r, _ := g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{
  23. Duration: time.Hour,
  24. Name: "vip-user",
  25. Force: false,
  26. }).Where("uid", 1).One()
  27. g.Log().Debug(ctx, r.Map())
  28. }
  29. // Perform an update operation and clear the query cache with a specified name
  30. _, err = g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{
  31. Duration: -1,
  32. Name: "vip-user",
  33. Force: false,
  34. }).Data(gdb.Map{"name": "smith"}).Where("uid", 1).Update()
  35. if err != nil {
  36. g.Log().Fatal(ctx, err)
  37. }
  38. // Execute the query again enabling the query cache feature
  39. r, _ := g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{
  40. Duration: time.Hour,
  41. Name: "vip-user",
  42. Force: false,
  43. }).Where("uid", 1).One()
  44. g.Log().Debug(ctx, r.Map())
  45. }

The output after execution is (the test table data structure is for reference only):

  1. 2022-02-08 17:36:19.817 [DEBU] {c0424c75f1c5d116d0df0f7197379412} {"name":"john","site":"https://goframe.org","uid":1}
  2. 2022-02-08 17:36:19.817 [DEBU] {c0424c75f1c5d116d0df0f7197379412} {"name":"john","site":"https://goframe.org","uid":1}
  3. 2022-02-08 17:36:19.817 [DEBU] {c0424c75f1c5d116d0df0f7197379412} [ 0 ms] [default] [rows:1 ] UPDATE `user` SET `name`='smith' WHERE `uid`=1
  4. 2022-02-08 17:36:19.818 [DEBU] {c0424c75f1c5d116d0df0f7197379412} [ 1 ms] [default] [rows:1 ] SELECT * FROM `user` WHERE `uid`=1 LIMIT 1
  5. 2022-02-08 17:36:19.818 [DEBU] {c0424c75f1c5d116d0df0f7197379412} {"name":"smith","site":"https://goframe.org","uid":1}

It can be observed that:

  1. To demonstrate cache effects conveniently, data debug feature is enabled, so any SQL operation will be output to the terminal.
  2. The One method is executed twice for data querying. The first time an SQL query is executed, the second time it directly uses the cache without submitting the SQL to the database, hence only one query SQL is printed, and the results of both queries are consistent.
  3. Note that a custom name vip-user is set for caching the query, facilitating subsequent cache clearing upon update. If cache clearing is not needed, a cache name does not need to be set.
  4. The cache with a specified name is cleared when the Update operation is executed.
  5. The query is executed again to re-cache the new data.