面试题答案
一键面试Go语言实现联合查询、聚合操作及分页功能
- 数据库连接与初始化
package main import ( "database/sql" "fmt" _ "github.com/go - sql - driver/mysql" ) var db *sql.DB func init() { var err error db, err = sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/your_database?parseTime=true") if err!= nil { panic(err.Error()) } err = db.Ping() if err!= nil { panic(err.Error()) } fmt.Println("Connected to the database!") }
- 联合查询、聚合操作及分页实现
func queryData(page, pageSize int) ([]map[string]interface{}, error) { // 预编译语句 query := ` SELECT t1.column1, t2.column2, COUNT(*) as count FROM table1 t1 JOIN table2 t2 ON t1.id = t2.table1_id GROUP BY t1.column1, t2.column2 LIMIT?,? ` rows, err := db.Query(query, (page - 1)*pageSize, pageSize) if err!= nil { return nil, err } defer rows.Close() var results []map[string]interface{} for rows.Next() { var column1 string var column2 string var count int err := rows.Scan(&column1, &column2, &count) if err!= nil { return nil, err } result := map[string]interface{}{ "column1": column1, "column2": column2, "count": count, } results = append(results, result) } if err = rows.Err(); err!= nil { return nil, err } return results, nil }
性能优化
- 连接池管理
database/sql
包默认实现了连接池。sql.DB
结构体管理着一个连接池,sql.Open
函数初始化连接池,但此时连接池为空。db.Ping
或第一次执行查询时会初始化连接。- 可以通过
db.SetMaxIdleConns
设置连接池中的最大空闲连接数,db.SetMaxOpenConns
设置连接池中的最大打开连接数。例如:
db.SetMaxIdleConns(10) db.SetMaxOpenConns(100)
- 预编译语句的使用
- 在上述代码中,
db.Query
方法接收预编译的SQL语句和参数。预编译语句在数据库端被编译一次并缓存,后续使用相同的预编译语句时直接执行,减少了SQL解析和编译的开销,提高了性能,同时也防止了SQL注入攻击。
- 在上述代码中,
- 查询索引优化
- 分析SQL查询,为连接条件(如
ON t1.id = t2.table1_id
)和聚合、排序、分组条件添加合适的索引。例如,如果table1
的id
列和table2
的table1_id
列经常用于连接,在这两列上添加索引:
CREATE INDEX idx_table1_id ON table1(id); CREATE INDEX idx_table2_table1_id ON table2(table1_id);
- 分析SQL查询,为连接条件(如
高并发读写情况下保障数据一致性和系统稳定性
- 事务处理
- 使用事务来确保一组操作要么全部成功,要么全部失败。例如:
func updateData() error { tx, err := db.Begin() if err!= nil { return err } _, err = tx.Exec("UPDATE table1 SET column1 =? WHERE id =?", value1, id1) if err!= nil { tx.Rollback() return err } _, err = tx.Exec("UPDATE table2 SET column2 =? WHERE table1_id =?", value2, id1) if err!= nil { tx.Rollback() return err } err = tx.Commit() if err!= nil { return err } return nil }
- 锁机制
- 对于高并发读写场景,使用数据库锁(如行锁、表锁)来确保数据一致性。例如,在更新操作前对相关行加锁:
SELECT * FROM table1 WHERE id =? FOR UPDATE;
- 在Go语言中,使用
sync
包中的锁(如sync.Mutex
)来保护共享资源,避免竞态条件。例如,如果有一个全局变量用于记录某些状态,在读写该变量时加锁:
var mu sync.Mutex var globalStatus int func readStatus() int { mu.Lock() defer mu.Unlock() return globalStatus } func updateStatus(newStatus int) { mu.Lock() defer mu.Unlock() globalStatus = newStatus }
- 缓存
- 使用缓存(如Redis)来减轻数据库的压力。对于读多写少的场景,可以将查询结果缓存起来。在数据发生变化时,及时更新缓存。例如,在更新数据库后,删除对应的缓存键:
// 假设使用redigo操作Redis func updateDataAndCache() error { // 更新数据库 err := updateData() if err!= nil { return err } // 删除缓存 conn, err := redis.Dial("tcp", "127.0.0.1:6379") if err!= nil { return err } defer conn.Close() _, err = conn.Do("DEL", "cache_key") if err!= nil { return err } return nil }