From 82ad75f33323c769eeb520f6a04c9a75e8167867 Mon Sep 17 00:00:00 2001 From: muqiuhan Date: Mon, 29 Dec 2025 13:46:46 +0000 Subject: deploy: 323db013241e734568decc8caa835220048698d2 --- search.xml | 253 ++++++++++++++++++++++++++++++------------------------------- 1 file changed, 126 insertions(+), 127 deletions(-) (limited to 'search.xml') diff --git a/search.xml b/search.xml index 5f8bf750..cff9eca2 100644 --- a/search.xml +++ b/search.xml @@ -875,49 +875,6 @@ Given the impracticality of prolonged hospitalizations and advancing technology Medicine - - N+1 selects problem 与 Prisma ORM - /2025/04/02/N-1-selects-problem-%E4%B8%8E-Prisma-ORM/ - N+1 查询问题是指在通过 ORM 查询数据时,执行了一次初始查询来获取父对象列表(这 1 次查询),然后为列表中的每一个父对象都单独执行了一次额外的查询来获取其关联的子对象(这 N 次查询)。最终导致总共执行了 1 + N 次数据库查询,其中 N 是初始查询返回的父对象的数量。

-

举个例子:

-

假设有两个数据库模型:User(用户)和 Post(帖子),一个用户可以有多篇帖子(一对多关系)。

-

现在,需要获取前 10 个用户以及他们各自的所有帖子。

-

一种有问题的 ORM 实现(或不当的使用方式)可能会这样执行:

-
    -
  1. 第一次查询 (The “1”): 获取前 10 个用户。
    SELECT * FROM User LIMIT 10;
    -
  2. -
  3. 接下来的 N (=10) 次查询 (The “N”): 对于上一步获取到的每一个用户,单独执行一次查询来获取该用户的帖子。
    -- 用户 1
    SELECT * FROM Post WHERE authorId = 1;
    -- 用户 2
    SELECT * FROM Post WHERE authorId = 2;
    -- 用户 3
    SELECT * FROM Post WHERE authorId = 3;
    -- ... 直到 用户 10
    SELECT * FROM Post WHERE authorId = 10;
    -
  4. -
-

在这个场景下,总共执行了 1 + 10 = 11 次数据库查询。如果 N 的值很大(比如获取 1000 个用户),就会产生 1001 次查询,这对数据库造成巨大的、不必要的压力,并显著增加应用程序的响应时间。每一次数据库交互都有网络延迟和数据库处理的开销,N+1 次查询会将这些开销放大 N 倍。

-

N+1 问题通常源于 ORM 处理关联数据的方式,特别是与“懒加载”(Lazy Loading)相关的策略。懒加载是指只有在显式访问关联属性时,ORM 才会去数据库加载这些数据。虽然这在某些情况下可以避免加载不需要的数据,但如果在循环中访问关联属性,就很容易触发 N+1 问题。

-

然而,问题的根源在于没有有效地预先加载(或批量加载)所需的关联数据。即使不使用严格意义上的懒加载,如果 ORM 在处理关联查询时不够智能,采用了逐个获取关联对象的策略,同样会产生 N+1 查询。

-

在 Prisma 出现之前或在其他 ORM 中,解决 N+1 问题常见的方法包括:

-
    -
  1. 预先加载(Eager Loading): 在执行初始查询时,就明确指示 ORM 同时将关联数据也查询出来。这通常通过 SQL 的 JOIN 操作实现。例如,一次性查询出用户和他们的帖子。虽然这减少了查询次数,但复杂的 JOIN 可能会导致查询本身变得庞大和低效,并可能返回冗余数据。
  2. -
  3. 批量加载(Batch Loading): 先执行初始查询获取父对象列表,然后收集所有父对象的 ID,在第二次查询中使用 WHERE IN (...) 子句一次性加载所有相关的子对象。这种方式通常需要两次查询,但避免了 N 次单独的查询。
  4. -
-

Prisma ORM 在设计上就考虑了 N+1 问题,并提供了一种既方便开发者又高效的解决方案。当使用 Prisma Client 查询数据并需要包含关联模型时,Prisma 会自动优化查询,避免产生 N+1 查询,主要通过关系查询(Relation Queries)中的 include 选项或嵌套读取(nested reads)来实现这一点:

-

假设想获取所有用户及其发布的帖子,使用 Prisma Client,可以这样写:

