All Products
Search
Document Center

Lindorm:Use Go to develop applications

Last Updated:Mar 28, 2026

Use Go's database/sql package with LindormTable SQL statements to connect to LindormTable and perform CRUD operations. This guide walks you through:

  • Adding the Go MySQL Driver dependency

  • Configuring connection parameters and connection pool settings

  • Creating a table, writing, querying, and deleting data

  • Running the complete sample application and verifying results

Prerequisites

Before you begin, ensure that you have:

Connection and reliability notes

  • LindormTable SQL frontend nodes use Server Load Balancer (SLB) to distribute client requests across frontend nodes. To keep connections evenly distributed, avoid long connection keep-alive times. Configure SetConnMaxLifetime to refresh connections periodically.

  • In complex network environments — high latency, packet loss, or network jitter — connections may be interrupted. Configure an appropriate connection pool and add retry logic in your application code.

  • When the server restarts during upgrades, connections may be temporarily dropped. Even with a connection pool, your application may encounter exceptions. Catch and retry these errors.

  • Tune connection pool settings to match your traffic. Too few connections cause requests to queue and increase response time (RT). Print the pool status to diagnose connection issues:

    fmt.Printf("%+v\n", db.Stats())

Connect to LindormTable

Step 1: Add the driver dependency

In your project's go.mod file, add the Go MySQL Driver:

require github.com/go-sql-driver/mysql v1.7.1

Step 2: Configure connection parameters

Set your connection constants:

const (
    user           = "<your-username>"
    password       = "<your-password>"
    host           = "<LindormTable-endpoint-for-MySQL>"
    port           = 33060
    database       = "default"
    connectTimeout = "10s"
)

Parameter reference

ParameterDescription
userUsername for LindormTable access. To reset the password, see Manage users.
passwordPassword for LindormTable access.
hostLindormTable Endpoint For MySQL. See View endpoints. For ECS-deployed applications, use the VPC endpoint for lower latency and higher security. For internet access, enable the public endpoint in the console: Database Connections > Wide Table Engine > Enable Public Endpoint.
portFixed value: 33060.
databaseDatabase name. Defaults to default.
connectTimeoutConnection timeout. Unit: seconds (s).

Step 3: Open a connection and configure the pool

Build the connection URL and configure pool settings before running any queries:

url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%s",
    user, password, host, port, database, connectTimeout)

db, err := sql.Open("mysql", url)
if err != nil {
    panic(err.Error())
}
defer db.Close()

// Maximum number of open connections. Default: 0 (unlimited).
// Set this to avoid waiting when connections are exhausted.
db.SetMaxOpenConns(20)

// Maximum number of idle connections. Default: 2.
// Set equal to SetMaxOpenConns to avoid unnecessary connection churn.
db.SetMaxIdleConns(20)

// Maximum idle time before a connection is closed. Default: 0 (no limit).
// 8 minutes is a safe default to reclaim stale idle connections.
db.SetConnMaxIdleTime(8 * time.Minute)

// Maximum connection lifetime. Rotate connections to avoid imbalance
// caused by long-lived persistent connections. 30 minutes recommended.
db.SetConnMaxLifetime(30 * time.Minute)

Sample code snippets

All examples use db.Exec, db.Query, and db.Prepare from Go's standard database/sql package.

List databases

rows, err := db.Query("show databases")
if err != nil {
    panic(err.Error())
}
defer rows.Close()

for rows.Next() {
    var dbName string
    if err := rows.Scan(&dbName); err != nil {
        panic(err.Error())
    }
    fmt.Println(dbName)
}

Create a table

_, err := db.Exec("create table if not exists user_test(id int, name varchar,age int, primary key(id))")
if err != nil {
    fmt.Println("create table error", err)
    return
}

Write data

Direct write

_, err = db.Exec("upsert into user_test(id,name,age) values(1,'zhangsan',17)")
if err != nil {
    fmt.Println("insert data error", err)
    return
}

Parameterized write (recommended for repeated writes — reuse Stmt for high-throughput scenarios)

stmt, err := db.Prepare("upsert into user_test(id,name,age) values(?,?,?)")
if err != nil {
    fmt.Println("prepare error", err)
    return
}
defer stmt.Close()

_, err = stmt.Exec(2, "lisi", 18)
if err != nil {
    fmt.Println("upsert error", err)
    return
}

Query data

Direct query

rows, err := db.Query("select * from user_test")
if err != nil {
    fmt.Println("query data error", err)
    return
}
defer rows.Close()

var id int
var name string
var age int
for rows.Next() {
    if err = rows.Scan(&id, &name, &age); err != nil {
        fmt.Println("scan data error", err)
        return
    }
    fmt.Println("id:", id, "name:", name, "age:", age)
}

Parameterized query

stmt, err := db.Prepare("select * from user_test where id=?")
if err != nil {
    fmt.Println("prepare error", err)
    return
}
defer stmt.Close()

rows, err := stmt.Query(1)
if err != nil {
    fmt.Println("query data error", err)
    return
}
defer rows.Close()

var id int
var name string
var age int
for rows.Next() {
    if err = rows.Scan(&id, &name, &age); err != nil {
        fmt.Println("scan data error", err)
        return
    }
    fmt.Println("id:", id, "name:", name, "age:", age)
}

Delete data

_, err = db.Exec("delete from user_test where id=1")
if err != nil {
    fmt.Println("delete data error", err)
    return
}

Complete sample code

The following is a runnable end-to-end example that connects to LindormTable, creates a table, writes rows, queries them, and deletes one.

In production, load credentials from environment variables instead of hardcoding them in source code.
package main

import (
    "database/sql"
    "fmt"
    "time"
    _ "github.com/go-sql-driver/mysql"
)

const (
    // Set user to the username used to access LindormTable
    user = "user"
    // Set password to the password used to access LindormTable
    password = "test"
    // Set host to the LindormTable endpoint for MySQL
    host = "ld-uf6k8yqb741t3****-proxy-sql-lindorm-public.lindorm.rds.aliyuncs.com"
    // Port is fixed to 33060
    port = 33060
    // Set database to the name of the database to connect to
    database = "default"
    // Connection timeout
    connectTimeout = "20s"
)

func main() {
    // Build the connection URL and open the connection
    url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%s",
        user, password, host, port, database, connectTimeout)
    db, err := sql.Open("mysql", url)
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()

    // Configure connection pool
    db.SetMaxOpenConns(20)
    db.SetMaxIdleConns(20)
    db.SetConnMaxIdleTime(8 * time.Minute)
    db.SetConnMaxLifetime(30 * time.Minute)

    // Print pool status every 10 seconds for diagnostics
    go func() {
        for {
            fmt.Printf("%+v\n", db.Stats())
            time.Sleep(10 * time.Second)
        }
    }()

    // List databases
    {
        rows, err := db.Query("show databases")
        if err != nil {
            panic(err.Error())
        }
        defer rows.Close()
        for rows.Next() {
            var dbName string
            if err := rows.Scan(&dbName); err != nil {
                panic(err.Error())
            }
            fmt.Println(dbName)
        }
    }

    // Create a table
    {
        _, err := db.Exec("create table if not exists user_test(id int, name varchar,age int, primary key(id))")
        if err != nil {
            fmt.Println("create table error ", err)
            return
        }
    }

    // Write data — method 1: direct write
    {
        _, err = db.Exec("upsert into user_test(id,name,age) values(1,'zhangsan',17)")
        if err != nil {
            fmt.Println("insert data error", err)
            return
        }
    }

    // Write data — method 2: parameterized write
    {
        stmt, err := db.Prepare("upsert into user_test(id,name,age) values(?,?,?)")
        if err != nil {
            fmt.Println("prepare error", err)
            return
        }
        defer stmt.Close()
        _, err = stmt.Exec(2, "lisi", 18)
        if err != nil {
            fmt.Println("upsert error", err)
            return
        }
    }

    // Query data — method 1: direct query
    {
        rows, err := db.Query("select * from user_test")
        if err != nil {
            fmt.Println("query data error", err)
            return
        }
        defer rows.Close()
        var id int
        var name string
        var age int
        for rows.Next() {
            if err = rows.Scan(&id, &name, &age); err != nil {
                fmt.Println("scan data error", err)
                return
            }
            fmt.Println("id:", id, "name:", name, "age:", age)
        }
    }

    // Query data — method 2: parameterized query
    {
        stmt, err := db.Prepare("select * from user_test where id=?")
        if err != nil {
            fmt.Println("prepare error", err)
            return
        }
        defer stmt.Close()
        rows, err := stmt.Query(1)
        if err != nil {
            fmt.Println("query data error", err)
            return
        }
        defer rows.Close()
        var id int
        var name string
        var age int
        for rows.Next() {
            if err = rows.Scan(&id, &name, &age); err != nil {
                fmt.Println("scan data error", err)
                return
            }
            fmt.Println("id:", id, "name:", name, "age:", age)
        }
    }

    // Delete data
    {
        _, err = db.Exec("delete from user_test where id=1")
        if err != nil {
            fmt.Println("delete data error", err)
            return
        }
    }
}
Always call rows.Close() after iterating results. For high-throughput scenarios, reuse Stmt objects. When using context-aware APIs such as QueryContext and ExecContext, avoid very short timeouts — client garbage collection (GC) pauses can trigger context timeouts and cause connection resets.

Verify results

When the instance contains only the default database, the output is:

default
information_schema
id: 1 name: zhangsan age: 17
id: 2 name: lisi age: 18
id: 1 name: zhangsan age: 17

This output confirms:

  • default and information_schema — two databases exist on the instance.

  • id: 1 name: zhangsan age: 17 and id: 2 name: lisi age: 18 — both rows were written successfully.

  • id: 1 name: zhangsan age: 17 — the parameterized query (where id=?) returned the row with id=1.

What's next