后端Go语言基础笔记:
这是我参与【第五届青训营】伴学笔记创作活动的第4天。
在第十节的学习中掌握了:
Go内存分配:
(1)分块 (2)缓存:Go的分配器中做了多种缓存,当mspans未满,就从mspans取,如果已经满了,就从mcentral里面拿
内存管理优化:
(1)象分配是非常高频的操作: 每秒分配 GB 级别的内存 (2)小对象占比较高 (3)Go 内存分配比较耗时: ①分配路径长: g -> m -> p -> mcache -> mspan -> memory block -> return pointerpprof: ②对象分配的函数是最频繁调用的函数之
编译器和静态分析
编译器的结构
重要的系统软件:
(1) 识别符合语法和非法的程序 (2) 生成正确且高效的代码
分析部分(前端 front end)
(1) 词法分析,生成词素(lexeme) (2) 语法分析,生成语法树 (3) 语义分析,收集类型信息,进行语义检查 (4) 中间代码生成,生成 intermediate representation (IR)
综合部分 (后端 back end)
(1) 代码优化,机器无关优化,生成优化后的 IR (2) 代码生成,生成目标代码
静态分析:
(1)不执行程序代码,推导程序的行为,分析程序的性质。 (2)控制流(Control flow): 程序执行的流程 (3)数据流(Data flow): 数据在控制流上的传递 (4)通过分析控制流和数据流,我们可以知道更多关于程序的性质 (properties) (5)根据这些性质优化代码
过程内分析和过程间分析
过程内分析(Intra-procedural analysis) : 仅在函数内部进行分析 过程间分析(Inter-procedural analysis) : 考虑过程调用时参数传递和返回值的数据流和控制流
Go编译器优化
函数内联(Inlining)
内联 : 将被调用函数的函数体(callee) 的副本替换到调用位置(caller) 上,同时重写代码以反映参数的绑定
优点 : (1)消除函数调用开销,例如传递参数、保存寄存器等 (2)将过程间分析转化为过程内分析,帮助其他优化,例如逃逸分析 缺点 (1)函数体变大,instruction cache (icache) 不友好 (2)编译生成的 Go 镜像变大 函数内联在大多数情况下是正向优化
逃逸分析: 分析代码中指针的动态作用域: 指针在何处可以被访问
从对象分配处出发,沿着控制流,观察对象的数据流,若发现指针 p 在当前作用域 s:
(1)作为参数传递给其他函数 (2)传递给全局变量 (3)传递给其他的 goroutine (4)传递给已逃逸的指针指向的对象 则指针 p 指向的对象逃逸出 s,反之则没有逃逸出 s
Beast mode: 函数内联拓展了函数边界,更多对象不逃逸
优化: 未逃逸的对象可以在栈上分配 (1)对象在栈上分配和回收很快: 动 sp (2)减少在 heap 上的分配,降低 GC 负担
第十一节内容:
database/sql:
driver连接接口
import( //import driver实现
"database/sql"
_"github.com/go-sql-driver/mysql"
)
func main(){
db,err :=sql.Open("mysql","user:password@tcp(127.0.0.1:3306)/hello") //使用driver+dns初始化db连接
rows,err :=db.Query("select id,name from user where id=?",1)//执行一条sql,通过rows取回返回的数据处理完毕,需要释放链接
if err!=nil{
//xxx
}
defer rows.Close();
var users []User
for rows.Next(){
var user User
err := rows.Scan(&user.ID,&user.Name)
if err!=nil{//....
}
users = append(users,user)//数据错误处理
}
if rows.Err()!=nil{//处理错误
//..
}
}
import "github .com/go-sql-driver/mysql"
func mainO f
db,err := sql.Open("mysql", "gorm:gormetcp(localhost:9910)/gorm?charset=utf8&parseTime-True&loc=Local";
// github.com/go-sql-driver/mysql/driver.go
//注册 Driver
func initO fsql.Register("mysql",&MySQLDriverf)
}
DB 连接的几种类型:
(1)直接连接/ Conn (2)预编译/Stmt (3)事务/Tx
处理返回数据的几种方式:
Exec / ExecContext -> Result Query / QueryContext -> Rows (Columns) QueryRow / QueryRowContext -> Row (Rows 简化)
rows的一些函数:
type driver.Rows interface [
// 返回 columns 名字
ColumnsO string
//实现数据库协议
// 解析数据到 database/sql.Rows.Lastcols
Next(dest Value) error
//多批数据解析
HasNextResultSet() bool
NextResultSetO error
gorm连接数据库
import("gorm, io/gorm"
gorm.io/driver/mysql
)
func main(){
db,err := gorm.Open(
mysql.Open("user:passwordetcp(127.0.0.1:3306)/hello")
)
var users []User
err = db.Select("id","name").Find(&users, 1).Error
}
基本用法-CRUD
//操作数据库
db.AutoMigrate(&Product3)
db.Migrator).CreateTable(&Product+)
// https://gorm.io/docs/migration.html
// 版本管理 - https://github.com/go-gormigrate/gormigrate
//创建
user := UserfName: "Jinzhu",Age: 18,Birthday: time.Now )}
result := db.Create(&user) // pass pointer of data to Create
user.ID //返回主键 last insert id
result.Error // 返回 error
result.RowsAffected // 返回影响的行数
//批量创建
var users = User[fName: "jinzhu1"了,[Name:"jinzhu2"了,[Name: "jinzhu3"}}
db.Create(&users)
db.CreateInBatches(users,100)
for _, user := range users {
user.ID // 1,2,3
}
//读取
var product Product
db.First(&product,1)// 查询id为1的product)
db.First(&product,"code =?","L1212")// 查询code为L1212的product
result := db.Find(&users, []int{1,2,3})
result.RowsAffected // 返回找到的记录数
errors,Is(result.Error,gorm,ErrRecordNotFound) // First,Last,Take 查不到数据
// 更新某个字段
db.Model(&product).Update("Price"2000)
db .Model(&product).UpdateColumn("Price"2000)
//更新多个字段
db.Model(&product).Updates(ProductfPrice: 2000,Code:"L1212"}) db.Model(&product).Updates(map[stringlinterface {"Price": 2000,"Code":"L1212"})
//批量更新
db.Model(&Product+).Where("price < ?"2000).Updates(map[stringlinterface {"Price": 2000})
//删除-删除
productdb.Delete(&product)
可以参考的配置的连接:https://gormio/docs/conventions.html
关联操作-CRUD
//保存用户及其关联 (Upsert)
db.Save(&User{
Name: "jinzhu",
Languages: []Language {{Name:"zh-CN”},{Name: "en-Us"}},
})
//关联模式
LangAssociation := db.Model(&user).Association("Languages")
//查询关联
langAssociation.Find(&languages)
// 将汉语,英语语添加到用户掌握的语言中
LangAssociation.Append([]LanguageflanguageZH,LanguageEN})
// 把用户掌握的语言替换为汉语,德语
langAssociation.Replace([]LanguageflanguageZH,languageDE})
//删除用户掌握的两个语言
langAssociation.Delete(languageZH,languageEN)//删除用户所有掌握的语言
LangAssociation.Clear()
// 返回用户所掌握的语言的数量
LangAssociation.Count()
//批量模式 Append,Replace
var users = 门Userfuser1,user2,user3}langAssociation := db.Model(&users).Association("Languages")
//批量模式 Append,Replace,参数需要与源数据长度相同
//例如:我们有 3个 user:将 userA 添加到 user1 的 Team
db.Model(&users).Association("Team").Append(&user A,&user B,&[]User{userA,userB,userC})
关联操作还包括:Preload/Joins预加载,级联删除
级联删除:
//方法1:使用数据库约束自动删除
type User struct f
ID uint
Name string
Account Account `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADEOnDelete:CASCADE:"`
Orders []order `gorm: "constraint:OnUpdate:CASCADE ,OnDelete:CASCADE;"`
// 需要使用 GORM Migrate 数据库迁移数据库外键才行
db.AutoMigrate(&User{})
// 如果未启用软删除,在删除 User 时会自动删除其依赖
db.Delete(&User{})
// 方法2:使用 Select 实现级联删除,不依赖数据库约束及软删除
//删除 user 时,也删除 user 的 account
db.Select("Account").Delete(&user)
//删除 user 时,也删除 user 的 Orders、CreditCards 记录
db.Select("Orders","CreditCards").Delete(&user)
//删除 user 时,也删除 user 的 Orders、CreditCards 记录,也删除订单的 BillingAddress
db.Select("Orders","Orders.BillingAddress","CreditCards").Delete(&user)
//删除 user 时,也删除用户以及其依赖的所有has等记录
db.Select(clause.Associations).Delete(&user)