-
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function getUsersWithPosts() {
const usersWithPosts = await prisma.user.findMany({
include: {
posts: true, // 指示 Prisma 加载关联的 posts
},
})
// usersWithPosts 包含了用户列表,每个用户对象中都有一个 posts 数组
console.log(usersWithPosts)
}

getUsersWithPosts()
.catch((e) => {
throw e
})
.finally(async () => {
await prisma.$disconnect()
})
-

当执行上述查询时,Prisma 不会 生成 N+1 个 SQL 查询。而是首先会分析请求,并将其转化为数量非常有限的高效 SQL 查询。对于上面这个一对多关系的 include 查询,Prisma 通常会执行以下两步(类似于批量加载策略):

-
    -
  1. 查询父模型: 获取所有 User 记录。
    SELECT "public"."User"."id", "public"."User"."name", /* ... other user fields */ FROM "public"."User" WHERE 1=1
    -
  2. -
  3. 查询关联的子模型: 使用上一步获取到的所有用户 id,通过 WHERE IN (...) 子句一次性查询所有相关的 Post 记录。
    SELECT "public"."Post"."id", "public"."Post"."title", "public"."Post"."authorId", /* ... other post fields */ FROM "public"."Post" WHERE "public"."Post"."authorId" IN ($1, $2, $3, ...) /* 这里的 $1, $2, ... 是第一步查到的用户 ID 列表 */
    -
  4. -
-

Prisma Client 在内存中将这两次查询的结果高效地组合起来,最终返回嵌套的、符合 TypeScript 类型的数据。

-

Refs.

- -]]>
- - Technique - -
多人协作系统中的实现策略 (CRDT,锁等实现方案) /2025/05/08/Multiplayer-Collaborative-Systems-tips/ @@ -1028,6 +985,66 @@ await prisma.$transaction(async tx => { +]]> + + Technique + + + + N+1 selects problem 与 Prisma ORM + /2025/04/02/N-1-selects-problem-%E4%B8%8E-Prisma-ORM/ + N+1 查询问题是指在通过 ORM 查询数据时,执行了一次初始查询来获取父对象列表(这 1 次查询),然后为列表中的每一个父对象都单独执行了一次额外的查询来获取其关联的子对象(这 N 次查询)。最终导致总共执行了 1 + N 次数据库查询,其中 N 是初始查询返回的父对象的数量。

+

举个例子:

+

假设有两个数据库模型:User(用户)和 Post(帖子),一个用户可以有多篇帖子(一对多关系)。

+

现在,需要获取前 10 个用户以及他们各自的所有帖子。

+

一种有问题的 ORM 实现(或不当的使用方式)可能会这样执行:

+
    +
  1. 第一次查询 (The “1”): 获取前 10 个用户。
    SELECT * FROM User LIMIT 10;
    +
  2. +
  3. 接下来的 N (=10) 次查询 (The “N”): 对于上一步获取到的每一个用户,单独执行一次查询来获取该用户的帖子。
    -- 用户 1
    SELECT * FROM Post WHERE authorId = 1;
    -- 用户 2
    SELECT * FROM Post WHERE authorId = 2;
    -- 用户 3
    SELECT * FROM Post WHERE authorId = 3;
    -- ... 直到 用户 10
    SELECT * FROM Post WHERE authorId = 10;
    +
  4. +
+

在这个场景下,总共执行了 1 + 10 = 11 次数据库查询。如果 N 的值很大(比如获取 1000 个用户),就会产生 1001 次查询,这对数据库造成巨大的、不必要的压力,并显著增加应用程序的响应时间。每一次数据库交互都有网络延迟和数据库处理的开销,N+1 次查询会将这些开销放大 N 倍。

+

N+1 问题通常源于 ORM 处理关联数据的方式,特别是与“懒加载”(Lazy Loading)相关的策略。懒加载是指只有在显式访问关联属性时,ORM 才会去数据库加载这些数据。虽然这在某些情况下可以避免加载不需要的数据,但如果在循环中访问关联属性,就很容易触发 N+1 问题。

+

然而,问题的根源在于没有有效地预先加载(或批量加载)所需的关联数据。即使不使用严格意义上的懒加载,如果 ORM 在处理关联查询时不够智能,采用了逐个获取关联对象的策略,同样会产生 N+1 查询。

+

在 Prisma 出现之前或在其他 ORM 中,解决 N+1 问题常见的方法包括:

