这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记
概述
了解 Go 的 Database/SQL的实现,了解 GORM 的实现原理、使用简单,以及如何基于 GORM 做一些定制化开发。
理解 Database/SQL
统一接口操作多种数据库
Database/SQL 的基本用法
- line8:
import driver实现- 使用
driver+DSN(Data_source_name)初始化DB链接
- 使用
- line10-14:执行一条sql,通过rows取回返回的数据,处理完毕需要释放连接
- line16-26:数据、错误处理
- close过程中,理论需要检查,因为有可能会返回错误,如line14的代码
- line28-30:处理错误
- 数据错误,非常的重要!!!!
设计原理
- 相同的连接接口、操作接口暴露给应用程序
- 连接池:池化技术
- 池化技术:池化技术是一种十分常见的编程技巧,当程序出现高并发时,能够明显的优化程序,降低系统频繁创建销毁连接等额外开销。我们经常接触到的池化技术有数据库连接池、线程池、对象池等等。池化技术的特点是==将一些高成本的资源维护在一个特定的池子(内存)中,规定其最小连接数、最大连接数、阻塞队列,溢出规则等配置,方便统一管理。一般情况下也会附带一些监控,强制回收等配套功能==。
- 管理方法
- 连接池一系列配置、用方法进行配置
- 取出连接池的状态进行管理
- 操作过程的实现
- 从连接池获取连接,尝试连接两次
- 有空闲连接->reuse->max life time
- 新建连接->max open
- 将连接放回连接池
validateConnection有无错误- max life time,max idle conns检查
- 连接实现
driver.Queryer,driver.Exever等interface
- 从连接池获取连接,尝试连接两次
DRIVER连接接口- 注册driver
- 调用:
- import driver、
main方法调用连接、注册一个全局driver(不好用)- DSN是一个特别常见的字符串,参数不透明
- import过程没有编译检查
- panic问题
- 顺序不对也会有异常
- 使用结构体进行创建连接。
- import driver、
基础概念
- DB连接的几种类型
- 直接连接/Conn
- 预编译/Stmt:执行同样的接口的时候会进行Prepare Statement,根据reference id,如果后面执行重复sql就不要重新编译,根据reference id调用
- 事务/Tx
- 处理返回数据的几种方式
- Exec / ExecContext -> Result 成功失败信息、ID
- Query / QueryContext-> Rows(Columns) 行方式返回数据
- QueryRow / QueryRowContext -> Row (Rows简化)只读取row的一行数据
GORM 的使用简介
“设计简洁、功能强大、自由扩展的全功能ORM”
- 设计原则: API精简、测试优先、最小惊讶、灵活扩展、无依赖可信赖
- 功能完善:
- 关联:一对一、一对多、单表自关联、多态;Preload、Joins 预加载、级联删除;关联模式;自定义关联表
- 事务:事务代码块、嵌套事务、Save Point
- 多数据库、读写分离、命名参数、Map、子查询、分组条件、代码共享、SQL表达式(查询、创建、更新)、自动选字段、查询优化器
- 字段权限、软删除、批量数据处理、Prepared Stmt、自定义类型、命名策略、虚拟字段、自动track时间、SQLBuilder、Logger
- ·代码生成、复合主键、Constraint、Prometheus、Auto Migration、真·跨数据库兼容
- 多模式灵活自由扩展
- Developer Friendly
GORM 的基本用法
Model 定义
惯例约定
约定优于配置
- 表名为struct name的snake_cases复数格式
- 字段名为field name的snake__case单数格式
- ID/ ld字段为主键,如果为数字,则为自增主键
- CreatedAt字段,创建时,保存当前时间
- UpdatedAt字段,创建、更新时,保存当前时间
- gorm.DeletedAt字段,默认开启soft delete模式
一切都可配置gorm.io/docs/conven…
关联介绍
多表、单表、belong to、多态
CURD
save保存关联,存在才更新
Preload / Joins 预加载
级联删除
GORM 的设计原理
SQL 生成的机制
-
GORM STATEMENT
- Chain Method:给GORM STATEMENT添加GORM语句,语句组成来生成最终的SQL
- Finisher Method:决定GORM STATEMENT最终类型并执行的方法
-
GORM API方法添加Clauses至GORM Statement
-
GORM Finisher方法执行GORM Statement
- 取出query返回processor
- go把select的DQL支持语句找出,放在BA方法进行编译
- 交给Connection Call(CLAUSES)进行执行
-
优点
- 自定义Clause Builder
- 方便扩展 Clause
- 自由选择Clauses
-
自定义builder
- 版本初始化参数
- 根据参数使用接口
- 根据当前数据库配置和类型制定不同SQL语句的生成
-
扩展子句
-
上传语句——DB自定义CLAUSES
插件扩展机制
- Create
- 注册事务
- 取出当前model的beforecreate的一些方法并一一执行,错误就会返回并rollback
- 保存前缀、创建关联
- 执行,结果返回应用程序
- 保存后缀
- 调用AfterCreate的方法并一一执行,
- 错误就会返回并rollback
- 扩展插件
- 优点——灵活定制、自由扩展
- 多租户
- 多数据库、读写分离
- 加解密、混沌工程..
- 多租户
- 多租户数据隔离
- 查询增加过滤条件,可以查询自动的时候自动过滤,不用手动编码
- 避免系统事故
- 多数据库、读写分离
ConnPool 扩展机制
- 新的一层抽象——Interface
- 实现过程
- 查找缓存的预编译SQL
- 全局:所有DB操作都会预编译并缓存(缓存不含参数)
- 会话:后续会话的操作都会预编译并缓存
- 未找到,将收到的SQL 和 Vars预编译
- 使用缓存的预编译SQL执行
- 最开始的问题
interpolateParams=false执行有参数sql的时候会预编译sql,使用预编译的sql值查询再关闭(true会变慢)——使用完就扔掉没有缓存不能用于后面的加速
- bytedgorm
- 最开始的问题
Dialector 扩展机制
- go怎么连接mysql和数据库
- clickhouse查询数据
- caches定制缓存,fallback策略
GORM 最佳实践
数据序列化与SQL表达式
SQL表达式更新创建
SQL表达式查询
数据序列化
批量数据操作
批量创建/查询
批量更新
批量数据加速操作
代码复用、分库分表、Sharding
代码复用
分库分表
Sharding
混沌工程/压测
Logger / Trace
Migrator
数据库迁移管理
数据库迁移
Gen代码生成/Raw SQL
Raw SQL
GEN
安全
课后作业
-
给某个数据库实现 driver (tidb, 国产数据库...),通过 docker compose 搭建测试环境,并实现测试 CRUD
-
使用 GORM 的插件机制完成加密字段,支持从 ctx 中读取不同租户的密钥加密,通过 docker compose 搭建测试环境,并完成相就加解密存入、取出数据库进行测试