设计模式之Database/SQL与GORM实践|青训营笔记

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: 本篇笔记暂时的定位是以介绍两种方式操作(MySQL)数据库为主,并辅以一些源码的解读帮助大家更好理解数据库连接的过程。

highlight: xcode

前言

课程导学:https://juejin.cn/post/7095977466094682148/#heading-26

课程视频:https://live.juejin.cn/4354/yc_design

课程pdf:https://bytedance.feishu.cn/file/boxcnct7Jc8Td2Oolfbm6t4HfJg

本次课程内容十分的底层,GORM作者张金柱老师介绍了Go语言原生database/sql的实现原理和使用方式,并且进一步给出了自己设计GORM框架的出发点和一些实现细节。我感受深刻,也看到了技术人的热情。

仰望星空,脚踏实地,本篇笔记暂时的定位是以介绍两种方式操作(MySQL)数据库为主,并辅以一些源码的解读帮助大家更好理解数据库连接的过程。

我笔记中连接的数据库是使用docker容器部署的MySQL实例,各位也可以根据自己情况而定,换成本地也是很方便的。

docker的学习可以参看这篇文章:https://juejin.cn/post/7064024576497303565/

一、database/sql的基础使用

英文手册:Go database/sql tutorial

1.1 连接数据库

image-20220516145120617

这里import了两个包,一个是database/sql,这个包是Go原生的负责数据库操作的包,定义了很多连接和操作数据库实例的接口(定义了很多方法)。另一个包是数据库提供商负责提供的数据库驱动包github.com/go-sql-driver/mysql,这个包去实现了Go原生database/sql包中接口定义的方法(实现了操作某数据库实例的具体细节)。

这样对于Go用户来说,我只要调用我接口定义的方法,import入MySQL提供的driver包,那么我就无须关注底层的具体实现,从而可以使用Go的API与MySQL进程进行通信,发送CRUD请求,然后再由MySQL进程去管理MySQL磁盘文件,持久化数据变更。

这里留意上图我给出的一些注释,它们来自源码包中的对应方法的注释,以sql.Open()方法为例,它说了Open方法可能只是会校验参数的语法而非真正建立连接,为了验证数据源的有效性可以用Ping方法,并且第二个红框中介绍到返回的DB引用是并发安全的,而作为一个数据库操作服务一般也是会不断给前端提供CRUD服务,因此它说很少需要关闭一个DB(等需要关闭的时候,服务也就挂了)

image-20220516145618720

也可以再看一下sql.DB的结构体,它维护着所连接的数据对象,持有一个连接池,维护很多的连接,并且有很多参数项目:如最大连接数最大闲置连接数等等。(DB是并发安全的,因为并发进来的连接都要与其通信交互)

image-20220516150538782

1.2 新建数据库

接下来从新建数据库开始使用Go原生database/sql包体验一下CRUD

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(255) NOT NULL COMMENT '姓名',
  `age` int NOT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3;
​
-- ----------------------------
-- Records of user
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES (1, '小明', 12);
INSERT INTO `user` VALUES (2, '小红', 15);
INSERT INTO `user` VALUES (3, '小蓝', 11);
COMMIT;

1.3 查询

  • 单行查询
func getUserById(id int) error {
  sqlStr := "select id, name, age from user where id = ?"
  var user User
  // 这里的1是参数,用于查询id=1的行记录
  if err := db.QueryRow(sqlStr, 1).Scan(&user.id, &user.name, &user.age); err != nil {
    return err
  }
  fmt.Println(user.id, user.name, user.age)
  return nil
}
  • 多行查询
func queryUsers() error {
   sqlStr := "select id, name, age from user where id > ?"
   rows, err := db.Query(sqlStr, 1)
   if err != nil {
      return err
   }
   // 关闭rows释放持有的数据库链接
   defer rows.Close()
​
   // 循环读取结果集中的数据
   for rows.Next() {
      var user User
      if err := rows.Scan(&user.id, &user.name, &user.age); err != nil {
         return err
      }
      fmt.Printf("id:%d name:%s age:%d\n", user.id, user.name, user.age)
   }
   return nil
}

1.4 插入

这里要说明一下,下面的插入、更新、删除使用到的API是同一个:db.Exec,args参数是任意个占位参数。

image-20220516202520455

func insertUser() error {
   sqlStr := "insert into user(name, age) values (?,?)"
   ret, err := db.Exec(sqlStr, "小黄", 17)
   if err != nil {
      return err
   }
   theID, err := ret.LastInsertId() // 新插入数据的id
   if err != nil {
      return err
   }
   fmt.Printf("insert success, the id is %d.\n", theID)
   return nil
}

1.5 更新

func updateUser() error {
   sqlStr := "update user set age=? where id = ?"
   ret, err := db.Exec(sqlStr, 20, 3)
   if err != nil {
      return err
   }
   n, err := ret.RowsAffected() // 操作影响的行数
   if err != nil {
      return err
   }
   fmt.Printf("update success, affected rows:%d\n", n)
   return nil
}

1.6 删除

