時序數據庫介紹和使用


原文地址:https://blog.csdn.net/liukuan73/article/details/79950329

1.基礎
1.1 時序數據的定義
什么是時間序列數據(Time Series Data,TSD,以下簡稱時序)從定義上來說,就是一串按時間維度索引的數據。用描述性的語言來解釋什么是時序數據,簡單的說,就是這類數據描述了某個被測量的主體在一個時間范圍內的每個時間點上的測量值。它普遍存在於IT基礎設施、運維監控系統和物聯網中。
  
對時序數據進行建模的話,會包含三個重要部分,分別是:主體,時間點和測量值。套用這套模型,你會發現你在日常工作生活中,無時無刻不在接觸着這類數據。

如果你是一個股民,某只股票的股價就是一類時序數據,其記錄着每個時間點該股票的股價。
如果你是一個運維人員,監控數據是一類時序數據,例如對於機器的CPU的監控數據,就是記錄着每個時間點機器上CPU的實際消耗值。
時序數據從時間維度上將孤立的觀測值連成一條線,從而揭示軟硬件系統的狀態變化。孤立的觀測值不能叫時序數據,但如果把大量的觀測值用時間線串起來,我們就可以研究和分析觀測值的趨勢及規律。

1.2 時序數據的特點
1.2.1 時序數據的數學模型
上面介紹了時序數據的基本概念,也說明了分析時序數據的意義。那么時序數據該怎樣存儲呢?數據的存儲要考慮其數學模型和特點,時序數據當然也不例外。所以這里先介紹時序數據的數學模型和特點。

下圖為一段時序數據,記錄了一段時間內的某個集群里各機器上各端口的出入流量,每半小時記錄一個觀測值。這里以圖中的數據為例,介紹下時序數據的數學模型(不同的時序數據庫中,基本概念的稱謂有可能不同,這里以騰訊CTSDB為准):

measurement: 度量的數據集,類似於關系型數據庫中的 table;

point: 一個數據點,類似於關系型數據庫中的 row;

timestamp: 時間戳,表征采集到數據的時間點;

tag: 維度列,代表數據的歸屬、屬性,表明是哪個設備/模塊產生的,一般不隨着時間變化,供查詢使用;

field: 指標列,代表數據的測量值,隨時間平滑波動,不需要查詢。

如上圖所示,這組數據的measurement為Network,每個point由以下部分組成:

timestamp:時間戳

兩個tag:host、port,代表每個point歸屬於哪台機器的哪個端口

兩個field:bytes_in、bytes_out,代表piont的測量值,半小時內出入流量的平均值
同一個host、同一個port,每半小時產生一個point,隨着時間的增長,field(bytes_in、bytes_out)不斷變化。如host:host4,port:51514,timestamp從02:00 到02:30的時間段內,bytes_in 從 37.937上漲到38.089,bytes_out從2897.26上漲到3009.86,說明這一段時間內該端口服務壓力升高。

1.2.2 時序數據特點
數據模式: 時序數據隨時間增長,相同維度重復取值,指標平滑變化:這點從上面的Network表的數據變化可以看出。
寫入: 持續高並發寫入,無更新操作:時序數據庫面對的往往是百萬甚至千萬數量級終端設備的實時數據寫入(如摩拜單車2017年全國車輛數為千萬級),但數據大多表征設備狀態,寫入后不會更新。
查詢: 按不同維度對指標進行統計分析,且存在明顯的冷熱數據,一般只會頻繁查詢近期數據。
1.3 時序數據的存儲
1.3.1 傳統關系型數據庫存儲時序數據的問題
有了時序數據后,該存儲在哪里呢?首先我們看下傳統的關系型數據庫解決方案在存儲時序數據時會遇到什么問題。

很多人可能認為在傳統關系型數據庫上加上時間戳一列就能作為時序數據庫。數據量少的時候確實也沒問題。但時序數據往往是由百萬級甚至千萬級終端設備產生的,寫入並發量比較高,屬於海量數據場景。

MySQL在海量的時序數據場景下存在如下問題:

