Prometheus--PromQL


官方文檔:https://prometheus.io/docs/introduction/overview/

 中文文檔:https://prometheus.fuckcloudnative.io/

 

一、PromQL表達式

1.1、語言數據類型

PromQL表達式語言中,一個表達式或者子表達式可以表示以下4種類型之一:

  • Instant vector:瞬時向量,一組time series(時間序列),每個time series包括了一個時間戳的數據點,所有time series數據點擁有相同的時間戳。
  • Range vector:范圍向量,一組time series包含一個時間范圍內的一組數據點。

  • Scalar:標量,為一個浮點數值。
  • String:字符串,為一個字符串數值。當前未使用。

 

1.2、Literals數據格式

1.2.1 String literals

 字符串可以用單引號(‘’)、雙引號(“”)或反引號(``)指定為文字。

 PromQL遵循與Go相同的轉義規則。在單引號或者雙引號中,反斜桿開始轉義序列,后面可以跟a, b, f, n, r, t, v, \。可以使用八進制(\nnn)或者十六進制(\xnn,\unnnn和\Unnnnnnnn)提供特定字符。

 在反引號內沒有處理轉義。與Go不同的是,PromQL不會丟棄反引號中的換行符。

 

1.2.2 Float literals

 浮點類型數值的格式為:-[.(digits)]
 如:-2.43

 

1.3、Time series(時間序列)選擇器

1.3.1 Instant vector selectors(即時矢量選擇器)

 瞬時向量選擇器用於選擇一組time series和每個time series對應的某一個時間戳數據點,唯一的要求是必須指定metric指標名。

 例:查詢指標名http_requests_total對應的所有time series表達式:

http_requests_total

 可以通過在花括號 ( {}) 中附加一個逗號分隔的標簽匹配器列表來進一步過濾這些時間序列。

 例:僅選擇具有http_requests_total 度量名稱且job標簽設置為prometheus且其 group標簽設置為的時間序列canary

http_requests_total{job="prometheus",group="canary"}

 通過負匹配或正則表達式匹配tag選擇時間序列,支持如下匹配運算符:

  • = 等於
  • != 不等於
  • =~ 選擇與提供的字符串進行正則表達式匹配的標簽
  • !~ 選擇與提供的字符串不匹配的標簽

 例:選擇staging,testing和development;除去GET的請求方法

http_requests_total{environment=~"staging|testing|development",method!="GET"}

 匹配空標簽值的標簽匹配器還會選擇根本沒有設置特定標簽的所有時間序列。同一個標簽名稱可以有多個匹配器。

 矢量選擇器必須指定一個名稱或至少一個與空字符串不匹配的標簽匹配器。以下表達式是非法的:

{job=~".*"} # Bad!

 相反,這些表達式是有效的,因為它們都有一個不匹配空標簽值的選擇器

{job=~".+"}              # Good!
{job=~".*",method="get"} # Good!

 標簽匹配器也可以通過與內部標簽匹配來應用於指標名稱 __name__

 例:表達式http_requests_total 等價於 {__name__="http_requests_total"},也可以使用=!==~, )以外的匹配器

 !~以下表達式選擇名稱以 開頭的所有指標job:

{__name__=~"job:.*"}

 指標名稱不能是關鍵字bool、on、ignoring和group_left之一group_right。以下表達式是非法的:

on{} # Bad!

 此限制的解決方法是使用__name__標簽

{__name__="on"} # Good!

 

1.3.2 Range vector selectors(范圍矢量選擇器)

 范圍向量字面量的工作方式類似於即時向量字面量,唯一區別是選擇的時間序列是一個時間范圍內的時序數據

 從語法上講,持續時間附加在[]向量選擇器末尾的方括號 ( ) 中,以指定應該為每個結果范圍向量元素獲取多遠的時間值

 例:為所有時間序列選擇了過去 5 分鍾內記錄的所有值,這些時間序列的指標名稱http_requests_total和job標簽設置為prometheus:

http_requests_total{job="prometheus"}[5m]
 Time Durations(持續時間)

  持續時間被指定為一個數字,后跟以下單位之一:

  • ms  毫秒
  • s     秒
  • m    分鍾
  • h     小時
  • d     天(假設一天總是 24 小時)
  • w    周(假設一周總是7天)
  • y     年(假設一年總是365天)

 