+
    +
  1. 预先加载(Eager Loading): 在执行初始查询时,就明确指示 ORM 同时将关联数据也查询出来。这通常通过 SQL 的 JOIN 操作实现。例如,一次性查询出用户和他们的帖子。虽然这减少了查询次数,但复杂的 JOIN 可能会导致查询本身变得庞大和低效,并可能返回冗余数据。
  2. +
  3. 批量加载(Batch Loading): 先执行初始查询获取父对象列表,然后收集所有父对象的 ID,在第二次查询中使用 WHERE IN (...) 子句一次性加载所有相关的子对象。这种方式通常需要两次查询,但避免了 N 次单独的查询。
  4. +
+

Prisma ORM 在设计上就考虑了 N+1 问题,并提供了一种既方便开发者又高效的解决方案。当使用 Prisma Client 查询数据并需要包含关联模型时,Prisma 会自动优化查询,避免产生 N+1 查询,主要通过关系查询(Relation Queries)中的 include 选项或嵌套读取(nested reads)来实现这一点:

+

假设想获取所有用户及其发布的帖子,使用 Prisma Client,可以这样写:

+
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function getUsersWithPosts() {
const usersWithPosts = await prisma.user.findMany({
include: {
posts: true, // 指示 Prisma 加载关联的 posts
},
})
// usersWithPosts 包含了用户列表,每个用户对象中都有一个 posts 数组
console.log(usersWithPosts)
}

getUsersWithPosts()
.catch((e) => {
throw e
})
.finally(async () => {
await prisma.$disconnect()
})
+

当执行上述查询时,Prisma 不会 生成 N+1 个 SQL 查询。而是首先会分析请求,并将其转化为数量非常有限的高效 SQL 查询。对于上面这个一对多关系的 include 查询,Prisma 通常会执行以下两步(类似于批量加载策略):

+
    +
  1. 查询父模型: 获取所有 User 记录。
    SELECT "public"."User"."id", "public"."User"."name", /* ... other user fields */ FROM "public"."User" WHERE 1=1
    +
  2. +
  3. 查询关联的子模型: 使用上一步获取到的所有用户 id,通过 WHERE IN (...) 子句一次性查询所有相关的 Post 记录。
    SELECT "public"."Post"."id", "public"."Post"."title", "public"."Post"."authorId", /* ... other post fields */ FROM "public"."Post" WHERE "public"."Post"."authorId" IN ($1, $2, $3, ...) /* 这里的 $1, $2, ... 是第一步查到的用户 ID 列表 */
    +
  4. +
+

Prisma Client 在内存中将这两次查询的结果高效地组合起来,最终返回嵌套的、符合 TypeScript 类型的数据。

+

Refs.

+ +]]>
+ + Technique + +
+ + .NET AOT 下的 F# 命令行参数解析库选择 + /2024/06/06/NET-AOT-%E4%B8%8B%E7%9A%84-F-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%BA%93%E9%80%89%E6%8B%A9/ + Argu 不支持 AOT,不过用 F# 的话可以看整个 .NET 的生态,我看了一下 C# 的 CommandLineParser:
+https://github.com/commandlineparser

+

在这个 PR 中支持了 Native AOT
+https://github.com/commandlineparser/commandline/pull/913

+

除此之外,还有一个更加精巧的 F# 库可以用,只有两百多行:
+https://github.com/B2R2-org/FsOptParse/

+

AOT 后的大小很可观,并且支持 full trim.

+

用例:

+
(** defines a state to pass to the option parser *)
type opts =
{
optX : int;
optY : bool;
optZ : string;
}

(** default option state *)
let defaultOpts =
{
optX = 0;
optY = false;
optZ = "";
}

