设计模式之Database/SQL | 青训营笔记

236 阅读6分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记

概述

了解 Go 的 Database/SQL的实现,了解 GORM 的实现原理、使用简单,以及如何基于 GORM 做一些定制化开发。

理解 Database/SQL

统一接口操作多种数据库

Database/SQL 的基本用法

image-20220516233039301

  • line8:import driver实现
    • 使用driver+DSN(Data_source_name)初始化DB链接
  • line10-14:执行一条sql,通过rows取回返回的数据,处理完毕需要释放连接
  • line16-26:数据、错误处理
    • close过程中,理论需要检查,因为有可能会返回错误,如line14的代码
  • line28-30:处理错误
    • 数据错误,非常的重要!!!!

设计原理

image-20220516233941752

  • 相同的连接接口、操作接口暴露给应用程序
  • 连接池:池化技术
    • 池化技术:池化技术是一种十分常见的编程技巧,当程序出现高并发时,能够明显的优化程序,降低系统频繁创建销毁连接等额外开销。我们经常接触到的池化技术有数据库连接池、线程池、对象池等等。池化技术的特点是==将一些高成本的资源维护在一个特定的池子(内存)中,规定其最小连接数、最大连接数、阻塞队列,溢出规则等配置,方便统一管理。一般情况下也会附带一些监控,强制回收等配套功能==。
    • 管理方法
      • 连接池一系列配置、用方法进行配置
      • 取出连接池的状态进行管理
  • 操作过程的实现
    • 从连接池获取连接,尝试连接两次
      • 有空闲连接->reuse->max life time
      • 新建连接->max open
    • 将连接放回连接池
      • validateConnection有无错误
      • max life time,max idle conns检查
    • 连接实现driver.Queryer,driver.Exeverinterface
  • DRIVER连接接口
    • 注册driver
  • 调用:
    • import driver、main方法调用连接、注册一个全局driver(不好用)
      • DSN是一个特别常见的字符串,参数不透明
      • import过程没有编译检查
      • panic问题
      • 顺序不对也会有异常
    • 使用结构体进行创建连接。

基础概念

  • 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 的基本用法

image-20220517001525861

image-20220517001536906

Model 定义

image-20220517001559419

惯例约定

约定优于配置

  • 表名为struct name的snake_cases复数格式
  • 字段名为field name的snake__case单数格式
  • ID/ ld字段为主键,如果为数字,则为自增主键
  • CreatedAt字段,创建时,保存当前时间
  • UpdatedAt字段,创建、更新时,保存当前时间
  • gorm.DeletedAt字段,默认开启soft delete模式

一切都可配置gorm.io/docs/conven…

关联介绍

image-20220517001720131

多表、单表、belong to、多态

CURD

image-20220517001759316

save保存关联,存在才更新

Preload / Joins 预加载

image-20220517001834542

级联删除

image-20220517001911553

GORM 的设计原理

image-20220517001932679

SQL 生成的机制

image-20220517002605940

  • GORM STATEMENT

    • Chain Method:给GORM STATEMENT添加GORM语句,语句组成来生成最终的SQL
    • Finisher Method:决定GORM STATEMENT最终类型并执行的方法
  • GORM API方法添加Clauses至GORM Statement

    • image-20220517003013693
  • GORM Finisher方法执行GORM Statement

    • image-20220517002945846
    • 取出query返回processor
    • go把select的DQL支持语句找出,放在BA方法进行编译
    • 交给Connection Call(CLAUSES)进行执行
  • 优点

    • 自定义Clause Builder
    • 方便扩展 Clause
    • 自由选择Clauses
  • 自定义builder

    • image-20220517003246350
    • 版本初始化参数
    • 根据参数使用接口
    • 根据当前数据库配置和类型制定不同SQL语句的生成
  • 扩展子句

    • image-20220517003552570
  • 上传语句——DB自定义CLAUSES

    • image-20220517003321156

插件扩展机制

image-20220517003355610

  • Create
    • image-20220517003731110
    • 注册事务
    • 取出当前model的beforecreate的一些方法并一一执行,错误就会返回并rollback
    • 保存前缀、创建关联
    • 执行,结果返回应用程序
    • 保存后缀
    • 调用AfterCreate的方法并一一执行,
    • 错误就会返回并rollback
  • 扩展插件
    • image-20220517004159332
  • 优点——灵活定制、自由扩展
    • 多租户
    • 多数据库、读写分离
    • 加解密、混沌工程..
  • 多租户
    • image-20220517004258069
    • 多租户数据隔离
    • 查询增加过滤条件,可以查询自动的时候自动过滤,不用手动编码
    • 避免系统事故
  • 多数据库、读写分离
    • image-20220517004329219

ConnPool 扩展机制

image-20220517004413416

  • 新的一层抽象——Interface
  • 实现过程
    • image-20220517140506222
    • 查找缓存的预编译SQL
      • 全局:所有DB操作都会预编译并缓存(缓存不含参数)
      • 会话:后续会话的操作都会预编译并缓存
    • 未找到,将收到的SQL 和 Vars预编译
    • 使用缓存的预编译SQL执行
  • image-20220517140720737
    • 最开始的问题
      • image-20220517140755963
        • interpolateParams=false执行有参数sql的时候会预编译sql,使用预编译的sql值查询再关闭(true会变慢)——使用完就扔掉没有缓存不能用于后面的加速
      • image-20220517140807351
        • bytedgorm

Dialector 扩展机制

image-20220517142100153

  1. go怎么连接mysql和数据库
  2. clickhouse查询数据
  3. caches定制缓存,fallback策略

image-20220517142045758

GORM 最佳实践

数据序列化与SQL表达式

SQL表达式更新创建

image-20220517142450519

SQL表达式查询

image-20220517142517346

数据序列化

image-20220517142543220

批量数据操作

批量创建/查询

image-20220517142618544

批量更新

image-20220517142649275

批量数据加速操作

image-20220517142711181

代码复用、分库分表、Sharding

代码复用

image-20220517142733578

分库分表

image-20220517142752356

Sharding

image-20220517142810307

混沌工程/压测

image-20220517142826236

Logger / Trace

image-20220517142839839

Migrator

数据库迁移管理

image-20220517142856676

数据库迁移

image-20220517142913215

Gen代码生成/Raw SQL

Raw SQL

image-20220517142932133

GEN

image-20220517142948880

安全

image-20220517143022773

课后作业

  1. 给某个数据库实现 driver (tidb, 国产数据库...),通过 docker compose 搭建测试环境,并实现测试 CRUD

  2. 使用 GORM 的插件机制完成加密字段,支持从 ctx 中读取不同租户的密钥加密,通过 docker compose 搭建测试环境,并完成相就加解密存入、取出数据库进行测试