存儲成本大:對於時序數據壓縮不佳,需占用大量機器資源;
維護成本高:單機系統,需要在上層人工的分庫分表,維護成本高;
寫入吞吐低:單機寫入吞吐低,很難滿足時序數據千萬級的寫入壓力;
查詢性能差:適用於交易處理,海量數據的聚合分析性能差。
另外,使用Hadoop生態(Hadoop、Spark等)存儲時序數據會有以下問題:

數據延遲高:離線批處理系統,數據從產生到可分析,耗時數小時、甚至天級;
查詢性能差:不能很好的利用索引,依賴MapReduce任務,查詢耗時一般在分鍾級。
可以看到時序數據庫需要解決以下幾個問題:

時序數據的寫入:如何支持每秒鍾上千萬上億數據點的寫入。
時序數據的讀取:如何支持在秒級對上億數據的分組聚合運算。
成本敏感:由海量數據存儲帶來的是成本問題。如何更低成本的存儲這些數據,將成為時序數據庫需要解決的重中之重。
1.3.2 時序數據庫
***時序數據庫產品的發明都是為了解決傳統關系型數據庫在時序數據存儲和分析上的不足和缺陷,這類產品被統一歸類為時序數據庫。***針對時序數據的特點對寫入、存儲、查詢等流程進行了優化,這些優化與時序數據的特點息息相關:

存儲成本:
利用時間遞增、維度重復、指標平滑變化的特性,合理選擇編碼壓縮算法,提高數據壓縮比;
通過預降精度,對歷史數據做聚合,節省存儲空間。

高並發寫入:
批量寫入數據,降低網絡開銷;
數據先寫入內存,再周期性的dump為不可變的文件存儲。

低查詢延時,高查詢並發:
優化常見的查詢模式,通過索引等技術降低查詢延時;
通過緩存、routing等技術提高查詢並發。

1.3.3 時序數據的存儲原理
傳統數據庫存儲采用的都是 B tree,這是由於其在查詢和順序插入時有利於減少尋道次數的組織形式。我們知道磁盤尋道時間是非常慢的,一般在 10ms 左右。磁盤的隨機讀寫慢就慢在尋道上面。對於隨機寫入 B tree 會消耗大量的時間在磁盤尋道上,導致速度很慢。我們知道 SSD 具有更快的尋道時間,但並沒有從根本上解決這個問題。

對於 90% 以上場景都是寫入的時序數據庫,B tree 很明顯是不合適的。

業界主流都是采用 LSM tree 替換 B tree,比如 Hbase, Cassandra 等 nosql 。這里我們詳細介紹一下。

LSM tree 包括內存里的數據結構和磁盤上的文件兩部分。分別對應 Hbase 里的 MemStore 和 HLog;對應 Cassandra 里的 MemTable 和 sstable。

LSM tree 操作流程如下:

數據寫入和更新時首先寫入位於內存里的數據結構。為了避免數據丟失也會先寫到 WAL 文件中。
內存里的數據結構會定時或者達到固定大小會刷到磁盤。這些磁盤上的文件不會被修改。
隨着磁盤上積累的文件越來越多,會定時的進行合並操作,消除冗余數據,減少文件數量。

可以看到 LSM tree 核心思想就是通過內存寫和后續磁盤的順序寫入獲得更高的寫入性能,避免了隨機寫入。但同時也犧牲了讀取性能,因為同一個 key 的值可能存在於多個 HFile 中。為了獲取更好的讀取性能,可以通過 bloom filter 和 compaction 得到,這里限於篇幅就不詳細展開。

###1.3.4 分布式存儲
時序數據庫面向的是海量數據的寫入存儲讀取,單機是無法解決問題的。所以需要采用多機存儲,也就是分布式存儲。

分布式存儲首先要考慮的是如何將數據分布到多台機器上面,也就是分片(sharding)問題。下面我們就時序數據庫分片問題展開介紹。分片問題由分片方法的選擇和分片的設計組成。

####分片方法
時序數據庫的分片方法和其他分布式系統是相通的。

哈希分片:這種方法實現簡單,均衡性較好,但是集群不易擴展。
一致性哈希:這種方案均衡性好,集群擴展容易,只是實現復雜。代表有 Amazon 的 DynamoDB 和開源的 Cassandra。
范圍划分:通常配合全局有序,復雜度在於合並和分裂。代表有 Hbase。
分片設計
分片設計簡單來說就是以什么做分片,這是非常有技巧的,會直接影響寫入讀取的性能。