1.3.3 Offset modifier(偏移修改器)

 Offset修改器允許修改查詢中瞬時向量和范圍向量的時間偏移。

 例:返回 http_requests_total 相對於當前查詢評估時間過去 5 分鍾的值

http_requests_total offset 5m

 注:offset修飾符總是需要立即跟隨選擇器

sum(http_requests_total{method="GET"} offset 5m) // GOOD
sum(http_requests_total{method="GET"}) offset 5m // Bad

 

1.4、Subquery(子查詢)

 子查詢允許針對給定的范圍和分辨率運行即時查詢。子查詢的結果是一個范圍向量。

<instant_query> [ <range> : [<resolution>]] [offset <duration>]

 注:<resolution>是可選的,默認為全局評估間隔

 例:回過去30分鍾CpuPercent 5m統計的rate(平均增長速率)值,精度是1分鍾。

rate(CpuPercent{}[5m])[30m:1m]

 

二、PromQL運算符

 PromQL支持基本的邏輯和算術運算。對於兩個瞬時向量的運算,其結果可能影響匹配行為。

2.1、算數運算符

  • +(加法)
  • - (減法)
  • * (乘法)
  • / (除法)
  • %(模數)
  • ^(冪)

 二進制算術運算符在標量/標量、向量/標量和向量/向量值對之間定義:

 scalar/scalar:兩個標量數值之間進行運算

 vector/scalar:對瞬時向量里每個時間序列的數據點和標量數值進行運算

 vector/vector:運算符應用於左邊向量里的每個條目和右邊向量匹配的條目(同一個time series),結果輸出到結果向量里。metric指標名將被丟棄。右邊向量未匹配的條目將不在結果向量里

 

 2.2、比較運算符

  • == (等於)
  • !=  (不等於)
  • >   (大於)
  • <   (小於)
  • >=(大於或等於)
  • <=(小於或等於)

 比較運算符在標量/標量、向量/標量和向量/向量值對之間定義。默認情況下,它們會過濾(滿足條件的數據留下)。可以在operator后面帶上bool標記來修改結果行為:返回0或者1

 scalar/scalar:bool必須提供修飾符,並且這些運算符會產生另一個標量,即0( false) 或1 ( true),具體取決於比較結果

 vector/scalar:比較瞬時向量里每個time series的值,過濾或者設置為bool

 vector/vector:比較兩個瞬時向量里每個time series的值,過濾或者設置為bool;如果左邊向量里的time series沒有在右邊找到,則為比較失敗

 

2.3、邏輯/集合運算符

 邏輯/集合二元運算符僅在即時向量之間定義:

  • and  過濾出兩個向量里都有的時間序列
  • or     過濾出兩個向量里所有的時間序列
  • unless   過濾出左邊向量里,右邊沒有的時間序列

 

2.4、聚合運算符

 Prometheus 支持以下內置聚合運算符,可用於聚合單個即時向量的元素,從而生成具有聚合值的元素更少的新向量

  • sum          (總和)
  • min           (最小值)
  • max               (最大值)
  • avg                (平均值)
  • group             (結果向量中的所有值都是1)
  • stddev           (計算維度上的總體標准偏差)
  • stdvar            (計算維度上的總體標准方差)
  • count             (計算向量中的元素個數)
  • count_values(計算具有相同值的元素個數)
  • bottomk        (樣本值的最小k個元素)
  • topk              (按樣本值計算的最大k個元素)
  • quantile        (在維度上計算 φ-quantile (0 ≤ φ ≤ 1))

 注:這些運算符既可以用於聚合所有標簽維度,也可以通過包含withoutorby子句來保留不同的維度;這些子句可以在表達式之前或之后使用。

     without將從結果中移除選定的tags,by則相反,將移除沒有選定的tags。

     其中count_values, quantile, topk, bottomk需要指定parameter,其他聚合函數不需要。

<aggr-op> [without|by (<label list>)] ([parameter,] <vector expression>)
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]

 

2.5、運算符優先級

  1. ^
  2. *,/,%
  3. +,-
  4. ==,!=,>,<,>=,<=
  5. and,unless
  6. or

 

三、PromQL內置函數

 一些函數有默認參數,例如year(v=vector(time()) instant-vector). 這意味着有一個參數v是一個即時向量,如果沒有提供,它將默認為表達式的值 vector(time())。

