10x Genomics 技术原理:微滴包裹、barcode 设计、文库构建与数据特点
各位好,今天我们来聊聊 10x Genomics 这套系统是怎么工作的。说实话,我第一次接触 10x 数据时,也被它背后的工程化设计震住了——它把单细胞测序从“手工定制”推到了“工业流水线”的水平。
10x 的核心思路很简单:给每个细胞一个独特的身份证——barcode。但这张身份证怎么发到每个细胞手里?发证后怎么读取?数据长什么样?这里头门道不少。我带大家一层层拆开看。
微滴包裹:让每个细胞住进独立小房间
10x 的 Paired-Lend 微流控芯片,说白了就是让细胞、酶、凝胶珠在一个小液滴里“相遇”。
- 微流控芯片:它有三个入口,分别通入含有细胞的悬液、含有逆转录酶的水相、以及含有 barcode 凝胶珠的油相。
- 液滴生成:水相和油相在芯片的“十字交叉口”相遇,在剪切力作用下形成微小的油包水液滴。每个液滴大约含一个细胞、一个凝胶珠和必要的酶。
- 凝胶珠:这是 10x 的“灵魂”所在。每个凝胶珠表面附着数百万条由
Barcode+UMI+Poly(dT)组成的捕获探针。
嗯,这里要注意:微滴包裹的关键是保证“每个液滴中,细胞数与凝胶珠数的比例约为 1:1”。为什么?比例高了,一个液滴里进两个细胞,会造成“双重细胞”;比例低了,大量凝胶珠会空跑。我在项目中遇到过,上机前一定要花时间算好细胞浓度和活性,否则后续分析会非常痛苦。
# 微滴生成过程中的典型参数(来自 10x 官方 protocol)
细胞悬液浓度:700 - 1200 cells/µL
细胞活性:>80%(通常要求 >90%)
目标回收细胞数:500 - 10,000 个
微滴大小:~80 µm(直径)
凝胶珠大小:~40 µm(直径)
油相流速:4.5 mL/min
水相流速:1.5 mL/min
液滴生成频率:约 2000 液滴/秒
关键指标:双细胞率控制在 0.4% 以下才算是合格实验。我习惯用 10x 官方提供的 cellranger count 命令做初步质控,它会自动报告多重态比率。
Barcode 设计:给每个分子刻上独一无二的编号
微滴包裹只是第一步。真正巧妙的,是凝胶珠上的 barcode 设计。10x 同一代系统(Chromium)使用了一种两段式设计:
- 10 bp Barcode:凝胶珠特有的序列,所有在同一个液滴中逆转录的 mRNA 分子共享同一个 barcode。
- 12 bp UMI (Unique Molecular Identifier):每个捕获的 mRNA 分子附加一个随机序列,用于区分不同原始分子。
- Poly(dT) 序列:用于捕获 mRNA 的 poly-A 尾巴,启动逆转录。
为什么会这样设计?说白了就是解决单细胞测序的两大核心问题:
- 细胞身份识别——同一个液滴里出来的所有片段,barcode 都相同,能分清来自哪个细胞。
- 分子计数去重—— PCR 扩增后同一个原始分子产生的多个副本,通过 UMI 比对即可合并为一条独立计数。
举个例子,你想想看:一个基因在某个细胞里只表达了一个 mRNA 分子,但经过文库构建和测序可能出了几千条 reads。如果没有 UMI,你会觉得它表达量很高,实际上这是个假象。UMI 就是用来撕掉这个假象的。
我个人习惯在做数据分析前,先检查一下 barcode 和 UMI 的序列分布完整性。10x 的 barcode 设计是有“纠错”能力的——它使用了 whitelist 机制,确定哪些 barcode 是凝胶珠上已知设计的,如果测到的 barcode 不在 whitelist 里,就认为它可能是测序错误,可以纠正到一个附近的正确 barcode。
# barcode 结构示例(来自 10x 凝胶珠的捕获探针)
5' - Barcode (10 bp) - UMI (12 bp) - Poly(dT) - 3'
# 一条 10x reads 的原始数据片段(R1 为 barcode + UMI,R2 为 cDNA 插入片段)
@NB552084:10:HTNF2BGXF:1:11101:1785:1031 1:N:0:ATCACG
ATCGAACGATCCGTTTACTCGATCTACACTGAAGTACAGCG
+
BBBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
@NB552084:10:HTNF2BGXF:1:11101:1785:1031 2:N:0:ATCACG
GCTGAAGAGAGTGCGGGTGTCGTGTTGCCCGGGAAGAAATCCGACTTAAGAT
+
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
文库构建:从 mRNA 到可测序文库
微滴破乳后,我们得到的是cDNA 文库。但要让它在 Illumina 测序仪上能被读取,还需要完成几个关键步骤:
- 逆转录:凝胶珠上的 Poly(dT) 捕获 mRNA,然后逆转录酶合成 cDNA 链,cDNA 链的一端带有 barcode 和 UMI。
- cDNA 打断与末端修复:将长链 cDNA 片段化到合适长度(约 300-500 bp)。
- 接头连接:在 cDNA 两端连接 Illumina P5/P7 接头,用于流动槽表面结合。
- PCR 扩增:使用与 P5/P7 互补的引物进行富集 PCR,同时添加 Index 序列(用于区分同一次运行的不同样本)。
避坑指南:我曾经遇到过文库构建失败的情况,后来发现是打断步骤出了问题。10x 官方推荐的酶切打断条件会很关键,要么使用 Covaris 超声打断。如果用酶切法,注意要严格控制酶切时间和温度,过度打断会损失长片段基因的覆盖度。
数据特点:稀疏性、UMI 去重与基因覆盖度
10x 的数据是什么样的?我用三个特点来总结:
| 特点 | 说明 | 对分析的影响 |
|---|---|---|
| 高稀疏性 | 约 85-95% 的细胞-基因组合为零表达 | 需要专门的稀疏矩阵处理方式,不宜用常规统计方法 |
| UMI 去重后分子计数 | 每个 mRNA 分子只计一次,消除 PCR 偏好 | 基因表达量可直接用 UMI 计数,不再依赖 fragments 数 |
| 基因覆盖度偏低 | 每个细胞平均检出 3000-5000 个基因,3' 端偏重 | 适用于基因表达分析,不适合全长转录本结构解析 |
说白了,10x 的数据本质上是一个“细胞 × 基因”的稀疏计数矩阵。很多初学者在分析时会直接拿原始 FASTQ 来做差异表达分析,那是不对的——必须先经过 cellranger count 处理,输出表达矩阵。否则,PCR 重复会造成严重的表达量高估。
我建议在处理 10x 数据时,第一件要做的事情就是调整过滤参数。默认的 cellranger 参数有时候会保留很多“空液滴”,也就是那些只捕获到背景 RNA 的微滴。可以用 EmptyDrops 算法或调整 min_umis 和 min_genes 来剔除它们。
# cellranger count 的典型调用命令
cellranger count --id=sample1 \
--transcriptome=/path/to/refdata-gex-GRCh38-2020-A \
--fastqs=/path/to/fastq/dir \
--sample=my_sample \
--expect-cells=5000 \
--localcores=16 \
--localmem=64
# 输出文件结构(关键文件)
./sample1/outs/filtered_feature_bc_matrix.h5
./sample1/outs/filtered_feature_bc_matrix/
barcodes.tsv.gz
features.tsv.gz
matrix.mtx.gz
./sample1/outs/raw_feature_bc_matrix.h5
./sample1/outs/molecule_info.h5
./sample1/outs/analysis/
注意:expect-cells 参数不能随意填。如果你设置过低或过高,barcode 过滤算法都会异常。我建议先做一次粗跑,查看 barcode 的 UMI 累积曲线,找到“拐点”位置,再调整参数重跑一次。这虽然多花点时间,但对数据质量影响很大。
从数据结构上看,10x 的输出可以清晰分为三部分:基因表达矩阵、barcode 质控统计、降维聚类结果。我做数据整合时,会先读取 filtered_feature_bc_matrix.h5 作为主数据,然后利用 molecule_info.h5 检查 UMI 数量和片段长度分布,确保没有出现过度扩增的 batch 效应。
# 在 R 中使用 Seurat 读取 10x 数据的典型代码
library(Seurat)
data.dir <- './sample1/outs/filtered_feature_bc_matrix/'
sample1 <- Read10X(data.dir = data.dir)
sample1.obj <- CreateSeuratObject(counts = sample1, project = "sample1", min.cells = 3, min.features = 200)
不知道大家有没有这种感觉:10x 系统让单细胞测序变得简单了,但背后的生物学复杂度一点没少。掌握这些,才算真正搞明白了 10x 测序数据是怎么来的。
我个人的经验:在做10x数据分析时,不要一上来就跑到降维聚类。一定要花点时间检查 barcode 和 UMI 的读取情况,以及文库片段长度分布。这一步跑好了,后面的分析会顺很多。我之前遇到过一个项目,做出来的聚类总是一团模糊,后来检查发现是打断过度,导致大部分片段都小于 200 bp,数据浪费了大半。