**結合時序數據庫的特點,根據 measurement+tags 分片是比較好的一種方式,因為往往會按照一個時間范圍查詢,這樣相同 metric 和 tags 的數據會分配到一台機器上連續存放,順序的磁盤讀取是很快的。**再結合上面講到的單機存儲內容,可以做到快速查詢。

進一步我們考慮時序數據時間范圍很長的情況,需要根據時間范圍再分成幾段,分別存儲到不同的機器上,這樣對於大范圍時序數據就可以支持並發查詢,優化查詢速度。

如下圖,第一行和第三行都是同樣的 tag(sensor=95D8-7913;city= 上海),所以分配到同樣的分片,而第五行雖然也是同樣的 tag,但是根據時間范圍再分段,被分到了不同的分片。第二、四、六行屬於同樣的 tag(sensor=F3CC-20F3;city= 北京)也是一樣的道理。


1.4 開源時序數據庫介紹
1.4.1開源時序數據庫對比
目前行業內比較流行的開源時序數據庫產品有 InfluxDB、OpenTSDB、Prometheus、Graphite等,其產品特性對比如下圖所示:


1.4.2 InfluxDB介紹
InfluxDB是一個開源的時序數據庫,使用GO語言開發,特別適合用於處理和分析資源監控數據這種時序相關數據。而InfluxDB自帶的各種特殊函數如求標准差,隨機取樣數據,統計數據變化比等,使數據統計和實時分析變得十分方便。

重要概念
influxdb里面有一些重要概念:database,timestamp,field key, field value, field set,tag key,tag value,tag set,measurement, retention policy ,series,point。結合下面的例子數據來說明這幾個概念:

name: census
-————————————
time butterflies honeybees location scientist
2015-08-18T00:00:00Z 12 23 1 langstroth
2015-08-18T00:00:00Z 1 30 1 perpetua
2015-08-18T00:06:00Z 11 28 1 langstroth
2015-08-18T00:06:00Z 3 28 1 perpetua
2015-08-18T05:54:00Z 2 11 2 langstroth
2015-08-18T06:00:00Z 1 10 2 langstroth
2015-08-18T06:06:00Z 8 23 2 perpetua
2015-08-18T06:12:00Z 7 22 2 perpetua
1
2
3
4
5
6
7
8
9
10
11
timestamp
既然是時間序列數據庫,influxdb的數據都有一列名為time的列,里面存儲UTC時間戳。

field key,field value,field set
butterflies和honeybees兩列數據稱為字段(fields),influxdb的字段由field key和field value組成。其中butterflies和honeybees為field key,它們為string類型,用於存儲元數據。

而butterflies這一列的數據12-7為butterflies的field value,同理,honeybees這一列的23-22為honeybees的field value。field value可以為string,float,integer或boolean類型。field value通常都是與時間關聯的。

field key和field value對組成的集合稱之為field set。如下:

butterflies = 12 honeybees = 23
butterflies = 1 honeybees = 30
butterflies = 11 honeybees = 28
butterflies = 3 honeybees = 28
butterflies = 2 honeybees = 11
butterflies = 1 honeybees = 10
butterflies = 8 honeybees = 23
butterflies = 7 honeybees = 22
1
2
3
4
5
6
7
8
在influxdb中,字段必須存在。注意,字段是沒有索引的。如果使用字段作為查詢條件,會掃描符合查詢條件的所有字段值,性能不及tag。類比一下,fields相當於SQL的沒有索引的列。

tag key,tag value,tag set
location和scientist這兩列稱為標簽(tags),標簽由tag key和tag value組成。location這個tag key有兩個tag value:1和2,scientist有兩個tag value:langstroth和perpetua。tag key和tag value對組成了tag set,示例中的tag set如下:

location = 1, scientist = langstroth
location = 2, scientist = langstroth
location = 1, scientist = perpetua
location = 2, scientist = perpetua
1
2
3
4
tags是可選的,但是強烈建議你用上它,因為tag是有索引的,tags相當於SQL中的有索引的列。tag value只能是string類型 如果你的常用場景是根據butterflies和honeybees來查詢,那么你可以將這兩個列設置為tag,而其他兩列設置為field,tag和field依據具體查詢需求來定。