// 删除数据
func deleteUser() error {
   sqlStr := "delete from user where id = ?"
   ret, err := db.Exec(sqlStr, 5)
   if err != nil {
      return err
   }
   n, err := ret.RowsAffected() // 操作影响的行数
   if err != nil {
      return err
   }
   fmt.Printf("delete success, affected rows:%d\n", n)
   return nil
}

最后放一下整体的main函数调用流程

image-20220516170242958

二、GORM的基础使用

Go英文手册:https://gorm.io/docs

image-20220516195027675

Go中文手册:https://learnku.com/docs

image-20220516194947641

原本我尝试按照文档做了一些操作的案例,但中途认为,这样做的意义并不是很大,加上上面已经罗列了使用原生database/sql进行CRUD的步骤。GORM文档已经十分清爽完备,最终决定不再笔记中二次赘述。

小结

image-20220516195755096

GORM还是原生database/sql,都是在应用程序层面,向用户隐藏了底层连接数据库,操作数据库的实现细节。使得开发者通过API就可以实现和数据库进程的通信,而数据库进程才是直接操作数据库文件的,而非应用程序。

或者说应用程序进程的工作只是将SQL命令传递给了数据库进程,最终一条SQL的执行将完全在数据库进程内完成。以MySQL为例收到一条SQL后将涉及如下方面的工作:

  • 语句的解析
  • 查询的优化
  • 存储引擎的工作:

    • 索引的选择与实现
    • 数据一致性的保证(redo log)
    • 事务的实现,不同事务隔离级别如何实现(多版本并发控制MVCC、undo log)
    • 锁的实现(表级锁、行级锁、间隙锁)
  • 数据恢复与同步,主从复制(bin log)
  • 等等...

上面罗列的这些条目在运行时会产生很多业务相关的问题,由此衍生出很多MySQL性能优化的技巧,常见的如索引如何设定可以提升查询效率,慢SQL如何排错,为什么我的索引失效了,事务如何使用并发性能会更高等等,这些技巧或许在后续的项目实践中可以使用到,也希望大家关注。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
设计模式 编解码 C++
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用(一)
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用
50 0
|
24天前
|
SQL 关系型数据库 MySQL
【MySQL实战笔记】02.一条SQL更新语句是如何执行的-1
【4月更文挑战第4天】SQL更新语句执行涉及查询和日志模块,主要为`redo log`和`binlog`。`redo log`先写日志再写磁盘,保证`crash-safe`;`binlog`记录逻辑日志,支持所有引擎,且追加写入。执行过程分为执行器查找数据、更新内存和`redo log`(prepare状态)、写入`binlog`、提交事务(`redo log`转commit)。两阶段提交确保日志逻辑一致,支持数据库恢复至任意时间点。
20 0
|
2月前
|
设计模式 存储 缓存
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用(二)
【ffmpeg 视频播放】深入探索:ffmpeg视频播放优化策略与设计模式的实践应用
28 0
|
11天前
|
设计模式 Java
【设计模式系列笔记】抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是一种设计模式,属于创建型模式之一。它提供了一种方式来创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂模式通过引入抽象的工厂接口,使得客户端代码可以使用抽象的接口来创建一组相关的产品,而不关心这些产品的具体实现。
107 4
|
3天前
|
设计模式 算法 搜索推荐
【PHP开发专栏】PHP设计模式解析与实践
【4月更文挑战第29天】本文介绍了设计模式在PHP开发中的应用,包括创建型(如单例、工厂模式)、结构型和行为型模式(如观察者、策略模式)。通过示例展示了如何在PHP中实现这些模式,强调了它们在提升代码可维护性和可扩展性方面的作用。设计模式是解决常见问题的最佳实践,但在使用时需避免过度设计,根据实际需求选择合适的设计模式。
|
4天前
|
设计模式 Java
Java 设计模式:混合、装饰器与组合的编程实践
【4月更文挑战第27天】在面向对象编程中,混合(Mixins)、装饰器(Decorators)和组合(Composition)是三种强大的设计模式,用于增强和扩展类的功能。
10 1
|
8天前
|
SQL 存储 Oracle
《SQL必知必会》个人笔记
《SQL必知必会》个人笔记
13 1
|
8天前
|
设计模式 算法 程序员
Python从入门到精通:2.1.3深入学习面向对象编程——设计模式的学习与实践
Python从入门到精通:2.1.3深入学习面向对象编程——设计模式的学习与实践
|
8天前
|
设计模式 调度
【设计模式系列笔记】中介者模式
中介者模式(Mediator Pattern)是一种行为设计模式,它通过将对象之间的直接通信转移到一个中介对象中,来减少对象之间的耦合度。这种模式被用来处理一个对象与其他对象之间的交互,使得各对象之间不需要直接相互了解。
15 0
|
10天前
|
设计模式 Java 容器
【设计模式系列笔记】组合模式
组合模式(Composite Pattern)是一种结构型设计模式,它允许将对象组合成树状结构以表示部分-整体的层次结构。组合模式使得客户端可以统一处理单个对象和对象组合,而无需区分它们的类型。
40 12