Skip to content

Commit 90f8a8e

Browse files
committed
add redis
1 parent 54bcbcc commit 90f8a8e

1 file changed

Lines changed: 156 additions & 0 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
title: go-redis - go-redis v9 에서 hash 타입을 다루는 방법
3+
layout: single
4+
author_profile: false
5+
read_time: true
6+
share: true
7+
related: true
8+
categories:
9+
- Redis
10+
tags:
11+
- Redis
12+
- Go-redis
13+
toc: false
14+
toc_stiky: true
15+
description: ""
16+
article_section: ""
17+
meta_keywords: ""
18+
completed: false
19+
visible:
20+
status:
21+
created: 2024-06-30T19:08
22+
last_modified_at: 2024-07-07T18:15:23+09:00
23+
thumnail_url:
24+
priority:
25+
---
26+
# TL;DR
27+
- go-reds v9 버전부터
28+
- hashset을 저장할때 interface로 전달하지 않고 type 을 전달해도 저장됩니다.
29+
- 역직렬화 과정에서도 scan을 통해 HGetAll, MGet 등을 type으로 unmarshal 할 수 있습니다.
30+
# Overview
31+
go-redis/v9 버전 이상에서 사용 가능한 기능들을 발견하여, 용례를 정리하고 성능 등 문제가 없는지 비교해봅니다.
32+
## Usage
33+
### go-redis/v8 이하
34+
- HashSet
35+
- map interface를 만들어서 저장해야 한다.
36+
- type을 만들어서 전달할 경우 아래의 에러 메세지를 받는다.
37+
- `redis: can't marshal main.User (implement encoding.BinaryMarshaler)`
38+
```go
39+
40+
func populateDataV8(rdb *redisv8.Client, n int) {
41+
for i := 1; i <= n; i++ {
42+
user := User{
43+
Name: fmt.Sprintf("User%d", i),
44+
Age: 20 + (i % 30),
45+
Email: fmt.Sprintf("user%d@example.com", i),
46+
Country: "Country" + strconv.Itoa(i%10),
47+
}
48+
49+
userMap := map[string]interface{}{
50+
"name": user.Name,
51+
"age": user.Age,
52+
"email": user.Email,
53+
"country": user.Country,
54+
}
55+
56+
err := rdb.HSet(ctx, fmt.Sprintf("user:%d", i), userMap).Err()
57+
if err != nil {
58+
log.Fatalf("Failed to set user %d: %v", i, err)
59+
}
60+
}
61+
}
62+
63+
```
64+
65+
- HGetAll
66+
- map interface로 전달받은 값을 map을 참조하여 결과를 구성
67+
```go
68+
func fetchDataV8(rdb *redisv8.Client, n int) {
69+
for i := 1; i <= n; i++ {
70+
fields, err := rdb.HGetAll(ctx, fmt.Sprintf("user:%d", i)).Result()
71+
if err != nil {
72+
log.Fatalf("Failed to get user %d: %v", i, err)
73+
}
74+
75+
var user User
76+
user.Name = fields["name"]
77+
user.Age, _ = strconv.Atoi(fields["age"])
78+
user.Email = fields["email"]
79+
user.Country = fields["country"]
80+
// Print for debug purpose, comment out during benchmarking
81+
// fmt.Printf("user:%d - %+v\n", i, user)
82+
}
83+
}
84+
85+
```
86+
87+
88+
### go-redis/v9 이상
89+
- HashSet
90+
- type으로 생성된 결과를 그대로 전달해도 저장된다.
91+
- string, int 여러 타입이 섞여 전달 될 수 있다.
92+
```go
93+
for i := 1; i <= n; i++ {
94+
user := User{
95+
Name: fmt.Sprintf("User%d", i),
96+
Age: 20 + (i % 30),
97+
Email: fmt.Sprintf("user%d@example.com", i),
98+
Country: "Country" + strconv.Itoa(i%10),
99+
}
100+
101+
err := rdb.HSet(ctx, fmt.Sprintf("user:%d", i), user).Err()
102+
if err != nil {
103+
log.Fatalf("Failed to set user %d: %v", i, err)
104+
}
105+
}
106+
107+
```
108+
- HGetAll
109+
- Scan 을 통해 타입에 맞도록 결과를 unmarshal 한다.
110+
```go
111+
112+
func fetchDataV9(rdb *redisv9.Client, n int) {
113+
for i := 1; i <= n; i++ {
114+
res := rdb.HGetAll(ctx, fmt.Sprintf("user:%d", i))
115+
116+
var user User
117+
if err := res.Scan(&user); err != nil {
118+
log.Fatalf("Failed to get user %d: %v", i, err)
119+
}
120+
// Print for debug purpose, comment out during benchmarking
121+
// fmt.Printf("user:%d - %+v\n", i, user)
122+
}
123+
}
124+
125+
```
126+
127+
128+
129+
130+
131+
### Benchmark
132+
- go-redis v8, v9의 hash저장, 조회 관련된 operation 을 수행하는 동작을 테스트한 결과는 아래와 같습니다.
133+
- v9이 v8에 비해 성능면에서는 다소 느려진 점이 있으나, 무시해도 될만한 수준의 차이입니다.
134+
```go
135+
=== RUN BenchmarkPopulateDataV8
136+
BenchmarkPopulateDataV8
137+
BenchmarkPopulateDataV8-10 20 57818098 ns/op 99482 B/op 2010 allocs/op
138+
=== RUN BenchmarkPopulateDataV9
139+
BenchmarkPopulateDataV9
140+
BenchmarkPopulateDataV9-10 21 67450760 ns/op 69820 B/op 2213 allocs/op
141+
=== RUN BenchmarkFetchDataV8
142+
BenchmarkFetchDataV8
143+
BenchmarkFetchDataV8-10 18 60724053 ns/op 67652 B/op 1716 allocs/op
144+
=== RUN BenchmarkFetchDataV9
145+
BenchmarkFetchDataV9
146+
BenchmarkFetchDataV9-10 18 62911891 ns/op 71903 B/op 2030 allocs/op
147+
```
148+
149+
## Conclusion
150+
redis에 데이터를 저장할때, type을 지정해두고 값을 저장, 조회한다면 가독성과 코드 구조가 많이 개선될 수 있을 것 같습니다.
151+
무조건 버전을 올리기보다, 필요한 기능으로 적용이 필요할때 올리면 좋을 것 같습니다.
152+
# References
153+
- [https://github.com/redis/go-redis/issues/672](https://github.com/redis/go-redis/issues/672)
154+
- https://github.com/redis/go-redis/blob/f8cbf483f4a193d441fac2cf14be3d84783848c6/example_test.go#L281
155+
- https://github.com/redis/go-redis/discussions/2454
156+
- https://github.com/redis/go-redis

0 commit comments

Comments
 (0)