measurement
measurement是fields,tags以及time列的容器,measurement的名字用於描述存儲在其中的字段數據,類似mysql的表名。如上面例子中的measurement為census。measurement相當於SQL中的表,本文中我在部分地方會用表來指代measurement。

retention policy
retention policy指數據保留策略,示例數據中的retention policy為默認的autogen。它表示數據一直保留永不過期,副本數量為1。你也可以指定數據的保留時間,如30天。

series
series是共享同一個retention policy,measurement以及tag set的數據集合。示例中數據有4個series,如下:

Arbitrary series number Retention policy Measurement Tag set
series 1 autogen census location = 1,scientist = langstroth
series 2 autogen census location = 2,scientist = langstroth
series 3 autogen census location = 1,scientist = perpetua
series 4 autogen census location = 2,scientist = perpetua
1
2
3
4
5
point
point則是同一個series中具有相同時間的field set,points相當於SQL中的數據行。如下面就是一個point:

name: census
-----------------
time butterflies honeybees location scientist
2015-08-18T00:00:00Z 1 30 1 perpetua
1
2
3
4
database
上面提到的結構都存儲在數據庫中,示例的數據庫為my_database。一個數據庫可以有多個measurement,retention policy, continuous queries以及user。influxdb是一個無模式的數據庫,可以很容易的添加新的measurement,tags,fields等。而它的操作卻和傳統的數據庫一樣,可以使用類SQL語言查詢和修改數據。

influxdb不是一個完整的CRUD數據庫,它更像是一個CR-ud數據庫。它優先考慮的是增加和讀取數據而不是更新和刪除數據的性能,而且它阻止了某些更新和刪除行為使得創建和讀取數據更加高效。

更多詳細介紹請見:https://www.jianshu.com/p/a1344ca86e9b

2.部署
2.1 influxdb部署
yum部署:
<1> 配置YUM源

cat <<EOF| sudo tee /etc/yum.repos.d/influxdb.repo
[influxdb]
name = InfluxDB Repository- RHEL\$releasever
baseurl = https://repos.influxdata.com/rhel/\$releasever/\$basearch/stable
enabled = 1
gpgcheck = 1
gpgkey = https://repos.influxdata.com/influxdb.key
EOF
1
2
3
4
5
6
7
8
<2> yum安裝

yum install influxdb -y
systemctl start influxdb
systemctl enable influxdb
1
2
3
rpm部署:

wget https://dl.influxdata.com/influxdb/releases/influxdb-1.5.2.x86_64.rpm
yum -y localinstall influxdb-1.5.2.x86_64.rpm
1
2
docker部署:

docker run --name=influxdb -d -p 8086:8086 -v /etc/localtime:/etc/localtime daocloud.io/liukuan73/influxdb:1.4

1
2
備注:假如是收集collectd的內容,還需要映射25826端口-p 25826:25826
##2.2 grafana部署
rpm部署:

wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-5.0.4-1.x86_64.rpm
yum -y localinstall grafana-5.0.4-1.x86_64.rpm
systemctl enable grafana-server
systemctl start grafana-server
1
2
3
4
docker部署:

docker run -d -p 3000:3000 --name=grafana -e "GF_SERVER_HTTP_PORT=3000" -e "GF_AUTH_BASIC_ENABLED=false" -e "GF_AUTH_ANONYMOUS_ENABLED=true" -e "GF_AUTH_ANONYMOUS_ORG_ROLE=Admin" -e "GF_SERVER_ROOT_URL=/" daocloud.io/liukuan73/grafana:5.0.0

1
2
3.使用
有三種方法可以將數據寫入InfluxDB,包括:

客戶端庫
調用restapi
命令行(類sql語句)
##3.1 調用客戶端庫操作influxdb
以collectd+influxdb+grafana為例介紹通過collectd采集主機性能指標,然后通過influxdb的客戶端庫寫入influxdb,最后在grafana展示的完整過程。
3.1.1 collectd部署
1、yum安裝

sudo yum -y install epel-release
sudo yum -y install collectd
1
2
2、數據寫入 influxdb ,修改配置

