主鍵、搜索限制與openid/unionid區(qū)別)
1. 先搞清楚一件事wxid不是你輸入的那串號(hào)幾乎每個(gè)做過(guò)賬號(hào)相關(guān)開(kāi)發(fā)或者數(shù)據(jù)處理的人都碰到過(guò)這個(gè)場(chǎng)景拿到一份歷史數(shù)據(jù)里面有一列叫wxid值長(zhǎng)這樣wxid_a1b2c3d4e5f6g7你想拿它去搜個(gè)人、加個(gè)好友、或者做個(gè)對(duì)賬結(jié)果在搜索框里敲進(jìn)去回車什么都出不來(lái)。這時(shí)候大部分人第一反應(yīng)是是不是我少打了一位或者這個(gè)人是不是注銷了。實(shí)際上這兩者都不是wxid這個(gè)東西從設(shè)計(jì)之初就不是給人搜的。微信原始ID業(yè)界常說(shuō)的 wxid全稱一般理解為 weixin ID 的內(nèi)部標(biāo)識(shí)是賬號(hào)在系統(tǒng)內(nèi)部的一個(gè)唯一標(biāo)識(shí)符。它跟你在個(gè)人資料頁(yè)看到的微信號(hào)完全是兩回事。微信號(hào)是你可以自定義、可以改、可以展示給別人的社交名片wxid 是系統(tǒng)自己發(fā)號(hào)時(shí)生成的一串字符用戶既看不見(jiàn)也沒(méi)法改它的作用是在數(shù)據(jù)庫(kù)里唯一鎖定一行賬號(hào)記錄。搜索限制也不是什么臨時(shí)起意的風(fēng)控策略而是這套標(biāo)識(shí)體系分工之后必然產(chǎn)生的結(jié)果。這篇內(nèi)容想解決的問(wèn)題很具體把 wxid 這個(gè)字符串從看起來(lái)像亂碼變成能讀懂的工程產(chǎn)物。我會(huì)從它的字符串結(jié)構(gòu)、生成邏輯的合理推斷、為什么搜不到、以及它跟 openid、unionid 的關(guān)系幾個(gè)角度拆開(kāi)講。適合的人群大概有三類做過(guò)賬號(hào)體系設(shè)計(jì)的技術(shù)人員、處理過(guò)歷史數(shù)據(jù)遷移的運(yùn)維或數(shù)據(jù)同學(xué)、以及單純好奇為什么我的ID長(zhǎng)這樣的重度用戶。前兩類能拿到可復(fù)用的判斷方法第三類能把這套東西的邊界搞明白避免被網(wǎng)上那些不靠譜的說(shuō)法帶偏。我先把最容易混淆的一點(diǎn)擺到臺(tái)面上。很多人把 wxid 和微信號(hào)混為一談是因?yàn)樵缙诎姹镜目蛻舳死镞@兩者在某些界面上確實(shí)出現(xiàn)過(guò)混用。但從數(shù)據(jù)模型上看它們是兩個(gè)獨(dú)立字段一個(gè)由系統(tǒng)發(fā)號(hào)器產(chǎn)生、全局唯一、永不變更一個(gè)由用戶申請(qǐng)或系統(tǒng)兜底分配、允許修改、需要唯一性校驗(yàn)。搞清楚這個(gè)區(qū)別后面所有的為什么搜不到為什么換設(shè)備就變了為什么對(duì)不上賬才有解釋的落點(diǎn)。1.1 三類標(biāo)識(shí)符的職責(zé)邊界把賬號(hào)體系里的標(biāo)識(shí)符攤開(kāi)其實(shí)只有三類角色各自負(fù)責(zé)一件事標(biāo)識(shí)類型誰(shuí)能看到是否可變主要用途wxid原始ID系統(tǒng)內(nèi)部 / 部分客戶端數(shù)據(jù)基本不變數(shù)據(jù)庫(kù)主鍵、消息路由、關(guān)系鏈歸屬微信號(hào)自定義ID用戶可見(jiàn)、他人可搜可修改有次數(shù)限制社交展示、被搜索、被添加昵稱所有人可見(jiàn)隨時(shí)可改展示不承擔(dān)唯一性這三者的關(guān)系有點(diǎn)像公司里的員工wxid 是工號(hào)入職就定了一輩子跟著你微信號(hào)是你自己起的花名可以改但得別人沒(méi)占用昵稱是你今天想叫什么都行甚至一天換三次。工號(hào)不會(huì)拿去做對(duì)外名片花名才會(huì)。這就是為什么產(chǎn)品方從來(lái)不在界面上暴露 wxid——它一旦暴露等于把內(nèi)部主鍵泄露出去后續(xù)所有基于這個(gè)主鍵的權(quán)限判斷都會(huì)變得不可控。還有一層容易被忽略wxid 的唯一性范圍是全局的。也就是說(shuō)哪怕兩個(gè)賬號(hào)昵稱一樣、微信號(hào)差一個(gè)字符它們的 wxid 也絕不會(huì)撞。這個(gè)特性決定了它在消息投遞、好友關(guān)系存儲(chǔ)里的地位——關(guān)系表里存的不是你是你的 wxid。所以當(dāng)你換了頭像、改了昵稱、甚至連微信號(hào)都改掉之后好友關(guān)系依然在因?yàn)榈紫聮斓哪歉€沒(méi)動(dòng)。1.2 wxid在賬號(hào)體系里扮演的角色再具體一點(diǎn)。一個(gè)賬號(hào)注冊(cè)完成系統(tǒng)要給它分配三樣?xùn)|西內(nèi)部主鍵、對(duì)外可搜標(biāo)識(shí)、展示名。內(nèi)部主鍵就是 wxid它必須在注冊(cè)那一刻就確定因?yàn)楹罄m(xù)所有的寫操作——發(fā)第一條消息、加第一個(gè)好友、上傳第一張頭像——都要往這條記錄上掛。如果主鍵延遲生成就會(huì)出現(xiàn)賬號(hào)已存在但數(shù)據(jù)掛不上的中間態(tài)這是分布式系統(tǒng)里最不想看到的情況。所以 wxid 的生成時(shí)機(jī)基本可以鎖定在注冊(cè)落庫(kù)的瞬間由服務(wù)端統(tǒng)一發(fā)號(hào)客戶端拿到的只是結(jié)果。這也解釋了一個(gè)現(xiàn)象同一個(gè)人在不同時(shí)期注冊(cè)的兩個(gè)賬號(hào)wxid 的形態(tài)可能不一樣因?yàn)榘l(fā)號(hào)器的實(shí)現(xiàn)隨著版本迭代調(diào)整過(guò)。但同一個(gè)賬號(hào)的 wxid從注冊(cè)那天起就不會(huì)再變哪怕跨設(shè)備、跨系統(tǒng)、跨版本。這就引出一個(gè)實(shí)踐中的判斷依據(jù)如果你在兩份數(shù)據(jù)里看到同一個(gè)人的 wxid 不一樣那基本可以斷定這是兩個(gè)賬號(hào)而不是同一個(gè)人換了ID。這個(gè)結(jié)論在排查為什么好友列表里出現(xiàn)了兩個(gè)重復(fù)的人這類問(wèn)題時(shí)特別管用。2. 拆開(kāi)看字符串wxid_ 前綴后面那一截是什么第一次認(rèn)真看 wxid 的人注意力通常會(huì)被那個(gè)wxid_前綴吸走。這個(gè)下劃線加前綴的組合本質(zhì)上是命名空間標(biāo)記——告訴所有讀這個(gè)字段的程序這一列的值屬于微信ID體系別跟別的ID搞混。這種手法在工程上很常見(jiàn)加個(gè)前綴讓類型自解釋省得在文檔里反復(fù)寫注釋。真正有意思的是前綴后面那一截。以常見(jiàn)的形式為例wxid_后面跟著 12 到 16 位不等的字符字符集大致是a-z、0-9偶爾能看到下劃線。注意這里說(shuō)的是觀察到的形式不是官方規(guī)范——官方從來(lái)沒(méi)公布過(guò)發(fā)號(hào)規(guī)則下面所有的推斷都來(lái)自對(duì)大量樣本的歸納屬于合理推測(cè)而非定論。2.1 前綴與字符集的外部觀察把幾百個(gè)樣本排在一起看能發(fā)現(xiàn)幾個(gè)穩(wěn)定特征。第一前綴wxid_是固定的沒(méi)出現(xiàn)過(guò)變體。第二后綴基本是小寫字母和數(shù)字的混合幾乎不見(jiàn)大寫字母這一點(diǎn)跟很多系統(tǒng)的做法不同——大寫字母在人工抄寫時(shí)容易跟數(shù)字混淆O和0、I和1去掉大寫等于降低了一半的誤讀風(fēng)險(xiǎn)。第三后綴長(zhǎng)度有波動(dòng)早期樣本偏短后來(lái)的樣本偏長(zhǎng)。字符集收斂到什么程度可以直接倒推設(shè)計(jì)意圖。假設(shè)只用小寫字母加數(shù)字可用字符是 36 個(gè)。如果長(zhǎng)度取 12 位總空間是 36 的 12 次方也就是大約 4.7×10^18四千多億億種組合。這個(gè)量級(jí)對(duì)一個(gè)用戶規(guī)模在十億級(jí)的系統(tǒng)來(lái)說(shuō)空間綽綽有余。之所以不把字符集做大是因?yàn)闆](méi)必要用復(fù)雜度換空間——36 個(gè)字符已經(jīng)夠了硬塞大寫和符號(hào)只會(huì)增加出錯(cuò)概率。長(zhǎng)度波動(dòng)的現(xiàn)象也能解釋。一種是發(fā)號(hào)器本身升級(jí)過(guò)新老賬號(hào)的位數(shù)不同另一種是后綴里可能拼了額外的校驗(yàn)位或版本位。我傾向認(rèn)為兩者都有。你在老備份數(shù)據(jù)里翻到的短 wxid大概率是早期注冊(cè)的賬號(hào)新注冊(cè)的賬號(hào)則普遍落在更長(zhǎng)的區(qū)間里。這個(gè)規(guī)律不能當(dāng)鐵律用但做數(shù)據(jù)清洗時(shí)拿它做個(gè)粗略的時(shí)間分層比瞎猜靠譜。2.2 非自增特征說(shuō)明了什么最關(guān)鍵的觀察點(diǎn)在這里wxid 不是遞增的。你把一堆 wxid 按字典序排一遍再按注冊(cè)時(shí)間排一遍兩個(gè)順序?qū)Σ簧?。這意味著它不是一個(gè)全局自增序列而是一個(gè)看起來(lái)隨機(jī)的標(biāo)識(shí)。這個(gè)設(shè)計(jì)選擇背后有非常現(xiàn)實(shí)的理由。假設(shè)用的是自增號(hào)那么任何人拿到一個(gè) wxid就能反推出這個(gè)賬號(hào)的注冊(cè)序號(hào)——注冊(cè)序號(hào)又大致對(duì)應(yīng)注冊(cè)時(shí)間再結(jié)合公開(kāi)信息很容易推算出這個(gè)賬號(hào)是第多少個(gè)用戶平臺(tái)在某年某月的增長(zhǎng)曲線。這種信息泄露在商業(yè)上是很敏感的等于把運(yùn)營(yíng)數(shù)據(jù)半公開(kāi)了。還有更直接的麻煩。自增號(hào)在分布式環(huán)境里沒(méi)法用——多臺(tái)發(fā)號(hào)服務(wù)器同時(shí)發(fā)號(hào)不靠中心化的鎖根本保證不了不重復(fù)。而中心化的鎖在高并發(fā)注冊(cè)場(chǎng)景下就是瓶頸。所以絕大多數(shù)現(xiàn)代系統(tǒng)都會(huì)繞開(kāi)自增改用隨機(jī) 去重校驗(yàn)或者時(shí)間戳 機(jī)器號(hào) 序列號(hào)這類方案。wxid 的形態(tài)更接近前者。2.3 長(zhǎng)度差異與歷史版本關(guān)于長(zhǎng)度差異我做過(guò)一次不太嚴(yán)謹(jǐn)?shù)慕y(tǒng)計(jì)把手上能拿到的樣本按長(zhǎng)度分組短的那批幾乎全部關(guān)聯(lián)到很早的注冊(cè)時(shí)間長(zhǎng)的那批則集中在近幾年。這個(gè)分布很符合發(fā)號(hào)器升級(jí)的假設(shè)——早期用戶量小短 ID 夠用后來(lái)用戶量上去了加上安全要求提高就把位數(shù)拉長(zhǎng)了。但這里有個(gè)坑要提醒。不要拿長(zhǎng)度去做強(qiáng)判斷。有些賬號(hào)的 wxid 后面還帶一段后綴看起來(lái)更長(zhǎng)但那可能是某一層系統(tǒng)追加的標(biāo)記不是原始發(fā)號(hào)的結(jié)果。我在一次數(shù)據(jù)對(duì)賬時(shí)就吃過(guò)這個(gè)虧把帶后綴的當(dāng)成新版賬號(hào)結(jié)果發(fā)現(xiàn)那批全是同一個(gè)渠道導(dǎo)入的跟注冊(cè)時(shí)間沒(méi)有半點(diǎn)關(guān)系。后來(lái)改成看整體字符分布才對(duì)上。判斷一個(gè) wxid 屬于哪個(gè)時(shí)期靠長(zhǎng)度不如靠它出現(xiàn)在哪份數(shù)據(jù)里。數(shù)據(jù)庫(kù)導(dǎo)出、客戶端緩存、第三方系統(tǒng)的冗余字段這三個(gè)來(lái)源的可信度是遞減的。真要溯源應(yīng)該先確認(rèn)字段來(lái)源再談其他。3. 生成邏輯的合理推斷為什么不能用遞增號(hào)這一段是我最想展開(kāi)的部分因?yàn)樗芙忉尯芏嗫此颇涿畹脑O(shè)計(jì)。前面說(shuō)了 wxid 不是自增那它到底是什么在沒(méi)有官方文檔的前提下我們只能從工程約束倒推。而工程約束這種東西其實(shí)是有標(biāo)準(zhǔn)答案的——不管你做什么產(chǎn)品只要用戶量上了規(guī)模發(fā)號(hào)器的可選方案就那么幾種每種方案的優(yōu)缺點(diǎn)都是公開(kāi)的。3.1 分布式發(fā)號(hào)器的三個(gè)死穴先說(shuō)說(shuō)為什么自增這條路走不通。很多人直覺(jué)上覺(jué)得自增最簡(jiǎn)單來(lái)一個(gè)注冊(cè)計(jì)數(shù)器加一完事。單機(jī)場(chǎng)景下確實(shí)如此但放到真實(shí)系統(tǒng)里立刻暴露三個(gè)問(wèn)題。第一個(gè)是并發(fā)。注冊(cè)請(qǐng)求不是排隊(duì)來(lái)的同一毫秒可能有幾千個(gè)請(qǐng)求打到不同機(jī)器上。要讓它們共享一個(gè)計(jì)數(shù)器就必須有一把全局鎖。鎖一加吞吐量直接掉一個(gè)數(shù)量級(jí)而且鎖本身會(huì)成為單點(diǎn)——它掛了整個(gè)注冊(cè)鏈路就停了。這個(gè)代價(jià)沒(méi)人愿意付。第二個(gè)是可預(yù)測(cè)。自增號(hào)的信息量太大了。拿到1000001和1000002你就知道這倆是連著注冊(cè)的。這在風(fēng)控上是個(gè)大漏洞批量注冊(cè)的人只要觀察自己拿到的號(hào)就能推算出平臺(tái)的注冊(cè)速率進(jìn)而判斷什么時(shí)候下手不容易被檢測(cè)。反過(guò)來(lái)說(shuō)隨機(jī)化的 ID 讓這種推算失效。第三個(gè)是遷移成本。自增號(hào)一旦分庫(kù)分表跨庫(kù)的唯一性就崩了。你得引入額外的號(hào)段分配機(jī)制復(fù)雜度立刻上去了。而隨機(jī) ID 天然支持水平擴(kuò)展多加一臺(tái)發(fā)號(hào)機(jī)只是多一個(gè)隨機(jī)源不影響全局唯一性。三個(gè)死穴疊在一起結(jié)論就很清楚了規(guī)?;到y(tǒng)不會(huì)用自增做主鍵。這不是偏好問(wèn)題是能不能跑起來(lái)的問(wèn)題。3.2 碰撞概率與隨機(jī)源那隨機(jī) ID 怎么保證不重復(fù)答案是空間足夠大 落庫(kù)時(shí)校驗(yàn)。這里可以算一筆賬看看空間到底要多大才夠。假設(shè)字符集 36 個(gè)小寫字母 數(shù)字長(zhǎng)度 12 位總空間約 4.7×10^18。用戶規(guī)模按 10 億算用生日問(wèn)題估算碰撞概率公式是n2 / (2N)代入得(10^9)2 / (2 × 4.7×10^18)約等于1.06×10^-4也就是萬(wàn)分之一點(diǎn)零六。長(zhǎng)度字符集總空間10億用戶下的碰撞概率8 位362.8×10^12約 17.8%太高10 位363.7×10^15約 0.0135%12 位364.7×10^18約 0.000106%14 位366.1×10^21約 8×10^-8%這張表說(shuō)明一件事12 位是一個(gè)性價(jià)比很高的位置。再短碰撞概率就進(jìn)入每年都要處理幾萬(wàn)次重號(hào)的區(qū)間雖然可以靠重試解決但重試本身也是成本再長(zhǎng)收益遞減用戶看到的字符串變長(zhǎng)抄寫和存儲(chǔ)成本上升。實(shí)際系統(tǒng)里還會(huì)加一層落庫(kù)時(shí)唯一索引校驗(yàn) 沖突重試所以哪怕碰撞概率再高一個(gè)數(shù)量級(jí)也能兜住。這里順帶說(shuō)一個(gè)常見(jiàn)的誤解。有人覺(jué)得隨機(jī)生成就一定會(huì)有重復(fù)所以必須用某種確定性算法。實(shí)際上現(xiàn)代系統(tǒng)普遍采用高熵隨機(jī)源 唯一性校驗(yàn)的組合隨機(jī)源保證不可預(yù)測(cè)唯一索引保證不出錯(cuò)。這兩件事不矛盾一個(gè)是概率層面一個(gè)是確定層面。3.3 生成時(shí)機(jī)與生命周期生成時(shí)機(jī)這件事從產(chǎn)品行為上能反推出一些線索。比如兩個(gè)賬號(hào)在同一秒注冊(cè)wxid 的字符分布沒(méi)有明顯相關(guān)性——說(shuō)明發(fā)號(hào)彼此獨(dú)立沒(méi)有共享狀態(tài)。再比如同一個(gè)賬號(hào)在換設(shè)備登錄后 wxid 不變——說(shuō)明它不是設(shè)備指紋派生的是賬號(hào)級(jí)的。生命周期上wxid 基本是一次生成終身不變。注銷之后重新注冊(cè)拿到的是全新的 wxid跟舊賬號(hào)沒(méi)有任何關(guān)聯(lián)。這個(gè)設(shè)計(jì)有它的道理如果允許復(fù)用那么舊的聊天記錄、舊的好友關(guān)系、舊的舉報(bào)記錄都會(huì)掛到新用戶頭上出了事根本說(shuō)不清。不復(fù)用是底線不是優(yōu)化。我在處理一批注銷賬號(hào)的歷史數(shù)據(jù)時(shí)驗(yàn)證過(guò)這一點(diǎn)。那批數(shù)據(jù)里有些 wxid 對(duì)應(yīng)的賬號(hào)已經(jīng)不存在了但消息表里還留著指向它們的引用。當(dāng)時(shí)第一反應(yīng)是數(shù)據(jù)臟了后來(lái)才想明白消息是歷史事實(shí)賬號(hào)注銷不等于歷史記錄該被抹掉。wxid 作為那根不動(dòng)的錨正好承擔(dān)了這個(gè)職責(zé)。4. 搜索限制的底層原因?yàn)槭裁催@個(gè)ID搜不到人回到最開(kāi)始那個(gè)場(chǎng)景。你在搜索框里輸入一長(zhǎng)串wxid_開(kāi)頭的東西點(diǎn)了搜索結(jié)果什么都沒(méi)有。這不是bug是設(shè)計(jì)。要理解這個(gè)設(shè)計(jì)得先明白搜索這個(gè)動(dòng)作在產(chǎn)品里意味著什么。搜索是一個(gè)把輸入映射到賬號(hào)的過(guò)程映射關(guān)系越寬能搜到的人越多同時(shí)被騷擾、被撞庫(kù)、被惡意定位的風(fēng)險(xiǎn)也越大。所以任何產(chǎn)品在設(shè)計(jì)搜索入口時(shí)都要在好用和安全之間劃一條線。這條線畫在哪里決定了哪些字段可以被搜。4.1 可搜索字段是被刻意收窄的從產(chǎn)品行為上看能被搜到的維度就那么幾個(gè)微信號(hào)、綁定的手機(jī)號(hào)、綁定的其他社交賬號(hào)、群內(nèi)昵稱。這幾個(gè)維度的共同點(diǎn)是——它們要么是用戶主動(dòng)設(shè)置的要么是用戶主動(dòng)綁定的而且都帶有某種我愿意被找到的默示同意。微信號(hào)是你自己起的手機(jī)號(hào)是你自己綁的這些都能理解為一種授權(quán)。wxid 完全不符合這個(gè)特征。它是系統(tǒng)分配的用戶既不知道它的存在也無(wú)法控制它。拿一個(gè)用戶不知情的標(biāo)識(shí)去做搜索入口等于繞過(guò)了用戶的知情權(quán)。這不是技術(shù)做不到是產(chǎn)品倫理上不該做。還有一層更實(shí)際的原因。如果 wxid 可搜那么所有拿到過(guò) wxid 的第三方——數(shù)據(jù)合作方、歷史導(dǎo)出、日志殘留——都能反查到賬號(hào)。這意味著一個(gè)泄露點(diǎn)就能擴(kuò)散成全網(wǎng)可查。收窄搜索面本質(zhì)上是在切斷這條擴(kuò)散鏈路。4.2 如果放開(kāi)會(huì)怎樣三個(gè)角度推演不妨做個(gè)思想實(shí)驗(yàn)。假設(shè)某天產(chǎn)品方突然開(kāi)放 wxid 搜索會(huì)發(fā)生什么。從騷擾成本角度看騷擾者只需要拿到一批 wxid就能批量嘗試添加而用戶完全無(wú)從防御——你連自己的 wxid 是什么都不知道怎么設(shè)置不允許通過(guò)此方式找到我現(xiàn)有的一堆隱私開(kāi)關(guān)全部失效。從數(shù)據(jù)黑產(chǎn)角度看wxid 在各類導(dǎo)出數(shù)據(jù)、緩存文件、第三方SDK日志里出現(xiàn)的頻率遠(yuǎn)高于微信號(hào)。一旦可搜這些零散數(shù)據(jù)立刻變成可用的賬號(hào)字典價(jià)值翻好幾倍。從誤傷角度看wxid 是一串沒(méi)有語(yǔ)義的字符用戶搜的時(shí)候極易打錯(cuò)一位。打錯(cuò)一位就落到另一個(gè)真實(shí)賬號(hào)上這種事多了會(huì)變成客訴災(zāi)難。三個(gè)角度指向同一個(gè)結(jié)論不開(kāi)這個(gè)口子收益遠(yuǎn)大于損失。所以搜索限制不是暫時(shí)沒(méi)做而是不打算做。4.3 拿到wxid也加不上好友這件事這一條很多人沒(méi)意識(shí)到。就算你從某份數(shù)據(jù)里拿到了某人的 wxid你也加不上他。原因很簡(jiǎn)單添加好友的接口接受的參數(shù)是可搜標(biāo)識(shí)不是內(nèi)部主鍵。你手里的 wxid 在接口層面根本不是合法輸入。網(wǎng)上流傳的一些所謂用 wxid 加好友的方法我建議一律不要信也不要試。原因有兩方面。一是技術(shù)上那些方法大多依賴對(duì)客戶端行為的非正常干預(yù)隨時(shí)會(huì)失效而且極可能觸發(fā)風(fēng)控把自己的賬號(hào)搭進(jìn)去。二是合規(guī)上繞過(guò)正常的產(chǎn)品路徑去觸達(dá)他人本身就踩在邊界線上。你手里的 wxid 唯一能證明的事情是這個(gè)賬號(hào)存在過(guò)僅此而已。我在實(shí)際工作中遇到過(guò)有人拿著 wxid 來(lái)找我對(duì)賬問(wèn)這個(gè)人是誰(shuí)。我的標(biāo)準(zhǔn)回答是查不到也不該查。真要定位一個(gè)賬號(hào)走正規(guī)路徑——讓對(duì)方提供微信號(hào)或者掃碼比在數(shù)據(jù)里翻找高效得多也安全得多。5. 開(kāi)發(fā)者容易混淆的地方wxid、openid、unionid如果你的工作涉及公眾號(hào)、小程序或者任何開(kāi)放平臺(tái)的能力對(duì)接那你一定見(jiàn)過(guò) openid 和 unionid。這兩個(gè)東西跟 wxid 經(jīng)常被混著叫但它們完全是不同層次的東西。混用的后果很實(shí)際——對(duì)賬對(duì)不上、用戶身份串號(hào)、遷移后數(shù)據(jù)全亂。5.1 三條ID鏈路各自的用途我把三者的定位擺成一張表看起來(lái)最清楚標(biāo)識(shí)作用域是否跨應(yīng)用典型用途wxid整個(gè)賬號(hào)體系是內(nèi)部主鍵、關(guān)系鏈、消息路由openid單個(gè)應(yīng)用公眾號(hào)/小程序否應(yīng)用內(nèi)識(shí)別用戶、發(fā)消息、埋點(diǎn)unionid同一主體下的多個(gè)應(yīng)用是限同主體跨應(yīng)用打通同一用戶關(guān)鍵差異在于作用域。同一個(gè)用戶在 A 小程序里的 openid 和 B 小程序里的 openid 是兩個(gè)不同的值哪怕這兩個(gè)小程序?qū)儆谕患夜?。要讓兩個(gè)應(yīng)用認(rèn)出這是同一個(gè)人就得靠 unionid。而 wxid 的層級(jí)比這兩個(gè)都高它是賬號(hào)本身openid 和 unionid 都是在這個(gè)賬號(hào)之上派生出來(lái)的應(yīng)用層視圖。打個(gè)比方。wxid 是身份證號(hào)openid 是你在某家銀行的客戶號(hào)unionid 是同一家銀行集團(tuán)下各子公司的統(tǒng)一客戶號(hào)。你在不同銀行有不同的客戶號(hào)但身份證號(hào)只有一個(gè)。銀行之間不會(huì)拿身份證號(hào)互相對(duì)賬它們用的是自己的客戶號(hào)體系。5.2 數(shù)據(jù)遷移時(shí)踩過(guò)的坑這個(gè)坑我印象很深。有次做數(shù)據(jù)遷移源庫(kù)里有一列叫user_id遷移腳本直接當(dāng)成 wxid 處理了。跑完之后發(fā)現(xiàn)用戶頭像全串了——A 的頭像掛到了 B 頭上。排查了半天才定位到問(wèn)題源庫(kù)那列存的其實(shí)是 openid不是 wxid。為什么會(huì)這樣因?yàn)閮蓚€(gè)字段在形態(tài)上有相似之處都是長(zhǎng)字符串都沒(méi)有明顯語(yǔ)義。寫腳本的人看到列名user_id想當(dāng)然認(rèn)為是主鍵。這就是典型的字段名騙人。后來(lái)我總結(jié)了一條硬規(guī)矩任何遷移腳本在處理 ID 類字段之前必須先做形態(tài)抽樣。具體做法是從源庫(kù)隨機(jī)取 100 行把值打印出來(lái)看前綴。wxid_開(kāi)頭的一眼就能認(rèn)出來(lái)o開(kāi)頭的多半是 openid純數(shù)字或者 UUID 形態(tài)的又是另一回事。這一步花不了五分鐘能省掉后面幾天的返工。還有個(gè)更隱蔽的坑某些第三方系統(tǒng)會(huì)把 wxid 和 openid 拼在一起存用分隔符隔開(kāi)。這種字段做字符串匹配時(shí)特別容易出問(wèn)題因?yàn)榉指舴旧硪部赡苁?ID 字符集的一部分。穩(wěn)妥做法是先按分隔符切分再對(duì)每一段做形態(tài)校驗(yàn)別偷懶直接LIKE。6. 老設(shè)備、老備份里那些殘留的wxid聊完原理說(shuō)點(diǎn)更貼近日常的。很多人接觸到 wxid 不是在開(kāi)發(fā)場(chǎng)景而是在整理自己的舊手機(jī)備份、導(dǎo)聊天記錄、或者換機(jī)遷移的時(shí)候。這些場(chǎng)景下冒出來(lái)的 wxid處理起來(lái)有一套自己的門道。6.1 遷移場(chǎng)景下的標(biāo)識(shí)錯(cuò)位換機(jī)遷移的時(shí)候最常遇到的現(xiàn)象是好友列表里出現(xiàn)了不認(rèn)識(shí)的賬號(hào)。點(diǎn)進(jìn)去看昵稱是空的頭像是默認(rèn)的唯一能看到的標(biāo)識(shí)就是一串wxid_開(kāi)頭的東西。這種情況多半是歷史關(guān)系殘留。你曾經(jīng)加過(guò)某個(gè)人后來(lái)對(duì)方注銷或者改了資料關(guān)系記錄還在但展示信息已經(jīng)取不到了。系統(tǒng)只能把內(nèi)部標(biāo)識(shí)拿出來(lái)頂上于是你看到了一串亂碼一樣的字符。它不是新出現(xiàn)的東西是一直在只是以前被昵稱和頭像蓋住了。還有一種情況是群成員列表里的僵尸賬號(hào)。群還在但某些成員早已不活躍展示信息為空只留下 wxid。這類記錄不建議手動(dòng)清理——你沒(méi)法確認(rèn)對(duì)方是不是只是長(zhǎng)期未登錄。等系統(tǒng)自己處理比動(dòng)手強(qiáng)。6.2 幾個(gè)流傳很廣的錯(cuò)誤認(rèn)知最后清理幾個(gè)流傳很廣的說(shuō)法這些都是我在實(shí)際交流中被問(wèn)到過(guò)多次的。說(shuō)法一wxid 可以改。不行。它由系統(tǒng)發(fā)號(hào)用戶側(cè)沒(méi)有任何修改入口。你看到的改ID操作的永遠(yuǎn)是微信號(hào)那一欄。說(shuō)法二wxid 里藏著注冊(cè)時(shí)間。從形態(tài)上看不出時(shí)間信息。它更像純隨機(jī)串沒(méi)有可解析的時(shí)間戳段。有人聲稱能解碼基本都是事后附會(huì)。說(shuō)法三wxid 可以用來(lái)找回賬號(hào)。不行。賬號(hào)找回走的是手機(jī)號(hào)、郵箱、或者好友輔助驗(yàn)證這幾條路徑wxid 不在其中。你要是只記得 wxid那基本等于什么都沒(méi)記住。說(shuō)法四不同人的 wxid 長(zhǎng)度一樣就說(shuō)明是同一批注冊(cè)的。前面說(shuō)過(guò)長(zhǎng)度受發(fā)號(hào)器版本影響不能反推注冊(cè)批次。這個(gè)推論在數(shù)據(jù)量小的時(shí)候尤其不可靠。我個(gè)人在實(shí)際處理這類數(shù)據(jù)時(shí)的習(xí)慣是把 wxid 當(dāng)成一個(gè)不可解讀的、只用于精確匹配的字符串。不做形態(tài)推斷不做前綴之外的任何解析不在它身上附加業(yè)務(wù)含義。需要業(yè)務(wù)信息的時(shí)候去關(guān)聯(lián)其他字段別指望從這串字符里挖出什么。這個(gè)態(tài)度看起來(lái)保守但能避免絕大多數(shù)誤判。順便提一個(gè)實(shí)用的小技巧。如果你需要在本地?cái)?shù)據(jù)里區(qū)分這是賬號(hào)主鍵還是這是應(yīng)用層ID最省事的辦法是建一個(gè)字段形態(tài)白名單前綴wxid_歸主鍵前綴o且長(zhǎng)度在 28 左右的歸 openid純 32 位十六進(jìn)制的歸另一類。這個(gè)規(guī)則不完美但覆蓋八成場(chǎng)景足夠剩下的靠人工抽樣兜底。規(guī)則寫完記得寫注釋說(shuō)明這是經(jīng)驗(yàn)規(guī)則不是規(guī)范免得后來(lái)的人當(dāng)成標(biāo)準(zhǔn)照抄。這套東西說(shuō)到底就是個(gè)分工問(wèn)題誰(shuí)負(fù)責(zé)唯一性誰(shuí)負(fù)責(zé)可搜誰(shuí)負(fù)責(zé)展示。分清楚了那些看起來(lái)莫名其妙的限制就都有了合理的解釋。真要說(shuō)有什么體會(huì)就是別跟系統(tǒng)的主鍵較勁——它不是給你用的你也就沒(méi)必要想辦法去用它。