1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
| package tmysql
import (
"database/sql"
_ "database/sql"
"math/rand"
"runtime"
"strings"
"time"
"github.com/go-sql-driver/mysql"
"github.com/google/uuid"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
type DBSetting struct {
ConnMaxLifeTime time.Duration
MaxOpenConns int
MaxIdleConns int
ConnMaxIdleTime time.Duration
}
type BchSetting struct {
WriteClientNum int
WriteInterval time.Duration
ReadClientNum int
ReadInternal time.Duration
}
func StartServer(dns string, setting *DBSetting, bchSetting *BchSetting) error {
// 1. 服务启动
dbCfg, err := mysql.ParseDSN(dns)
if err != nil {
return errors.Wrap(err, "parse dns got err")
}
log.Infof("db cfg: %+v", dbCfg)
// 2. DB连接实例
db, err := sql.Open("mysql", dns)
if err != nil {
return errors.Wrapf(err, "sql open got err,")
}
// 3. Mysql实例+配置
// See "Important settings" section.
db.SetConnMaxLifetime(setting.ConnMaxLifeTime)
db.SetMaxOpenConns(setting.MaxOpenConns)
db.SetMaxIdleConns(setting.MaxIdleConns)
db.SetConnMaxIdleTime(setting.ConnMaxIdleTime)
// 4. 模拟多协程读、写用户(10w写入,50w读取)
go srvStart(db, bchSetting)
// 5. 启动协程,定期输出监控数据 db.Stat
go srvMonitor(db)
select {}
}
// 服务监控
func srvMonitor(db *sql.DB) {
for {
select {
case <-time.Tick(1 * time.Second):
log.Infof("db status: %+v", db.Stats())
log.Infof("%s", strings.Repeat("-", 50))
log.Infof("go routine num: %d", runtime.NumGoroutine())
}
}
}
// 模拟启动并发读、写协程
func srvStart(db *sql.DB, bchCfg *BchSetting) {
// 读
for i := 0; i < bchCfg.ReadClientNum; i++ {
go func() {
GetUser(bchCfg, db, uuid.New().String())
}()
}
// 写
for i := 0; i < bchCfg.WriteClientNum; i++ {
go func() {
NewUser(bchCfg, db, randUser())
}()
}
}
func NewUser(bcfg *BchSetting, db *sql.DB, u *User) {
for {
select {
case <-time.Tick(bcfg.WriteInterval):
// log.Infof("insert name[%s]", u.Name)
_, err := db.Exec("insert into user (name) values(?)", u.Name)
if err != nil {
log.Errorf("insert db exec got err: %s", err)
}
}
}
}
func GetUser(bcfg *BchSetting, db *sql.DB, s string) {
for {
select {
case <-time.Tick(bcfg.ReadInternal):
// rows, err := db.Query("select * from user where name like ?", "%"+s) : 可以人为改成为慢查询,这样连接池就会被打爆
rows, err := db.Query("select * from user where name like ?", s)
if err != nil {
log.Errorf("read db query got err: %s", err)
continue
}
rows.Close() // 不关闭的话,会有资源死锁问题
}
}
}
type User struct {
ID int
Name string
}
func randUser() *User {
return &User{
Name: uuid.New().String(),
}
}
|