gdcache is a pure non-intrusive cache library implemented by golang, you can use it to implement your own cache. 中文文档
- Automatically cache sql
- Reuse id cache
- Adapt to Xorm and Gorm framework
- Support cache joint key
- Lightweight
- Non-invasive
- High performance
- Flexible
The core principle of gdcache is to convert sql into id and cache it, and cache the entity corresponding to id. In this way, each sql has the same id and can reuse the corresponding entity content.
As shown in the figure above, each piece of sql can be converted to the corresponding sql, and the bottom layer will reuse these ids. If these ids are not queried, because we don’t know whether they are out of date or because these values do not exist in the database, we will all be in the database and access these entities that cannot be retrieved from the cache from the database. Get it once, and if it can get it, it will cache it once.
The conventional caching framework will cache the content of the result, but the gdcache cache library is different from it. It will only cache the id of the result and find the value through the id. The advantage of this is that the value can be reused, and the value corresponding to id will only be cached once.
go get github.com/ulovecode/gdcache- The class to be cached must implement the
TableName()method, and usecache:"id"to indicate the cached key. The default is to cache byid, and the value of thecachetag corresponds to Fields in the database, usually can be ignored.
typeUserstruct {
Iduint64`cache:"id"`// Or omit the tagNamestringAgeint
}
func (uUser) TableName() string {
return"user"
}- If you want to use a joint key, you can add a
cachetag to multiple fields
typePublicRelationsstruct {
RelatedIduint64`cache:"related_id"`RelatedTypestringSourceIduint64`cache:"source_id"`SourceTypestring
}
func (uPublicRelations) TableName() string {
return"public_relations"
}- Implement the
ICacheinterface, you can use redis or gocache as the underlying implementation.
typeMemoryCacheHandlerstruct {
datamap[string][]byte
}
func (mMemoryCacheHandler) StoreAll(keyValues...gdcache.KeyValue) (errerror) {
for_, keyValue:=rangekeyValues {
m.data[keyValue.Key] =keyValue.Value
}
returnnil
}
func (mMemoryCacheHandler) Get(keystring) (data []byte, hasbool, errerror) {
bytes, has:=m.data[key]
returnbytes, has, nil
}
func (mMemoryCacheHandler) GetAll(keys schemas.PK) (data []gdcache.ReturnKeyValue, errerror) {
returnKeyValues:=make([]gdcache.ReturnKeyValue, 0)
for_, key:=rangekeys {
bytes, has:=m.data[key]
returnKeyValues=append(returnKeyValues, gdcache.ReturnKeyValue{
KeyValue: gdcache.KeyValue{
Key: key,
Value: bytes,
},
Has: has,
})
}
returnreturnKeyValues, nil
}
func (mMemoryCacheHandler) DeleteAll(keys schemas.PK) error {
for_, k:=rangekeys {
delete(m.data, k)
}
returnnil
}
funcNewMemoryCacheHandler() *MemoryCacheHandler {
return&MemoryCacheHandler{
data: make(map[string][]byte, 0),
}
}Implement the IDB interface
typeGormDBstruct {
db*gorm.DB
}
func (gGormDB) GetEntries(entriesinterface{}, sqlstring) error {
tx:=g.db.Raw(sql).Find(entries)
returntx.Error
}
func (gGormDB) GetEntry(entryinterface{}, sqlstring) (bool, error) {
tx:=g.db.Raw(sql).Take(entry)
ifgorm.ErrRecordNotFound==tx.Error {
returnfalse, nil
}
returntx.Error!=gorm.ErrRecordNotFound, tx.Error
}
funcNewGormCacheHandler() *gdcache.CacheHandler {
returngdcache.NewCacheHandler(NewMemoryCacheHandler(), NewGormDd())
}
funcNewGormDd() gdcache.IDB {
db, err:=gorm.Open(mysql.Open("root:root@tcp(127.0.0.1:3306)/test?charset=utf8&parseTime=True&loc=Local"), &gorm.Config{})
iferr!=nil {
panic(err)
}
returnGormDB{
db: db,
}
}Implement the IDB interface
typeXormDBstruct {
db*xorm.Engine
}
func (gXormDB) GetEntries(entriesinterface{}, sqlstring) ( error) {
err:=g.db.SQL(sql).Find(entries)
returnerr
}
func (gXormDB) GetEntry(entryinterface{}, sqlstring) ( bool, error) {
has, err:=g.db.SQL(sql).Get(entry)
returnhas, err
}
funcNewXormCacheHandler() *gdcache.CacheHandler {
returngdcache.NewCacheHandler(NewMemoryCacheHandler(), NewXormDd())
}
funcNewXormDd() gdcache.IDB {
db, err:=xorm.NewEngine("mysql", "root:root@/test?charset=utf8")
iferr!=nil {
panic(err)
}
returnXormDB{
db: db,
}
}Implement the IDB interface
typeMemoryDbstruct {
}
funcNewMemoryDb() *MemoryDb {
return&MemoryDb{}
}
func (mMemoryDb) GetEntries(entriesinterface{}, sqlstring) error {
mockEntries:=make([]MockEntry, 0)
mockEntries=append(mockEntries, MockEntry{
RelateId: 1,
SourceId: 2,
PropertyId: 3,
})
marshal, _:=json.Marshal(mockEntries)
json.Unmarshal(marshal, entries)
returnnil
}
func (mMemoryDb) GetEntry(entryinterface{}, sqlstring) (bool, error) {
mockEntry:=&MockEntry{
RelateId: 1,
SourceId: 2,
PropertyId: 3,
}
marshal, _:=json.Marshal(mockEntry)
json.Unmarshal(marshal, entry)
returntrue, nil
}
funcNewMemoryCache() *gdcache.CacheHandler {
returngdcache.NewCacheHandler(NewMemoryCacheHandler(), NewMemoryDb())
}When querying a single entity, query through the entity's id and fill it into the entity. When getting multiple entities, you can use any sql query and finally fill it into the entity. Both methods must be introduced into the body's pointer.
funcTestNewGormCache(t*testing.T) {
handler:=NewGormCacheHandler()
user:=User{
Id: 1,
}
has, err:=handler.GetEntry(&user)
iferr!=nil {
t.FailNow()
}
ifhas {
t.Logf("%v", user)
}
users:=make([]User, 0)
err=handler.GetEntries(&users, "SELECT * FROM user WHERE name = '33'")
iferr!=nil {
t.FailNow()
}
for_, user:=rangeusers {
t.Logf("%v", user)
}
err=handler.GetEntries(&users, "SELECT * FROM user WHERE id in (3)")
iferr!=nil {
t.FailNow()
}
for_, user:=rangeusers {
t.Logf("%v", user)
}
count, err=handler.GetEntriesAndCount(&users1, "SELECT * FROM user WHERE id in (1,2)")
iferr!=nil {
t.FailNow()
}
for_, user:=rangeusers1 {
t.Logf("%v", user)
}
t.Log(count)
}
users3:=make([]User, 0)
ids:=make([]uint64, 0)
count, err=handler.GetEntriesAndCount(&users3, "SELECT * FROM user WHERE id in ?", ids)
iferr!=nil {
t.FailNow()
}
for_, user:=rangeusers1 {
t.Logf("%v", user)
}
t.Log(count)
count, err=handler.GetEntriesAndCount(&users1, "SELECT * FROM user WHERE id = ?", 1)
iferr!=nil {
t.FailNow()
}
for_, user:=rangeusers1 {
t.Logf("%v", user)
}
t.Log(count)
condition:= []User{{Id: 1,},{Id: 2,},{Id: 3,}}
err=handler.GetEntriesByIds(&users1, condition)
iferr!=nil {
t.FailNow()
}
for_, user:=rangeusers1 {
t.Logf("%v", user)
}
t.Log(count)Support placeholder ?, replacement arrays and basic types
You can help provide better gdcahe by submitting pr.
© Jovanzhu, 2021~time.Now
Released under the MIT License