(*
An example command line specification, which is a list of Options.
Each Option describes a command line option (switch) that is specified with
either a short (a single-dash option) or long option (a double-dash option).
*)
let spec =
[
(* This option can be specified with -x <NUM>. There is an extra argument to
specify a value in integer. *)
Option ((* description of the option *)
descr="this is a testing param X",
(* how many extra argument must be provided by a user? *)
extra=1,
(* callback sets up the option and returns it *)
callback=(fun opts arg -> {opts with optX=(int) arg.[0]}),
(* use a short option style -x *)
short="-x"
);

(* This option can be specified with -y. There is no extra argument. This
option just sets a flag, optY. *)
Option ((* description of the option *)
descr="this is a testing param Y",
(* set the option to be true *)
callback=(fun opts _ -> {opts with optY=true}),
(* use a short option style (-y) *)
short="-y",
(* also use a long option style (--yoohoo) *)
long="--yoohoo"
);

(* A dummy option to pretty-print the usage *)
Option ((* description of the option *)
descr="",
dummy=true
);
Option ((* description of the option *)
descr="[Required Options]",
descrColor=System.ConsoleColor.DarkCyan,
dummy=true
);

(* The third option is a required option. In other words, option parsing
will raise an exception if this option is not given by a user. This
option takes in an additional integer argument, and set it to the global
variable z. *)
Option ((* description of the option *)
descr="required parameter <STRING> with an integer option",
(* callback to set the optZ value *)
callback=(fun opts arg -> {opts with optZ=arg.[0]}),
(* specifying this is a required option *)
required=true,
(* one additional argument to specify an integer value *)
extra=1,
(* use only a long option style *)
long="--req"
);
]

let _ =
let prog = "opttest.fsx"
let args = System.Environment.GetCommandLineArgs ()
let usageGetter () = "[Usage]\n %p %o"
try
let left, opts = optParse spec usageGetter prog args defaultOpts
printfn "Rest args: %A, x: %d, y: %b, z: %s"
left opts.optX opts.optY opts.optZ
0
with
| SpecErr msg ->
eprintfn "Invalid spec: %s" msg
exit 1
| RuntimeErr msg ->
eprintfn "Invalid args given by user: %s" msg
usagePrint spec prog usageGetter (fun () -> exit 1)
]]>
Technique @@ -1056,23 +1073,6 @@ await prisma.$transaction(async tx => { Medicine
- - .NET AOT 下的 F# 命令行参数解析库选择 - /2024/06/06/NET-AOT-%E4%B8%8B%E7%9A%84-F-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0%E8%A7%A3%E6%9E%90%E5%BA%93%E9%80%89%E6%8B%A9/ - Argu 不支持 AOT,不过用 F# 的话可以看整个 .NET 的生态,我看了一下 C# 的 CommandLineParser:
-https://github.com/commandlineparser

-

在这个 PR 中支持了 Native AOT
-https://github.com/commandlineparser/commandline/pull/913

-

除此之外,还有一个更加精巧的 F# 库可以用,只有两百多行:
-https://github.com/B2R2-org/FsOptParse/

-

AOT 后的大小很可观,并且支持 full trim.

-

用例:

-
(** defines a state to pass to the option parser *)
type opts =
{
optX : int;
optY : bool;
optZ : string;
}

(** default option state *)
let defaultOpts =
{
optX = 0;
optY = false;
optZ = "";
}

(*
An example command line specification, which is a list of Options.
Each Option describes a command line option (switch) that is specified with
either a short (a single-dash option) or long option (a double-dash option).
*)
let spec =
[
(* This option can be specified with -x <NUM>. There is an extra argument to
specify a value in integer. *)
Option ((* description of the option *)
descr="this is a testing param X",
(* how many extra argument must be provided by a user? *)
extra=1,
(* callback sets up the option and returns it *)
callback=(fun opts arg -> {opts with optX=(int) arg.[0]}),
(* use a short option style -x *)
short="-x"
);

(* This option can be specified with -y. There is no extra argument. This
option just sets a flag, optY. *)
Option ((* description of the option *)
descr="this is a testing param Y",
(* set the option to be true *)
callback=(fun opts _ -> {opts with optY=true}),
(* use a short option style (-y) *)
short="-y",
(* also use a long option style (--yoohoo) *)
long="--yoohoo"
);

(* A dummy option to pretty-print the usage *)
Option ((* description of the option *)
descr="",
dummy=true
);
Option ((* description of the option *)
descr="[Required Options]",
descrColor=System.ConsoleColor.DarkCyan,
dummy=true
);

(* The third option is a required option. In other words, option parsing
will raise an exception if this option is not given by a user. This
option takes in an additional integer argument, and set it to the global
variable z. *)
Option ((* description of the option *)
descr="required parameter <STRING> with an integer option",
(* callback to set the optZ value *)
callback=(fun opts arg -> {opts with optZ=arg.[0]}),
(* specifying this is a required option *)
required=true,
(* one additional argument to specify an integer value *)
extra=1,
(* use only a long option style *)
long="--req"
);
]

let _ =
let prog = "opttest.fsx"
let args = System.Environment.GetCommandLineArgs ()
let usageGetter () = "[Usage]\n %p %o"
try
let left, opts = optParse spec usageGetter prog args defaultOpts
printfn "Rest args: %A, x: %d, y: %b, z: %s"
left opts.optX opts.optY opts.optZ
0
with
| SpecErr msg ->
eprintfn "Invalid spec: %s" msg
exit 1
| RuntimeErr msg ->
eprintfn "Invalid args given by user: %s" msg
usagePrint spec prog usageGetter (fun () -> exit 1)
-]]>
- - Technique - -
OCaml Core.Int.pow 的实现 /2023/10/21/OCaml-Core-Int-pow-%E7%9A%84%E5%AE%9E%E7%8E%B0/ @@ -3564,6 +3564,28 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 Life + + 临床研究中盲态规则存在的原因 + /2025/12/19/%E4%B8%B4%E5%BA%8A%E7%A0%94%E7%A9%B6%E4%B8%AD%E7%9B%B2%E6%80%81%E8%A7%84%E5%88%99%E5%AD%98%E5%9C%A8%E7%9A%84%E5%8E%9F%E5%9B%A0/ + 在临床药物试验中,盲态是一种核心方法学设计,旨在消除主观偏见对试验结果的影响。其本质在于对研究参与者、研究者或评估者中的一方或多方隐藏治疗分配信息,从而确保数据收集和结果判读的客观性。盲态的实施贯穿试验设计、执行、数据管理和分析的全链条,其规定与存在原因植根于循证医学对科学严谨性的追求。

