背景
表格存儲可以為Spark提供KV查詢(主表,全局二級索引表)、多元索引查詢兩套資料通路方式,以支援海量結構化資料快速讀寫和豐富的SQL查詢分析能力。其分布式存儲的特點和強大的索引引擎能夠支援PB級存儲、千萬TPS以及毫秒級延遲的服務能力。
KV通路方式指的是主表和全局二級索引通路方式,其中主表指的是Tablestore的源資料主表,全局二級索引和多元索引的介紹見文章
海量結構化資料存儲技術揭秘:Tablestore存儲和索引引擎詳解。
Spark多元索引通路與KV通路方式的差別:
KV查詢方式在過濾字段是主鍵的場景下效率較高,但不适合過濾字段變動零活且過濾字段中非主鍵列較多的場景。多元索引是Tablestore基于反向索引組織的資料,可以提供類似ES的
豐富查詢、統計聚合、分詞查詢、地理位置查詢功能。在下述資料通路場景中,推薦使用多元索引來代替KV通路方式:
- 少量且對延時要求較高的實時資料分析場景
- SQL語句中非主鍵的過濾字段較多,多數字段無法被二級索引主鍵或者主表主鍵包含
- SQL語句中過濾字段的篩選效率較高即可以通過某一字段條件過濾掉大部分資料,比如select * from table where col = 1000中,col是非主鍵列,且 a = 1000 這個字段條件可以過濾掉大部分資料
- 查詢條件中包含三種特殊的地理坐标查詢方式,KV通路方式不支援地理坐标查詢
接下來結合下圖以
select * from table where col1 like '阿%' or col2 = 'a'
為例解釋多元索引的優勢。通過多元索引通路資料時,在多元索引表col1中我們可以拿到col1為a的兩行資料:pk1 = 1和pk1 = 2;在索引表col2中,我們可以拿到col2的值為‘阿%’的一行資料:pk1 = 1;進而将兩次中間結果的資料進行union,即可得到我們想要的資料:
pk1 = 1,col1 = ‘阿裡雲’,col2 = ‘a'
KV查詢方式中,查詢主體是Tablestore的主表,主表隻在主鍵上擁有索引能力。如果要查詢的SQL語句中的過濾字段不是主表的主鍵,則需要進行全表掃描。以
select * from table where col1 = 'a' or col2 like '阿%'
為例,由于col1不是主表的主鍵,Tablestore會全表掃描找到col1為a的兩行資料;由于col2不是主表的主鍵,會再一次全表掃描找到col2的值為‘阿%’的一行資料,進而做資料的union。當然,我們可以通過建構一個主鍵為col1,col2的二級索引表來支援該查詢,但這種方式的靈活性明顯不如多元索引高。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLiJWNhNGN3IGNkFzY2ADZ0kjM0QmMlNTY0cTNjNjZiN2Lc12bj5ycj5Wd5lGbh5SdvtWYpp2ZuFGa61ibj1ycz9mLn1WatITY0F2Lc9CX6MHc0RHaiojIsJye.png)
Spark對接多元索引實操見文章,下文将繼續介紹資料類型支援、謂詞支援和自定義謂詞下推配置。
資料類型支援
基礎資料類型
Spark資料類型 | Scala中的值類型 | 多元索引類型 | Tablestore内部類型 |
---|---|---|---|
ByteType | Byte | Long | INTEGER |
ShortType | Short | ||
IntegerType | Int | ||
LongType | |||
FloatType | Float | Double | DOUBLE |
DoubleType | |||
StringType | String | Keyword/Text | STRING |
BinaryType | Array[Byte] | Binary | BINARY |
BooleanType | Boolean | BOOLEAN | |
地理坐标(String Json) | String(Json) | GEO_POINT | STRING(Json) |
複雜資料類型-地理類型(Geo類型)
地理坐标查詢是多元索引支援的查詢方式,我們将其提供到計算層,使得Spark不僅可以查詢分析基礎類型資料,也可以結合地理位置對資料進行查詢分析。
地理位置查詢支援三種類型查詢:半徑圓查詢、矩形查詢、多邊形查詢。
地理坐标(String Json)半徑圓 | |||
地理坐标(String Json)矩形 | |||
地理坐标(String Json)多邊形 |
應用場景:
地理坐标查詢應用場景十分廣泛,物聯網裝置位置資訊、騎手訂單、打卡位置資訊、快遞地理資訊等應用場景下皆有使用者在使用。
使用方式:
- 多元索引原生查詢方式見連結: 多元索引地理類型
- SparkSQL查詢方式舉例:
//地理半徑圓查詢
select * from table where val_geo = '{"centerPoint":"3,0", "distanceInMeter": 100000}' and name like 'ali%'
//地理矩形查詢
select * from table where geo = '{"topLeft":"8,0", "bottomRight": "0,10"}' and id in { 123 , 321 }
//地理多邊形查詢
select * from table where geo = '{"points":["5,0", "5,1", "6,1", "6,10"]}'
謂詞支援
謂詞支援清單
Spark | 是否支援 | SQL舉例 |
---|---|---|
And | 是 | select * from table where a > 1 and b < 0 |
Or | select * from table where a > 1 or b < 0 | |
Not | select * from table where a != 1 | |
EqualTo | select * from table where a = 1 | |
Not+EqualTo | select * from table where a != 1 | |
IsNull | select * from table where a is null table表中沒有a列的行 | |
In | select * from table where a in {1,2,3} 預設最大限制1024 | |
LessThan | select * from table where a < 10 如果SQL語句中使用該謂詞的列的類型為Long或者String,則可以通過謂詞下推配置設定是否下推,詳情見下一節"謂詞下推支援說明" | |
LessThanOrEqual | select * from table where a <=10 如果SQL語句中使用該謂詞的列的類型為Long或者String,則可以通過謂詞下推配置設定是否下推,詳情見下一節"謂詞下推支援說明" | |
GreaterThan | select * from table where a > 10 如果SQL語句中使用該謂詞的列的類型為Long或者String,則可以通過謂詞下推配置設定是否下推,詳情見下一節"謂詞下推支援說明" | |
GreaterThanOrEqual | select * from table where a >= 10 如果SQL語句中使用該謂詞的列的類型為Long或者String,則可以通過謂詞下推配置設定是否下推,詳情見下一節"謂詞下推支援說明" | |
StringStartsWith | select * from table where a like "tablestore%" table表中a列以tablestore為prefix的行全部傳回 | |
地理坐标(String Json)中心距離 | select * from table where val_geo = '{"centerPoint":"3,0", "distanceInMeter": 100000}' 圓心半徑決定的地理圓圈 | |
地理坐标(String Json)矩形框 | select * from table where geo = '{"topLeft":"8,0", "bottomRight": "0,10"}' 左上角,右下角決定的地理矩形 | |
地理坐标(String Json)多邊形框 | select * from table where geo = '{"points":["5,0", "5,1", "6,1", "6,10"]}' 多個點組成的地理多邊形 |
自定義謂詞下推
自定義謂詞下推目前支援與Long,String類型的列做大小比較的謂詞(LessThan LessThanOrEqual GreaterThan GreaterThanOrEqual)是否下推。
謂詞下推配置參數清單:
參數名稱 | 預設值 | 可選值 | 說明 |
---|---|---|---|
push.down.range.long | true | true,false | 為false時表示與Long類型的列做大小比較的謂詞(LessThan LessThanOrEqual GreaterThan GreaterThanOrEqual)不下推 |
push.down.range.string | 為false時表示與String類型的列做大小比較的謂詞(LessThan LessThanOrEqual GreaterThan GreaterThanOrEqual)不下推 |
适用場景:
多元索引中,如果多字段過濾的中間結果資料量較大,則中間結果的union較為耗時。将某些字段的過濾從存儲層提到計算層進行來做可以提高效率。比如select * from table where a = 10 and b < 999999999, 假設 a = 10 傳回的結果隻有1000條,b < 999999999的結果有一億條 ,果在存儲層這1000條結果與一億條結果作union是比較耗時的,但如果把b < 999999999提到計算層,則Spark隻需要對存儲層傳回的1000條資料作過濾,存儲層的壓力大大降低。
使用方式:
push.down.range.string與push.down.range.long在建立Spark外表的時候配置,預設為true,如果選擇false則為不下推。
建立Spark外表同時連接配接多元索引,各個參數的說明如下:
• endpoint: 表格存儲執行個體通路位址,EMR叢集裡使用VPC位址。
• access.key.id: 阿裡雲賬号AK ID。
• access.key.secret: 阿裡雲賬号AK Secret。
• instance.name: 執行個體名。
• table.name: Tablestore表名。
• search.index.name: 多元索引名。
• max.split.count: 多元索引Parallel Scan的查詢并發度,并發數和Spark的Split數對應。
• push.down.range.long: 與Long類型做Range( >= > < <= )比較的謂詞是否下推
• push.down.range.string: 與String類型做Range( >= > < <= )比較的謂詞是否下推
DROP TABLE IF EXISTS geo_table;
CREATE TABLE geo_table (
pk1 STRING, val_keyword1 STRING, val_keyword2 STRING, val_keyword3 STRING,
val_bool BOOLEAN, val_double DOUBLE, val_long1 LONG, val_long2 LONG,
val_text STRING, val_geo STRING COMMENT "geo stored in string format"
)
USING tablestore
OPTIONS(
endpoint="https://sparksearchtest.cn-hangzhou.vpc.tablestore.aliyuncs.com",
access.key.id="",
access.key.secret="",
instance.name="sparksearchtest",
table.name="geo_table",
search.index.name="geo_table_index",
max.split.count=64,
push.down.range.long = false,
push.down.range.string = false
);
下推規則詳解:
謂詞下推的配置在建立Spark外表的時候設定,預設全部下推,可設定push.down.range.long=false/push.down.range.string=false使得與Long,String類型的列做大小比較的謂詞(LessThan LessThanOrEqual GreaterThan GreaterThanOrEqual)不下推。
當SQL語句中的邏輯謂詞隻有AND和NOT(即不可以存在邏輯謂詞OR),則可按照如下規則選擇謂詞下推
- 配置push.down.range.long=false:不下推與Long類型的列做大小比較的謂詞(LessThan LessThanOrEqual GreaterThan GreaterThanOrEqual),如下表例2
- push.down.range.string:不下推與String類型的列做大小比較的謂詞(LessThan LessThanOrEqual GreaterThan GreaterThanOrEqual),如下表例3
當SQL語句中的邏輯謂詞存在OR時,push.down.range.long、string=false不會生效,所有謂詞全部下推
SQL舉例:
舉例ID | 邏輯謂詞 | 下推配置 | 預期效果 | |
---|---|---|---|---|
1 | 全為AND | push.down.range.long=true push.down.range.string=true | select * from table where val_long1 > 1000 and val_long1 is null and name like 'table%' and pk in {12341,213432} | SQL正常 |
2 | push.down.range.long=false | select * from table where val_long1 > 1 and name like 'table%' | 與Long類型做大小比較的謂詞(LessThan LessThanOrEqual GreaterThan GreaterThanOrEqual)都不會被下推,該謂詞交由Spark層來做過濾 spark層擷取到的是name like 'table%'的資料,val_long1 > 1交由spark過濾 | |
3 | push.down.range.string=false | select * from table where val_string1 > 'string1' and name like 'table%' | 與String類型做大小比較的謂詞(LessThan LessThanOrEqual GreaterThan GreaterThanOrEqual)都不會被下推,該謂詞交由Spark層來做過濾 spark層擷取到的是name like 'table%'的資料,val_string1 > 'string1'交由spark過濾 | |
4 | 存在地理列 | select * from table where val_geo = '{"centerPoint":"3,0", "distanceInMeter": 100000}' and val_long1 = 37691900 and val_long2 > 2134234 | ||
val_long2 > 2134234能否被過濾則取決于push.down.range.long的配置 | ||||
5 | 存在OR | long,string都配置為可以下推 | select * from table where val_long1 > 1000 or val_long1 is null or name like 'table%' and pk in {12341,213432} | |
6 | select * from table where val_geo = '{"centerPoint":"3,0", "distanceInMeter": 100000}' or val_long1 = 37691900 | |||
7 | 且SQL語句中存在rangeLong 的過濾字段 | 此時push.down.range.long=false不生效,SQL謂詞全部下推 | ||
8 | 且SQL語句中存在rangeString 的過濾字段 | 此時push.down.range.string=false不生效,SQL謂詞全部下推 |