go开发个人博客项目遇到的问题记录

go的反射

  1. IsZero()

    • 用于检查反射值是否为该类型的零值
    • 适用于所有类型的反射值(Value)
    • 对于数值类型,零值是0;对于字符串,是"";对于指针、接口、切片、map等,是nil;对于结构体,是所有字段都是零值的结构体
  2. IsNil()

    • 专门用于检查指针、接口、切片、map、通道或函数类型的反射值是否为nil
    • 如果对非这些类型的反射值调用IsNil()会panic
    • 主要用于检查可空类型的nil状态
  3. IsValid()

    • 检查反射值是否代表一个有效的值(非零Value)
    • 如果反射值是通过非法方式获得的(如未初始化的接口或零值Value),会返回false
    • 是所有反射值的基本有效性检查
  • 有效值:能正确反映原始值的反射对象
  • 无效值:无法对应任何实际值的反射对象(如零值Value或nil接口的反射)

使用案例

go 复制代码
//根据库	"github.com/jinzhu/copier"学习而来

reflect.ValueOf(fromValue) //获取反射
from.Kind() //判断类型

例如
	for from.Kind() == reflect.Ptr { //判断类型是否为指针
		if from.IsNil() { 
			return nil // 源指针为nil则直接返回
		}
		from = from.Elem() //解引用 例如fromValue是个指针,直接打印是拿不到值,而是地址,而Elem()执行后获取对应的值
	}

  from.NumField() //获取结构体的总数量
  for i := 0; i < from.NumField(); i++ {
      fromField := from.NumField(i) //s获取结构体下面属性的反射

    // 判断类型是否能安全赋值 a->b
  if fromField.Type().AssignableTo(toField.Type()) {
				toField.Set(fromField)
  } else {
  // 处理类型转换(如int到bool等)
  	if fromField.Kind() == reflect.Int16 && toField.Kind() == reflect.Bool{
  		toField.SetBool(fromField.Int() == 1)
  	 }
			
    }
  }

goN+1的问题

go 复制代码
var categoryList []model.CategoryModel
	var count int64
	config.Database.
		Scopes(
			sql.ConditionQueryCategory(body),
			sql.Paginate(body.PageRequest),
		).
		Where("create_time BETWEEN  ? AND ?", body.StartTime, body.EndTime).
		Find(&categoryList).
		Count(&count)
	var respList []response.CategoryResponse

	//性能问题
	for _, v := range categoryList {
		var resp response.CategoryResponse

		copier.Copy(&resp, &v)
		config.Database.
			Model(&model.ArticleModel{}).
			Where("category_id = ?", resp.ID).
			Count(&resp.ArticleCount)
		respList = append(respList, resp)
	}

	utils.ResponseSuccessData(app, map[string]any{
		"page":  respList,
		"total": count,
	})

上面的查询存在n+1的性能问题,总体时延600ms

同样的逻辑在 java的mybatis-plus上时延迟100ms左右

原因

1. 网络往返开销
  • 每次查询都需要:建立连接 → 发送请求 → 等待响应 → 解析结果
  • 假设网络延迟 10ms,10 次额外查询 = 100ms 纯等待
2. 数据库执行开销
  • 每次 COUNT() 查询都需要:

    • 解析 SQL
    • 查询优化
    • 执行计划生成
    • 索引/表扫描
  • 10 次 COUNT 查询 = 10 次完整执行流程

3. 连接池压力
  • 高并发场景下,大量连接被占用
  • 可能导致连接池耗尽,新请求阻塞
4. 索引失效风险
  • 单次 COUNT 能用索引,但多次查询无法利用批量优化
  • 数据库无法整体优化查询计划

永远不要在循环中执行数据库查询!

分页查询下只返回10条总数据问题

go 复制代码
	db := config.Database.
		Table("t_category as c").
		Select("c.*, COUNT(a.id) as article_count").
		Joins("LEFT JOIN t_article a ON c.id = a.category_id").
		Where("c.create_time BETWEEN ? AND ?", body.StartTime, body.EndTime).
		Group("c.id, c.category_name").
		Scopes(
      sql.ConditionQueryCategory(body),
      sql.Paginate(body.PageRequest),
    )

	db.Count(&count)
	db.
		Find(&categoryList)

该查询中无论如何只有10条,原因在于分页条件提前,生成的两条sql一条的查count带limit10,查列表也有limit10,需求为列表有limit10,而count不需要

正确解法

go 复制代码
//归纳总体查询条件,否则会出现条件查出来有2条但总的有30来条的情况
	db := config.Database.
		Table("t_category as c").
		Select("c.*, COUNT(a.id) as article_count").
		Joins("LEFT JOIN t_article a ON c.id = a.category_id").
		Where("c.create_time BETWEEN ? AND ?", body.StartTime, body.EndTime).
		Group("c.id, c.category_name").
		Scopes(sql.ConditionQueryCategory(body))

	db.Count(&count) //先聚合总数
	db.
		Scopes(sql.Paginate(body.PageRequest)). //在进行分页查询
		Find(&categoryList)
相关推荐
AI小智1 小时前
后端变全栈,终于可以给大家推出我的LangChain学习小站了!
后端
lkf197111 小时前
商品中心—1.B端建品和C端缓存
开发语言·后端·缓存
我的ID配享太庙呀2 小时前
Django 科普介绍:从入门到了解其核心魅力
数据库·后端·python·mysql·django·sqlite
java叶新东老师2 小时前
goland编写go语言导入自定义包出现: package xxx is not in GOROOT (/xxx/xxx) 的解决方案
开发语言·后端·golang
码事漫谈4 小时前
C++模板元编程从入门到精通
后端
_風箏4 小时前
Java【代码 14】一个用于判断磁盘空间和分区表是否需要清理的工具类
后端
_風箏4 小时前
Java【代码 13】前端动态添加一条记后端使用JDK1.8实现map对象根据key的部分值进行分组(将map对象封装成指定entity对象)
后端
_風箏4 小时前
Java【代码 12】判断一个集合是否包含另一个集合中的一个或多个元素 retainAll() 及其他方法
后端
Java中文社群4 小时前
Coze开源版?别吹了!
人工智能·后端·开源
懂得节能嘛.4 小时前
【SpringAI实战】ChatPDF实现RAG知识库
java·后端·spring