+

从试验启动到结束,盲态管理需遵循标准化流程。在方案设计阶段,需明确盲法层级(如单盲、双盲或三盲),并制定相应的盲法实施计划。随机化编码由独立于临床团队的统计人员或第三方机构生成,治疗药物与对照品(如安慰剂)需在外观、气味、包装上完全一致,以确保盲态的完整性。在试验执行期间,发药、药物清点及受试者管理均由不参与疗效评估的人员负责,避免无意间泄露分组信息。当受试者出现严重不良事件,且必须知晓具体用药才能实施救治时,可启动紧急揭盲程序,但需严格记录揭盲原因、时间及操作人员,并在最终报告中说明。数据录入与管理环节,所有标识治疗组别的字段均需隐藏,数据库锁定前由盲态审核委员会进行一致性核查,确保无意外破盲。直至最终统计分析完成,研究团队方可正式揭盲。这一系列措施在《药物临床试验质量管理规范》及ICH-GCP指南中均有详细规定,旨在维护试验结果的科学可靠性[1]。

+

盲态的核心价值在于控制两类主要偏倚:性能偏倚与检测偏倚。若研究者或受试者知晓治疗分配,可能在评估症状改善、记录不良事件或调整合并用药时产生倾向性,例如对试验组过度乐观或对对照组过度严苛,这种主观介入会扭曲药物真实疗效的测量。此外,在患者报告结局或生活质量评分等软终点指标中,知晓分组可能直接影响受试者的心理预期与反馈,进一步干扰结果有效性。通过盲态设计,可最大程度剥离人为因素对终点事件的干扰,从而更准确地估计药物与安慰剂或对照药之间的差异。从统计学角度,盲态有助于维持随机化带来的组间均衡性,使基线特征与未知混杂因素在组间均匀分布,提升因果推断的可靠性。历史上多项研究显示,非盲试验倾向于高估治疗效果约25%左右,尤其在主观性终点中偏倚更为显著[2]。

+
+

参考文献

+
+
+
    +
  1. International Council for Harmonisation of Technical Requirements for Pharmaceuticals for Human Use. ICH Harmonised Guideline: Integrated Addendum to ICH E6(R1): Guideline for Good Clinical Practice E6(R2). 2016. https://database.ich.org/sites/default/files/E6_R2_Addendum.pdf ↩︎

    +
  2. +
  3. Hróbjartsson A, et al. Blinded trials taken to the test: an analysis of randomized clinical trials that report tests for the success of blinding. International Journal of Epidemiology. 2007;36(3):654-663. https://doi.org/10.1093/ije/dym020 ↩︎

    +
  4. +