abs()

  abs(v instant-vector)  返回輸入向量,其中所有樣本值都轉換為其絕對值。

absent()

  absent(v instant-vector)  如果傳遞給它的瞬時向量有任何元素,則返回一個空向量;如果傳遞給它的向量沒有元素,則返回一個值為 1 的 1 元素向量

 通常用於設置告警,判斷給定的指標和標簽沒有數據時產生告警。

absent_over_time()

  absent_over_time(v range-vector)  如果傳遞給它的范圍向量有任何元素,則返回一個空向量;如果傳遞給它的范圍向量沒有元素,則返回一個值為 1 的 1 元素向量。

 同absent(),常用於判斷指標與標簽組合,不存在於時間序列時報警。

ceil()

  ceil(v instant-vector)  將所有元素的樣本值四舍五入到最接近的整數。

changes()

  changes(v range-vector)  計算給出的時間范圍內,value是否發生變化,將發生變化時的條目作為即時向量返回

clamp()

  clamp(v instant-vector, min scalar, max scalar)  設定所有元素的樣本值的上限與下限

 注:當min>max時,返回一個空向量:NaN if min or max is NaN

clamp_max()

  clamp_max(v instant-vector, max scalar)  設定所有元素的樣本值最大值為max,若 value > max 則修改max,value < max 則不變

clamp_min()

  clamp_min(v instant-vector, min scalar)  設定所有元素的樣本值最小值為min,若 value > min 則不變,value < min 則修改min

day_of_month()

  day_of_month(v=vector(time()) instant-vector)  以 UTC 格式返回每個給定時間的月份日期,返回值從 1 到 31。

day_of_week()

  day_of_week(v=vector(time()) instant-vector)  以 UTC 格式返回每個給定時間的星期幾,返回值從 0 到 6,其中 0 表示星期日。

days_in_month()

  days_in_month(v=vector(time()) instant-vector)  以 UTC 格式返回每個給定時間該月的天數,返回值從 28 到 31。

delta()

  delta(v range-vector)  計算范圍向量中每個時間序列元素的第一個值和最后一個值之間的差,返回具有給定增量和等效標簽的即時向量。增量被外推以覆蓋范圍向量選擇器中指定的整個時間范圍,因此即使樣本值都是整數,也可以獲得非整數結果。

 注:delta()函數在可視化界面上只能用於儀表盤

 例:返回現在和 2 小時前 CPU 溫度的差異:

delta(cpu_temp_celsius{host="zeus"}[2h])

deriv()

  deriv(v range-vector)  使用簡單線性回歸的方法計算時間序列在范圍向量 中的每秒導數

 注:deriv()函數在可視化頁面上只能用於儀表盤

exp()

  exp(v instant-vector)  計算v中所有元素的指數函數

 特殊情況:

Exp(+Inf) = +Inf
Exp(NaN) = NaN

floor()

  floor(v instant-vector)  將所有元素的樣本值v向下舍入到最接近的整數。

histogram_quantile() 分位直方圖

  histogram_quantile(φ scalar, b instant-vector)  從instant vector中獲取數據計算q分位(0<= q <=1)。b里的樣本是每個桶的計數。每個樣本必須具有標簽le,表示桶的上限。直方圖指標類型會自動提供了_bucket后綴和相應tags的time series。

 使用rate()函數指定分位計算的時間窗口。

 例1:一個直方圖指標名:http_request_duration_seconds。計算其對應所有time series過去10分鍾90分位數,使用如下表達式:

histogram_quantile(0.9, rate(http_request_duration_seconds_bucket[10m]))

 例2:為指標名http_request_duration_seconds對應的每個tags組合(每個time series)計算90分位數。為了聚合數據,比如使用sum()進行聚合,需要通過by子句包含le標簽

histogram_quantile(0.9, sum(rate(http_request_duration_seconds_bucket[10m])) by (job, le))

 例3:聚合所有time series

histogram_quantile(0.9, sum(rate(http_request_duration_seconds_bucket[10m])) by (le))

holt_winters()

  holt_winters(v range-vector, sf scalar, tf scalar)  基於平滑因子sf和tf計算range_vector里time series的平滑值。sf越低則老數據越重要,tf越高則更多考慮數據的趨勢。sf和tf的值介於0~1之間。

 注:holt_winters()函數在可視化界面上只能用於儀表盤。

