PolarDB MySQL版支持Readable Protobuf功能,即針對存儲在數據庫中的經過Protobuf序列化的Blob類型的字段,您可以在對應的字段上配置Protobuf schema,并通過可視化函數PROTO_TO_JSON(blob_field)
來讀取數據。同時,您也可以使用JSON_EXTRACT()
函數來抽取數據中的部分信息用于創建索引或者虛擬列。
背景信息
在游戲行業,某些信息在存儲時經過了Protobuf序列化,甚至可能還經過了ZLIB壓縮,然后才寫入數據庫中的Blob類型的字段中。這時,數據庫中的Blob類型的數據沒有辦法直接被讀取,對于軟件調試和開發工作很不友好,并且在數據分析場景也需要維護額外的組件來讀取數據。
PolarDB MySQL版提供的Readable Protobuf功能,支持使用可視化函數來直接讀取經過Protobuf序列化且經過ZLIB壓縮的數據,而不需要借助額外的組件。
前提條件
PolarDB集群版本需為PolarDB MySQL版8.0版本且Revision version為8.0.2.2.5及以上,您可以通過查詢版本號確認集群版本。
使用方法
配置Protobuf schema
語法
ALTER TABLE table_name ALTER COLUMN column_name [PROTO_NAME = protobuf_schema_name] PROTO_TEXT = protobuf_schema_definition PROTO_MESSAGE = protobuf_message [COMPRESSION = compression_algorithm]
參數說明
參數
是否必選
說明
PROTO_NAME
否
Protobuf的schema名稱。
PROTO_TEXT
是
Protobuf schema的定義。
PROTO_MESSAGE
是
序列化的Protobuf Message。
COMPRESSION
否
當序列化的Protobuf Message數據在寫入數據庫之前,經過了ZLIB壓縮時需要配置該選項。目前僅支持配置為ZLIB。
說明經過ZLIB壓縮的數據可以使用
UNCOMPRESS()
函數來進行解壓,且解壓后的數據為十六進制的數據。取消字段的Protobuf schema定義
將Protobuf schema設置為空,即可取消字段的Protobuf schema定義。命令如下:
ALTER TABLE table_name ALTER COLUMN column_name PROTO_NAME="" PROTO_TEXT="" PROTO_MESSAGE='';
說明取消字段的Protobuf schema定義前,請確保該字段與相關的索引和虛擬列已解除關聯關系。
查看字段的Protobuf schema定義
執行以下命令,將display_readable_proto_info設置為true。
SET display_readable_proto_info=true;
執行以下命令,查看字段的Protobuf schema定義。
SHOW columns FROM table_name
示例
以表t1
為例,介紹如何使用Readable Protobuf功能,以及如何使用可視化函數PROTO_TO_JSON(blob_field)
提取數據并用來創建索引或虛擬列等。
創建表
t1
,建表語句如下:CREATE TABLE t1(c1 INT, c2 BLOB);
其中,
c2
是經過Protobuf序列化的Blob類型的字段。為
c2
字段添加Protobuf Schema定義。此處使用Protobuf社區的addressbook.proto,如下:
syntax = "proto2"; package tutorial; message Person { optional string name = 1; optional int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { optional string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phones = 4; } message AddressBook { repeated Person people = 1; }
數據未經過ZLIB壓縮時,示例如下:
ALTER TABLE t1 ALTER COLUMN c2 PROTO_NAME="AddressBook" PROTO_TEXT="syntax = \"proto2\";\n\npackage tutorial;\n\nmessage Person {\n optional string name = 1;\n optional int32 id = 2;\n optional string email = 3;\n\n enum PhoneType {\n MOBILE = 0;\n HOME = 1;\n WORK = 2;\n }\n\n message PhoneNumber {\n optional string number = 1;\n optional PhoneType type = 2 [default = HOME];\n }\n\n repeated PhoneNumber phones = 4;\n}\n\nmessage AddressBook {\n repeated Person people = 1;\n}" PROTO_MESSAGE='AddressBook';
數據經過ZLIB壓縮時,示例如下:
ALTER TABLE t1 ALTER COLUMN c2 PROTO_NAME="AddressBook" PROTO_TEXT="syntax = \"proto2\";\n\npackage tutorial;\n\nmessage Person {\n optional string name = 1;\n optional int32 id = 2;\n optional string email = 3;\n\n enum PhoneType {\n MOBILE = 0;\n HOME = 1;\n WORK = 2;\n }\n\n message PhoneNumber {\n optional string number = 1;\n optional PhoneType type = 2 [default = HOME];\n }\n\n repeated PhoneNumber phones = 4;\n}\n\nmessage AddressBook {\n repeated Person people = 1;\n}" PROTO_MESSAGE='AddressBook' COMPRESSION='zlib';
將通過Protobuf序列化后的數據寫入表
t
。數據未經過ZLIB壓縮時,示例如下:
INSERT INTO t1 VALUES(1, X'0a380a0b56697375616c50726f746f10011a1776697375616c70726f746f40706f6c617264622e636f6d220e0a0a313233343536373839301002');
數據經過ZLIB壓縮時,示例如下:
INSERT INTO t1 VALUES(1, X'3C000000785ee3b2e0e20ecb2c2e4dcc0928ca2fc9176094122f03730b405c8782fc9cc4a29424bde4fc5c253e2e2e432363135333730b4b03012600183d10de');
經過ZLIB壓縮后的數據可以通過
UNCOMPRESS()
函數進行解壓,以經過ZLIB壓縮的數據為例,示例如下:SELECT HEX(uncompress(X'3C000000785ee3b2e0e20ecb2c2e4dcc0928ca2fc9176094122f03730b405c8782fc9cc4a29424bde4fc5c253e2e2e432363135333730b4b03012600183d10de')) AS UNCOMPRESS_DATA;
解壓后的十六進制數據如下:
+----------------------------------------------------------------------------------------------------------------------+ | UNCOMPRESS_DATA | +----------------------------------------------------------------------------------------------------------------------+ | 0A380A0B56697375616C50726F746F10011A1776697375616C70726F746F40706F6C617264622E636F6D220E0A0A313233343536373839301002 | +----------------------------------------------------------------------------------------------------------------------+
讀取
c2
列的數據或提取c2
列的數據來創建索引或虛擬列。讀取
c2
列的數據。未使用可視化函數
PROTO_TO_JSON(blob_field)
時,讀取c2
列的數據。數據未經過ZLIB壓縮時,執行如下命令讀取
c2
列的數據:SELECT c2 FROM t1\G
讀取的數據內容如下:
*************************** 1. row *************************** c2: 8 VisualProtovisualpr***@polardb.com" 1234567890
數據經過ZLIB壓縮時,執行如下命令讀取
c2
列的數據:SELECT c2 FROM t1\G
讀取的數據內容如下:
*************************** 1. row *************************** c2: < x^????,.M? (?/?`?/s @\?????$???\%>..C#cS3s K& =?
通過可視化函數
PROTO_TO_JSON(blob_field)
讀取c2
的內容。SELECT PROTO_TO_JSON(c2) FROM t1;
讀取的數據內容如下:
+------------------------------------------------------------------------------------------------------------------------------------------+ | PROTO_TO_JSON(c2) | +------------------------------------------------------------------------------------------------------------------------------------------+ | {"people": [{"id": 1, "name": "VisualProto", "email": "visualpr***@polardb.com", "phones": [{"type": "WORK", "number": "1234567890"}]}]} | +------------------------------------------------- ----------------------------------------------------------------------+
說明通過可視化函數
PROTO_TO_JSON(blob_field)
讀取數據時,經過ZLIB壓縮的數據和未經過ZLIB壓縮的數據均能被讀取。使用JSON函數提取
c2
列的部分數據,示例如下:SELECT json_extract(PROTO_TO_JSON(c2), '$.people[0].name') FROM t1;
提取的數據內容如下:
+-----------------------------------------------------+ | json_extract(PROTO_TO_JSON(c2), '$.people[0].name') | +-----------------------------------------------------+ | "VisualProto" | +-----------------------------------------------------+
提取
c2
列的數據來創建索引,示例如下:CREATE INDEX i_email ON t1((cast(JSON_UNQUOTE(json_extract(PROTO_TO_JSON(c2), '$.people[0].email')) AS char(100))));
使用EXPLAIN 命令檢測SQL語句的執行性能,示例如下:
EXPLAIN SELECT * FROM t1 WHERE (cast(JSON_UNQUOTE(json_extract(PROTO_TO_JSON(c2), '$.people[0].ema
執行結果如下:
+----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+ | 1 | SIMPLE | t1 | NULL | ref | i_email | i_email | 403 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------+---------+---------+-------+------+----------+-------+
提取
c2
列的數據來創建虛擬列,示例如下:ALTER TABLE t1 ADD COLUMN c3 varchar(100) AS (json_extract(proto_to_json(`c2`), _utf8mb4'$.people[0].email'));
執行如下命令,查看
t1
表的表結構:desc t1;
t1
表的表結構如下:+-------+--------------+------+-----+---------+-------------------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------------------+ | c1 | int(11) | YES | | NULL | | | c2 | blob | YES | | NULL | | | c3 | varchar(100) | YES | | NULL | VIRTUAL GENERATED | +-------+--------------+------+-----+---------+-------------------+
其中,
c3
為新創建的虛擬列。