+
+]]>
+ + Medicine + +
乙酰半脱氨酸的药理学与药代动力学特性 /2025/12/30/%E4%B9%99%E9%85%B0%E5%8D%8A%E8%84%B1%E6%B0%A8%E9%85%B8%E7%9A%84%E8%8D%AF%E7%90%86%E5%AD%A6%E4%B8%8E%E8%8D%AF%E4%BB%A3%E5%8A%A8%E5%8A%9B%E5%AD%A6%E7%89%B9%E6%80%A7/ @@ -3591,28 +3613,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 -]]> - - Medicine - - - - 临床研究中盲态规则存在的原因 - /2025/12/19/%E4%B8%B4%E5%BA%8A%E7%A0%94%E7%A9%B6%E4%B8%AD%E7%9B%B2%E6%80%81%E8%A7%84%E5%88%99%E5%AD%98%E5%9C%A8%E7%9A%84%E5%8E%9F%E5%9B%A0/ - 在临床药物试验中,盲态是一种核心方法学设计,旨在消除主观偏见对试验结果的影响。其本质在于对研究参与者、研究者或评估者中的一方或多方隐藏治疗分配信息,从而确保数据收集和结果判读的客观性。盲态的实施贯穿试验设计、执行、数据管理和分析的全链条,其规定与存在原因植根于循证医学对科学严谨性的追求。

-

从试验启动到结束,盲态管理需遵循标准化流程。在方案设计阶段,需明确盲法层级(如单盲、双盲或三盲),并制定相应的盲法实施计划。随机化编码由独立于临床团队的统计人员或第三方机构生成,治疗药物与对照品(如安慰剂)需在外观、气味、包装上完全一致,以确保盲态的完整性。在试验执行期间,发药、药物清点及受试者管理均由不参与疗效评估的人员负责,避免无意间泄露分组信息。当受试者出现严重不良事件,且必须知晓具体用药才能实施救治时,可启动紧急揭盲程序,但需严格记录揭盲原因、时间及操作人员,并在最终报告中说明。数据录入与管理环节,所有标识治疗组别的字段均需隐藏,数据库锁定前由盲态审核委员会进行一致性核查,确保无意外破盲。直至最终统计分析完成,研究团队方可正式揭盲。这一系列措施在《药物临床试验质量管理规范》及ICH-GCP指南中均有详细规定,旨在维护试验结果的科学可靠性[1]。

-

盲态的核心价值在于控制两类主要偏倚:性能偏倚与检测偏倚。若研究者或受试者知晓治疗分配,可能在评估症状改善、记录不良事件或调整合并用药时产生倾向性,例如对试验组过度乐观或对对照组过度严苛,这种主观介入会扭曲药物真实疗效的测量。此外,在患者报告结局或生活质量评分等软终点指标中,知晓分组可能直接影响受试者的心理预期与反馈,进一步干扰结果有效性。通过盲态设计,可最大程度剥离人为因素对终点事件的干扰,从而更准确地估计药物与安慰剂或对照药之间的差异。从统计学角度,盲态有助于维持随机化带来的组间均衡性,使基线特征与未知混杂因素在组间均匀分布,提升因果推断的可靠性。历史上多项研究显示,非盲试验倾向于高估治疗效果约25%左右,尤其在主观性终点中偏倚更为显著[2]。

-
-

参考文献

-
-
-
    -
  1. International Council for Harmonisation of Technical Requirements for Pharmaceuticals for Human Use. ICH Harmonised Guideline: Integrated Addendum to ICH E6(R1): Guideline for Good Clinical Practice E6(R2). 2016. https://database.ich.org/sites/default/files/E6_R2_Addendum.pdf ↩︎

    -
  2. -
  3. Hróbjartsson A, et al. Blinded trials taken to the test: an analysis of randomized clinical trials that report tests for the success of blinding. International Journal of Epidemiology. 2007;36(3):654-663. https://doi.org/10.1093/ije/dym020 ↩︎

    -
  4. -
-
]]>
Medicine @@ -3864,6 +3864,15 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的

直到他们在梦中期待新的一天中无限的未来。

可惜这都与我无关,那是我在学校的最后一天,我和他们一起放假,再也没回过学校。

但我当天的确睡了个好觉。

+]]> + + gallery + +
+ + 二〇二五年六月二十九日 + /2025/06/29/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%85%AD%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B9%9D%E6%97%A5/ + 我很想你们。

]]>
gallery @@ -3906,15 +3915,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的

希望我们再见时,可以向你讲述沿途风景,讲述我感受到的世界与爱。

也希望你在那边一切都好。

在星空的另一端,思念从未停止。