hour()

  hour(v=vector(time()) instant-vector)  以 UTC 格式返回每個給定時間的一天中的小時,返回值從 0 到 23。

idelta()

  idelta(v range-vector)  計算范圍向量中最后兩個樣本之間的差異,返回具有給定增量和等效標簽的即時向量。

 注:idelta()函數在可視化界面上只能用於儀表盤。

increase()

  increase(v range-vector)  計算范圍向量中時間序列的增量。單調性的中斷(例如由於目標重新啟動而導致的計數器重置)會自動調整。該增加被外推以覆蓋范圍向量選擇器中指定的整個時間范圍,因此即使計數器僅增加整數增量,也可以獲得非整數結果。

 例:返回范圍向量中每個時間序列在過去 5 分鍾內測量的 HTTP 請求數:

increase(http_requests_total{job="api-server"}[5m])

 注:increase只能與counter類型指標(按增長率展示,即相鄰兩個時間點的差值除以時間差)一起使用。它是rate(v)乘以指定時間范圍窗口下的秒數(相當於rate()乘以秒數),更易於人類理解。在記錄規則中使用rate,以便每秒一致地跟蹤增長。

irate()

  irate(v range-vector)  計算范圍向量中時間序列的每秒瞬時增長率。這是基於最后兩個數據點。單調性的中斷(例如由於目標重新啟動而導致的計數器重置)會自動調整。

 例:返回針對范圍向量中每個時間序列的兩個最近數據點的 HTTP 請求的每秒速率,最多可追溯 5 分鍾:

irate(http_requests_total{job="api-server"}[5m])

 注:irate僅應在繪制易失性、快速移動的計數器時使用。用於rate警報和緩慢移動的計數器,因為速率的短暫變化可以重置FOR子句,並且完全由罕見峰值組成的圖形難以閱讀。

     當irate()與 聚合運算符(例如sum())或隨時間聚合的函數(任何以 結尾的函數_over_time)結合時,總是先取一個irate(),然后再聚合。否則irate(),當目標重新啟動時,無法檢測到計數器重置。

label_join()

  label_join(v instant-vector, dst_label string, separator string, src_label_1 string, src_label_2 string, ...)   

  對於v中的每個時間序列,將連接所有src_labels using的所有值,separator並返回帶有dst_label包含連接值的標簽的時間序列。此函數中可以有任意數量src_labels。

 例:返回一個向量,其中每個時間序列都有一個foo帶有添加值的標簽a,b,c:

label_join(up{job="api-server",src1="a",src2="b",src3="c"}, "foo", ",", "src1", "src2", "src3")

label_replace()

  label_replace(v instant-vector, dst_label string, replacement string, src_label string, regex string) 

  對於v中的每個時間序列,將正則表達式regex與標簽的值進行匹配src_label。如果匹配,則dst_label返回的時間序列中的標簽值將是 的擴展replacement,以及輸入中的原始標簽。

  正則表達式中的捕獲組可以用 , 等引用$1。$2如果正則表達式不匹配,則返回時間序列不變。

 例:返回帶有a:c標簽servicea標簽值的時間序列foo

label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*")

ln()

  ln(v instant-vector)  計算v中所有元素的自然對數

 特殊情況:

ln(+Inf) = +Inf
ln(0) = -Inf
ln(x < 0) = NaN
ln(NaN) = NaN

log2()

  log2(v instant-vector)  計算v中所有元素的二進制對數

 特殊情況:等價於ln

log10()

  log10(v instant-vector)  計算v中所有元素的十進制對數

 特殊情況:等價於ln

minute()

  minute(v=vector(time()) instant-vector)  以 UTC 格式返回每個給定時間的小時分鍾,返回值從 0 到 59。

month()

  month(v=vector(time()) instant-vector)  以 UTC 格式返回每個給定時間的一年中的月份,返回值從 1 到 12,其中 1 表示一月。

predict_linear()

  predict_linear(v range-vector, t scalar)  根據范圍向量,使用簡單線性回歸的方法預測從現在開始的到未來t秒的時間序列的值

 注:predict_linear()函數在可視化界面上只能用於儀表盤。

