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)
相关推荐
舒一笑几秒前
Mac 上安装并使用 frpc(FRP 内网穿透客户端)指南
后端·网络协议·程序员
每天学习一丢丢6 分钟前
Spring Boot + Vue 项目用宝塔面板部署指南
vue.js·spring boot·后端
邹小邹6 分钟前
Go 1.25 强势来袭:GC 速度飙升、并发测试神器上线,内存检测更精准!
后端·go
lichenyang45310 分钟前
管理项目服务器连接数据库
数据库·后端
生无谓12 分钟前
在Windows系统上安装多个JDK版本并切换
后端
唐叔在学习20 分钟前
万字长文深度解析HTTPS协议
后端·https
赵星星52036 分钟前
透彻理解Java中的深拷贝与浅拷贝:从误区到最佳实践
java·后端
黑客影儿1 小时前
Java技术总监的成长之路(技术干货分享)
java·jvm·后端·程序人生·spring·tomcat·maven
京东云开发者1 小时前
库存平台稳定性建设实践
后端
wenb1n1 小时前
SmartDB:AI与数据库的“翻译官”,开启无缝交互新时代!
后端