-]]> - - gallery - -
- - 二〇二五年六月二十九日 - /2025/06/29/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%85%AD%E6%9C%88%E4%BA%8C%E5%8D%81%E4%B9%9D%E6%97%A5/ - 我很想你们。

]]>
gallery @@ -4085,40 +4085,6 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的

我眯着眼看远处的灯火,

想那里面会不会有一盏,

是你为我留的。

-]]> - - gallery - -
- - 二〇二五年十二月二十八日 - /2025/12/28/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AB%E6%97%A5/ - 我曾经是个极度渴望远方的人,十六七岁的时候,我就觉得生活应该在别处。去极北的冰原看极光,在热带的雨林里听蝉鸣。那时候,我以为勇敢就是不断地出发,不断地跨越国境线,去触摸那些地图上遥远的坐标。

-

直到那些宏大的风景在眼里渐渐褪色,变成了一张张大同小异的明信片。我发现,自己并不渴望那个虚无缥缈的“远方”,我只是想找到一个可爱的地方,能让心跳安稳下来。

-

于是,我回到了这座南方的小城。

-

小城多雨,空气里总带着一股湿润的青苔味。

-

我撑着一把黑色的旧伞,这把伞是我在离开这座城市的前两年就花十几元购入的,当时陈桉说我背着它像一个中二的少年。

-

我走在交错纵横的弄堂里。这里的建筑保留着旧时代的局促与温情,电线杆上晾着洗得发白的床单,偶尔有几只野猫从瓦片上跳过。

-

跨过那座被称为“通济”的石拱桥。桥下的河水浑浊而缓慢,像流淌不动的时光。我并没有明确的目的地,只是随性地穿梭在一条又一条弄堂之间。

-

就在转过一个转角,走进一条地图上没有标注的、始料未及的小巷时,我停住了脚步。

-

雨不知道什么时候停了。巷子很窄,两旁的红砖墙上爬满了枯萎的藤蔓。你就站在巷子的尽头,手里提着一袋刚出炉的生煎包,正低头躲避屋檐落下的水滴。

-

在我的旅行生涯中,曾独自面对过一望无际的沙海,也曾在暴风雪的极夜徒步。我一直以为那些是勇敢。

-

可直到这一刻,看着几步之外的你,我才意识到,自己一生中最勇敢的瞬间,竟然是此时此刻,是在这个平凡得不能再平凡的小巷里,决定不再逃避,决定停下流浪的脚步,去面对一个真实的、近在咫尺的人。

-

你抬起头,看见了我。

-

眼神里先是错愕,随即漾开了一层淡淡的、如同湖水般的温柔。

-

“你回来了?”你轻声问,语气自然得就像我只是刚刚下楼买了一份报纸。

-

我没有说话。我看着你,觉得你明明就在眼前,却又像从世界的尽头跋涉而来。

-

或者说,有你在的地方,就是我所有流浪的终点。

-

后来你说我的眼中藏着星点,那是久违的、对生活的热忱,你说我的嘴角不自觉地勾起一丝弧线,那是被治愈的证明。

-

“我以为你还在地平线的那头。”你走近了几步,把那一小袋生煎包往怀里捂了捂,似乎怕冷风吹散了热气。

-

“我找过了,地平线没有终点。”我接过你手中的伞,声音有些沙哑。

-

这句话说出来很俗气,但在这一刻,在这一条被夕阳余晖染成橘色的小巷里,它是唯一的真理。

-

我突然想把时间揉成碎片,捧在手心里。不想再去计算明天要去哪座城市,不想再去规划下一次航行。只想把这些碎片化的时间,全部挥霍在眼前的琐碎里:比如陪你走完这条巷子,比如和你一起吃掉这袋生煎,比如在下一个转角讨论晚饭的菜单。

-

“还没吃晚饭吧?”你笑着问我,眼睛弯成了好看的月牙。

-

“没呢。”

-

“那……”你歪着头,眨了眨眼。

-

我认真地看着你,郑重地点了点头。

-

我知道,这次没有期限。在经历了无数个孤独的远方之后,在这一条始料未及的小巷里,再见面,就是永远。

]]>
gallery @@ -4167,6 +4133,40 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的

我依然是那个木讷的我。

海浪一遍遍慢慢地拍打着沙滩,没关系,慢一点也没关系。

在这片蓝色的寂静里听懂自己。