rate()

  rate(v range-vector)  計算范圍向量中時間序列的每秒平均增長率。單調性的中斷(例如由於目標重新啟動而導致的計數器重置)會自動調整。此外,計算推斷到時間范圍的末端,允許錯過刮擦或刮擦周期與該范圍的時間段的不完美對齊。

 例:返回在過去 5 分鍾內測量的每秒 HTTP 請求速率,范圍向量中的每個時間序列:

rate(http_requests_total{job="api-server"}[5m])

 注:rate()只能與counter類型指標一起使用。它最適合警報和緩慢移動計數器的圖形。

     當rate()與聚合運算符(例如sum())或隨時間聚合的函數(任何以 結尾的函數_over_time)結合時,總是先取一個rate(),然后再聚合。否則rate(),當您的目標重新啟動時,無法檢測到計數器重置。

resets()

  resets(v range-vector)  對於每個輸入時間序列,resets(v range-vector)將提供的時間范圍內的計數器重置次數作為即時向量返回。兩個連續樣本之間值的任何減少都被解釋為計數器復位。

 注:resets()只能作用於counter類型指標

round()

  round(v instant-vector, to_nearest=1 scalar)  將所有元素的樣本值四舍五入為v最接近的整數。平局通過四舍五入解決。可選to_nearest參數允許指定樣本值應該四舍五入的最接近的倍數。這個倍數也可以是分數。默認為1。

scalar()

  scalar(v instant-vector)  給定一個單元素輸入向量,scalar(v instant-vector)以標量形式返回該單元素的樣本值。如果輸入向量沒有恰好一個元素,scalar將返回NaN

sgn()

  sgn(v instant-vector)  返回一個向量,其中所有樣本值都轉換為它們的符號,定義如下:如果 v 為正則為 1,如果 v 為負則為 -1,如果 v 等於 0,則為 0。

sort()

  sort(v instant-vector)  返回按樣本值升序排序的向量元素。

sort_desc()

  sort_desc(v instant-vector)  與sort()相同,但按降序排序。

sqrt()

  sqrt(v instant-vector)  計算v中所有元素的平方根

time()

  time()  返回自 1970 年 1 月 1 日 UTC 以來的秒數。請注意,這實際上並不返回當前時間,而是要計算表達式的時間。

timestamp()

  timestamp(v instant-vector)  返回給定向量的每個樣本的時間戳,作為自 1970 年 1 月 1 日 UTC 以來的秒數(Prometheus 2.0+)

vector()

  vector(s scalar)  將標量s作為沒有標簽的向量返回

year()

  year(v=vector(time()) instant-vector)  以 UTC 格式返回每個給定時間的年份。

<aggregation>_over_time()

 以下函數允許隨時間聚合給定范圍向量的每個系列,並返回具有每個系列聚合結果的即時向量:

  • avg_over_time(range_vector)                           指定區間內所有點的平均值
  • min_over_time(range_vector)                           指定區間內所有點的最小值
  • max_over_time(range_vector)                          指定區間內所有點的最大值
  • sum_over_time(range_vector)                          指定區間內所有值的總和
  • count_over_time(range_vector)                        指定區間內所有值的計數
  • quantile_over_time(s scalar, range_vector)      指定區間內的值的 φ 分位數 (0 ≤ φ ≤ 1)
  • stddev_over_time(range_vector)                      指定區間內值的總體標准差
  • stdvar_over_time(range_vector)                       指定區間內值的總體標准方差
  • last_over_time(range-vector)                            指定區間內最近的點值
  • present_over_time(range-vector)                      指定間隔內任何系列的值 1

 注:指定區間中的所有值在聚合中具有相同的權重,即使這些值在整個區間中的間距不相等

 另:標准差值(Population Standard Deviation)計算方法:

  1. 計算一組value的平均值mean
  2. 對每個value計算和mean的差值,並對結果做平方計算得到sd
  3. 對這組sd計算一個平均值sd-mean
  4. 將sd-mean做平方根計算得到result
  5. result即為標准差值

  標准方差計算方法參考:https://www.wallstreetmojo.com/population-variance-formula/

