《PostgreSQL在阿里的應(yīng)用》要點(diǎn):
本文介紹了PostgreSQL在阿里的應(yīng)用,希望對(duì)您有用。如果有疑問,可以聯(lián)系我們。
相關(guān)主題:PostgreSQL教程
周正中(digoal)
PostgreSQL中國社區(qū)發(fā)起人之一、杭州分會(huì)會(huì)長,PostgreSQL中國社區(qū)大學(xué)發(fā)起人之一,10余項(xiàng)數(shù)據(jù)庫相關(guān)專利.
就職于阿里云,ApsaraDB內(nèi)核組.
ostgreSQL這幾年的發(fā)展非常迅猛,在國內(nèi)掀起了一波PostgreSQL的熱潮,但運(yùn)維人才還是比較緊缺,所以在一些公司沒有大面積鋪開,不過不用擔(dān)心,很多云廠商都提供了PostgreSQL的數(shù)據(jù)庫服務(wù).
阿里云的RDSPostgreSQL除了提供公有云服務(wù),同時(shí)也對(duì)阿里巴巴集團(tuán)提供內(nèi)部的服務(wù).接下來我會(huì)分享幾個(gè)在阿里巴巴內(nèi)部使用PostgreSQL的一些場(chǎng)景.大家可以想想思考一下,如果用其他數(shù)據(jù)庫和技術(shù)手段怎么解決這些問題.
- 海量導(dǎo)購文實(shí)時(shí)去重
- 精準(zhǔn)廣告投放
- TOB 實(shí)時(shí)畫像
- 任意字段組合
- 任意字段模糊匹配
首先是海量導(dǎo)購文實(shí)時(shí)去重.在日常生活中特別是妹子很喜歡看導(dǎo)購的推送消息(比如每日白菜價(jià));特別是家庭主婦,在家里沒事就瀏覽白菜價(jià),如果家里有小孩的,小孩才一兩個(gè)月,已經(jīng)買到十幾歲的衣服了,這和導(dǎo)購?fù)扑陀忻芮嘘P(guān)系.
這么多的導(dǎo)購文章,每一篇文章都會(huì)推很多的商品,如果你是一個(gè)用戶每天翻看這些文章都是一樣的商品,是很令人討厭的.
所以在導(dǎo)購平臺(tái)就有一個(gè)很重要的工作,得過濾這些別人已經(jīng)推薦過的商品,別人推薦過的商品你就不要再推薦了.
比如說每日白菜精選的文章里可能會(huì)涉及到幾十個(gè)商品,整個(gè)導(dǎo)購平臺(tái)可能會(huì)沉積上億的文章,如果平均五十個(gè)商品的話,就會(huì)有五十億個(gè)商品.當(dāng)然里面有重疊,一篇文章里跟另外一篇文章可能有一兩個(gè)重疊,這是沒有關(guān)系的,但是你不能80%以上都重疊,這個(gè)重疊比例是需要可以設(shè)定的.
因?yàn)榈昙視?huì)去做推廣,就會(huì)有傭金,網(wǎng)絡(luò)寫手會(huì)為了傭金去寫導(dǎo)購文章.但是為了防止出現(xiàn)盜文現(xiàn)象,就需要審核導(dǎo)購文章,最原始的做法是什么樣的?比如我剛新建了這樣一個(gè)導(dǎo)購平臺(tái),用戶數(shù)也不是特別多,這時(shí)候請(qǐng)一些較為廉價(jià)的勞動(dòng)力來幫你解決審核的問題,最早期的為勞動(dòng)力密集型.
發(fā)展到第二代,用計(jì)算機(jī)幫你做這個(gè)事情,新提交一個(gè)文章的時(shí)候,要在上億的文章里去辨別跟我剛剛提交的商品的重復(fù)率是什么樣的,涉及的運(yùn)算量非常大,因此并不能做到實(shí)時(shí)的審核,通常是隔天的.
對(duì)于導(dǎo)購文章的編輯來說,這個(gè)效率是低下的,網(wǎng)絡(luò)寫手提交文章后,第二天才能告知有沒有通過,才能發(fā)到網(wǎng)站上面,這樣可能就錯(cuò)過了商家的營銷時(shí)機(jī).
回到原始的數(shù)據(jù)去看,每一篇導(dǎo)購文章里面涉及到50個(gè)商品,按平均數(shù)來,商品使用一個(gè)數(shù)組來存儲(chǔ),這里面的每個(gè)數(shù)值對(duì)應(yīng)的都是商品的 ID,每個(gè)記錄會(huì)涉及到大概 50 個(gè)這樣的值.
當(dāng)用戶提交一個(gè)新的導(dǎo)購文章來的時(shí)候,我們看到又有一堆的值進(jìn)來,怎么做呢?
我們需要去比對(duì)庫里的每一條記錄,看他們重疊的元素有多少個(gè),比如說新上的文章推薦了十個(gè)商品,與歷史導(dǎo)購文章中某記錄重疊的有八個(gè)商品 ID,意味著你新上傳的文章有 80% 跟其中的某一篇文章的商品是重疊的,審核結(jié)果是拒絕.
在 PostgreSQL 里面有一個(gè)什么技術(shù)能夠幫你高效實(shí)現(xiàn)應(yīng)用場(chǎng)景呢?我們用了 GIN 索引,它就是一個(gè)倒排索引,在你的一條記錄里,可以對(duì)數(shù)組去建索引,一個(gè)數(shù)組有50個(gè)商品,第一列是一個(gè)行號(hào),解開之后一個(gè)行號(hào)對(duì)應(yīng)這么多值,倒轉(zhuǎn)一下會(huì)變成這個(gè)值對(duì)應(yīng)的行號(hào)是什么,就是幫你做了翻轉(zhuǎn).
有什么用呢?做完了翻轉(zhuǎn),用戶上傳了一篇新的導(dǎo)購文章,里面涉及到假設(shè)商品的 ID,我們看最左邊的一列,1,3,101,198,上傳這些商品 ID 上來,在索引上怎么搜索,比如說對(duì)1這個(gè) ID,因?yàn)樗行刑?hào),里面對(duì)應(yīng)的是數(shù)據(jù)塊的 ID 加上這條記錄在數(shù)據(jù)塊的偏移,對(duì)應(yīng)的號(hào)拿出來之后,在第一個(gè)數(shù)據(jù)塊出現(xiàn)過,通過索引搜索出來,在第101個(gè)數(shù)據(jù)塊里也出現(xiàn)過,數(shù)據(jù)庫會(huì)幫你把每個(gè)數(shù)據(jù)塊里的命中商品總數(shù)給記錄下來,就變成了最下面的一行記錄,213 422,什么意思呢?代表是在101這個(gè)數(shù)據(jù)塊里有命中四個(gè)商品ID.
比如設(shè)置了重復(fù)數(shù)>4,通過索引可以直接拒絕發(fā)布.如果設(shè)置為>=3那么只要提取出第49號(hào)數(shù)據(jù)塊和第101的數(shù)據(jù)塊的數(shù)據(jù).進(jìn)入第二道工序.
剛才講的是做的第一層過濾,第二層過濾是幫你定位到兩個(gè)數(shù)據(jù)塊了,然后你去每一條檢查,因?yàn)槊總€(gè)數(shù)據(jù)塊涉及的記錄也就是幾百條,整個(gè)下來效率就非常高.
根據(jù)真實(shí)數(shù)據(jù)的特征,構(gòu)建一批仿真數(shù)據(jù).
被推薦的商品數(shù)總量:超過1千萬,每一篇導(dǎo)購文章平均下來涉及的商品,從歷史數(shù)據(jù)來看是11到50個(gè)商品,一篇文章會(huì)推11到50個(gè)商品給你.
熱點(diǎn)商品:是說非常大的店鋪給的傭金非常高,很多人愿意去寫文章推薦這種商品,這是熱點(diǎn)商品.
在一千萬的商品里占到了2%的樣子,我們來看熱點(diǎn)商品被推薦的次數(shù),比如說在整個(gè)6千萬的推薦文章里面,有一千萬的文章是推薦熱點(diǎn)商品,接下來看一下效果.
這些測(cè)試對(duì)應(yīng)的就是實(shí)際的應(yīng)用場(chǎng)景,一篇新的文章上來之后,多快的時(shí)間能夠告訴你有沒有人跟你重疊,如果你是普通商品的話,過濾39個(gè)跟你重復(fù)的,我就把文章替掉,不讓你發(fā)布了.
如果一篇文章里推的都是熱點(diǎn)商品,因?yàn)楸煌扑]的次數(shù)多,記錄所涉及的數(shù)據(jù)塊更多,因此一級(jí)過濾出來的數(shù)據(jù)塊比較多,二級(jí)過濾做的就比較多,但是也能夠達(dá)到15個(gè)毫秒.
吞吐量達(dá)到1萬,相比以往隔天要告訴你能夠?qū)徍送ㄟ^,現(xiàn)在可以做到實(shí)時(shí)響應(yīng).
這是 PostgreSQL 在阿里的導(dǎo)購平臺(tái)的應(yīng)用,因?yàn)槭褂闷渌募夹g(shù)根本沒法解決這個(gè)問題,因?yàn)槲覀冊(cè)诎⒗镉幸粋€(gè) ATA 的技術(shù)論壇發(fā)帖子,剛好他們看到這個(gè)技術(shù),找到我們團(tuán)隊(duì),去給他們做了這個(gè)方案并上線.
第二個(gè)應(yīng)用場(chǎng)景是精準(zhǔn)的廣告投放,這個(gè)對(duì)應(yīng)的是廣告營銷的產(chǎn)品,數(shù)量級(jí)也是龐大的.我們看這個(gè)場(chǎng)景的介紹,在你使用產(chǎn)品的時(shí)候一些瀏覽行為,比如你瀏覽了哪些店鋪、購買了哪些商品,這些在數(shù)據(jù)平臺(tái)里都是有跟蹤記錄的,比如說每個(gè)人瀏覽了哪些店鋪,瀏覽了多少次,瀏覽了哪些商品,這些數(shù)據(jù)就可以用來做精準(zhǔn)的廣告投放.
什么意思呢?比如說最近經(jīng)常瀏覽化妝品或者所關(guān)注的商品,你在購買前可能會(huì)一直瀏覽,根據(jù)這些行為,可以圈出一些最近都在關(guān)注化妝品的人群,賣化妝品的商家就可以定向推送運(yùn)營的活動(dòng),把消息告訴這些人,因?yàn)檫@些人可能最近就要買了.
對(duì)于一個(gè)營銷系統(tǒng)來說,它的非常重要的指標(biāo),一個(gè)是精準(zhǔn)性,另外是實(shí)時(shí)性,如果你今天瀏覽完之后可能下單了,第二天再告訴你沒有任何意義,就不會(huì)再給你重復(fù)推薦了.因此這兩個(gè)重要的因素一結(jié)合,我們看PostgreSQL在里面怎么幫助平臺(tái)達(dá)到效果.
首先看數(shù)據(jù)結(jié)構(gòu),一個(gè)是用戶ID,然后是店鋪軌跡,你瀏覽這個(gè)店鋪多少次,瀏覽這個(gè)商品多少次,最后你買了這個(gè)商品多少數(shù)量,會(huì)有一些這樣的軌跡,然后會(huì)有地理位置,基于地理位置的推薦,也會(huì)存位置信息.根據(jù)這些,我們可以根據(jù)時(shí)間區(qū)間、位置、瀏覽的店鋪、瀏覽的商品等條件,圈選人群.
再看數(shù)據(jù)量,整個(gè)量級(jí)是百億級(jí)別,商鋪是億級(jí)別,單個(gè)用戶軌跡平均瀏覽一千個(gè)用戶,還有瀏覽的商品量級(jí)以及購買的商品量級(jí),基本上是在千這個(gè)級(jí)別,當(dāng)然這個(gè)級(jí)別估得比較高,設(shè)計(jì)時(shí)需要考慮未來的體量.
業(yè)務(wù)需求剛才已經(jīng)講過,根據(jù)某個(gè)商品,比如瀏覽某個(gè)商品超過多少次的人群,某個(gè)區(qū)域?yàn)g覽某些商品超過多少次的人群,相當(dāng)于是精準(zhǔn)圈人的意思.我們?cè)趺磳?duì)這個(gè)場(chǎng)景做設(shè)計(jì)?
首先是數(shù)字,瀏覽了多少次商品,是個(gè)精準(zhǔn)的數(shù)字,如果說這些完完全全精準(zhǔn)的存在里面的話,不利于后期的優(yōu)化.
所以我們首先會(huì)做一個(gè)階梯化,這是汽車的變速箱,比如說10AT的汽車,對(duì)于精準(zhǔn)營銷來說可能十檔不夠,要做得精準(zhǔn),我就要投放超過瀏覽次數(shù)100次的.
這是檔位階梯化,對(duì)行為軌跡精準(zhǔn)次數(shù)做范圍,根據(jù)不同范圍定階梯,定完階梯,我的每個(gè)用戶 ID 加上對(duì)應(yīng)的軌跡,相當(dāng)于是我給你撥一個(gè)檔位一樣,比如六檔,說明你這個(gè)用戶是落在六檔這個(gè)范圍,這樣做之后就可以把左上角的這個(gè)值,這是原來的值,代表這個(gè)用戶瀏覽了這個(gè)用戶98次,我把它階梯化之后就變成 711 767 740,表示它是1檔,拼成一個(gè)值,把這個(gè)東西加上去.
圖中所示公式為轉(zhuǎn)換公式,怎么把老的值轉(zhuǎn)成新的值,原來我用兩個(gè)新的元素表示的數(shù)字,變成一個(gè)數(shù)字來表示.然后我就可以對(duì)軌跡建倒排索引,這個(gè)索引建完之后,就可以實(shí)現(xiàn)定向圈人.比如說瀏覽的商品包含什么,比如包含某一個(gè)商品的 ID 加檔位數(shù),這是對(duì)數(shù)組的操作.
瀏覽了十個(gè)店家任意一家超過多少次的,我就用 overlap 的做法做.
當(dāng)用戶量是 3.2 億加上 64 個(gè)分區(qū)數(shù),標(biāo)簽總量是 400 萬,個(gè)人平均標(biāo)簽數(shù)是4千個(gè),在一毫秒就可以挖出一萬多的人群出來,實(shí)現(xiàn)了精準(zhǔn)的實(shí)時(shí)營銷.從量級(jí)來算,如果不用數(shù)組類型的話要存多少條記錄?
每個(gè)人都有四千個(gè)標(biāo)簽,如果不用數(shù)組的話,每一個(gè)就是一條記錄,這樣乘3.2億,相當(dāng)于是1.2萬億的數(shù)字,而使用一臺(tái)主機(jī)就可以解決.使用 PostgreSQL 的數(shù)組和GIN索引,巧妙的解決了業(yè)務(wù)的問題.
我們?cè)倏?TOB 實(shí)時(shí)畫像,這是阿里云對(duì)外的一個(gè)項(xiàng)目,TOB 的實(shí)時(shí)畫像的業(yè)務(wù).
跟前面一個(gè)例子項(xiàng)目非常類似,差別就在于 TAG 數(shù)不一樣.前面講的例子TAG數(shù)有400萬個(gè),在這個(gè)系統(tǒng)里面是1萬個(gè) TAG 來描述 TOB 的用戶,就好像我給你看相一樣,貼一萬個(gè) TAG,基本上把特征描述得清楚了.
當(dāng)初設(shè)計(jì)1萬個(gè) TAG,基本上一千多個(gè)列已經(jīng)是極限了,因?yàn)橐粭l記錄是不能跨數(shù)據(jù)塊的,所以對(duì)于這種超過兩千個(gè)字段的表,要么就是拆表,根據(jù)ID去聯(lián)合起來.
因此一張表肯定是搞不定的,搜索的條件是按照包含哪些 TAG 并且不包含哪些 TAG,這種比較類似于挖掘型的操作,原來使用了8臺(tái)物理機(jī),1億個(gè)用戶,1萬臺(tái) TAG 去解決這個(gè)問題.
思考一下將來的設(shè)計(jì),這個(gè)用戶如果只是換一個(gè)平臺(tái)可能沒什么興趣,他是說將來要用戶量級(jí)再漲一倍,同時(shí)希望壓縮成本,因?yàn)橛昧撕芏喑杀?
TAG 數(shù)有1萬,每個(gè) TAG 一列,如果存一張表肯定是搞不定的,用戶規(guī)模加 TAG 是一萬億 user tags,貼標(biāo)簽、刪除標(biāo)簽、更新標(biāo)簽都要求分鐘級(jí)的延遲.
由于是 TOB 的系統(tǒng),查詢的并發(fā)要求200到300,用戶根據(jù) TAG 字段查出包含、不包含、或者多個(gè)字段的組合.最后就是響應(yīng)時(shí)間的要求,這種查詢響應(yīng)時(shí)間要毫秒級(jí).
我們來想怎么設(shè)計(jì)這個(gè)表結(jié)構(gòu),因?yàn)橐粡埍硎谴娌幌乱蝗f個(gè)字段的,如果每個(gè) TAG 一個(gè)字段的話,你要寫很多的 and 跟 or,基本上做不到毫秒,80臺(tái)機(jī)器也做不到,這就是問題.是不是每個(gè)字段都要建索引,第二是超寬表怎么解決,這都是場(chǎng)景要面臨的問題.
我們來看解決方案的優(yōu)缺點(diǎn).首先是用數(shù)組存,一個(gè)數(shù)組最大長度是1個(gè)G,如果我用 int4 做標(biāo)簽的話可以存 2.6 億個(gè) TAG,然后還可以進(jìn)行分區(qū).
query 的寫法也簡單,原來要包含某一些 TAG 的數(shù)組出來,包含兩個(gè)的就可以.指定 TAG 之一的話就是看有沒有相交,最后是不包含,是沒有辦法支持索引的,這是方案一.
再看方案二,把 TAG 變成 BTI,但是有個(gè)問題是我不能對(duì)每個(gè) BTI 建索引,比如說要求第10個(gè) BTI 等于1的,把你這個(gè)用戶撈出來,最大的缺點(diǎn)就是沒法建索引,但是數(shù)據(jù)量下降很多,因?yàn)橐粋€(gè) BTI 和一個(gè)數(shù)組字節(jié)差了幾十倍.這個(gè)方案要么通過 CPU 多核并行測(cè)算,一個(gè)32核的機(jī)器在這11條記錄里搜一遍也要花十幾秒.
方案三使用了阿里云 RDS PostgreSQL 提供的 varbitx 擴(kuò)展.針對(duì)方案2做一次翻轉(zhuǎn),一個(gè) TAG 一條記錄,每個(gè)用戶一個(gè) BTI,它的空間跟第二方案是一樣的,比第一個(gè)縮小差不多80倍的樣子.
我們?cè)谑褂眠@個(gè)方案之后要去撈一批用戶出來,是記錄與記錄之間的運(yùn)算,因?yàn)槲抑挥?萬條記錄,當(dāng)然建索引是最好的,你只要在1萬條記錄上建索引.
最終我們的方案是選擇了方案三,這是我們給他們?cè)O(shè)計(jì)的數(shù)據(jù)合并過程,最終支持了他的應(yīng)用場(chǎng)景,通過一臺(tái)機(jī)器就解決了原來八臺(tái)機(jī)器無法解決的問題.
這是方案的對(duì)比,首先是空間上的對(duì)比.如果使用方案1,需要8個(gè)T的空間,如果是方案2或者3只要100個(gè)G空間就可以.
除了對(duì)比空間還要對(duì)比查詢效率,第一個(gè)方案對(duì)空間要求很多,但是產(chǎn)生的效率也不低,包括插入更新、查詢響應(yīng)都是滿足客戶需求的,只是占用空間比較多.
對(duì)于方案2,我得用 BIT 運(yùn)行.
方案3,每個(gè)指標(biāo)都是滿足需求,包括數(shù)據(jù)的合并寫入,查詢并發(fā)是超過了用戶最初預(yù)計(jì)的要求.
優(yōu)化前用了8臺(tái)物理機(jī),用戶體量翻了一個(gè)量級(jí)之后,原來的更新是天級(jí)別,現(xiàn)在是分鐘級(jí)別.查詢并發(fā)也是翻了一倍,響應(yīng)也從原來的分鐘級(jí)降到毫秒級(jí).
未來在內(nèi)核層的優(yōu)化,如果只更新了一些用戶的 TAG,將來更新某一小塊數(shù)據(jù)的時(shí)候,不用更新現(xiàn)在整個(gè)大的一個(gè) BIT,將來是可以做到快捷更新.
再分享兩個(gè)場(chǎng)景,一個(gè)是任意字段的組合查詢,也是一個(gè)比較常見的場(chǎng)景.
在瀏覽一些頁面的時(shí)候有很多選項(xiàng),你可以勾是不是贈(zèng)送退貨運(yùn)費(fèi)險(xiǎn)或者也可以勾選是否要二手或者天貓,對(duì)用戶是有很多選擇的,而對(duì)于設(shè)計(jì)人員來說就得考慮,你的每一個(gè)選擇對(duì)應(yīng)數(shù)據(jù)庫里都是一個(gè)字段,是不是每個(gè)字段都要建索引,如果我給用戶60個(gè)選擇,是不是都要建索引.
每個(gè)字段都建索引會(huì)帶來一定的影響,在更新、插入的時(shí)候,索引的變更會(huì)引入 RT,比如每更新一次索引,增加 0.1 毫秒的 RT,建十個(gè)索引的時(shí)候就變成一個(gè)毫秒,這是非常嚴(yán)重的問題.但是業(yè)務(wù)又要求每個(gè)字段都要勾選,怎么快速響應(yīng)給用戶,這個(gè)問題是很矛盾的.
該在哪個(gè)字段建索引,還是用倒排索引,除了倒排作用,還可以為每個(gè)字段上面都建符合倒排索引,達(dá)到什么效果呢?比如說有6個(gè)列是給用戶可以選擇的,我原來可能要建6個(gè)字段,一個(gè)階層的任意組合的索引才能達(dá)到效果.
但是現(xiàn)在建一個(gè)索引,看任意六個(gè)列,任意列做 OR 或者 AND 的查詢達(dá)到什么效果?可以看響應(yīng)時(shí)間,任意 AND 都可以達(dá)到零點(diǎn)幾個(gè)毫秒,也就是在這樣的場(chǎng)景下,可以通過一個(gè)這樣的索引解決你原來根本就不知道建多少個(gè)索引才能解決的問題.如果大家想了解GIN索引的內(nèi)部原理,可以參考我的 GITHUB (https://gith
再引入下面一個(gè)應(yīng)用場(chǎng)景,是我們的客戶關(guān)系系統(tǒng),我們?cè)谶@個(gè)系統(tǒng)里是為用戶提供了類似于你覺得搜索引擎在能干的事情.
比如用戶提供一些關(guān)鍵字來搜索,而且是任意階段的,只要匹配這個(gè)關(guān)鍵字就反饋給你,原來我根本不知道怎么建索引.比如是 URL 地址,如果建全文檢索根本就沒用,因?yàn)槿臋z索里面是得分詞的,得有字典,而URL是沒有什么意義的,可能取的名字根本沒法分詞分出來,所以檢索解決不了.
還有一些公司名稱,公司的名稱可能不是一個(gè)常見詞,往往全文檢索詞庫中沒有,沒法進(jìn)行分詞.因此全文檢索無法解決模糊查詢的問題.搜索引擎能不能這個(gè)問題呢?
可以解決,但是它得跟搜索引擎同步數(shù)據(jù),還有考慮數(shù)據(jù)庫和搜索引起的一致性問題.額外的產(chǎn)生的費(fèi)用還有維護(hù)成本的問題,因?yàn)槟氵€得維護(hù)一個(gè)搜索引擎,包括數(shù)據(jù)的同步,還有更新,包括數(shù)據(jù)的過期等等.
既然這兩個(gè)都解決不了,有什么方法能解決這個(gè)問題?在 PostgreSQL 里面有一個(gè)叫做 PGTRGM 的小組件,可以幫你的詞拆成連續(xù)一個(gè)一個(gè)的字段,去做匹配,可以達(dá)到非常高的查詢效率.最終達(dá)到的效果,數(shù)據(jù)量是億級(jí)別,用戶任意的模糊搜索是可以達(dá)到毫秒級(jí)別的響應(yīng).
這是我的微信,我同時(shí)也是PostgreSQL 中國社區(qū)發(fā)起人之一,社區(qū)每年會(huì)在各個(gè)地方舉辦 PostgreSQL 的技術(shù)分享活動(dòng),大家可以關(guān)注PG的公眾號(hào)或者我的微信.同時(shí)每年會(huì)舉辦一屆全國性的用戶峰會(huì),有各個(gè)行業(yè)的同仁包括金融,物聯(lián)網(wǎng),互聯(lián)網(wǎng),社交,證券,物流,政府,科研機(jī)構(gòu),高校等行業(yè).PG 是一個(gè)跨行業(yè)非常多的開源數(shù)據(jù)庫產(chǎn)品, BSD 許可非常寬松,從使用到售賣都沒有任何法律風(fēng)險(xiǎn),大家可以放心的使用.
文章來自微信公眾號(hào):
轉(zhuǎn)載請(qǐng)注明本頁網(wǎng)址:
http://www.snjht.com/jiaocheng/2218.html