/images/avatar.png

Schaepher's Blog

GORM 底层实现

查询原理

  1. 执行查询
    gorm.io/gorm/callbacks/query.go::Query(db *gorm.DB)
  2. 扫描结果集
    gorm.io/gorm/scan.go::Scan(rows *sql.Rows, db *DB, initialized bool)

扫描结果集的过程

  1. 取出结果集的所有字段
  2. 根据 Find() 时传入的引用判断存放的容器。比如用 map[string]interface{},或者结构体切片 []User,其他类型不一一列出。以下描述的是结构体切片的流程。
  3. 根据结果集字段的个数,生成与其长度相等的 interface{} 类型的 values 切片,以及长度相等的 Field 类型的 fields 切片。
    • values 切片将会被重复使用
  4. 找出结构体内与结果集列字段匹配的字段,并且在 fields 切片与列字段相同下标的槽里面放入结构体字段的信息。
  5. 生成结构体变量
  6. 在 values 切片的每个槽都设置与 fields 切片相同位置上的字段类型。
    • values 的每个 item 是 interface{},如果不在每次扫描之前都重新赋值,就会影响到已赋值的数据。
  7. 调用原生库扫描数据行到 value 切片
  8. 取出 fields 里有效的 field,在 values 里面取出相同位置的数据,并使用 field 将得到的数据赋值到结构体变量里面。
  9. 结构体变量加入 ReflectValue
  10. 回到 5 继续扫描下一行

Eager Load

  1. 执行驱动(driving)表的查询,将结果按照 foreignKey 的值分组
  2. 取所有 foreignKey ,到被驱动(driven)表查询
  3. 对于 driven 表的查询结果,逐个根据 referenceKey 找到 driving 表的结果分组,给分组里每个对象的相应 field 赋值

给结构体变量和结构体指针赋值有区别么?

如果是结构体指针,那么当 foreignKey 存在相同的数据时,同一个结构体指针会赋值给不同的 driving 表结果。一旦其中一个修改,另一个也会被修改。

Linux 文件系统快速备份

LVM(Logical Volume Manager) 快照机制

快照指的是在创建完快照后,快照里的数据不再发生改变。就算原始数据发生变化,读取快照的数据仍然是创建快照的时间点的数据。

MongoDB存储时间点数据

比较通用的一种方式

{
    "date" : "2020-11-05 00:00:00.000",
    "domain": "test.com",
    "data": {
        "00:00" : {
            "ts": 1604505600,
            "bandwidth": 111.222,
            "cost": 333.444
        },
        "00:01" : {
            "ts": 1604505660,
            "bandwidth": 555.666,
            "cost": 777.888
        }
    }
}

查询支持按五分钟合并:

db.oc_domain.aggregate([
    {
        "$match": {
            "domain": "test.com"
        }
    },
    {
        $project: {
            "domain": 1,
            "data": {
                $objectToArray: "$data"
            }
        }
    },
    {
        $unwind: "$data"
    },
    {
        $group: {
            "_id": {
                "domain": "$domain",
                "timestamp": {
                    "$subtract": [
                        "$data.v.time",
                        {
                            "$mod": [
                                "$data.v.time",
                                5 * 60
                            ]
                        }
                    ]
                }
            },
            "bandwidth": {
                $sum: "$data.v.bandwidth"
            }
        }
    },
    {
        $project: {
            "_id": 0,
            "timestamp": "$_id.timestamp",
            "domain": "$_id.domain",
            "bandwidth": "$bandwidth"
        }
    }
])

不需要直观地看某个时间点的数据

{
    "date" : "2020-11-05 00:00:00.000",
    "domain": "test.com",
    "data": [
        {
            "ts": 1604505600,
            "bandwidth": 111.222,
            "cost": 333.444
        },
        {
            "ts": 1604505660,
            "bandwidth": 555.666,
            "cost": 777.888
        }
    ]
}

查询支持按五分钟合并:

Python

子进程

通过 Shell 执行:

# -*- coding: utf-8 -*-
from subprocess import Popen, PIPE
p = Popen(["echo '1+1' | bc"], shell=True, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
print(stdout, stderr)

不通过 Shell 执行:

# -*- coding: utf-8 -*-
from subprocess import Popen, PIPE
p1 = Popen(["echo", "1+1"], shell=False, stdout=PIPE, stderr=PIPE)
# p1.stdout 作为 p2.stdin
p2 = Popen(["bc"], shell=False, stdin=p1.stdout ,stdout=PIPE, stderr=PIPE)
# 在 p2 先于 p1 退出时,p1 能够接收到 SIGPIPE 信号
p1.stdout.close()
stdout, stderr = p2.communicate()
print(stdout, stderr)

时间转换

# -*- coding: utf-8 -*-
from datetime import datetime, timezone, timedelta

# 如果在生成 date 的时候,没有指定 tz,则应该在后续通过 replace 设置时区
# date: 'datetime = datetime.fromisoformat("2021-05-05 16:00:00").replace(tzinfo=timezone.utc)'
date: 'datetime = datetime.now(tz=timezone.utc)'

# astimezone 默认把时区转换为本地时区
# 对于 Asia/Shanghai 时区,等同于 date.astimezone(timezone(timedelta(hours=8)))
tz_aware_date = date.astimezone()
# 通过设置 timespec="seconds",把毫秒去掉
iso8601format = tz_aware_date.isoformat(timespec="seconds")
print(iso8601format)

时间计算

datetime.now() + timedelta(days=1)

时间比较

if datetime.now() < datetime.now():
    return

打印一天内的每一分钟

[print("%02d:%02d:00" % (hour, minute)) for hour in range(0,24) for minute in range(0,60)]

Excel xls

# -*- coding: utf-8 -*-
from xlwt.Workbook import Workbook
from xlwt.Worksheet import Worksheet

wb = Workbook()
sheet = wb.add_sheet("test")
i = 1
for r in range(1, 100):
    for c in range(1, 100):
        sheet.write(r, c, i)
        i += 1

wb.save("test.xls")

Excel xlsx

# -*- coding: utf-8 -*-
from openpyxl import Workbook,utils
from openpyxl.worksheet.worksheet import Worksheet

wb = Workbook()
wb.remove(wb.active)
sheet = wb.create_sheet("test")

i = 1
for r in range(1, 100):
    for c in range(1, 100):
        sheet.cell(r, c, i)
        i += 1

wb.save("test.xlsx")

自适应:

进程和线程的二级概念

守护线程: 用于服务用户线程的线程。进程在终止时,不会考虑守护线程的状态。因为如果被服务的线程已经结束,则守护线程没有存在的意义。

用户线程: 进程终止前需要确保所有用户线程已经结束。如果有无限循环的用户线程,则进程会一直等待,无法退出。