mirror of
https://github.com/go-kratos/kratos.git
synced 2025-03-17 21:07:54 +02:00
Merge pull request #145 from bilibili/kratos/cache_doc
kratos/cache_doc
This commit is contained in:
commit
6ad8e6ead7
@ -0,0 +1,235 @@
|
||||
# 准备工作
|
||||
|
||||
推荐使用[kratos工具](kratos-tool.md)快速生成项目,如我们生成一个叫`kratos-demo`的项目。目录结构如下:
|
||||
|
||||
```
|
||||
├── CHANGELOG.md
|
||||
├── CONTRIBUTORS.md
|
||||
├── LICENSE
|
||||
├── README.md
|
||||
├── cmd
|
||||
│ ├── cmd
|
||||
│ └── main.go
|
||||
├── configs
|
||||
│ ├── application.toml
|
||||
│ ├── grpc.toml
|
||||
│ ├── http.toml
|
||||
│ ├── log.toml
|
||||
│ ├── memcache.toml
|
||||
│ ├── mysql.toml
|
||||
│ └── redis.toml
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
└── internal
|
||||
├── dao
|
||||
│ └── dao.go
|
||||
├── model
|
||||
│ └── model.go
|
||||
├── server
|
||||
│ └── http
|
||||
│ └── http.go
|
||||
└── service
|
||||
└── service.go
|
||||
```
|
||||
|
||||
# 开始使用
|
||||
|
||||
## 配置
|
||||
|
||||
创建项目成功后,进入项目中的configs目录,打开memcache.toml,我们可以看到:
|
||||
|
||||
```toml
|
||||
demoExpire = "24h"
|
||||
[demo]
|
||||
name = "kratos-demo"
|
||||
proto = "tcp"
|
||||
addr = "127.0.0.1:11211"
|
||||
active = 50
|
||||
idle = 10
|
||||
dialTimeout = "100ms"
|
||||
readTimeout = "200ms"
|
||||
writeTimeout = "300ms"
|
||||
idleTimeout = "80s"
|
||||
```
|
||||
在该配置文件中我们可以配置memcache的连接方式proto、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
|
||||
|
||||
这里可选添加mc的过期时间设置。
|
||||
|
||||
|
||||
## 初始化
|
||||
|
||||
进入项目的internal/dao目录,打开dao.go,其中:
|
||||
|
||||
```go
|
||||
var (
|
||||
mc struct {
|
||||
Demo *memcache.Config
|
||||
DemoExpire xtime.Duration
|
||||
}
|
||||
)
|
||||
checkErr(paladin.Get("memcache.toml").UnmarshalTOML(&mc))
|
||||
```
|
||||
使用paladin配置管理工具将上文中的memcache.toml中的配置解析为我们需要使用的配置。
|
||||
|
||||
```go
|
||||
// Dao dao.
|
||||
type Dao struct {
|
||||
mc *memcache.Memcache
|
||||
mcExpire int32
|
||||
}
|
||||
```
|
||||
|
||||
在dao的主结构提中定义了memcache的连接池对象和过期时间。
|
||||
|
||||
```go
|
||||
dao = &Dao{
|
||||
// memcache
|
||||
mc: memcache.New(mc.Demo),
|
||||
mcExpire: int32(time.Duration(mc.DemoExpire) / time.Second),
|
||||
}
|
||||
```
|
||||
|
||||
使用kratos/pkg/cache/memcache包的New方法进行连接池对象的初始化,需要传入上文解析的配置。
|
||||
|
||||
## Ping
|
||||
|
||||
```go
|
||||
// Ping ping the resource.
|
||||
func (d *Dao) Ping(ctx context.Context) (err error) {
|
||||
return d.pingMC(ctx)
|
||||
}
|
||||
|
||||
func (d *Dao) pingMC(ctx context.Context) (err error) {
|
||||
if err = d.mc.Set(ctx, &memcache.Item{Key: "ping", Value: []byte("pong"), Expiration: 0}); err != nil {
|
||||
log.Error("conn.Set(PING) error(%v)", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
生成的dao层模板中自带了memcache相关的ping方法,用于为负载均衡服务的健康监测提供依据,详见[blademaster](blademaster-quickstart.md)。
|
||||
|
||||
## 关闭
|
||||
|
||||
```go
|
||||
// Close close the resource.
|
||||
func (d *Dao) Close() {
|
||||
d.mc.Close()
|
||||
}
|
||||
```
|
||||
|
||||
在关闭dao层时,通过调用memcache连接池对象的Close方法,我们可以关闭该连接池,从而释放相关资源。
|
||||
|
||||
# 常用方法
|
||||
|
||||
推荐使用[memcache代码生成器](kratos-genmc.md)帮助我们生成memcache操作的相关代码。
|
||||
|
||||
以下我们来逐一解析以下kratos/pkg/cache/memcache包中提供的常用方法。
|
||||
|
||||
## 单个查询
|
||||
|
||||
```go
|
||||
// CacheDemo get data from mc
|
||||
func (d *Dao) CacheDemo(c context.Context, id int64) (res *Demo, err error) {
|
||||
key := demoKey(id)
|
||||
res = &Demo{}
|
||||
if err = d.mc.Get(c, key).Scan(res); err != nil {
|
||||
res = nil
|
||||
if err == memcache.ErrNotFound {
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
prom.BusinessErrCount.Incr("mc:CacheDemo")
|
||||
log.Errorv(c, log.KV("CacheDemo", fmt.Sprintf("%+v", err)), log.KV("key", key))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
如上为代码生成器生成的进行单个查询的代码,使用到mc.Get(c,key)方法获得返回值,再使用scan方法将memcache的返回值转换为golang中的类型(如string,bool, 结构体等)。
|
||||
|
||||
## 批量查询使用
|
||||
|
||||
```go
|
||||
replies, err := d.mc.GetMulti(c, keys)
|
||||
for _, key := range replies.Keys() {
|
||||
v := &Demo{}
|
||||
err = replies.Scan(key, v)
|
||||
}
|
||||
```
|
||||
|
||||
如上为代码生成器生成的进行批量查询的代码片段,这里使用到mc.GetMulti(c,keys)方法获得返回值,与单个查询类似地,我们需要再使用scan方法将memcache的返回值转换为我们定义的结构体。
|
||||
|
||||
## 设置KV
|
||||
|
||||
```go
|
||||
// AddCacheDemo Set data to mc
|
||||
func (d *Dao) AddCacheDemo(c context.Context, id int64, val *Demo) (err error) {
|
||||
if val == nil {
|
||||
return
|
||||
}
|
||||
key := demoKey(id)
|
||||
item := &memcache.Item{Key: key, Object: val, Expiration: d.demoExpire, Flags: memcache.FlagJSON | memcache.FlagGzip}
|
||||
if err = d.mc.Set(c, item); err != nil {
|
||||
prom.BusinessErrCount.Incr("mc:AddCacheDemo")
|
||||
log.Errorv(c, log.KV("AddCacheDemo", fmt.Sprintf("%+v", err)), log.KV("key", key))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
如上为代码生成器生成的添加结构体进入memcache的代码,这里需要使用到的是mc.Set方法进行设置。
|
||||
这里使用的item为memcache.Item结构体,包含key, value, 超时时间(秒), Flags。
|
||||
|
||||
### Flags
|
||||
|
||||
|
||||
上文添加结构体进入memcache中,使用到的flags为:memcache.FlagJSON | memcache.FlagGzip代表着:使用json作为编码方式,gzip作为压缩方式。
|
||||
|
||||
Flags的相关常量在kratos/pkg/cache/memcache包中进行定义,包含编码方式如gob, json, protobuf,和压缩方式gzip。
|
||||
|
||||
```go
|
||||
const(
|
||||
// Flag, 15(encoding) bit+ 17(compress) bit
|
||||
|
||||
// FlagRAW default flag.
|
||||
FlagRAW = uint32(0)
|
||||
// FlagGOB gob encoding.
|
||||
FlagGOB = uint32(1) << 0
|
||||
// FlagJSON json encoding.
|
||||
FlagJSON = uint32(1) << 1
|
||||
// FlagProtobuf protobuf
|
||||
FlagProtobuf = uint32(1) << 2
|
||||
// FlagGzip gzip compress.
|
||||
FlagGzip = uint32(1) << 15
|
||||
)
|
||||
```
|
||||
|
||||
## 删除KV
|
||||
|
||||
```go
|
||||
// DelCacheDemo delete data from mc
|
||||
func (d *Dao) DelCacheDemo(c context.Context, id int64) (err error) {
|
||||
key := demoKey(id)
|
||||
if err = d.mc.Delete(c, key); err != nil {
|
||||
if err == memcache.ErrNotFound {
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
prom.BusinessErrCount.Incr("mc:DelCacheDemo")
|
||||
log.Errorv(c, log.KV("DelCacheDemo", fmt.Sprintf("%+v", err)), log.KV("key", key))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
如上为代码生成器生成的从memcache中删除KV的代码,这里需要使用到的是mc.Delete方法。
|
||||
和查询时类似地,当memcache中不存在参数中的key时,会返回error为memcache.ErrNotFound。如果不需要处理这种error,可以参考上述代码将返回出去的error置为nil。
|
||||
|
||||
# 扩展阅读
|
||||
|
||||
[memcache代码生成器](kratos-genmc.md)
|
||||
[redis模块说明](cache-redis.md)
|
@ -0,0 +1,222 @@
|
||||
# 准备工作
|
||||
|
||||
推荐使用[kratos工具](kratos-tool.md)快速生成项目,如我们生成一个叫`kratos-demo`的项目。目录结构如下:
|
||||
|
||||
```
|
||||
├── CHANGELOG.md
|
||||
├── CONTRIBUTORS.md
|
||||
├── LICENSE
|
||||
├── README.md
|
||||
├── cmd
|
||||
│ ├── cmd
|
||||
│ └── main.go
|
||||
├── configs
|
||||
│ ├── application.toml
|
||||
│ ├── grpc.toml
|
||||
│ ├── http.toml
|
||||
│ ├── log.toml
|
||||
│ ├── memcache.toml
|
||||
│ ├── mysql.toml
|
||||
│ └── redis.toml
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
└── internal
|
||||
├── dao
|
||||
│ └── dao.go
|
||||
├── model
|
||||
│ └── model.go
|
||||
├── server
|
||||
│ └── http
|
||||
│ └── http.go
|
||||
└── service
|
||||
└── service.go
|
||||
```
|
||||
|
||||
# 开始使用
|
||||
|
||||
## 配置
|
||||
|
||||
创建项目成功后,进入项目中的configs目录,打开redis.toml,我们可以看到:
|
||||
|
||||
```toml
|
||||
demoExpire = "24h"
|
||||
|
||||
[demo]
|
||||
name = "kratos-demo"
|
||||
proto = "tcp"
|
||||
addr = "127.0.0.1:6389"
|
||||
idle = 10
|
||||
active = 10
|
||||
dialTimeout = "1s"
|
||||
readTimeout = "1s"
|
||||
writeTimeout = "1s"
|
||||
idleTimeout = "10s"
|
||||
```
|
||||
|
||||
在该配置文件中我们可以配置redis的连接方式proto、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
|
||||
|
||||
这里可选添加redis的过期时间设置。
|
||||
|
||||
|
||||
## 初始化
|
||||
|
||||
进入项目的internal/dao目录,打开dao.go,其中:
|
||||
|
||||
```go
|
||||
var (
|
||||
rc struct {
|
||||
Demo *redis.Config
|
||||
DemoExpire xtime.Duration
|
||||
}
|
||||
)
|
||||
checkErr(paladin.Get("redis.toml").UnmarshalTOML(&rc))
|
||||
```
|
||||
使用paladin配置管理工具将上文中的redis.toml中的配置解析为我们需要使用的配置。
|
||||
|
||||
```go
|
||||
// Dao dao.
|
||||
type Dao struct {
|
||||
redis *redis.Pool
|
||||
redisExpire int32
|
||||
}
|
||||
```
|
||||
|
||||
在dao的主结构提中定义了redis的连接池对象和过期时间。
|
||||
|
||||
```go
|
||||
dao = &Dao{
|
||||
// redis
|
||||
redis: redis.NewPool(rc.Demo),
|
||||
redisExpire: int32(time.Duration(rc.DemoExpire) / time.Second),
|
||||
}
|
||||
```
|
||||
|
||||
使用kratos/pkg/cache/redis包的NewPool方法进行连接池对象的初始化,需要传入上文解析的配置。
|
||||
|
||||
## Ping
|
||||
|
||||
```go
|
||||
// Ping ping the resource.
|
||||
func (d *Dao) Ping(ctx context.Context) (err error) {
|
||||
return d.pingRedis(ctx)
|
||||
}
|
||||
|
||||
func (d *Dao) pingRedis(ctx context.Context) (err error) {
|
||||
conn := d.redis.Get(ctx)
|
||||
defer conn.Close()
|
||||
if _, err = conn.Do("SET", "ping", "pong"); err != nil {
|
||||
log.Error("conn.Set(PING) error(%v)", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
生成的dao层模板中自带了redis相关的ping方法,用于为负载均衡服务的健康监测提供依据,详见[blademaster](blademaster-quickstart.md)。
|
||||
|
||||
## 关闭
|
||||
|
||||
```go
|
||||
// Close close the resource.
|
||||
func (d *Dao) Close() {
|
||||
d.redis.Close()
|
||||
}
|
||||
```
|
||||
|
||||
在关闭dao层时,通过调用redis连接池对象的Close方法,我们可以关闭该连接池,从而释放相关资源。
|
||||
|
||||
# 常用方法
|
||||
|
||||
## 发送单个命令 Do
|
||||
|
||||
```go
|
||||
// DemoIncrby .
|
||||
func (d *Dao) DemoIncrby(c context.Context, pid int) (err error) {
|
||||
cacheKey := keyDemo(pid)
|
||||
conn := d.redis.Get(c)
|
||||
defer conn.Close()
|
||||
if _, err = conn.Do("INCRBY", cacheKey, 1); err != nil {
|
||||
log.Error("DemoIncrby conn.Do(INCRBY) key(%s) error(%v)", cacheKey, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
如上为向redis server发送单个命令的用法示意。这里需要使用redis连接池的Get方法获取一个redis连接conn,再使用conn.Do方法即可发送一条指令。
|
||||
注意,在使用该连接完毕后,需要使用conn.Close方法将该连接关闭。
|
||||
|
||||
## 批量发送命令 Pipeline
|
||||
|
||||
kratos/pkg/cache/redis包除了支持发送单个命令,也支持批量发送命令(redis pipeline),比如:
|
||||
|
||||
```go
|
||||
// DemoIncrbys .
|
||||
func (d *Dao) DemoIncrbys(c context.Context, pid int) (err error) {
|
||||
cacheKey := keyDemo(pid)
|
||||
conn := d.redis.Get(c)
|
||||
defer conn.Close()
|
||||
if err = conn.Send("INCRBY", cacheKey, 1); err != nil {
|
||||
return
|
||||
}
|
||||
if err = conn.Send("EXPIRE", cacheKey, d.redisExpire); err != nil {
|
||||
return
|
||||
}
|
||||
if err = conn.Flush(); err != nil {
|
||||
log.Error("conn.Flush error(%v)", err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
if _, err = conn.Receive(); err != nil {
|
||||
log.Error("conn.Receive error(%v)", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
和发送单个命令类似地,这里需要使用redis连接池的Get方法获取一个redis连接conn,在使用该连接完毕后,需要使用conn.Close方法将该连接关闭。
|
||||
|
||||
这里使用conn.Send方法将命令写入客户端的buffer(缓冲区)中,使用conn.Flush将客户端的缓冲区内的命令打包发送到redis server。redis server按顺序返回的reply可以使用conn.Receive方法进行接收和处理。
|
||||
|
||||
|
||||
## 返回值转换
|
||||
|
||||
与[memcache包](cache-mc.md)类似地,kratos/pkg/cache/redis包中也提供了Scan方法将redis server的返回值转换为golang类型。
|
||||
|
||||
除此之外,kratos/pkg/cache/redis包提供了大量返回值转换的快捷方式:
|
||||
|
||||
### 单个查询
|
||||
|
||||
单个查询可以使用redis.Uint64/Int64/Float64/Int/String/Bool/Bytes进行返回值的转换,比如:
|
||||
|
||||
```go
|
||||
// GetDemo get
|
||||
func (d *Dao) GetDemo(ctx context.Context, key string) (string, error) {
|
||||
conn := d.redis.Get(ctx)
|
||||
defer conn.Close()
|
||||
return redis.String(conn.Do("GET", key))
|
||||
}
|
||||
```
|
||||
|
||||
### 批量查询
|
||||
|
||||
批量查询时候,可以使用redis.Int64s,Ints,Strings,ByteSlices方法转换如MGET,HMGET,ZRANGE,SMEMBERS等命令的返回值。
|
||||
还可以使用StringMap, IntMap, Int64Map方法转换HGETALL命令的返回值,比如:
|
||||
|
||||
```go
|
||||
// HGETALLDemo get
|
||||
func (d *Dao) HGETALLDemo(c context.Context, pid int64) (res map[string]int64, err error) {
|
||||
var (
|
||||
key = keyDemo(pid)
|
||||
conn = d.redis.Get(c)
|
||||
)
|
||||
defer conn.Close()
|
||||
if res, err = redis.Int64Map(conn.Do("HGETALL", key)); err != nil {
|
||||
log.Error("HGETALL %v failed error(%v)", key, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
# 扩展阅读
|
||||
|
||||
[memcache模块说明](cache-mc.md)
|
@ -0,0 +1,23 @@
|
||||
# 背景
|
||||
|
||||
我们需要统一的cache包,用于进行各类缓存操作。
|
||||
|
||||
# 概览
|
||||
|
||||
* 缓存操作均使用连接池,保证较快的数据读写速度且提高系统的安全可靠性。
|
||||
|
||||
# Memcache
|
||||
|
||||
提供protobuf,gob,json序列化方式,gzip的memcache接口
|
||||
|
||||
[memcache模块说明](cache-mc.md)
|
||||
|
||||
# Redis
|
||||
|
||||
提供redis操作的各类接口以及各类将redis server返回值转换为golang类型的快捷方法。
|
||||
|
||||
[redis模块说明](cache-redis.md)
|
||||
|
||||
-------------
|
||||
|
||||
[文档目录树](summary.md)
|
@ -0,0 +1,230 @@
|
||||
# 准备工作
|
||||
|
||||
推荐使用[kratos工具](kratos-tool.md)快速生成项目,如我们生成一个叫`kratos-demo`的项目。目录结构如下:
|
||||
|
||||
```
|
||||
├── CHANGELOG.md
|
||||
├── CONTRIBUTORS.md
|
||||
├── LICENSE
|
||||
├── README.md
|
||||
├── cmd
|
||||
│ ├── cmd
|
||||
│ └── main.go
|
||||
├── configs
|
||||
│ ├── application.toml
|
||||
│ ├── grpc.toml
|
||||
│ ├── http.toml
|
||||
│ ├── log.toml
|
||||
│ ├── memcache.toml
|
||||
│ ├── mysql.toml
|
||||
│ └── redis.toml
|
||||
├── go.mod
|
||||
├── go.sum
|
||||
└── internal
|
||||
├── dao
|
||||
│ └── dao.go
|
||||
├── model
|
||||
│ └── model.go
|
||||
├── server
|
||||
│ └── http
|
||||
│ └── http.go
|
||||
└── service
|
||||
└── service.go
|
||||
```
|
||||
|
||||
# 开始使用
|
||||
|
||||
## 配置
|
||||
|
||||
创建项目成功后,进入项目中的configs目录,mysql.toml,我们可以看到:
|
||||
|
||||
```toml
|
||||
[demo]
|
||||
addr = "127.0.0.1:3306"
|
||||
dsn = "{user}:{password}@tcp(127.0.0.1:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8"
|
||||
readDSN = ["{user}:{password}@tcp(127.0.0.2:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8mb4,utf8","{user}:{password}@tcp(127.0.0.3:3306)/{database}?timeout=1s&readTimeout=1s&writeTimeout=1s&parseTime=true&loc=Local&charset=utf8,utf8mb4"]
|
||||
active = 20
|
||||
idle = 10
|
||||
idleTimeout ="4h"
|
||||
queryTimeout = "200ms"
|
||||
execTimeout = "300ms"
|
||||
tranTimeout = "400ms"
|
||||
```
|
||||
|
||||
在该配置文件中我们可以配置mysql的读和写的dsn、连接地址addr、连接池的闲置连接数idle、最大连接数active以及各类超时。
|
||||
|
||||
如果配置了readDSN,在进行读操作的时候会优先使用readDSN的连接。
|
||||
|
||||
## 初始化
|
||||
|
||||
进入项目的internal/dao目录,打开dao.go,其中:
|
||||
|
||||
```go
|
||||
var (
|
||||
dc struct {
|
||||
Demo *sql.Config
|
||||
}
|
||||
)
|
||||
checkErr(paladin.Get("mysql.toml").UnmarshalTOML(&dc))
|
||||
```
|
||||
使用paladin配置管理工具将上文中的mysql.toml中的配置解析为我们需要使用mysql的相关配置。
|
||||
|
||||
```go
|
||||
// Dao dao.
|
||||
type Dao struct {
|
||||
db *sql.DB
|
||||
}
|
||||
```
|
||||
|
||||
在dao的主结构提中定义了mysql的连接池对象。
|
||||
|
||||
```go
|
||||
dao = &Dao{
|
||||
db: sql.NewMySQL(dc.Demo),
|
||||
}
|
||||
```
|
||||
|
||||
使用kratos/pkg/database/sql包的NewMySQL方法进行连接池对象的初始化,需要传入上文解析的配置。
|
||||
|
||||
## Ping
|
||||
|
||||
```go
|
||||
// Ping ping the resource.
|
||||
func (d *Dao) Ping(ctx context.Context) (err error) {
|
||||
return d.db.Ping(ctx)
|
||||
}
|
||||
```
|
||||
|
||||
生成的dao层模板中自带了mysql相关的ping方法,用于为负载均衡服务的健康监测提供依据,详见[blademaster](blademaster-quickstart.md)。
|
||||
|
||||
## 关闭
|
||||
|
||||
```go
|
||||
// Close close the resource.
|
||||
func (d *Dao) Close() {
|
||||
d.db.Close()
|
||||
}
|
||||
```
|
||||
|
||||
在关闭dao层时,通过调用mysql连接池对象的Close方法,我们可以关闭该连接池,从而释放相关资源。
|
||||
|
||||
# 常用方法
|
||||
|
||||
## 单个查询
|
||||
|
||||
```go
|
||||
// GetDemo 用户角色
|
||||
func (d *Dao) GetDemo(c context.Context, did int64) (demo int8, err error) {
|
||||
err = d.db.QueryRow(c, _getDemoSQL, did).Scan(&demo)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
log.Error("d.GetDemo.Query error(%v)", err)
|
||||
return
|
||||
}
|
||||
return demo, nil
|
||||
}
|
||||
```
|
||||
|
||||
db.QueryRow方法用于返回最多一条记录的查询,在QueryRow方法后使用Scan方法即可将mysql的返回值转换为Golang的数据类型。
|
||||
|
||||
当mysql查询不到对应数据时,会返回sql.ErrNoRows,如果不需处理,可以参考如上代码忽略此error。
|
||||
|
||||
## 批量查询
|
||||
|
||||
```go
|
||||
// ResourceLogs ResourceLogs.
|
||||
func (d *Dao) GetDemos(c context.Context, dids []int64) (demos []int8, err error) {
|
||||
rows, err := d.db.Query(c, _getDemosSQL, dids)
|
||||
if err != nil {
|
||||
log.Error("query error(%v)", err)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var tmpD int8
|
||||
if err = rows.Scan(&tmpD); err != nil {
|
||||
log.Error("scan demo log error(%v)", err)
|
||||
return
|
||||
}
|
||||
demos = append(demos, tmpD)
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
db.Query方法一般用于批量查询的场景,返回*sql.Rows和error信息。
|
||||
我们可以使用rows.Next()方法获得下一行的返回结果,并且配合使用rows.Scan()方法将该结果转换为Golang的数据类型。当没有下一行时,rows.Next方法将返回false,此时循环结束。
|
||||
|
||||
注意,在使用完毕rows对象后,需要调用rows.Close方法关闭连接,释放相关资源。
|
||||
|
||||
## 执行语句
|
||||
|
||||
```go
|
||||
// DemoExec exec
|
||||
func (d *Dao) DemoExec(c context.Context, id int64) (rows int64, err error) {
|
||||
res, err := d.db.Exec(c, _demoUpdateSQL, id)
|
||||
if err != nil {
|
||||
log.Error("db.DemoExec.Exec(%s) error(%v)", _demoUpdateSQL, err)
|
||||
return
|
||||
}
|
||||
return res.RowsAffected()
|
||||
}
|
||||
```
|
||||
|
||||
执行UPDATE/DELETE/INSERT语句时,使用db.Exec方法进行语句执行,返回*sql.Result和error信息:
|
||||
|
||||
```go
|
||||
|
||||
// A Result summarizes an executed SQL command.
|
||||
type Result interface {
|
||||
LastInsertId() (int64, error)
|
||||
RowsAffected() (int64, error)
|
||||
}
|
||||
```
|
||||
|
||||
Result接口支持获取影响行数和LastInsertId(一般用于获取Insert语句插入数据库后的主键ID)
|
||||
|
||||
|
||||
## 事务
|
||||
|
||||
kratos/pkg/database/sql包支持事务操作,具体操作示例如下:
|
||||
|
||||
开启一个事务:
|
||||
|
||||
```go
|
||||
tx := d.db.Begin()
|
||||
if err = tx.Error; err != nil {
|
||||
log.Error("db begin transcation failed, err=%+v", err)
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
在事务中执行语句:
|
||||
|
||||
```go
|
||||
res, err := tx.Exec(_demoSQL, did)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rows := res.RowsAffected()
|
||||
```
|
||||
|
||||
提交事务:
|
||||
|
||||
```go
|
||||
if err = tx.Commit().Error; err!=nil{
|
||||
log.Error("db commit transcation failed, err=%+v", err)
|
||||
}
|
||||
```
|
||||
|
||||
回滚事务:
|
||||
|
||||
```go
|
||||
if err = tx.Rollback().Error; err!=nil{
|
||||
log.Error("db rollback failed, err=%+v", rollbackErr)
|
||||
}
|
||||
```
|
||||
|
||||
# 扩展阅读
|
||||
|
||||
[tidb模块说明](database-tidb.md)
|
||||
[hbase模块说明](database-hbase.md)
|
Loading…
x
Reference in New Issue
Block a user