三角函數

 三角函數以弧度工作:

  • acos(v instant-vector)      計算 v 中所有元素的反余弦值(special cases
  • acosh(v instant-vector)    計算 v 中所有元素的反雙曲余弦值 (special cases)
  • asin(v instant-vector)       計算 v 中所有元素的反正弦值 (special cases)
  • asinh(v instant-vector)     計算 v 中所有元素的反雙曲正弦值 (special cases)
  • atan(v instant-vector)      計算 v 中所有元素的反正切 (special cases)
  • atanh(v instant-vector)    計算 v 中所有元素的反雙曲正切 (special cases)
  • cos(v instant-vector)       計算 v 中所有元素的余弦 (special cases)
  • cosh(v instant-vector)     計算 v 中所有元素的雙曲余弦 (special cases)
  • sin(v instant-vector)        計算 v 中所有元素的正弦值 (special cases)
  • sinh(v instant-vector)      計算 v 中所有元素的雙曲正弦值 (special cases)
  • tan(v instant-vector)        計算 v 中所有元素的正切 (special cases)
  • tanh(v instant-vector)      計算 v 中所有元素的雙曲正切 (special cases)

 度數和弧度進行轉換:

  • deg(v instant-vector)      將 v 中的所有元素的弧度轉換為度數
  • pi()                                  返回 pi
  • rad(v instant-vector)       將 v 中的所有元素的度數轉換為弧度

 

四、示例/詳解

4.1、運算符使用

 1)TPS(單位時間內執行 commit、rollback 命令的次數的和)

 指標為counter類型,先用sum聚合成標量,再進行算數運算

sum(irate(mysql_global_status_com_commit{cluster_mark="xxxx_33066",role="master"}[1m])) + sum(irate(mysql_global_status_com_rollback{cluster_mark="xxxx_33066",role="master"}[1m]))

 

 2)計算指標條數,如下,usedMemoryRatio計算的是每台實例的內存使用率,現需要計算整個集群的內存使用率,可先用sum聚合所有數據,再用count計算有多少台實例,相除即可得出集群的內存使用率

sum(usedMemoryRatio{bizName="xxxx",clusterName="xxxx"}) / count(usedMemoryRatio{bizName="xxxx",clusterName="xxxx"}) by (instance)

 

 

4.2、rate()和irate()的算法與區別

 摘抄自:https://blog.csdn.net/u010961631/article/details/105658516/

4.2.1 irate()

 流程:

  • 選取時間范圍內最后兩個點:end1,end2
  • 計算兩個點的差值,這里分兩種情況:

    正常情況下:end2 > end1 ,此時 value = end2 - end1

    異常情況下,end2 < end1 ,此時 value = end2

  • 計算兩個時間點的時間差time = time2 - time1
  • 計算最終結果:value / time 並換算毫秒單位

 特點:

  • 相當於區間內的最后時刻瞬時值
  • 只選取了區間最后兩個點進行計算
  • 如果出現了counter異常類型的值,就會計算出巨大的結果
4.2.2 rate()

 流程:

  • 查找點集合的值差

   如果始終 next > current,那么 value = end - begin

   如果中間存在 A = next < current,那么 value = end - begin + 每個異常的落差A

  • 計算點集合的時間跨度比例P
  • 計算開頭、結尾空隙
  • 計算時間跨度的有效值與時間范圍的比例P
  • 按照P等比例擴大value的值
  • 計算最終結果: value/區間時間跨度

 特點:

  • 相當於區間內的平均值
  • 讀取了區間內最后一個點與第一個點的差值
  • 又等比例擴大了點集合的時間范圍到區間跨度
  • 因此計算比irate較為嚴謹

 

4.3 increase()

 指標類型為counter,慢查詢次數使用rate()只能計算增速,這是無意義的,可以使用increase()來計算增長量

increase(mysql_global_status_slow_queries{cluster_mark="xxxx_33066",role="master"}[1m])

 

4.4 count_values()

 用於時間序列中每一個樣本值出現的次數。count_values 會為每一個唯一的樣本值輸出一個時間序列,並且每一個時間序列包含一個額外的標簽。這個標簽的名字由聚合參數指定,同時這個標簽值是唯一的樣本值。

 例:

  統計不同標簽值的數量:原本的指標是監控機房下實例的版本號,版本號是一直增長的,當實例版本號不一致時,需要觸發告警

count(count_values("version", checkpoint_version{zone_code=~"bjht|gzly"}) by (zone_code)) by (zone_code)

效果展示:

 

 


免責聲明!

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



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