+]]> + + gallery + +
+ + 二〇二五年十二月二十八日 + /2025/12/28/%E4%BA%8C%E3%80%87%E4%BA%8C%E4%BA%94%E5%B9%B4%E5%8D%81%E4%BA%8C%E6%9C%88%E4%BA%8C%E5%8D%81%E5%85%AB%E6%97%A5/ + 我曾经是个极度渴望远方的人,十六七岁的时候,我就觉得生活应该在别处。去极北的冰原看极光,在热带的雨林里听蝉鸣。那时候,我以为勇敢就是不断地出发,不断地跨越国境线,去触摸那些地图上遥远的坐标。

+

直到那些宏大的风景在眼里渐渐褪色,变成了一张张大同小异的明信片。我发现,自己并不渴望那个虚无缥缈的“远方”,我只是想找到一个可爱的地方,能让心跳安稳下来。

+

于是,我回到了这座南方的小城。

+

小城多雨,空气里总带着一股湿润的青苔味。

+

我撑着一把黑色的旧伞,这把伞是我在离开这座城市的前两年就花十几元购入的,当时陈桉说我背着它像一个中二的少年。

+

我走在交错纵横的弄堂里。这里的建筑保留着旧时代的局促与温情,电线杆上晾着洗得发白的床单,偶尔有几只野猫从瓦片上跳过。

+

跨过那座被称为“通济”的石拱桥。桥下的河水浑浊而缓慢,像流淌不动的时光。我并没有明确的目的地,只是随性地穿梭在一条又一条弄堂之间。

+

就在转过一个转角,走进一条地图上没有标注的、始料未及的小巷时,我停住了脚步。

+

雨不知道什么时候停了。巷子很窄,两旁的红砖墙上爬满了枯萎的藤蔓。你就站在巷子的尽头,手里提着一袋刚出炉的生煎包,正低头躲避屋檐落下的水滴。

+

在我的旅行生涯中,曾独自面对过一望无际的沙海,也曾在暴风雪的极夜徒步。我一直以为那些是勇敢。

+

可直到这一刻,看着几步之外的你,我才意识到,自己一生中最勇敢的瞬间,竟然是此时此刻,是在这个平凡得不能再平凡的小巷里,决定不再逃避,决定停下流浪的脚步,去面对一个真实的、近在咫尺的人。

+

你抬起头,看见了我。

+

眼神里先是错愕,随即漾开了一层淡淡的、如同湖水般的温柔。

+

“你回来了?”你轻声问,语气自然得就像我只是刚刚下楼买了一份报纸。

+

我没有说话。我看着你,觉得你明明就在眼前,却又像从世界的尽头跋涉而来。

+

或者说,有你在的地方,就是我所有流浪的终点。

+

后来你说我的眼中藏着星点,那是久违的、对生活的热忱,你说我的嘴角不自觉地勾起一丝弧线,那是被治愈的证明。

+

“我以为你还在地平线的那头。”你走近了几步,把那一小袋生煎包往怀里捂了捂,似乎怕冷风吹散了热气。

+

“我找过了,地平线没有终点。”我接过你手中的伞,声音有些沙哑。

+

这句话说出来很俗气,但在这一刻,在这一条被夕阳余晖染成橘色的小巷里,它是唯一的真理。

+

我突然想把时间揉成碎片,捧在手心里。不想再去计算明天要去哪座城市,不想再去规划下一次航行。只想把这些碎片化的时间,全部挥霍在眼前的琐碎里:比如陪你走完这条巷子,比如和你一起吃掉这袋生煎,比如在下一个转角讨论晚饭的菜单。

+

“还没吃晚饭吧?”你笑着问我,眼睛弯成了好看的月牙。

+

“没呢。”

+

“那……”你歪着头,眨了眨眼。

+

我认真地看着你,郑重地点了点头。

+

我知道,这次没有期限。在经历了无数个孤独的远方之后,在这一条始料未及的小巷里,再见面,就是永远。

]]>
gallery @@ -6676,8 +6676,7 @@ NestJS 依赖其底层 HTTP 适配器(默认为 Express)来解析传入的 谈谈软件工程 /2025/12/20/%E8%B0%88%E8%B0%88%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B/ - #软件工程

-
+

在有限认知下,持续对抗系统熵增,在相互冲突的目标间做出最优权衡,以可工程化的方式交付价值。

一、复杂性是固有属性,而非偶然缺陷

-- cgit v1.2.3