QuickSight本番導入で半年ハマった話|SPICE・RLS・権限の落とし穴と今の構成
「AWSネイティブだから楽なはず」と思って導入したら想定外の地獄が待っていた。SPICE管理・Row-Level Security・データソース設計で躓いた経験と、半年かけて辿り着いた構成を公開します。
QuickSightを導入した経緯と最初の地獄
半年前、うちのチームでデータ基盤の刷新プロジェクトが走っていて、その仕上げとしてBIツールをTableauからQuickSightに移行した。正直、最初は懐疑的だった。「AWSネイティブだからS3やRedshiftとの連携は楽だろう」と思っていたが、実際に動かしてみるとSpiceのキャパシティ管理、Row-Level Security(RLS)の設定、そして権限周りで予想外の落とし穴だらけだった。
MetabaseやSupersetとの比較検討もしていた(MetabaseとSuperset徹底比較2026の記事に詳しく書いたのでよければ)。最終的にQuickSightを選んだ理由はAWS IAMとの統合の容易さと、Q(生成AIアシスト)機能の成熟度だった。2026年時点のQuickSight Qは以前と比べて相当使い物になってきている。
「ちゃんと設計しておけば半分の苦労で済んだ」という知見をまとめておく。同じ轍を踏んでほしくない。
全体アーキテクチャ:うちの構成を晒す
実際に本番で動かしているAWS構成を書いておく。データソースからQuickSightへのデータフローを整理したのが以下だ。
graph TB
subgraph DataSources["データソース層"]
S3["S3 (Parquet/Iceberg)"]
RDS["Aurora PostgreSQL 17"]
end
subgraph ETL["変換・集約層"]
Glue["AWS Glue 4.0"]
dbt["dbt Cloud"]
GlueCatalog["Glue Data Catalog"]
end
subgraph DW["データウェアハウス"]
Redshift["Redshift Serverless"]
Athena["Athena v3"]
end
subgraph QS["QuickSight 層"]
SPICE["SPICE エンジン"]
DirectQuery["Direct Query"]
Datasets["Datasets (計算フィールド)"]
Analyses["Analyses"]
Dashboards["Dashboards"]
QFeature["QuickSight Q / AI アシスト"]
end
subgraph Security["認証・権限"]
IAM["IAM Identity Center"]
RLS["Row-Level Security"]
end
subgraph Consumers["利用者"]
Exec["経営層 (Reader)"]
Analyst["データアナリスト (Author)"]
BizUser["ビジネスユーザー (Reader)"]
end
S3 -->|Crawl| GlueCatalog
RDS -->|CDC / dbt| Redshift
GlueCatalog --> Athena
GlueCatalog --> Glue
Glue --> Redshift
Athena --> SPICE
Redshift --> SPICE
Redshift --> DirectQuery
SPICE --> Datasets
DirectQuery --> Datasets
Datasets --> Analyses
Analyses --> Dashboards
Dashboards --> QFeature
IAM --> QS
RLS --> Datasets
Dashboards --> Exec
Dashboards --> Analyst
Dashboards --> BizUser
ポイントは「SPICEとDirect Queryを意図的に使い分ける」ことだ。最初はすべてSPICEに突っ込もうとして、月のキャパシティコストが想定の2倍になった。この失敗から学んだ分類基準を次のセクションで詳しく書く。
Redshift Serverlessとの連携についてはRedshift Serverless運用で月50万円→18万円に削減した話も参考になるのであわせてどうぞ。
SPICE vs Direct Query:これを最初に決めないと死ぬ
半年運用してわかった一番の知見がここだ。SPICEとDirect Queryどちらを使うかを「なんとなく」で決めると、後でリプレイスするのがめちゃくちゃ大変になる。
判断基準はこれだけ
| 項目 | SPICE | Direct Query |
|---|---|---|
| データ鮮度 | 最短15分おきに更新 | リアルタイム |
| クエリ速度 | 高速(インメモリ) | データソース依存 |
| コスト | GB単価(0.38 USD/GB/月) | クエリ課金 |
| 向いているデータ量 | 〜数百GB | 大規模(TB級) |
| ユーザーの同時アクセス | 強い | 弱い(DBに負荷) |
| 計算フィールドの自由度 | 高い | 中程度 |
| 更新遅延の許容 | 要件次第 | リアルタイム必須 |
実際にうちで起きたことを言うと、売上ダッシュボードを最初はRedshiftへのDirect Queryで作った。ところが経営会議の時間帯に同時ユーザーが30人を超えると、Redshiftへのクエリが詰まってダッシュボードが応答しなくなった。結果的に売上集計のメインテーブル(約8GB)はSPICEに移行して、15分更新で運用している。リアルタイム性が求められる現場モニタリング(在庫状況など)だけDirect Queryを残した。個人的には「迷ったらSPICE、リアルタイム要件があるものだけDirect Queryに落とす」くらいのスタンスでちょうどいいと思っている。
# SPICEキャパシティの使用量をモニタリングするスクリプト(定期実行)
import boto3
import json
from datetime import datetime
client = boto3.client('quicksight', region_name='ap-northeast-1')
ACCOUNT_ID = 'YOUR_ACCOUNT_ID'
def get_spice_capacity():
response = client.describe_account_subscription(
AwsAccountId=ACCOUNT_ID
)
capacity_info = response['AccountInfo']
# 2026年現在: describe_account_settings で詳細確認
settings = client.describe_account_settings(
AwsAccountId=ACCOUNT_ID
)
return {
'timestamp': datetime.now().isoformat(),
'edition': capacity_info.get('Edition'),
}
def list_dataset_spice_usage():
"""データセットごとのSPICE使用量を確認"""
datasets = client.list_data_sets(AwsAccountId=ACCOUNT_ID)
usage_list = []
for ds in datasets['DataSetSummaries']:
if ds['ImportMode'] == 'SPICE':
detail = client.describe_data_set(
AwsAccountId=ACCOUNT_ID,
DataSetId=ds['DataSetId']
)
usage_list.append({
'name': ds['Name'],
'consumed_spice_capacity_in_bytes': detail['DataSet'].get(
'ConsumedSpiceCapacityInBytes', 0
),
'last_updated': str(ds.get('LastUpdatedTime', 'N/A'))
})
return sorted(
usage_list,
key=lambda x: x['consumed_spice_capacity_in_bytes'],
reverse=True
)
if __name__ == '__main__':
usage = list_dataset_spice_usage()
for item in usage[:5]:
size_gb = item['consumed_spice_capacity_in_bytes'] / (1024**3)
print(f"{item['name']}: {size_gb:.2f} GB (更新: {item['last_updated']})")
このスクリプトをLambdaで毎日実行してCloudWatchにメトリクスを送ることで、SPICEの使用量急増をアラートできるようにしている。地味に便利だし、これを仕掛けてなかったら4月の爆増にもっと遅く気づいていたと思う。
SPICE使用量の傾向
xychart-beta
title "月次SPICE使用量の推移 (GB)"
x-axis ["1月", "2月", "3月", "4月", "5月", "6月"]
y-axis "使用量 (GB)" 0 --> 500
bar [80, 130, 190, 280, 380, 420]
line [80, 130, 190, 280, 380, 420]
導入当初は80GBだったのが、ダッシュボードの数と利用ユーザーが増えるにつれて右肩上がりになっている。4月に一気に跳ね上がったのは、分析チームが承認なしでかなりでかいデータセットをSPICEに登録していたため。ガバナンスの話はあとで触れるが、これを防ぐ仕組みを早めに作っておくべきだったと反省している。
Row-Level Security設計——ここを舐めると情報漏洩事故になる
QuickSightのRLSは、ビジネスユーザーに自社のデータだけを見せたいマルチテナント構成や、営業部門ごとにデータを分けたい場合に必須になる。うちでは「部門長は自分の部門の数字だけ見られる」という要件があり、最初の設計が甘くて一度やり直した。正直、最初から設計しておけばよかったと今でも後悔している。
RLSの設計パターン
QuickSightのRLSはデータセット単位で設定する。ルールテーブル(権限テーブル)をS3またはRedshiftに用意して、そこにユーザー→フィルタ条件のマッピングを書く方式だ。
-- Redshiftにルールテーブルを作成する例
CREATE TABLE quicksight_rls_rules (
UserName VARCHAR(256), -- QuickSightのユーザー名 or グループ名
GroupName VARCHAR(256), -- IAM Identity Centerのグループ
department_code VARCHAR(20),
region_code VARCHAR(10)
);
-- 部門コードによるフィルタリング例
INSERT INTO quicksight_rls_rules VALUES
('tanaka@example.com', NULL, 'SALES', 'JP'),
('suzuki@example.com', NULL, 'MARKETING', 'JP'),
(NULL, 'finance-group', 'FINANCE', NULL); -- NULLはすべての値にマッチ
このルールテーブルをQuickSightのデータセット設定でRow-Level Securityとして指定すると、ログインユーザーのQuickSightユーザー名とUserName列が突合されて自動的にデータがフィルタリングされる。
ハマりポイントをまとめると:
UserNameとGroupNameの両方が指定されているとORで評価される(最初ANDだと思っていてデータが意図せず広く見えていた)- RLSを設定したデータセットは管理者でも「管理者ロール」として明示的に指定しないとデータが見えなくなる
- ルールテーブルのユーザー名はQuickSightのユーザー名(メールアドレスがデフォルト)と完全一致が必要
最初のOR評価の挙動は本当に気づきにくいので、RLS設定後は必ずテストユーザーで意図どおりにフィルタされているか確認してほしい。
# RLS設定をAPIで自動化する例(IAM Identity Center連携時)
import boto3
qs_client = boto3.client('quicksight', region_name='ap-northeast-1')
ACCOUNT_ID = 'YOUR_ACCOUNT_ID'
def update_rls_dataset(dataset_id: str, rls_dataset_arn: str):
"""データセットにRLSを設定/更新する"""
try:
response = qs_client.update_data_set(
AwsAccountId=ACCOUNT_ID,
DataSetId=dataset_id,
# ... その他の既存パラメータを維持しつつ
RowLevelPermissionDataSet={
'Arn': rls_dataset_arn,
'PermissionPolicy': 'GRANT_ACCESS', # or DENY_ACCESS
'FormatVersion': 'VERSION_2',
'Status': 'ENABLED'
}
)
print(f"RLS設定成功: {dataset_id}")
return response
except qs_client.exceptions.InvalidParameterValueException as e:
print(f"RLS設定エラー: {e}")
raise
QuickSight Q(生成AIアシスト)の実態——2026年現在
QuickSight Qが大きく変わったのは2025年後半あたりからで、2026年現在はGenerative BIとしてかなり実用的になった。まだ検証中の部分もあるが、うちで実際に使っている機能を正直に評価しながらまとめておく。
使ってみて良かった機能
1. Qトピックへの自然言語クエリ
ビジネスユーザーが「先月の東京エリアの売上上位5店舗は?」と日本語で入力すると、QuickSightが適切なビジュアルを自動生成する。精度は体感70〜80%くらいで、シンプルな集計や比較はほぼ問題ない。一方、複雑な条件(「前月比でかつ目標比100%以上」みたいなの)は崩れることがある。ここは過度な期待をしないほうがいい。
2. Dashboardsの自動インサイト
SPICEデータに対して異常値や傾向変化を自動検出してくれる機能。日次売上が急落したときに「○月○日から売上が前週比-23%になっています」と自動でコメントを出してくれるのはマジで助かった。インシデント対応の初動でデータ側の異常を検知するのに使っている(インシデント対応についてはインシデント対応の最新ベストプラクティス2026も参考に)。
3. Generative BIコプロット機能(2025年後半〜)
Author権限のユーザーが「このグラフに前年同期比の折れ線を追加して」と指示すると、分析設定を自動変更してくれる機能。これは想像以上に使える。非エンジニアのアナリストが自力でダッシュボードをカスタマイズできるようになって、「ちょっと直してほしい」系の依頼がかなり減った。
sequenceDiagram
actor User as ビジネスユーザー
participant QSQ as QuickSight Q
participant Topic as Qトピック
participant SPICE as SPICE データ
participant Dashboard as ダッシュボード
User->>QSQ: "先月の部門別売上を見たい"
QSQ->>Topic: 意図解析・エンティティ抽出
Topic->>SPICE: 最適化クエリ生成・実行
SPICE-->>Topic: 集計結果
Topic-->>QSQ: ビジュアル候補生成
QSQ-->>User: 棒グラフ + インサイト表示
User->>QSQ: "前月比も追加して"
QSQ->>Dashboard: Generative BI コプロット
Dashboard-->>User: 更新されたビジュアル
ガバナンス設計——野良ダッシュボードが増え始めたら手遅れ
導入から3ヶ月くらいは順調だったが、4〜5ヶ月目あたりから「誰が作ったかわからないダッシュボード」が増え始めた。AWSのリソース管理で似たような問題にぶつかったことある人、多いんじゃないかな(AWSの「誰が作ったかわからない」リソースで月200万円溶けてた話、笑えない話だけど参考になる)。ガバナンスルールは「必要になってから整備する」では遅い。3ヶ月以内に仕組みを作っておくのが正直なところのおすすめだ。
実際に整備したルール
flowchart TD
A[新規ダッシュボード作成依頼] --> B{AuthorかAdmin?}
B -->|Author| C[テンプレートから作成]
B -->|Admin| D[ゼロから作成可]
C --> E[命名規則チェック]
D --> E
E --> F{承認者レビュー}
F -->|NG| G[修正依頼]
G --> C
F -->|OK| H[タグ付け必須]
H --> I[フォルダ配置]
I --> J{公開範囲設定}
J -->|全社| K[Reader権限付与]
J -->|部門限定| L[グループ権限付与]
J -->|開発中| M[Author限定]
K --> N[定期棚卸し対象に登録]
L --> N
M --> O[3ヶ月後に自動アーカイブ検討]
命名規則は以下のフォーマットで統一した。
[部門]-[用途]-[更新頻度]
例:SALES-daily-revenue-realtime
MARKETING-campaign-analysis-weekly
EXEC-kpi-summary-daily
タグは最低限 Owner、Department、Environment(prod/staging)の3つを必須にした。これをQuickSight側のタグで管理して、四半期に一度棚卸しを実施している。運用してみると、タグがないと棚卸しのときに本当に困るので最初から徹底することをおすすめする。
2026年時点での権限モデル
| ロール | 月額コスト(目安) | できること |
|---|---|---|
| Admin | − | 全操作、RLS・テーマ管理 |
| Author | $24/月 | データセット作成・分析・ダッシュボード作成 |
| Author Pro | $54/月 | Author + Pixel Perfect + アラート設定 |
| Reader | $0.30/セッション(上限$5/月) | ダッシュボード閲覧のみ |
| Reader Pro | $0.50/セッション(上限$10/月) | Reader + Q自然言語クエリ |
うちでは経営層と一般ビジネスユーザーにReader Pro、アナリストにAuthor、インフラ/データエンジニアにAdminを割り当てている。Readerのセッション課金は月の上限があるのでコスト予測がしやすく、Tableau時代と比べると読み取り専用ユーザーのライセンス費用はかなり抑えられた。
コスト推移(実際の月額)
xychart-beta
title "QuickSight 月額コスト推移(万円)"
x-axis ["1月", "2月", "3月", "4月", "5月", "6月"]
y-axis "費用 (万円)" 0 --> 30
bar [8, 10, 13, 22, 19, 17]
line [8, 10, 13, 22, 19, 17]
4月に急騰したのが先述のSPICE爆増事件。5月に不要データセットを整理したら落ち着いた。ガバナンスルールを整備したのがちょうどこのタイミングで、「痛い目を見てから動く」という典型的なパターンだった。
Pixel Perfectレポートと埋め込みの活用(2026年版)
最後に、最近使い始めた機能の話をする。QuickSightにはPDF出力品質の高い「Pixel Perfectレポート」と、社内ポータルや顧客向けアプリにダッシュボードを埋め込む「Embedded Analytics」がある。
Embedded Analyticsの実装
社内の顧客ポータルにQuickSightを埋め込む構成を実装した。Cognitoで認証した後、QuickSightのAnonymous埋め込みURLを生成してiframeで表示している。
import boto3
import json
qs_client = boto3.client('quicksight', region_name='ap-northeast-1')
ACCOUNT_ID = 'YOUR_ACCOUNT_ID'
def get_dashboard_embed_url(
dashboard_id: str,
user_arn: str,
session_lifetime_minutes: int = 60
) -> str:
"""
QuickSightダッシュボードの埋め込みURLを生成
2026年: GenerateEmbedUrlForRegisteredUser を使う
"""
response = qs_client.generate_embed_url_for_registered_user(
AwsAccountId=ACCOUNT_ID,
SessionLifetimeInMinutes=session_lifetime_minutes,
UserArn=user_arn,
ExperienceConfiguration={
'Dashboard': {
'InitialDashboardId': dashboard_id,
'FeatureConfigurations': {
'StatePersistence': {'Enabled': True},
'SharedView': {'Enabled': True},
# 2026年追加: Q機能の埋め込み
'Bookmarks': {'Enabled': True}
}
}
},
AllowedDomains=['https://your-portal.example.com']
)
return response['EmbedUrl']
# 呼び出し例
embed_url = get_dashboard_embed_url(
dashboard_id='xxxx-xxxx-xxxx',
user_arn='arn:aws:quicksight:ap-northeast-1:123456789:user/default/tanaka@example.com'
)
print(f"埋め込みURL(有効期限60分): {embed_url[:60]}...")
実行結果イメージ:
埋め込みURL(有効期限60分): https://ap-northeast-1.quicksight.aws.amazon.com/embed/...
StatePersistenceを有効にすると、ユーザーがフィルタを変更した状態を保存できる。「毎回同じ条件でフィルタするのが面倒」という声がなくなったのは地味に効果が大きかった。これだけで問い合わせが減るので、埋め込みを使う場合はほぼ必須にしていいと思っている。
Qの精度について、日本語のビジネス用語(固有の商品カテゴリ名など)がうまく認識されないケースはまだある。Qトピックへの用語・シノニム登録を丁寧にやることで徐々に改善されるが、「最初から完璧」は期待しないほうがいい。育てる前提で付き合うのが正解だと思っている。
データ品質の話でいうと、QuickSightに食わせるデータそのものの品質管理が大切で、データ品質管理2026年版:最新ツール&ベストプラクティスも合わせて読むとデータパイプライン全体の設計が整理できると思う。
まとめ
半年で学んだことを整理する。
要点:
- SPICEとDirect Queryの使い分けを最初に決める:集計済みデータ・大量アクセスが想定されるダッシュボードはSPICE、リアルタイム性が必要なもののみDirect Queryにする
- Row-Level Securityは早期に設計する:後から追加するとデータセットの作り直しが必要になるケースが多い。ルールテーブルの更新自動化も忘れずに
- QuickSight Qの日本語対応は育てる前提で:Qトピックへの用語・シノニム登録を地道に続けることで精度が上がる
- ガバナンスルールは3ヶ月以内に整備する:野良ダッシュボードが増える前に命名規則・タグ・フォルダ構造・棚卸しプロセスを決める
- SPICE使用量は定期モニタリング必須:分析チームが承認なしでデータセットを登録するのを防ぐためにアラートを仕掛けておく
次のアクション:
- 既存のダッシュボードのSPICE vs Direct Queryを見直す:
list_data_setsAPIで現状確認から始めよう - RLSルールテーブルをIaC(CDK/Terraform)で管理する仕組みを整える
- QuickSight Qトピックにビジネス用語を20件以上登録して精度を計測してみる
正直、Generative BIまわりはまだ進化が速くて月次で機能が変わっている印象がある。継続的にAWSのアップデートをウォッチしながら設計を更新していくのが2026年のBIエンジニアの仕事かなと思っている。