0
0
Lập trình
Flame Kris
Flame Krisbacodekiller

Mẫu Thiết Kế Proxy trong Golang: Tối Ưu Hóa Truy Vấn Cơ Sở Dữ Liệu

Đăng vào 3 tuần trước

• 5 phút đọc

I. Proxy - Mẫu Thiết Kế Cấu Trúc

Mẫu thiết kế Proxy là một khái niệm quen thuộc trong lập trình, được sử dụng để đại diện cho một tài nguyên gốc. Proxy cung cấp các chức năng tương tự như tài nguyên thật nhưng với khả năng tùy biến cao.

Proxy thường được trách nhiệm là thay thế tài nguyên gốc, ẩn đi các chi tiết phức tạp và bổ sung các chức năng thiết yếu như kiểm soát truy cập, lưu trữ bộ nhớ đệm (caching), hoặc giảm tải xử lý cho hệ thống.

II. Ví Dụ Thực Tế

Trong nhiều hệ thống, việc truy vấn thông tin người dùng từ cơ sở dữ liệu chính diễn ra thường xuyên. Tuy nhiên, nếu truy vấn trực tiếp đến cơ sở dữ liệu, có thể dẫn đến độ trễ cao và giảm hiệu suất của hệ thống. Để giải quyết vấn đề này, chúng ta có thể áp dụng mẫu Proxy như một bộ đệm (cache) để lưu trữ tạm thời các dữ liệu thường xuyên được truy vấn.

Proxy sẽ kiểm tra bộ nhớ đệm trước. Nếu dữ liệu đã tồn tại trong bộ nhớ đệm, nó sẽ trả về ngay lập tức. Nếu không, Proxy sẽ thực hiện truy vấn từ cơ sở dữ liệu chính và sau đó cập nhật bộ nhớ đệm.

Trong trường hợp này, chúng ta sẽ mô tả cơ chế Proxy giúp tìm kiếm người dùng (User) giữa MainDB (cơ sở dữ liệu chính) và Stack (bộ nhớ đệm) nhằm cải thiện hiệu suất hệ thống.

1. Định Nghĩa User

User là một thực thể đơn giản với thuộc tính ID (kiểu int), đại diện cho một đối tượng người dùng.

2. Interface Chung

Giao diện UserFinder định nghĩa phương thức Find(ID int) để tìm kiếm người dùng theo ID. Cả MainDB và bộ nhớ đệm đều tuân theo giao diện này.

3. Cơ Sở Dữ Liệu Chính (MainDB)

UsersDB là nơi lưu trữ người dùng chính, cung cấp chức năng Find để tìm kiếm và Add để thêm người dùng mới.

4. Proxy (UserFinderProxy)

  • Là lớp trung gian sử dụng Stack (bộ nhớ đệm) để truy cập nhanh hơn trước khi tìm kiếm trong MainDB.
  • Khi một người dùng không có trong Stack, Proxy sẽ kiểm tra MainDB. Nếu tìm thấy, Proxy sẽ lưu người dùng đó vào Stack để tối ưu cho các lần truy xuất sau.
  • Stack có cơ chế giới hạn dung lượng và tự động cập nhật khi đầy.

Sơ Đồ Lớp

[Không có hình ảnh, chỉ cần đưa ra một sơ đồ lớp điển hình để trình bày cấu trúc mối quan hệ giữa các lớp trong thiết kế Proxy này]

III. Triển Khai

1. Định Nghĩa Type cho User Entity

go Copy
package proxy

type User struct {
	ID int
}

2. Định Nghĩa Interface Chung

go Copy
package proxy

type UserFinder interface {
	Find(ID int) (User, error)
}

3. Cơ Sở Dữ Liệu

go Copy
package proxy

import "fmt"

type UsersDB []User

func (u *UsersDB) Find(ID int) (User, error) {
	for _, user := range *u {
		if user.ID == ID {
			return user, nil
		}
	}
	return User{}, fmt.Errorf("user not found")
}

func (u *UsersDB) Add(user User) *UsersDB {
	fmt.Println("Adding to database: ", user)
	*u = append(*u, user)
	return u
}

4. Định Nghĩa Proxy

go Copy
package proxy

import "fmt"
type UsersStack []User

func (u *UsersStack) Find(ID int) (User, error) {
	for _, user := range *u {
		if user.ID == ID {
			return user, nil
		}
	}
	return User{}, fmt.Errorf("user not found")
}

func (u *UsersStack) Add(user User) *UsersStack {
	*u = append(*u, user)
	return u
}

type UserFinderProxy struct {
	MainDB   UsersDB
	Stack     UsersStack
	Capacity  int
}

func (u *UserFinderProxy) Find(ID int) (User, error) {
	user, err := u.Stack.Find(ID)
	if err == nil {
		fmt.Println("Found in stack: ", user)
		return user, nil
	}

	user, err = u.MainDB.Find(ID)
	if err != nil {
		return User{}, err
	}

	fmt.Println("Found in mainDB: ", user)
	u.AddToStack(user)
	return user, nil
}

func (u *UserFinderProxy) AddToStack(user User) error {
	fmt.Println("Adding to stack: ", user)
	if len(u.Stack) >= u.Capacity {
		u.Stack = append(u.Stack[1:], user)
	} else {
		u.Stack.Add(user)
	}
	return nil
}

Chạy Chương Trình

go Copy
// Ví dụ về Proxy
fmt.Println("*** Ví Dụ Proxy ***")

mainDB := proxy.UsersDB{}

user1 := proxy.User{ID: 1}
user2 := proxy.User{ID: 2}
user3 := proxy.User{ID: 3}

mainDB.Add(user1).Add(user2).Add(user3)

proxy := proxy.UserFinderProxy{
	MainDB:   mainDB,
	Stack:    proxy.UsersStack{},
	Capacity: 2,
}

proxy.Find(1)
proxy.Find(2)
proxy.Find(3)
proxy.Find(2)
proxy.Find(1)

fmt.Print("*** Kết Thúc Ví Dụ Proxy ***\n\n\n")

Kết Quả

[Không cần hình ảnh, chỉ cần mô tả kết quả kỳ vọng từ chương trình]

IV. Giải Thích Ví Dụ Proxy

Ví dụ này minh họa cách hoạt động của mẫu Proxy thông qua việc truy vấn người dùng (User) từ cơ sở dữ liệu chính (MainDB) và sử dụng bộ nhớ đệm (Stack) để tối ưu hóa các lần tìm kiếm lặp lại. Dưới đây là giải thích chi tiết từng bước trong quá trình khai thác:

1. Khởi Tạo Cơ Sở Dữ Liệu Chính MainDB

  • Khởi tạo cơ sở dữ liệu chứa danh sách các người dùng với 3 người dùng có ID = 1, 2, 3. Mọi truy vấn đều có thể tìm thấy các User này từ MainDB.

2. Khởi Tạo Proxy với MainDBStack

  • Tạo một Proxy với MainDB, Stack và xác định khả năng lưu trữ tối đa (Capacity).

3. Tìm Kiếm User

  • Lần 1: Tìm ID = 1, không thấy trong Stack, tìm trong MainDB, tìm thấy và thêm vào Stack.
  • Lần 2: Tìm ID = 2, không thấy trong Stack, tìm trong MainDB, tìm thấy và thêm vào Stack.
  • Lần 3: Tìm ID = 3, không thấy trong Stack, tìm trong MainDB, tìm thấy và thêm vào nhưng do Stack đã đầy nên loại bỏ User lâu nhất.
  • Lần 4: Tìm ID = 2, tìm thấy ngay trong Stack mà không cần truy vấn MainDB.
  • Lần 5: Tìm ID = 1, không thấy trong Stack, truy vấn trong MainDB, tìm thấy và thêm vào nhưng loại bỏ User lâu nhất (FIFO).

4. Kết Thúc Ví Dụ

  • Cuối cùng, Stack chứa các User mới nhất được tìm kiếm, giúp giảm thiểu số lượng truy cập đến MainDB và cải thiện hiệu suất hệ thống.

V. Lời Kết

Không phải lúc nào cũng cần áp dụng mẫu Proxy cho mọi bài toán. Trong nhiều trường hợp, truy cập trực tiếp vào cơ sở dữ liệu là đủ nếu không cần tối ưu hóa hiệu suất. Hãy lưu ý rằng các giải pháp như mẫu Decorator cũng có thể phù hợp với một số tình huống cụ thể. Quan trọng là lựa chọn giải pháp tối ưu cho vấn đề của bạn. Cảm ơn các bạn đã theo dõi bài viết!

VI. Tài Liệu Tham Khảo

  • Go Design Patterns (Mario Castro Contreras)
  • Mã nguồn hoàn chỉnh về mẫu thiết kế Golang: tại đây.
    source: viblo
Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào