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:
Go 1.17 or later. See Go for installation instructions.
MySQL compatibility enabled on your Lindorm instance. See Enable the MySQL compatibility feature.
Your client IP address added to the Lindorm instance whitelist. See Configure whitelists.
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
SetConnMaxLifetimeto 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.1Step 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
| Parameter | Description |
|---|---|
user | Username for LindormTable access. To reset the password, see Manage users. |
password | Password for LindormTable access. |
host | LindormTable 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. |
port | Fixed value: 33060. |
database | Database name. Defaults to default. |
connectTimeout | Connection 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 callrows.Close()after iterating results. For high-throughput scenarios, reuseStmtobjects. When using context-aware APIs such asQueryContextandExecContext, 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: 17This output confirms:
defaultandinformation_schema— two databases exist on the instance.id: 1 name: zhangsan age: 17andid: 2 name: lisi age: 18— both rows were written successfully.id: 1 name: zhangsan age: 17— the parameterized query (where id=?) returned the row withid=1.
What's next
Manage users — change passwords and manage access
View endpoints — find VPC and public endpoints
Configure whitelists — manage IP allowlists