vi /etc/collectd.conf
LoadPlugin network
<Plugin network>
# # client setup:
Server "10.142.232.155" "25826"
# <Server "239.192.74.66" "25826">
</Plugin>
systemctl enable collectd
systemctl start collectd
systemctl status collectd
1
2
3
4
5
6
7
8
9
10
3.1.2 influxdb配置
1、創建collectd數據庫

influx -host '10.142.232.155' -port '8086'
Connected to http://127.0.0.1:8086 version 1.5.2
InfluxDB shell version: 1.5.2
> create database collectd
> use collectd
> create user "collectd" with password '123456' with all privileges
1
2
3
4
5
6
2、配置influxdb,開啟對collectd數據的接收

vim /etc/influxdb/influxdb.conf
[[collectd]]
enabled = true
port = 25826
database = "collectd" #剛創建的collectd數據庫,來自collectd的數據寫入這個數據庫。沒事先創建好的話會啟動失敗
1
2
3
4
5
3.1.3 grafana配置
1、配置influxdb數據源
點擊“Add data source”配置數據源:


2.配置dashboard
網絡流量統計
創建graph,切換編輯模式“Toggle Edit Mode”, 然后輸入自定義SQL查詢

輸入查詢語句:

SELECT derivative("value") AS "value" FROM "interface_rx" WHERE "host" = 'k8sslave04' AND "type" = 'if_octets' AND "instance" = 'eno16777984'
1
備注:
函數 derivative 意為導數, 微積分中的概念. value 為傳輸總量(字節), derivative(“value”) 為 value 在時間上的增量.其中:

host = k8sslave04
type = if_octets
instance = eno16777984
系統負載

輸入查詢語句:

SELECT mean("value") FROM "load_longterm" WHERE "host" = 'k8sslave04' AND $timeFilter GROUP BY time($interval) fill(null)
SELECT mean("value") FROM "load_midterm" WHERE "host" = 'k8sslave04' AND $timeFilter GROUP BY time($interval) fill(null)
SELECT mean("value") FROM "load_shortterm" WHERE "host" = 'k8sslave04' AND $timeFilter GROUP BY time($interval) fill(null)
1
2
3
內存用量

輸入查詢語句:

SELECT mean("value") FROM "memory_value" WHERE "type_instance" = 'used' AND $timeFilter GROUP BY time($interval) fill(null)
1
效果


3.2 RestAPI操作
3.2.1 實例
創建數據庫:
要創建數據庫,請將POST請求發送到/query終結點,並將URL參數q設置為CREATE DATABASE。 下面的示例主機上運行的InfluxDB發送請求,並創建數據庫test:

curl -i -XPOST http://influxdb-ip:8086/query --data-urlencode "q=CREATE DATABASE test"
1
寫入單條數據:
通過向/write端點發送POST請求,HTTP-API是將數據寫入InfluxDB的主要方式。下面的例子向test數據庫寫了一個點。 數據由度量cpu_load_short,標簽鍵host和region和對應的標簽值server01和us-west,字段值為0.64的字段鍵value和時間戳1434055562000000000組成。

curl -i -XPOST 'http://influxdb-ip:8086/write?db=test' --data-binary 'cpu_load_short,host=server01,region=us-west value=0.64 1434055562000000000'
1
寫入點時,必須在db查詢參數中指定一個現有的數據庫。 如果您沒有使用rp查詢參數提供保留策略,則會將點寫入數據庫的默認保留策略。 請參閱API參考文檔以獲取可用查詢參數的完整列表。

寫入多條數據:
一次將多個點Post到不同序列,只需要用行將多個點分隔即可。這種批量方式具有高性能。以下示例將三個點寫入數據庫mydb。 第一點屬於擁有度量cpu_load_short及標簽集host = server02且用服務器本地時間戳的序列。第二點屬於擁有度量cpu_load_short及標簽集host = server02,region =us-west且具有指定時間戳1422568543702900257的序列。第三個點與第二個點具有相同的指定時間戳,但是將其寫入擁有度量cpu_load_short和標簽集direction=in,host=server01,region=us-west的序列。

