基於 GoLang 進行分布式数据库实践

简介: Hello folks,今天我们介绍一下如何基于 Golang Web 应用程序进行分布式数据库操作,这里我们以 CockroachDB 开源数据库为例。


    Hello folks,今天我们介绍一下如何基于 Golang Web 应用程序进行分布式数据库操作,这里我们以  CockroachDB 开源数据库为例。

    CockroachDB 是一款开源的分布式数据库,具有 NoSQL 对海量数据的存储管理能力,又保持了传统数据库支持的 ACID 和 SQL 等,还支持跨地域、去中心、高并发、多副本强一致和高可用等特性。支持 OLTP 场景,同时支持轻量级 OLAP 场景。

    接下来,我们开始进行部署操作,首先我们检查一下当前的环境信息,具体如下所示:


[leonli@192 go ] % go version
go version go1.18.1 darwin/arm64

    为了方便起见,我们基于 Docker-compose 新建一个 CockroachD 集群,并将其运行起来,具体如下:


[leonli@192 CockroachDB ] % more docker-compose-cockroach.yml
version: "3.5"
# Reference: https://www.cockroachlabs.com/docs/v21.1/start-a-local-cluster-in-docker-linux
services:
    # Node 1 (main)
    roach1:
        container_name: cockroach-db-1
        image: cockroachdb/cockroach:v21.2.10
        hostname: roach1
        # Assign Container to a Network
        networks: 
            - cockroach-net
        ports:
            - 26257:26257   # Cockroach Database port
            - 9090:8080     # Cockroach UI Panel port
        volumes:
            - ./cockroach-data/roach1:/cockroach/cockroach-data
        command: start --insecure  --join=roach1,roach2,roach3
    # Node 2
    roach2:
        container_name: cockroach-db-2
        image: cockroachdb/cockroach:v21.2.10
        hostname: roach2
        # Assign Container to a Network
        networks: 
            - cockroach-net
        volumes:
            - ./cockroach-data/roach2:/cockroach/cockroach-data
        command: start --insecure --join=roach1,roach2,roach3
    # Node 3
    roach3:
        container_name: cockroach-db-3
        image: cockroachdb/cockroach:v21.2.10
        hostname: roach3
        # Assign Container to a Network
        networks: 
            - cockroach-net
        volumes:
            - ./cockroach-data/roach3:/cockroach/cockroach-data
        command: start --insecure --join=roach1,roach2,roach3
    # First Time Initialization of Cockroach DB
    init-roach1:
        image: cockroachdb/cockroach:latest
        container_name: init-cockroach
        depends_on: 
            - roach1
        restart: "no"
        # Assign Container to a Network
        networks: 
            - cockroach-net
        entrypoint: ["bash", "-c" ,"./cockroach init --insecure --host cockroach-db-1:26257 | wc || exit"]
# Initialize a Network 
networks:
    cockroach-net:
        driver: bridge
[leonli@192 CockroachDB ] % docker-compose -f docker-compose-cockroach.yml up -d

    然后,在浏览器中输入:http://localhost:9090/#/overview/list 进行访问,如下所示:

    现在,我们进入 CockroachD 集群容器中,进行相关库表的创建,具体如下所示:


[leonli@192 CockroachDB ] % docker exec -it 05f9ff283fb8 bash
[[root@roach1 cockroach]] # ls -l
total 212444
-rwxr-xr-x  1 root root 217535728 May  2 17:51 cockroach
drwxr-xr-x 24 root root       768 May 22 05:44 cockroach-data
-rwxr-xr-x  1 root root       120 May  2 17:28 cockroach.sh
[[root@roach1 cockroach]] # cockroach sql --insecure
#
# Welcome to the CockroachDB SQL shell.
# All statements must be terminated by a semicolon.
# To exit, type: \q.
#
# Server version: CockroachDB CCL v21.2.10 (x86_64-unknown-linux-gnu, built 2022/05/02 17:38:58, go1.16.6) (same version as client)
# Cluster ID: 369395e8-8b75-4d7a-9b5a-b7212c4313bb
#
# Enter \? for a brief introduction.
#
[root@:26257/defaultdb> show databases;
  database_name | owner | primary_region | regions | survival_goal
----------------+-------+----------------+---------+----------------
  defaultdb     | root  | NULL           | {}      | NULL
  postgres      | root  | NULL           | {}      | NULL
  system        | node  | NULL           | {}      | NULL

[root@:26257/defaultdb> CREATE USER IF NOT EXISTS luga;
CREATE ROLE
Time: 276ms total (execution 272ms / network 3ms)
[root@:26257/defaultdb> CREATE DATABASE books;
CREATE DATABASE
Time: 42ms total (execution 42ms / network 1ms)
[root@:26257/defaultdb> GRANT ALL ON DATABASE books TO luga;
GRANT
Time: 139ms total (execution 138ms / network 1ms)
[root@:26257/defaultdb> use books
                    -> ;
SET
Time: 2ms total (execution 2ms / network 0ms)
[root@:26257/books> show tables;
SHOW TABLES 0
Time: 151ms total (execution 150ms / network 1ms)
[root@:26257/books> CREATE TABLE tblbooks(
id INT PRIMARY KEY,
name VARCHAR,
phone VARCHAR,
email VARCHAR,
stars INT,
category VARCHAR
);
CREATE TABLE
Time: 28ms total (execution 28ms / network 1ms)
[root@:26257/books> show tables;
  schema_name | table_name | type  | owner | estimated_row_count | locality
--------------+------------+-------+-------+---------------------+-----------
  public      | tblbooks     | table | root  |                   0 | NULL
(1 row)
Time: 120ms total (execution 119ms / network 1ms)
[root@:26257/books> show tables;
  schema_name | table_name | type  | owner | estimated_row_count | locality
--------------+------------+-------+-------+---------------------+-----------
  public      | tblbooks     | table | root  |                   0 | NULL
(1 row)
Time: 75ms total (execution 74ms / network 1ms)
[root@:26257/books> INSERT INTO tblbooks (id, name, phone, email, stars, category) VALUES
(1,'Lucy','130-0000-0000','lucy13000000000@example.net',8,'life'),
(2,'Green ','125-0456-0102','green1250456@example.com',1,'artity'),
(3,'Linda','857-555-0182','linda857@example.com',3,'moon'),
(4,'Wilium','999-555-000','wilium@example.com',5,'sunset'),
(5,'Lily','234-2123-1231','lily1900@example.com',5,'work'),
(6,'Luga','100-0555-0102','luga100@example.com',3,'golang'),
(7,'Qiuchi','777-5555-0000','qiuchi777@example.com',3,'freedom'),
(8,'walse','000-11111-22222','walse@example.com',5,'tutorial'),
(9,'Jhons','444-4433-1213','jhons444@example.com',5,'project'),
(10,'Leon','122-1214-5678','leon1221214@example.com',4,'confidence');
INSERT 10
Time: 30ms total (execution 30ms / network 1ms)

    此時,通過 CockroachD 控制台,我們可以看到之前的相關操作信心,具體如下所示:

    基于上述操作,我们新建了 books 数据库,并在此库中,新建 tblbooks 表,并对其进行用户角色、权限的配置。此时,我们开始着手于 Go 應用程序的編寫,首先,我們需要下載涉及的相關依賴,具體如下所示:


import (
"database/sql" 
"fmt" 
"net/http" 
"log" 
_ "github.com/lib/pq" 
)

    下面,我們針對所依賴的代碼包進行簡要解釋,具體如下:

    1、要在 Go 中使用 SQL 数据库和 SQL 等数据库,请使用 database/sql。

    2、与 Print() 函数类似的是 fmt 函数。它格式化为 I/O。

    3、使用 net/http 包对 HTTP 服务器和客户端进行 API 调用。

    4、database/sql 需要用于 Go 的 Postgres 驱动程序包 “github.com/lib/pq” 。也可以通过在代码行开头使用下划线 _ 来加载它而不显示代码中的名称。

    引入相關依賴包後,我們開始使用 struct 创建匹配数据库表的字段。通常情況下,使用基於 GoLang 的 struct 來创建与表 tbbook 相同的命名属性或字段。如下所示:


type Books struct {
  Id int 
  Name string 
  Phone string 
  Email string 
  Stars int 
  Category string 
}

    在成功导入依赖项,使用 GoLang 的 struct 命令创建匹配字段後 接下来,让我们進行 CockroachDB 数据库連接操作。在 GoLang 語法結構中,通过设置“db”全局包级别变量来获取包内的全局使用情况。即:


var db *sql.DB

    並使用 init() 函数進行快速无缝建立数据库连接,其最大好處在於:僅需調用一次。具體如下:


func init() {
  var err error 
  connStr := "postgres://luga:password@localhost:26257/books?sslmode=disable" 
  db, err = sql.Open("postgres", connStr) #訪問連接
  if err != nil { 
    panic(err) 
  } 
  if err = db.Ping(); err != nil { 
    panic(err) 
  } 
  fmt.Println("Connected to the database") 
}

    基於上述代碼,使用 db 检查任何引发的错误,以及 func main() 來验证数据库连接是否有效。上述代碼簡要描述了 GET /books 路由所需的所有初始化邏輯,現在,让我们为我们的 Web 应用程序編寫 booksIndex() HTTP 处理程序,即所謂的函數(方法)入口。具體如下所示:


func main(){
  http.HandleFunc("/books", booksIndex)
  http.ListenAndServe(":9090", nil) 
}

    当使用方法 http.HandleFunc() 向它传递第二个参数时,可以只传递函数的名称。一般情況下,需要通过消除括号将函数作为回调函数传递。

    1、此處声明 HTTP 请求中的函数名称是 booksIndex。

    2、“9090”為分配的監聽端口。

    最後,我們來看一下最為核心的內容,具體如下所示:


func booksIndex(w http.ResponseWriter, r *http.Request) {
        if r.Method != "GET" {
                http.Error(w, http.StatusText(405), http.StatusMethodNotAllowed)
                return
        }
        rows, err := db.Query("SELECT * FROM tblbooks;")
        if err != nil {
                http.Error(w, http.StatusText(500), 500)
                return
        }
        defer rows.Close()
        bks := make([]*Book, 0)
        for rows.Next() {
                bk := new(Book)
                err := rows.Scan(&bk.Id, &bk.Name, &bk.Phone, &bk.Email, &bk.Stars, &bk.Category)
                if err != nil {
                    http.Error(w, http.StatusText(500), 500)
                    return
                }
                bks = append(bks, bk)
        }
        if err = rows.Err(); err != nil {
                http.Error(w, http.StatusText(500), 500)
                return
        }
        for _, bk := range bks {
                fmt.Fprintf(w, "%d %s %s %s %d %s\n", bk.Id, bk.Name, bk.Phone, bk.Email, bk.Stars, bk.Category)
        }
}

    我們對如上的代碼進行簡要的解釋,具體:

    1、db.Query("SELECT * FROM tblbooks") ,基於此命令操作進行數據庫查詢,以返回相關結果。

    2、 rows.Close() ,基於此命令延迟释放资源。

    3、制作了一个切片并分配了 restos := make([]Restaurant, 0) 变量。

    4、使用 rows.Next() 完成迭代。

    5、使用 rows.Scan() 复制当前行的列。

   基於上述的各階段描述,最終的代碼實現如下所示:


package main
import (
        "database/sql"
        "fmt"
        "net/http"
        "log"
        _ "github.com/lib/pq"
)
type Book struct {
        Id int
        Name string
        Phone string
        Email string
        Stars int
        Category string
}
var db *sql.DB
func init() {
        var err error
        db, err = sql.Open("postgres", "postgres://luga:password@127.0.0.1:26257/books?sslmode=disable")
        if err != nil {
            log.Fatal(err)
        }
        if err = db.Ping(); err != nil {
            log.Fatal(err)
        }       
}
func main() {
    http.HandleFunc("/books", booksIndex)
    http.ListenAndServe(":9090", nil)
}
func booksIndex(w http.ResponseWriter, r *http.Request) {
        if r.Method != "GET" {
                http.Error(w, http.StatusText(405), http.StatusMethodNotAllowed)
                return
        }
        rows, err := db.Query("SELECT * FROM tblbook")
        if err != nil {
                http.Error(w, http.StatusText(500), 500)
                return
        }
        defer rows.Close()
        bks := make([]*Book, 0)
        for rows.Next() {
                bk := new(Book)
                err := rows.Scan(&bk.Id, &bk.Name, &bk.Phone, &bk.Email, &bk.Stars, &bk.Category)
                if err != nil {
                    http.Error(w, http.StatusText(500), 500)
                    return
                }
                bks = append(bks, bk)
        }
        if err = rows.Err(); err != nil {
                http.Error(w, http.StatusText(500), 500)
                return
        }
        for _, bk := range bks {
                fmt.Fprintf(w, "%d %s %s %s %d %s\n", bk.Id, bk.Name, bk.Phone, bk.Email, bk.Stars, bk.Category)
        }
}

    此時,我們在文件所在的目錄下執行如下命令:


[leonli@192 books ] % go run main.go
[leonli@192 books ] %

    其結果如下:


Lucy    130-0000-0000   lucy13000000000@example.net     8  life
   Green   125-0456-0102   green1250456@example.com        1  artity   
   Linda   857-555-0182    linda857@example.com            3  moon
   Wilium  999-555-000     wilium@example.com              5  sunset
   Lily    234-2123-1231   lily1900@example.com            5  work
   Luga    100-0555-0102   luga100@example.com             3  golang
   Qiuchi  777-5555-0000   qiuchi777@example.com           3  freedom
   walse   000-11111-22222 walse@example.com               5  tutorial
   Jhons   444-4433-1213   jhons444@example.com            5  project
   Leon    122-1214-5678   leon1221214@example.com         4  confidence

    當然,我們也可以通過 http://localhost:9090/books 進行訪問。基於上述結果,我們的操作到此結束。

    至此,一个简单的 Demo 先解析到此为止,希望大家有所收获!

    Adiós !

相关文章
|
1月前
|
关系型数据库 OLAP API
非“典型”向量数据库AnalyticDB PostgreSQL及RAG服务实践
本文介绍了非“典型”向量数据库AnalyticDB PostgreSQL及其RAG(检索增强生成)服务的实践应用。 AnalyticDB PostgreSQL不仅具备强大的数据分析能力,还支持向量查询、全文检索和结构化查询的融合,帮助企业高效构建和管理知识库。
81 19
|
29天前
|
缓存 NoSQL JavaScript
Vue.js应用结合Redis数据库:实践与优化
将Vue.js应用与Redis结合,可以实现高效的数据管理和快速响应的用户体验。通过合理的实践步骤和优化策略,可以充分发挥两者的优势,提高应用的性能和可靠性。希望本文能为您在实际开发中提供有价值的参考。
55 11
|
1月前
|
数据采集 人工智能 分布式计算
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
阿里云推出的MaxFrame是链接大数据与AI的分布式Python计算框架,提供类似Pandas的操作接口和分布式处理能力。本文从部署、功能验证到实际场景全面评测MaxFrame,涵盖分布式Pandas操作、大语言模型数据预处理及企业级应用。结果显示,MaxFrame在处理大规模数据时性能显著提升,代码兼容性强,适合从数据清洗到训练数据生成的全链路场景...
94 5
MaxFrame:链接大数据与AI的高效分布式计算框架深度评测与实践!
|
1月前
|
存储 运维 安全
盘古分布式存储系统的稳定性实践
本文介绍了阿里云飞天盘古分布式存储系统的稳定性实践。盘古作为阿里云的核心组件,支撑了阿里巴巴集团的众多业务,确保数据高可靠性、系统高可用性和安全生产运维是其关键目标。文章详细探讨了数据不丢不错、系统高可用性的实现方法,以及通过故障演练、自动化发布和健康检查等手段保障生产安全。总结指出,稳定性是一项系统工程,需要持续迭代演进,盘古经过十年以上的线上锤炼,积累了丰富的实践经验。
|
2月前
|
弹性计算 安全 关系型数据库
活动实践 | 自建数据库迁移到云数据库
通过阿里云RDS,用户可获得稳定、安全的企业级数据库服务,无需担心数据库管理与维护。该方案使用RDS确保数据库的可靠性、可用性和安全性,结合ECS和DTS服务,实现自建数据库平滑迁移到云端,支持WordPress等应用的快速部署与运行。通过一键部署模板,用户能迅速搭建ECS和RDS实例,完成数据迁移及应用上线,显著提升业务灵活性和效率。
|
2月前
|
运维 Kubernetes 调度
阿里云容器服务 ACK One 分布式云容器企业落地实践
阿里云容器服务ACK提供强大的产品能力,支持弹性、调度、可观测、成本治理和安全合规。针对拥有IDC或三方资源的企业,ACK One分布式云容器平台能够有效解决资源管理、多云多集群管理及边缘计算等挑战,实现云上云下统一管理,提升业务效率与稳定性。
|
2月前
|
Cloud Native 关系型数据库 分布式数据库
PolarDB 分布式版 V2.0,安全可靠的集中分布式一体化数据库管理软件
阿里云PolarDB数据库管理软件(分布式版)V2.0 ,安全可靠的集中分布式一体化数据库管理软件。
|
2月前
|
运维 监控 Cloud Native
云原生之运维监控实践:使用 taosKeeper 与 TDinsight 实现对 时序数据库TDengine 服务的监测告警
在数字化转型的过程中,监控与告警功能的优化对保障系统的稳定运行至关重要。本篇文章是“2024,我想和 TDengine 谈谈”征文活动的三等奖作品之一,详细介绍了如何利用 TDengine、taosKeeper 和 TDinsight 实现对 TDengine 服务的状态监控与告警功能。作者通过容器化安装 TDengine 和 Grafana,演示了如何配置 Grafana 数据源、导入 TDinsight 仪表板、以及如何设置告警规则和通知策略。欢迎大家阅读。
65 0
|
2月前
|
机器学习/深度学习 存储 运维
分布式机器学习系统:设计原理、优化策略与实践经验
本文详细探讨了分布式机器学习系统的发展现状与挑战,重点分析了数据并行、模型并行等核心训练范式,以及参数服务器、优化器等关键组件的设计与实现。文章还深入讨论了混合精度训练、梯度累积、ZeRO优化器等高级特性,旨在提供一套全面的技术解决方案,以应对超大规模模型训练中的计算、存储及通信挑战。
119 4
|
3月前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
101 8

热门文章

最新文章