Skip to content

Commit 6245234

Browse files
Merge pull request #21 from debeando/mysql-sql-parser-slow-queries
feat - mysql sql parser for slow queries.
2 parents ec617cf + 26c20c8 commit 6245234

File tree

4 files changed

+263
-1
lines changed

4 files changed

+263
-1
lines changed

mysql/sql/digest/list.go

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package digest
2+
3+
import (
4+
"sort"
5+
6+
"github.com/debeando/go-common/mysql/sql/parser/slow"
7+
)
8+
9+
type List []Stats
10+
11+
var FilterByQueryTime float64
12+
13+
func (l List) Index(q slow.Query) (int, bool) {
14+
for i := range l {
15+
if l[i].ID == q.DigestID {
16+
return i, true
17+
}
18+
}
19+
return 0, false
20+
}
21+
22+
func (l *List) Add(q slow.Query) {
23+
index, exists := l.Index(q)
24+
25+
if !exists {
26+
stats := Stats{}
27+
stats.Append(q)
28+
(*l) = append((*l), stats)
29+
} else {
30+
(*l)[index].Append(q)
31+
}
32+
}
33+
34+
func (l List) Count() (sum int) {
35+
for i := range l {
36+
sum = sum + l[i].Count
37+
}
38+
39+
return sum
40+
}
41+
42+
func (l List) FilterByQueryTime(v float64) {
43+
FilterByQueryTime = v
44+
}
45+
46+
func (l List) Filtered() (sum int) {
47+
for i := range l {
48+
if l[i].Time.Query < FilterByQueryTime {
49+
sum = sum + l[i].Count
50+
}
51+
}
52+
53+
return sum
54+
}
55+
56+
func (l List) Analyzed() (sum int) {
57+
for i := range l {
58+
if l[i].Time.Query >= FilterByQueryTime {
59+
sum = sum + l[i].Count
60+
}
61+
}
62+
63+
return sum
64+
}
65+
66+
func (l List) Unique() (sum int) {
67+
for i := range l {
68+
if l[i].Time.Query >= FilterByQueryTime {
69+
sum++
70+
}
71+
}
72+
73+
return sum
74+
}
75+
76+
func (l List) ScoreMax() (max float64) {
77+
for i := range l {
78+
if l[i].Time.Query >= FilterByQueryTime {
79+
if l[i].Score > max {
80+
max = l[i].Score
81+
}
82+
}
83+
}
84+
85+
return max
86+
}
87+
88+
func (l List) ScoreMin() (min float64) {
89+
for i := range l {
90+
if l[i].Time.Query >= FilterByQueryTime {
91+
if l[i].Score < min {
92+
min = l[i].Score
93+
}
94+
}
95+
}
96+
97+
return min
98+
}
99+
100+
func (l *List) Clean() {
101+
for i := len((*l)) - 1; i >= 0; i-- {
102+
if (*l)[i].Time.Query < FilterByQueryTime {
103+
(*l).Remove(i)
104+
}
105+
}
106+
}
107+
108+
func (l *List) Remove(i int) {
109+
if len((*l)) > i {
110+
(*l) = append((*l)[:i], (*l)[i+1:]...)
111+
}
112+
}
113+
114+
// Len is part of sort.Interface.
115+
func (l List) Len() int {
116+
return len(l)
117+
}
118+
119+
// Less is part of sort.Interface.
120+
// We use count as the value to sort by
121+
func (l List) Less(i, j int) bool {
122+
return l[i].Score > l[j].Score
123+
}
124+
125+
// Swap is part of sort.Interface.
126+
func (l List) Swap(i, j int) {
127+
l[i], l[j] = l[j], l[i]
128+
}
129+
130+
func (l List) SortByScore() {
131+
sort.Sort(l)
132+
}