curl -i -XPOST 'http://influxdb-ip:8086/write?db=test' --data-binary 'cpu_load_short,host=server02 value=0.67
cpu_load_short,host=server02,region=us-west value=0.55 1422568543702900257
cpu_load_short,direction=in,host=server01,region=us-west value=2.0 1422568543702900257'
1
2
3
寫入來自文件的數據:
通過將@filename傳給curl來從文件中寫入點。文件中的數據應該遵循InfluxDB的行協議語法。格式正確的文件示例(cpu_data.txt):

cpu_load_short,host=server02 value=0.67
cpu_load_short,host=server02,region=us-west value=0.55 1422568543702900257
cpu_load_short,direction=in,host=server01,region=us-west value=2.0 1422568543702900257
1
2
3
將cpu_data.txt中數據寫入mydb數據庫:

curl -i -XPOST 'http://influxdb-ip:8086/write?db=test' --data-binary @cpu_data.txt
1
注意:如果數據文件中超過5,000個點,可能需要將該文件分成幾個文件,以便將數據批量寫入InfluxDB。 默認情況下,HTTP請求在五秒鍾后超時。 InfluxDB在超時之后仍然會嘗試寫出這些點,但是不能確認它們是否成功寫入。

###3.2.2 HTTP響應總結

2xx:如果你的寫請求收到HTTP 204 No Content,那就成功了!
4xx:InfluxDB無法理解請求。
5xx:系統過載或嚴重受損。
無架構設計
InfluxDB是一個無架構的數據庫。 您可以隨時添加新的度量,標簽和字段。請注意,如果您嘗試寫入與已寫入數據類型不相同的數據(例如,將字符串寫入之前接受整數的字段),InfluxDB將拒絕這些數據。

錯誤響應的例子:

將浮點數寫入先前接受布爾值的字段中:

curl -i -XPOST 'https://influxdb-ip:8086/writedb=hamlet' --data-binary 'tobeornottobe booleanonly=true'

curl -i -XPOST 'https://influxdb-ip:8086/writedb=hamlet' --data-binary 'tobeornottobe booleanonly=5'
1
2
3
返回:

HTTP/1.1 400 Bad Request
Content-Type: application/json
Request-Id: [...]
X-Influxdb-Version: 1.4.x
Date: Wed, 01 Mar 2017 19:38:01 GMT
Content-Length: 150

{"error":"field type conflict: input field \"booleanonly\" on measurement \"tobeornottobe\" is type float, already exists as type boolean dropped=1"}
Writing a point to a database that doesn’t exist:
curl -i -XPOST 'https://localhost:8086/writedb=atlantis' --data-binary 'liters value=10'
returns:
HTTP/1.1 404 Not Found
Content-Type: application/json
Request-Id: [...]
X-Influxdb-Version: 1.4.x
Date: Wed, 01 Mar 2017 19:38:35 GMT
Content-Length: 45

{"error":"database not found: \"atlantis\""}
Next steps
Now that you know how to write data with the built-in HTTP API discover how to query them with the Querying Data guide! For more information about writing data with the HTTP API, please see the API reference documentation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
將點寫入不存在的數據庫

curl -i -XPOST 'https://localhost:8086/writedb=atlantis' --data-binary 'liters value=10'
1
返回:

HTTP/1.1 404 Not Found
Content-Type: application/json
Request-Id: [...]
X-Influxdb-Version: 1.4.x
Date: Wed, 01 Mar 2017 19:38:35 GMT
Content-Length: 45

{"error":"database not found: \"atlantis\""}
1
2
3
4
5
6
7
8
3.3命令行操作
InfluxDB提供類SQL語法,如果熟悉SQL的話會非常容易上手。
一些操作實例請見:https://www.linuxdaxue.com/influxdb-basic-operation.html

參考:
http://www.infoq.com/cn/articles/storage-in-sequential-databases
https://zhuanlan.zhihu.com/p/32627177
https://segmentfault.com/a/1190000006868587
http://www.zhimengzhe.com/shujuku/MySQL/414763.html
https://blog.csdn.net/a464057216/article/details/53043551
https://blog.csdn.net/wudufeng/article/details/78567866
https://www.linuxdaxue.com/influxdb-basic-operation.html
https://www.jianshu.com/p/a1344ca86e9b
————————————————
版權聲明:本文為CSDN博主「liukuan73」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/liukuan73/article/details/79950329


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM