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)
相关推荐
IT_10241 小时前
Spring Boot项目开发实战销售管理系统——系统设计!
大数据·spring boot·后端
ai小鬼头2 小时前
AIStarter最新版怎么卸载AI项目?一键删除操作指南(附路径设置技巧)
前端·后端·github
Touper.2 小时前
SpringBoot -- 自动配置原理
java·spring boot·后端
一只叫煤球的猫3 小时前
普通程序员,从开发到管理岗,为什么我越升职越痛苦?
前端·后端·全栈
一只鹿鹿鹿3 小时前
信息化项目验收,软件工程评审和检查表单
大数据·人工智能·后端·智慧城市·软件工程
专注VB编程开发20年3 小时前
开机自动后台运行,在Windows服务中托管ASP.NET Core
windows·后端·asp.net
程序员岳焱3 小时前
Java 与 MySQL 性能优化:MySQL全文检索查询优化实践
后端·mysql·性能优化
一只叫煤球的猫4 小时前
手撕@Transactional!别再问事务为什么失效了!Spring-tx源码全面解析!
后端·spring·面试
旷世奇才李先生4 小时前
Ruby 安装使用教程
开发语言·后端·ruby
沃夫上校7 小时前
Feign调Post接口异常:Incomplete output stream
java·后端·微服务