尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Solr集群数据迁移至Elasticsearch:全量、增量与双写策略实战
1. 为什么要把数据从Solr搬到Elasticsearch做过搜索相关项目的人大概都有这种体会Solr用着用着业务侧就开始提一些它不太好接的需求。比如实时聚合分析、复杂嵌套文档查询、和日志系统打通做统一检索入口这些场景下Elasticsearch的生态优势就显现出来了。我接手过几次Solr到Elasticsearch的迁移有的是因为Solr集群版本太老维护成本高有的是因为团队技术栈统一要往Elasticsearch靠还有的是因为Solr的分布式协调依赖ZooKeeper运维复杂度让运维团队叫苦不迭。先说清楚这件事的定位Solr集群数据迁移至Elasticsearch本质上是把存储在Solr中的索引数据包括原始文档字段、分词后的倒排信息、以及可能的存储字段完整地转移到Elasticsearch集群中并保证迁移后查询结果的一致性、性能不降级、业务无感知。这件事听起来像是导数据但实际操作中涉及的东西远比想象中多——字段类型映射、分词器差异、评分算法区别、深分页处理、增量同步策略每一个点都可能让你在半夜被叫起来处理线上问题。适合读这篇内容的人正在做或计划做搜索集群迁移的后端工程师、搜索平台维护人员、数据架构师。如果你手头正好有一个Solr集群要往Elasticsearch搬或者你只是好奇这两个搜索引擎之间数据怎么倒腾下面的内容应该能帮你少走不少弯路。我下面讲的东西一部分来自实际项目中的操作记录一部分是基于常见实践总结出来的合理方案。因为原始项目描述比较简略很多细节是我根据一个合格从业者在这个场景下最可能采用的做法来补全的你参考的时候结合自己的实际情况调整。2. 迁移之前必须搞清楚的几件事2.1 Solr和Elasticsearch的数据模型差异很多人一上来就想着写个程序把Solr的文档读出来往Elasticsearch里灌结果灌到一半发现字段类型对不上、日期格式解析报错、多值字段处理不了。这些问题的根源在于两个系统对数据模型的表达方式有本质区别。Solr的Schema定义在managed-schema或者schema.xml文件里字段类型有string、text_general、pdate、plong等其中text_general这种类型会经过分词器处理而string类型则是精确匹配不分词。Elasticsearch这边用mapping来定义类型有keyword、text、date、long等text类型默认会经过standard分词器keyword则不分词。看起来能对应上但细节差异很多。举个例子Solr里一个字段定义为text_general索引时用了StandardTokenizer加LowerCaseFilter查询时也走同样的分析链。迁移到Elasticsearch时如果你简单地把这个字段映射为text类型Elasticsearch默认用standard analyzer效果接近但不完全一样。更麻烦的是Solr里有些字段可能同时配置了index和stored属性indextrue表示要建索引storedtrue表示要存储原始值。Elasticsearch这边text类型默认会存储倒排索引但不存储原始值除非开启fielddata或者用_source字段keyword类型默认既建索引也存储。我一般会先做一张字段映射对照表把Solr里每个字段的类型、是否分词、是否存储、是否多值、是否有copyField都列出来然后逐个决定在Elasticsearch里怎么映射。这个工作看起来繁琐但能避免后面大量的返工。Solr字段类型典型Elasticsearch映射注意事项string (indexedtrue, storedtrue)keyword精确匹配场景注意ignore_above设置text_generaltext keyword子字段需要同时支持分词搜索和聚合pdatedate格式要显式指定如yyyy-MM-ddTHH:mm:ssZplonglong注意数值范围溢出booleanboolean直接对应multiValuedtrue数组类型Elasticsearch天然支持数组无需特殊处理2.2 分词器的对齐问题这是迁移中最容易被低估的坑。Solr里常用的分词器有StandardTokenizer、WhitespaceTokenizer、IK分词器中文场景、SynonymFilter等。Elasticsearch这边对应的有standard analyzer、whitespace analyzer、ik analyzer、synonym filter等。名字看起来一样但行为可能有细微差别。比如中文分词Solr里用IK分词器Elasticsearch里也有IK插件但两个版本的词典可能不同导致同一句话分出来的词不一样。再比如同义词过滤器Solr的SynonymFilter和Elasticsearch的synonym token filter在扩展方式上就有区别——Solr支持多词同义词的图结构扩展Elasticsearch在较新版本里也支持但配置语法不同。我的做法是迁移前先拿一批典型查询词分别在Solr和Elasticsearch里跑analyze API对比分词结果。如果差异在可接受范围内就继续如果差异很大要么调整Elasticsearch的分析器配置去逼近Solr的行为要么接受差异并评估对业务的影响。# Solr分析测试 curl http://solr-host:8983/solr/core_name/analysis/field?analysis.fieldnametitleanalysis.fieldvalue测试文本wtjson # Elasticsearch分析测试 curl -X POST http://es-host:9200/_analyze -H Content-Type: application/json -d { analyzer: ik_max_word, text: 测试文本 }2.3 评分算法和查询行为的差异Solr默认用Lucene的BM25相似度算法较新版本Elasticsearch从5.x开始也默认用BM25。但即使都用BM25参数可能不同——Solr的k1和b参数可以在schema里配置Elasticsearch也有对应的boost和similarity设置。更关键的是两个系统对查询的解析方式不同Solr的edismax查询解析器和Elasticsearch的multi_match查询在字段权重、短语匹配、模糊匹配上的默认行为有差异。这意味着即使数据完全一样同一个查询词在两个系统里返回的排序结果可能不同。对于搜索业务来说排序变化可能直接影响点击率和转化率。所以迁移后必须做A/B对比测试拿真实查询日志跑一遍看TopN结果的重合度。如果重合度低于某个阈值比如80%就需要调整Elasticsearch的查询DSL来逼近Solr的行为。3. 全量迁移的实操路线3.1 方案选型为什么不直接用官方工具Elasticsearch官方提供了一个Elasticsearch-Hadoop连接器理论上可以通过Spark把Solr数据读出来写到Elasticsearch。但实际用下来这个方案有几个问题一是需要引入Spark集群对于只是做一次迁移的场景来说太重二是Solr的RDD读取器对某些字段类型支持不好三是中间过程不透明出错了不好排查。另一个思路是用Solr的DataImportHandler把数据导出到中间存储比如CSV或JSON文件再用Elasticsearch的Bulk API导入。这个方案简单直接适合数据量不大比如几百万文档的场景。但DataImportHandler配置起来也不省心而且导出过程中如果Solr集群有写入可能漏掉增量数据。我最终采用的方案是写一个独立的迁移程序通过Solr的HTTP API分页读取文档经过字段转换后批量写入Elasticsearch。这个方案的好处是可控性强每个环节都能加日志和校验出问题容易定位。下面详细说这个方案怎么落地。3.2 从Solr分页读取文档的正确姿势Solr的查询API支持start和rows参数做分页但深分页性能很差。假设你有1000万文档用start9990000rows1000去读最后一批Solr需要扫描前面999万条记录响应时间可能达到几十秒甚至超时。正确的做法是用游标CursorMark。Solr从4.7版本开始支持cursorMark参数它基于排序字段的值来定位下一页的起始位置避免了深分页的性能问题。使用cursorMark的前提是必须指定一个唯一的排序字段通常是id或者唯一键并且排序顺序要稳定。import requests import json def read_solr_documents(solr_url, core_name, batch_size1000): 使用cursorMark从Solr分页读取文档 cursor_mark * base_url f{solr_url}/{core_name}/select while True: params { q: *:*, rows: batch_size, cursorMark: cursor_mark, sort: id asc, # 必须指定唯一排序字段 wt: json } resp requests.get(base_url, paramsparams, timeout60) resp.raise_for_status() data resp.json() docs data[response][docs] if not docs: break yield docs next_cursor data.get(nextCursorMark) if next_cursor cursor_mark: break cursor_mark next_cursor这里有几个细节要注意sort字段必须是唯一的如果id有重复cursorMark可能会跳过或重复某些文档。另外读取过程中如果Solr集群有文档更新cursorMark的快照语义可能不保证一致性所以最好在迁移期间暂停写入或者记录一个时间点迁移完成后用增量同步补上。3.3 字段转换与批量写入Elasticsearch从Solr读出来的文档是JSON格式字段名和值都是Solr的表示方式。比如日期字段可能是2024-01-15T10:30:00Z这样的字符串多值字段是数组某些字段可能包含HTML标签需要清洗。在写入Elasticsearch之前需要做一层转换。转换逻辑我一般写成一个独立的函数每个字段单独处理。这样做的好处是逻辑清晰出错了容易定位是哪个字段的问题。转换完成后用Elasticsearch的Bulk API批量写入每批500到1000条文档比较合适——太小了网络开销大太大了容易导致节点内存压力。from elasticsearch import Elasticsearch from elasticsearch.helpers import bulk def transform_doc(solr_doc): 将Solr文档转换为Elasticsearch文档 es_doc {} for field, value in solr_doc.items(): if field id: es_doc[_id] value es_doc[id] value elif field.endswith(_dt): # 日期字段转换 es_doc[field] value elif isinstance(value, list): # 多值字段Elasticsearch天然支持数组 es_doc[field] value else: es_doc[field] value return es_doc def write_to_es(es_client, index_name, docs): 批量写入Elasticsearch actions [] for doc in docs: action { _index: index_name, _id: doc.get(_id), _source: doc } actions.append(action) success, failed bulk(es_client, actions, raise_on_errorFalse) return success, failed写入过程中要监控几个指标写入速率docs/s、失败率、Elasticsearch集群的CPU和内存使用率、Bulk队列是否积压。如果失败率超过1%就要停下来排查原因常见的问题包括字段类型不匹配、文档过大超过默认的100MB限制、Elasticsearch磁盘水位过高导致写入被拒绝。3.4 迁移过程中的数据一致性保障全量迁移期间如果Solr集群还在接收写入就会出现迁移程序读到的数据不是最新的问题。解决这个问题有几种策略第一种是停机迁移。在业务低峰期比如凌晨暂停所有写入执行全量迁移迁移完成后切换流量到Elasticsearch再恢复写入。这个方案最简单但要求业务能接受一段时间的搜索不可用或者只读。第二种是双写。在迁移期间业务代码同时往Solr和Elasticsearch写数据。全量迁移程序只负责把历史数据搬过去增量部分由双写保证。这个方案对业务侵入性大需要改代码而且双写失败时的补偿逻辑比较复杂。第三种是增量同步。全量迁移完成后记录一个时间点然后从Solr的更新日志或者数据库中读取这个时间点之后的变更逐条同步到Elasticsearch。这个方案对业务无侵入但需要Solr有可用的变更捕获机制比如数据库的binlog或者Solr的real-time get。我实际项目中用的是第二种和第三种结合先停机做全量迁移然后开启双写跑一段时间确认Elasticsearch的数据和Solr一致后再逐步切流量最后停掉Solr的写入。这样风险可控每一步都有回退的余地。4. 增量同步与双写策略的落地细节4.1 基于时间戳的增量拉取如果业务数据本身有更新时间字段比如update_time可以用这个字段做增量拉取。迁移程序记录上一次拉取的最大时间戳下次从Solr查询update_time大于这个时间戳的文档。这个方案实现简单但有两个前提一是所有文档都有更新时间字段且会被正确更新二是Solr的查询能高效地按时间范围过滤。def incremental_sync(solr_url, core_name, es_client, index_name, last_sync_time): 基于时间戳的增量同步 query fupdate_time:[{last_sync_time} TO NOW] params { q: query, rows: 1000, sort: update_time asc, wt: json } resp requests.get(f{solr_url}/{core_name}/select, paramsparams) docs resp.json()[response][docs] if docs: transformed [transform_doc(d) for d in docs] write_to_es(es_client, index_name, transformed) # 更新last_sync_time为这批文档中最大的update_time new_max_time max(d[update_time] for d in docs) return new_max_time return last_sync_time这个方案有个坑如果同一秒内有多个文档更新而你的时间戳精度只到秒可能会漏掉部分文档。解决办法是用更细粒度的时间戳毫秒级或者结合文档id做二次校验。4.2 双写场景下的失败补偿双写意味着业务代码在写Solr的同时也要写Elasticsearch。如果Elasticsearch写入失败网络抖动、集群繁忙、磁盘满就会出现数据不一致。补偿机制一般有两种同步重试和异步补偿。同步重试是在写入失败时立即重试几次如果还是失败就记录到失败队列由后台任务定期重试。异步补偿是把失败的写入请求放到消息队列比如Kafka由消费者慢慢重试直到成功。我倾向于用异步补偿因为它不会阻塞业务主流程而且消息队列天然有持久化和重试机制。双写期间还要注意写入顺序问题。如果先写Solr再写Elasticsearch中间业务崩溃了Elasticsearch就少了这条数据。如果先写Elasticsearch再写SolrSolr少了数据但Elasticsearch有搜索结果是新的但业务数据可能不一致。一般来说搜索场景对数据一致性要求没那么高允许短暂的不一致所以先写主存储Solr或数据库再写Elasticsearch比较合理。4.3 迁移完成后的数据校验迁移做完不代表事情结束了必须做数据校验。校验分两个层面数量校验和内容校验。数量校验比较简单对比Solr和Elasticsearch的文档总数。如果数量不一致可能是迁移过程中有文档被跳过或者重复写入。注意Elasticsearch的count API返回的是文档数如果同一个id被写入多次后写入的会覆盖前面的所以count可能比实际迁移的文档数少。内容校验要抽样进行。随机抽取一批文档对比Solr和Elasticsearch里的字段值是否一致。对于文本字段要注意分词后的差异对于日期字段要注意时区转换对于数值字段要注意精度丢失。我一般会写一个校验脚本随机抽1000条文档逐字段对比输出差异报告。def validate_documents(solr_url, core_name, es_client, index_name, sample_size1000): 抽样校验Solr和Elasticsearch的文档一致性 # 从Solr随机抽取文档 params { q: *:*, rows: sample_size, sort: random_12345 asc, # Solr的随机排序 wt: json, fl: * } solr_docs requests.get(f{solr_url}/{core_name}/select, paramsparams).json()[response][docs] mismatches [] for solr_doc in solr_docs: doc_id solr_doc[id] es_doc es_client.get(indexindex_name, iddoc_id, ignore[404]) if es_doc.get(found) is False: mismatches.append({id: doc_id, reason: missing in ES}) continue es_source es_doc[_source] for field, solr_value in solr_doc.items(): es_value es_source.get(field) if not compare_values(solr_value, es_value): mismatches.append({ id: doc_id, field: field, solr: solr_value, es: es_value }) return mismatches校验中发现的问题要分类处理如果是字段类型转换错误修正转换逻辑后重新迁移这批文档如果是分词差异导致的评估对搜索效果的影响如果是数据丢失从Solr重新拉取补上。5. 迁移中那些让人头疼的坑5.1 深分页导致的迁移中断前面提到用cursorMark解决深分页问题但cursorMark本身也有坑。如果Solr集群在迁移期间有文档被删除cursorMark的游标可能会失效返回错误。另外如果排序字段的值有重复cursorMark可能会跳过某些文档。我遇到过一次迁移程序跑了几个小时突然报错说cursorMark无效。排查发现是因为迁移期间有运维操作重启了Solr节点导致游标状态丢失。解决办法是在迁移程序中加错误重试逻辑遇到cursorMark失效时从上一个成功的游标位置重新开始而不是从头再来。还有一种情况是文档在迁移过程中被更新导致排序字段的值变了cursorMark定位不到原来的位置。这种问题很难完全避免只能通过停机迁移或者接受少量数据不一致来规避。5.2 字段类型不匹配引发的写入失败Elasticsearch对字段类型很严格如果mapping里定义某个字段是long你写入一个字符串就会报错。Solr这边相对宽松有时候数值字段存的是字符串也能查。迁移时如果不做类型转换就会大量写入失败。常见的类型不匹配场景包括Solr的pdate字段读出来是字符串Elasticsearch的date字段要求特定格式Solr的plong字段可能是字符串形式的数字需要转成int或longSolr的boolean字段可能是true/false字符串Elasticsearch要求布尔值。我的做法是在transform_doc函数里对每个字段做显式类型转换转换失败时记录日志并跳过该文档而不是让整个批量写入失败。这样即使有少量脏数据也不会阻塞整体迁移进度。5.3 Elasticsearch的Bulk队列积压批量写入时如果Elasticsearch集群处理不过来Bulk队列会积压最终导致写入超时或拒绝。这种情况通常发生在集群资源不足或者mapping设计不合理比如字段太多导致索引膨胀时。监控Bulk队列的方法通过Elasticsearch的_nodes/stats API查看thread_pool.bulk.queue和thread_pool.bulk.rejected指标。如果queue持续大于0或者rejected在增长说明写入压力过大需要降低写入速率或者扩容集群。调整写入速率的办法减小批量大小比如从1000降到500、增加批次之间的间隔、限制并发写入的线程数。这些参数需要根据集群的实际承载能力来调没有固定值。5.4 分词差异导致的搜索结果不一致前面提过分词器对齐的问题实际影响可能比想象中大。我遇到过这样一个案例Solr里用IK分词器Elasticsearch里也用IK但两个版本的词典不同导致智能手机这个词在Solr里分成智能和手机在Elasticsearch里分成智能手机。用户搜智能时Solr能匹配到Elasticsearch匹配不到。解决办法是统一词典版本或者在Elasticsearch里配置自定义词典来对齐Solr的分词结果。如果无法完全对齐就要评估对业务的影响——如果只是少量长尾查询受影响可以接受如果核心查询词的分词结果不同就必须解决。6. 迁移后的性能调优与监控6.1 索引设置的针对性调整从Solr迁移过来的索引默认的Elasticsearch设置不一定最优。比如refresh_interval默认是1秒对于写入量大的场景会导致频繁的segment合并影响性能。迁移期间可以把refresh_interval设为-1关闭自动刷新迁移完成后再改回1s或30s。副本数也要根据集群规模调整。迁移期间为了加快写入速度可以先把副本数设为0迁移完成后再增加到1或2。但要注意副本数为0时如果节点故障数据会丢失所以只适合在迁移这种可重来的场景下使用。// 迁移期间优化写入性能 PUT /target_index/_settings { index: { refresh_interval: -1, number_of_replicas: 0, translog.durability: async, translog.sync_interval: 30s } } // 迁移完成后恢复 PUT /target_index/_settings { index: { refresh_interval: 1s, number_of_replicas: 1, translog.durability: request } }6.2 查询性能的对比测试迁移完成后必须做查询性能对比。拿Solr的慢查询日志或者真实用户查询分别在Solr和Elasticsearch上跑对比响应时间和结果质量。重点关注几个指标P50、P95、P99响应时间TopN结果的准确率聚合查询的耗时。如果Elasticsearch的查询性能不如Solr可能的原因包括mapping设计不合理导致无法利用索引、查询DSL写法低效、分片数设置不当、硬件资源不足。调优的方向包括优化mapping比如把不需要分词的字段设为keyword、重写查询DSL用filter代替query、避免wildcard查询、调整分片数一般建议单分片大小在10GB到50GB之间。6.3 长期运行的监控体系迁移不是一次性工作Elasticsearch集群上线后需要持续监控。核心监控指标包括集群健康状态green/yellow/red、节点CPU和内存使用率、磁盘使用率、JVM堆内存和GC情况、查询延迟和吞吐量、写入延迟和拒绝率。我一般会用Elasticsearch自带的监控API加上外部的监控系统比如Prometheus加Grafana来做可视化。设置告警阈值磁盘使用率超过85%告警、JVM老年代使用率超过80%告警、查询P99超过1秒告警、Bulk拒绝率大于0告警。# 查看集群健康状态 curl -X GET http://es-host:9200/_cluster/health?pretty # 查看节点统计信息 curl -X GET http://es-host:9200/_nodes/stats?pretty # 查看索引级别的性能指标 curl -X GET http://es-host:9200/target_index/_stats?pretty7. 一些零散但重要的经验迁移过程中我最大的体会是不要追求一步到位。先把核心字段迁过去跑通查询链路再逐步补充次要字段和高级功能。这样即使出问题影响面也可控。另外迁移脚本一定要支持断点续传。全量迁移可能跑几个小时甚至几天中间因为网络抖动、集群重启等原因中断是常事。如果每次中断都从头开始时间成本太高。实现断点续传的方法很简单记录每批成功写入的最后一个文档id或者cursorMark重启时从该位置继续。还有一点迁移前一定要备份Solr的数据。虽然迁移本身是只读操作不会影响Solr集群但万一迁移过程中需要回滚有备份心里踏实。备份方式可以用Solr的 replication API或者直接快照底层存储。最后说一个容易被忽略的点Elasticsearch的_source字段。Solr的stored字段在Elasticsearch里对应_source但_source默认会存储整个文档的原始JSON。如果某些字段很大比如长文本内容会导致_source膨胀影响查询性能和存储成本。可以在mapping里用_source: {excludes: [large_field]}来排除不需要返回的字段或者用stored_fields来精确控制返回哪些字段。迁移完成后我建议保留Solr集群至少一个月再下线。这期间如果Elasticsearch出现严重问题可以快速切回Solr。等确认Elasticsearch稳定运行、业务方反馈良好后再逐步停掉Solr的节点释放资源。这个双跑期虽然增加了一些运维成本但换来的是安心。
RELATED

相关推荐

数据湖治理中心:构建可发现、可理解、可信赖的数据资产闭环

数据湖治理中心:构建可发现、可理解、可信赖的数据资产闭环

简介:本资源是华为技术有限公司发布的《数据湖治理中心-数据治理方法论》官方方法论文档,面向数据治理工程师、数据平台架构师及企业数字化转型负责人,系统解决数据湖环境下多源异构数据的统一治理难题。文档完整覆盖数据治理框架、模块域&am…

📅 2026/10/15 5:47:56
2026 人才测评信效度怎么验,衡识人才测评实证解读

2026 人才测评信效度怎么验,衡识人才测评实证解读

一、别让测评报告变成一堆没人看的分数HR圈子里有个高频场景:花了几万块买了测评系统,报告出来了,各维度分数整整齐齐,但业务部门看两眼就放下了。不是报告不好看,是数据跟实际用人效果对不上号。有人自评高分&#xf…

📅 2026/10/15 5:47:56
理解网络分层:从PDU封装到Wireshark实战

理解网络分层:从PDU封装到Wireshark实战

1. 这道题为什么值得花20分钟重做一遍——从“抄答案”到真正理解网络分层的转折点你有没有过这样的经历:翻开《计算机网络原理》课后题第1题,扫一眼题目,翻到书末答案页,抄下几行字,打个勾,合上书&#xf…

📅 2026/10/15 5:47:56
MORE NEWS

更多资讯

📰

PyTorch核心设计原理与工业级训练实践

1. 这不是“学个框架”那么简单:PyTorch到底在解决什么真实问题?很多人第一次听说PyTorch,是在某次技术分享会上听到“动态图好调试”“API设计更Pythonic”这类评价;也有人是从Kaggle竞赛排行榜上看到大量Top方案的代码仓库里&am…

📰

从零搭建大学生就业信息管理系统:需求、设计到实现

1. 这个系统解决什么问题:需求拆解与功能边界大学生就业信息管理系统我接触得比较多,每年都会看到不少计算机相关专业的同学拿它当毕设选题。第一反应往往是“这不就是一个信息管理的增删改查吗”,但真正动手做起来才会发现,角色权…

📰

TensorFlow全流程实战:从环境搭建到模型部署的完整链路

这不是一篇入门教程,也不是API文档搬运。从我第一次在项目里跑通TensorFlow,到现在能用它顺畅地完成从数据准备到模型部署的全流程,中间绕了很远的路,也踩了不少坑。这篇文章把你真正会用到的那些东西——环境、数据、模型、训练、…

📰

airllm:低显存机器跑超大语言模型的实践指南

实际测试完成的部署方案:如何在低内存机器上跑超大容量语言模型最近 AI 本地部署群里讨论最多的一个话题,不是新模型的评测,而是“我到底要多少显存才能跑得动这些模型”。动不动几十GB的权重,让很多人的消费级电脑直接劝退。如果…

📰

RAR归档文件解压全指南:从格式识别到批量处理与避坑

简介:GC-PowerStation 2019中文版是面向表面贴装工艺工程师的坐标导出与光绘文件处理工具,支持导入电子表格、文本等多种坐标文件格式,并完成多层光绘文件的预览、合并与校验,帮助电子制造从业者在生产前检查设计准确性&#xff0…

📰

易语言乐玩插件窗口句柄获取与后台绑定模式实战指南

1. 窗口句柄获取与后台绑定模式的核心逻辑拆解1.1 为什么窗口句柄是一切自动化操作的起点做桌面自动化或者辅助工具开发,绕不开的第一个坎就是拿到目标窗口的句柄。你可以把窗口句柄理解成操作系统给每个窗口发的一张身份证号,它是一个长整型数值&#x…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