mysql/sql/digest/list_test.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package digest_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/debeando/go-common/mysql/sql/digest"
7+
"github.com/debeando/go-common/mysql/sql/parser/slow"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestIndex(t *testing.T) {
12+
queries := &digest.List{
13+
digest.Stats{ID: "a"},
14+
digest.Stats{ID: "b"},
15+
digest.Stats{ID: "c"},
16+
}
17+
18+
var cases = []struct {
19+
Query slow.Query
20+
Index int
21+
Exist bool
22+
}{
23+
{
24+
Query: slow.Query{DigestID: "a"},
25+
Index: 0,
26+
Exist: true,
27+
},
28+
{
29+
Query: slow.Query{DigestID: "b"},
30+
Index: 1,
31+
Exist: true,
32+
},
33+
{
34+
Query: slow.Query{DigestID: "z"},
35+
Index: 0,
36+
Exist: false,
37+
},
38+
}
39+
40+
for _, test := range cases {
41+
i, e := queries.Index(test.Query)
42+
assert.Equal(t, i, test.Index)
43+
assert.Equal(t, e, test.Exist)
44+
}
45+
}
46+
47+
func TestAddAndCount(t *testing.T) {
48+
queries := &digest.List{}
49+
queries.Add(slow.Query{DigestID: "a"})
50+
queries.Add(slow.Query{DigestID: "a"})
51+
queries.Add(slow.Query{DigestID: "b"})
52+
53+
assert.Equal(t, queries.Count(), 3)
54+
}
55+
56+
func TestAddAndUnique(t *testing.T) {
57+
queries := &digest.List{}
58+
queries.Add(slow.Query{DigestID: "a"})
59+
queries.Add(slow.Query{DigestID: "a"})
60+
queries.Add(slow.Query{DigestID: "b"})
61+
62+
assert.Equal(t, queries.Unique(), 2)
63+
}

mysql/sql/digest/stats.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package digest
2+
3+
import (
4+
"github.com/debeando/go-common/mysql/sql/parser/slow"
5+
)
6+
7+
type Stats struct {
8+
ID string
9+
Count int
10+
Score float64
11+
Time struct {
12+
Query float64
13+
Lock float64
14+
}
15+
Rows struct {
16+
Sent int64
17+
Examined int64
18+
}
19+
Queries []slow.Query
20+
}
21+
22+
func (s *Stats) Append(q slow.Query) {
23+
s.ID = q.DigestID
24+
s.Queries = append(s.Queries, q)
25+
s.Count = len(s.Queries)
26+
27+
s.SetQueryTime(q.QueryTime)
28+
s.SetLockTime(q.LockTime)
29+
s.SetRowsSent(q.RowsSent)
30+
s.SetRowsExamined(q.RowsExamined)
31+
s.SetScore()
32+
}
33+
34+
func (s *Stats) SetQueryTime(t float64) {
35+
if t > s.Time.Query {
36+
s.Time.Query = t
37+
}
38+
}
39+
40+
func (s *Stats) SetLockTime(t float64) {
41+
if t > s.Time.Lock {
42+
s.Time.Lock = t
43+
}
44+
}
45+
46+
func (s *Stats) SetRowsSent(t int64) {
47+
if t > s.Rows.Sent {
48+
s.Rows.Sent = t
49+
}
50+
}
51+
52+
func (s *Stats) SetRowsExamined(t int64) {
53+
if t > s.Rows.Examined {
54+
s.Rows.Examined = t
55+
}
56+
}
57+
58+
func (s *Stats) SetScore() {
59+
s.Score = (s.Time.Query * 0.4) +
60+
(s.Time.Lock * 0.1) +
61+
(float64(s.Count) * 0.2) +
62+
(float64(s.Rows.Examined) * 0.3)
63+
}

mysql/sql/parser/slow/query.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/debeando/go-common/cast"
8+
"github.com/debeando/go-common/crypt"
89
"github.com/debeando/go-common/mysql/sql/parser/digest"
910
)
1011

@@ -83,6 +84,8 @@ func QueryParser(query string) Query {
8384
property["query"] = strings.Trim(property["query"], "\n")
8485
property["user"] = UserParser(property["user@host"])
8586

87+
digestQuery := digest.Digest(property["query"])
88+
8689
return Query{
8790
Time: cast.StringToDateTime(property["time"], "2006-01-02T15:04:05.000000Z"),
8891
ID: cast.StringToInt64(property["id"]),
@@ -93,7 +96,8 @@ func QueryParser(query string) Query {
9396
Timestamp: cast.StringToInt64(property["timestamp"]),
9497
Raw: property["query"],
9598
User: property["user"],
96-
Digest: digest.Digest(property["query"]),
99+
Digest: digestQuery,
100+
DigestID: crypt.MD5(digestQuery),
97101
}
98102
}
99103

0 commit comments

Comments
 (0)