背景・目的
少し前ですが、2021年12月に、Redshiftでvarbyte型をサポートされたということで検証してみます。
まとめ
- Redshiftでバイナリを格納する場合に、varbyte型が利用できる。
- varbyte型とvarchar型を比較して性能差は見られなかった。
概要
varbyte型とは
- 可変長バイナリ文字列を格納するための可変長データ型
- 最大バイト数の範囲は、1〜1,024,000、デフォルトは64,000
- 表示できるようにするため、varbyte値は16進数で表わされる。
- キャストについて
- 以下のデータ型とのキャストをサポート
- CHAR
- VARCHAR
- SMALLINT
- INTEGER
- BIGINT
- 挙動
- CHARとVARCHARのキャストでは、utf-8が使用される。
- smallint、integer、bigintからキャストする場合は、元データ型のバイト数は維持される。
- smallintは、2バイト
- integerは、4バイト
- bigintは、8バイト
- 以下のデータ型とのキャストをサポート
制約事項
- Spectrumはvarbyteをサポートしてない。
- クエリエディタ(v2含む)では、現段階で(2022年9月時点で)完全にサポートしてない。他のSQLをクライアントを使用する。
- Python、LamdaのUDFではvarbyteを使用できない。
- varbyte列から、hillsketch列の作成、varbyte列でapproximate count distinctは使用できない。
実践
動作確認
「test」文字列を、varbyteにキャストし16進数で表示
# 文字列を表示
sample=# select 'test'
sample-# ;
?column?
----------
test
(1 row)
sample=#
# 文字列を「varchar」型にcast
sample=# select 'test'::varchar;
varchar
---------
test
(1 row)
sample=#
sample=# select 'test'::varbyte;
varbyte
----------
74657374
(1 row)
sample=#
検証
以下の確認を行います。
- varbyteの圧縮率
- 性能
事前準備
DDL作成
- スキーマはオリジナルのスキーマとします。
create schema varbytecomp;
- DDLは以下のとおりです。
- こちらのDDLを元に作成しています。※スキーマは、varvytecompとしています。
create table varbytecomp.users(
userid integer not null distkey sortkey,
username char(8) ENCODE ZSTD,
firstname varchar(30) ENCODE ZSTD,
lastname varchar(30) ENCODE ZSTD,
city varchar(30) ENCODE ZSTD,
state char(2) ENCODE ZSTD,
email varchar(100) ENCODE ZSTD,
phone char(14) ENCODE ZSTD,
likesports boolean,
liketheatre boolean,
likeconcerts boolean,
likejazz boolean,
likeclassical boolean,
likeopera boolean,
likerock boolean,
likevegas boolean,
likebroadway boolean,
likemusicals boolean);
create table varbytecomp.venue(
venueid smallint not null distkey sortkey,
venuename varchar(100) ENCODE ZSTD,
venuecity varchar(30) ENCODE ZSTD,
venuestate char(2) ENCODE ZSTD,
venueseats integer ENCODE AZ64 );
create table varbytecomp.category(
catid smallint not null distkey sortkey,
catgroup varchar(10) ENCODE ZSTD,
catname varchar(10) ENCODE ZSTD,
catdesc varchar(50) ENCODE ZSTD);
create table varbytecomp.date(
dateid smallint not null distkey sortkey,
caldate date not null,
day character(3) ENCODE ZSTD not null,
week smallint ENCODE AZ64 not null,
month character(5) ENCODE ZSTD not null,
qtr character(5) ENCODE ZSTD not null,
year smallint ENCODE AZ64 not null,
holiday boolean default('N'));
create table varbytecomp.event(
eventid integer not null distkey,
venueid smallint ENCODE AZ64 not null,
catid smallint ENCODE AZ64 not null,
dateid smallint ENCODE AZ64 not null sortkey,
eventname varchar(200) ENCODE ZSTD ,
starttime timestamp ENCODE AZ64
);
create table varbytecomp.listing(
listid integer not null distkey,
sellerid integer ENCODE AZ64 not null,
eventid integer ENCODE AZ64 not null,
dateid smallint ENCODE AZ64 not null sortkey,
numtickets smallint ENCODE AZ64 not null,
priceperticket decimal(8,2) ENCODE AZ64 ,
totalprice decimal(8,2) ENCODE AZ64 ,
listtime timestamp ENCODE AZ64
);
create table varbytecomp.sales(
salesid integer ENCODE AZ64 not null,
listid integer not null distkey,
sellerid integer ENCODE AZ64 not null,
buyerid integer ENCODE AZ64 not null,
eventid integer ENCODE AZ64 not null,
dateid smallint ENCODE AZ64 not null sortkey,
qtysold smallint ENCODE AZ64 not null,
pricepaid decimal(8,2) ENCODE AZ64 ,
commission decimal(8,2) ENCODE AZ64 ,
saletime timestamp ENCODE AZ64
);
- 結果を確認します。
select * from svv_all_tables where schema_name='varbytecomp';
===
database_name,schema_name,table_name,table_type,table_acl,remarks
sample,varbytecomp,users,TABLE,,
sample,varbytecomp,venue,TABLE,,
sample,varbytecomp,category,TABLE,,
sample,varbytecomp,date,TABLE,,
sample,varbytecomp,event,TABLE,,
sample,varbytecomp,listing,TABLE,,
sample,varbytecomp,sales,TABLE,,
データの準備
- データを準備します。
- こちらのページのtickitdb.zipをダウンロードし、Redshiftクラスタと同じリージョンのS3バケットにアップロードします。
$ aws s3 ls ${mybucket}/example/
2022-09-27 20:36:56 0
2022-09-27 20:37:51 445838 allevents_pipe.txt
2022-09-27 20:37:55 5893626 allusers_pipe.txt
2022-09-27 20:37:56 465 category_pipe.txt
2022-09-27 20:37:57 14534 date2008_pipe.txt
2022-09-27 20:38:02 11585036 listings_pipe.txt
2022-09-27 20:38:10 11260097 sales_tab.txt
2022-09-27 20:38:13 7988 venue_pipe.txt
$
- データをテーブルにCOPYします。
copy varbytecomp.users from 's3://${mybucket}/example/allusers_pipe.txt'
iam_role 'arn:aws:iam::${AccountId}:role/${RoleName}'
delimiter '|' region '${Region}';
copy varbytecomp.venue from 's3://${mybucket}/example/venue_pipe.txt'
iam_role 'arn:aws:iam::${AccountId}:role/${RoleName}'
delimiter '|' region '${Region}';
copy varbytecomp.category from 's3://${mybucket}/example/category_pipe.txt'
iam_role 'arn:aws:iam::${AccountId}:role/${RoleName}'
delimiter '|' region '${Region}';
copy varbytecomp.date from 's3://${mybucket}/example/date2008_pipe.txt'
iam_role 'arn:aws:iam::${AccountId}:role/${RoleName}'
delimiter '|' region '${Region}';
copy varbytecomp.event from 's3://${mybucket}/example/allevents_pipe.txt'
iam_role 'arn:aws:iam::${AccountId}:role/${RoleName}'
delimiter '|' region '${Region}';
copy varbytecomp.listing from 's3://${mybucket}/example/listings_pipe.txt'
iam_role 'arn:aws:iam::${AccountId}:role/${RoleName}'
delimiter '|' region '${Region}';
copy varbytecomp.sales from 's3://${mybucket}/example/sales_tab.txt'
iam_role 'arn:aws:iam::${AccountId}:role/${RoleName}'
delimiter '\t' timeformat 'MM/DD/YYYY HH:MI:SS' region '${Region}';
- データの件数を確認します。
select 'category', count(1) from varbytecomp.category
union all
select 'date', count(1) from varbytecomp.date
union all
select 'event', count(1) from varbytecomp.event
union all
select 'listing', count(1) from varbytecomp.listing
union all
select 'sales',count(1) from varbytecomp.sales
union all
select 'users', count(1) from varbytecomp.users
union all
select 'venue', count(1) from varbytecomp.venue
;
===
column,count
users,49990
date,365
listing,192497
venue,202
sales,172456
category,11
event,8798
varbyte型のデータを準備
- emailカラムがvarbyte型のテーブルを用意します。この時点では圧縮エンコードはマニュアルに特に書いていませんので指定はしません。
create table varbytecomp.users_test(
userid integer not null distkey sortkey,
username char(8) ENCODE ZSTD,
firstname varchar(30) ENCODE ZSTD,
lastname varchar(30) ENCODE ZSTD,
city varchar(30) ENCODE ZSTD,
state char(2) ENCODE ZSTD,
email varbyte ,
phone char(14) ENCODE ZSTD,
likesports boolean,
liketheatre boolean,
likeconcerts boolean,
likejazz boolean,
likeclassical boolean,
likeopera boolean,
likerock boolean,
likevegas boolean,
likebroadway boolean,
likemusicals boolean);
- 環境
INSERT INTO varbytecomp.users_test(
userid
,username
,firstname
,lastname
,city
,state
,email
,phone
,likesports
,liketheatre
,likeconcerts
,likejazz
,likeclassical
,likeopera
,likerock
,likevegas
,likebroadway
,likemusicals
)
SELECT
userid
,username
,firstname
,lastname
,city
,state
,TO_VARBYTE(email ,'utf-8')
,phone
,likesports
,liketheatre
,likeconcerts
,likejazz
,likeclassical
,likeopera
,likerock
,likevegas
,likebroadway
,likemusicals
FROM varbytecomp.users
- データの行数と、バイト列を確認します。
- email列の合計length、行数を確認しています。違いは無いようです。
select 'varbytecomp.users', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users
union all
select 'varbytecomp.users_test', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users_test
;
===
?column? | sum | sum1 | count
------------------------+---------+---------+-------
varbytecomp.users | 1375665 | 1375665 | 49990
varbytecomp.users_test | 1375665 | 1375665 | 49990
(2 rows)
- 列圧縮が最適になっているか確認します。
sample=# ANALYZE COMPRESSION varbytecomp.users;
Table | Column | Encoding | Est_reduction_pct
-------+---------------+----------+-------------------
users | userid | raw | 0.00
users | username | zstd | 0.00
users | firstname | zstd | 0.00
users | lastname | zstd | 0.00
users | city | zstd | 0.00
users | state | raw | 0.00
users | email | zstd | 0.00
users | phone | zstd | 0.00
users | likesports | raw | 0.00
users | liketheatre | raw | 0.00
users | likeconcerts | raw | 0.00
users | likejazz | raw | 0.00
users | likeclassical | raw | 0.00
users | likeopera | raw | 0.00
users | likerock | raw | 0.00
users | likevegas | raw | 0.00
users | likebroadway | raw | 0.00
users | likemusicals | raw | 0.00
(18 rows)
# emailは、圧縮エンコード「zstd」に変更したほうが良さそうです。
sample=# ANALYZE COMPRESSION varbytecomp.users_test;
Table | Column | Encoding | Est_reduction_pct
------------+---------------+----------+-------------------
users_test | userid | raw | 0.00
users_test | username | zstd | 0.00
users_test | firstname | zstd | 0.00
users_test | lastname | zstd | 0.00
users_test | city | zstd | 0.00
users_test | state | raw | 0.00
users_test | email | zstd | 33.80
users_test | phone | zstd | 0.00
users_test | likesports | raw | 0.00
users_test | liketheatre | raw | 0.00
users_test | likeconcerts | raw | 0.00
users_test | likejazz | raw | 0.00
users_test | likeclassical | raw | 0.00
users_test | likeopera | raw | 0.00
users_test | likerock | raw | 0.00
users_test | likevegas | raw | 0.00
users_test | likebroadway | raw | 0.00
users_test | likemusicals | raw | 0.00
(18 rows)
sample=#
- 圧縮エンコードをZSTDにしたものを作り、データ移行、再度確認します。
- 圧縮率可能率が0.00になりました。
create table varbytecomp.users_test_zstd(
userid integer not null distkey sortkey,
username char(8) ENCODE ZSTD,
firstname varchar(30) ENCODE ZSTD,
lastname varchar(30) ENCODE ZSTD,
city varchar(30) ENCODE ZSTD,
state char(2) ENCODE ZSTD,
email varbyte ENCODE ZSTD ,
phone char(14) ENCODE ZSTD,
likesports boolean,
liketheatre boolean,
likeconcerts boolean,
likejazz boolean,
likeclassical boolean,
likeopera boolean,
likerock boolean,
likevegas boolean,
likebroadway boolean,
likemusicals boolean);
INSERT INTO varbytecomp.users_test_zstd(
userid
,username
,firstname
,lastname
,city
,state
,email
,phone
,likesports
,liketheatre
,likeconcerts
,likejazz
,likeclassical
,likeopera
,likerock
,likevegas
,likebroadway
,likemusicals
)
SELECT
userid
,username
,firstname
,lastname
,city
,state
,TO_VARBYTE(email ,'utf-8')
,phone
,likesports
,liketheatre
,likeconcerts
,likejazz
,likeclassical
,likeopera
,likerock
,likevegas
,likebroadway
,likemusicals
FROM varbytecomp.users
ANALYZE COMPRESSION varbytecomp.users_test_zstd
sample=# ANALYZE COMPRESSION varbytecomp.users_test_zstd
sample-# ;
Table | Column | Encoding | Est_reduction_pct
-----------------+---------------+----------+-------------------
users_test_zstd | userid | raw | 0.00
users_test_zstd | username | zstd | 0.00
users_test_zstd | firstname | zstd | 0.00
users_test_zstd | lastname | zstd | 0.00
users_test_zstd | city | zstd | 0.00
users_test_zstd | state | raw | 0.00
users_test_zstd | email | zstd | 0.00
users_test_zstd | phone | zstd | 0.00
users_test_zstd | likesports | raw | 0.00
users_test_zstd | liketheatre | raw | 0.00
users_test_zstd | likeconcerts | raw | 0.00
users_test_zstd | likejazz | raw | 0.00
users_test_zstd | likeclassical | raw | 0.00
users_test_zstd | likeopera | raw | 0.00
users_test_zstd | likerock | raw | 0.00
users_test_zstd | likevegas | raw | 0.00
users_test_zstd | likebroadway | raw | 0.00
users_test_zstd | likemusicals | raw | 0.00
(18 rows)
sample=# select 'varbytecomp.users', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users
sample-# union all
sample-# select 'varbytecomp.users_test_zstd', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users_test_zstd
sample-# ;
?column? | sum | sum1 | count
-----------------------------+---------+---------+-------
varbytecomp.users | 1375665 | 1375665 | 49990
varbytecomp.users_test_zstd | 1375665 | 1375665 | 49990
(2 rows)
sample=#
sample=#
sample=#
select 'varbytecomp.users', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users
union all
select 'varbytecomp.users_test_zstd', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users_test_zstd
;
varbyteの圧縮率
- ブロック数は変わらない。336ブロック=1MB * 336ブロック=336MBで、かわらない。
- email列の圧縮エンコード変えても変更はない。
emailなど、数値や連番ではないので、圧縮効果はあまりないと思われる。
sample=# select name, cnt from
sample-# (
sample(# select tbl, count(*) as cnt
sample(# from stv_blocklist
sample(# where tbl in (
sample(# select id
sample(# from stv_tbl_perm)
sample(# group by tbl
sample(# ) block_list
sample-# inner join stv_tbl_perm
sample-# on block_list.tbl=stv_tbl_perm.id
sample-# where stv_tbl_perm.name like '%users%'
sample-# group by tbl, stv_tbl_perm.name, block_list.cnt
sample-# order by cnt;
name | cnt
----------------------------------------------------------------------------------------------------------------------------------+-----
users_test | 336
users | 336
users_test_zstd | 336
(3 rows)
sample=#
性能
以下の検証を行う。
- Where句に検索条件を指定した場合の性能
- 結合条件に指定した場合の性能
- 結果セットのキャッシュはオフの状態で試験する。
SET enable_result_cache_for_session = off;
Where句に検索条件を指定した場合の性能
- varcharで指定した場合の平均が、21.345msに対して、varbyteの場合の平均が21.994msであった。50,000件弱程度のレコードで約3%程度違いが見られた。
- varbyteのほうが3%程度遅い結果となった。
5万件程度で件数が少ないため、性能差は見られないと思われる。
検証結果
- varcharの検証結果
検証回数 | 実行時間(ms) |
---|---|
1 | 23.497 |
2 | 22.308 |
3 | 21.655 |
4 | 20.051 |
5 | 19.901 |
6 | 21.411 |
7 | 20.106 |
8 | 21.034 |
9 | 20.540 |
10 | 22.950 |
平均実行時間 | 21.345 |
- varbyteの検証結果
検証回数 | 実行時間(ms) |
---|---|
1 | 23.572 |
2 | 21.710 |
3 | 22.824 |
4 | 20.841 |
5 | 20.831 |
6 | 22.108 |
7 | 20.772 |
8 | 21.287 |
9 | 23.998 |
10 | 22.000 |
平均実行時間 | 21.994 |
実行メモ
- varchar型に保存されているemailアドレスをキーに確認する。
sample=# \timing
Timing is on.
sample=# SET enable_result_cache_for_session = off;
SET
Time: 3.090 ms
sample=# select * from varbytecomp.users where email='lorem.ipsum@Vestibulumante.com';
userid | username | firstname | lastname | city | state | email | phone | likesports | liketheatre | likeconcerts | likejazz | likeclassical | likeopera | likerock | likevegas | likebroadway | likemu
sicals
--------+----------+-----------+----------+---------+-------+--------------------------------+----------------+------------+-------------+--------------+----------+---------------+-----------+----------+-----------+--------------+-------
-------
15 | OWU78MTR | Scarlett | Mayer | Gadsden | GA | lorem.ipsum@Vestibulumante.com | (189) 882-8412 | t | f | t | | | t | | | t |
(1 row)
Time: 21.592 ms
sample=#
- varbyte型に保存されているemailアドレスをキーに確認する。
sample=# select * from varbytecomp.users_test_zstd where email= to_varbyte('lorem.ipsum@Vestibulumante.com','utf-8');
userid | username | firstname | lastname | city | state | email | phone | likesports | liketheatre | likeconcerts | likejazz | likeclassical | likeopera | likerock | lik
evegas | likebroadway | likemusicals
--------+----------+-----------+----------+---------+-------+--------------------------------------------------------------+----------------+------------+-------------+--------------+----------+---------------+-----------+----------+----
-------+--------------+--------------
15 | OWU78MTR | Scarlett | Mayer | Gadsden | GA | 6c6f72656d2e697073756d40566573746962756c756d616e74652e636f6d | (189) 882-8412 | t | f | t | | | t | |
| t |
(1 row)
Time: 23.527 ms
sample=#
結合条件に指定した場合の性能
テーブルとデータを用意
ジョインのためテーブルを作成し、データをINSERTする。
- varchar型のテーブルを作成&データをINSERT
create table varbytecomp.users_copy(
userid integer not null distkey sortkey,
username char(8) ENCODE ZSTD,
firstname varchar(30) ENCODE ZSTD,
lastname varchar(30) ENCODE ZSTD,
city varchar(30) ENCODE ZSTD,
state char(2) ENCODE ZSTD,
email varchar(100) ENCODE ZSTD,
phone char(14) ENCODE ZSTD,
likesports boolean,
liketheatre boolean,
likeconcerts boolean,
likejazz boolean,
likeclassical boolean,
likeopera boolean,
likerock boolean,
likevegas boolean,
likebroadway boolean,
likemusicals boolean);
INSERT INTO varbytecomp.users_copy(
userid
,username
,firstname
,lastname
,city
,state
,email
,phone
,likesports
,liketheatre
,likeconcerts
,likejazz
,likeclassical
,likeopera
,likerock
,likevegas
,likebroadway
,likemusicals
)
SELECT
userid
,username
,firstname
,lastname
,city
,state
,email
,phone
,likesports
,liketheatre
,likeconcerts
,likejazz
,likeclassical
,likeopera
,likerock
,likevegas
,likebroadway
,likemusicals
FROM varbytecomp.users;
- varbyte型のテーブルを作成&データをINSERT
create table varbytecomp.users_test_zstd_copy(
userid integer not null distkey sortkey,
username char(8) ENCODE ZSTD,
firstname varchar(30) ENCODE ZSTD,
lastname varchar(30) ENCODE ZSTD,
city varchar(30) ENCODE ZSTD,
state char(2) ENCODE ZSTD,
email varbyte ENCODE ZSTD ,
phone char(14) ENCODE ZSTD,
likesports boolean,
liketheatre boolean,
likeconcerts boolean,
likejazz boolean,
likeclassical boolean,
likeopera boolean,
likerock boolean,
likevegas boolean,
likebroadway boolean,
likemusicals boolean);
INSERT INTO varbytecomp.users_test_zstd_copy(
userid
,username
,firstname
,lastname
,city
,state
,email
,phone
,likesports
,liketheatre
,likeconcerts
,likejazz
,likeclassical
,likeopera
,likerock
,likevegas
,likebroadway
,likemusicals
)
SELECT
userid
,username
,firstname
,lastname
,city
,state
,email
,phone
,likesports
,liketheatre
,likeconcerts
,likejazz
,likeclassical
,likeopera
,likerock
,likevegas
,likebroadway
,likemusicals
FROM varbytecomp.users_test_zstd;
- データを確認する。
select 'varbytecomp.users_copy', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users_copy
union all
select 'varbytecomp.users', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users
union all
select 'varbytecomp.users_test_zstd', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users_test_zstd
union all
select 'varbytecomp.users_test_zstd_copy', sum(length(email)),sum(octet_length(email)),count(1) from varbytecomp.users_test_zstd_copy
?column? | sum | sum1 | count
----------------------------------+---------+---------+-------
varbytecomp.users_test_zstd | 1375665 | 1375665 | 49990
varbytecomp.users_copy | 1375665 | 1375665 | 49990
varbytecomp.users_test_zstd_copy | 1375665 | 1375665 | 49990
varbytecomp.users | 1375665 | 1375665 | 49990
- 圧縮サイズの確認
select name, cnt from
(
select tbl, count(*) as cnt
from stv_blocklist
where tbl in (
select id
from stv_tbl_perm)
group by tbl
) block_list
inner join stv_tbl_perm
on block_list.tbl=stv_tbl_perm.id
where stv_tbl_perm.name like '%users%'
group by tbl, stv_tbl_perm.name, block_list.cnt
order by cnt;
----------------------------------------------------------------------------------------------------------------------------------+-----
users_test | 336
users_copy | 336
users_test_zstd_copy | 336
users_test_zstd | 336
users | 336
(5 rows)
検証結果
- varcharは、varbyteで18%程度(6ms程度)の性能差が見られた。
- varcharの平均実行時間は、36.049ms
- varbyteの平均実行時間は、42.867ms
クエリ
- varcharの結合
select base.email from varbytecomp.users base, varbytecomp.users_copy dummy where base.email = dummy.email;
- varbyteの結合
select base.email from varbytecomp.users_test_zstd base, varbytecomp.users_test_zstd_copy dummy where base.email = dummy.email;
- varchar 検証結果
検証回数 | 実行時間(ms) |
---|---|
1 | 36.040 |
2 | 37.777 |
3 | 35.470 |
4 | 36.084 |
5 | 36.305 |
6 | 34.892 |
7 | 36.954 |
8 | 35.053 |
9 | 36.531 |
10 | 35.382 |
平均実行時間 | 36.049 |
- varbyteの検証結果
検証回数 | 実行時間(ms) |
---|---|
1 | 42.991 |
2 | 42.624 |
3 | 44.781 |
4 | 42.699 |
5 | 43.793 |
6 | 43.561 |
7 | 43.362 |
8 | 40.313 |
9 | 43.243 |
10 | 41.302 |
平均実行時間 | 42.867 |
考察
- 結合では、6ms程度(18%)、検索では、0.6ms(3%)程度の性能差となった。
- 件数が5万件程度では、さほど性能差が見られなかった。
参考