【ビッグデータの集合体】 22. HQL構文最適化の結合最適化

主に、最適化に使用される結合アルゴリズムを制御します。

1 つの共通結合

  Common Join は Hive で最も安定した結合アルゴリズムであり、デフォルトの結合アルゴリズムでもあり、MapReduce ジョブを通じて結合操作を完了します。Map エンドは、結合操作に必要なテーブルのデータを読み取り、関連付けられたフィールドに従って分割し、Shuffle を通じて Reduce エンドに送信する責任を負い、同じキーのデータによって最終的な結合操作が完了します。端を減らします。
ここに画像の説明を挿入
  SQL ステートメントの結合操作と実行プランの共通結合タスクは、1 対 1 の関係にありません。SQL ステートメント内で隣接し、同じ関連フィールドを持つ複数の結合操作は、共通結合に結合できます。関連するフィールドが異なる場合 Common Join タスクは 2 つだけ分割できます。

例えば:

--两个join操作的关联字段均为b表的key1字段,
--则该语句的两个join操作可以由一个common join任务实现,即通过一个MapReduce任务实现
select
    a.val,
    b.val,
    c.val
from a
join b on a.key=b.key1
join c on c.key=b.key1;

--两个join操作的关联字段都不同,
--则该语句的两个join操作需要各自通过一个common join任务实现,即通过2个MapReduce任务实现
select
    a.val,
    b.val,
    c.val
from a
join b on a.key=b.key1
join c on c.key=b.key2;

2 マップ結合

2.1 Map Join アルゴリズムの概要

  マップ結合アルゴリズムは、マップ ステージのみを使用して 2 つのジョブを通じて結合操作を完了できます。小さなテーブルと大きなテーブルを結合するのに適しています

  最初のジョブは、小さなテーブル データを読み取り、ハッシュ テーブルを作成し、Hadoop 分散キャッシュにアップロードします (基本的に HDFS にアップロードします)。
  2 番目のジョブは、まず分散キャッシュから小さなテーブル データを読み取り、マップ タスクのメモリにキャッシュし、次に大きなテーブル データをスキャンして、マップ側で関連付け操作を完了します。
ここに画像の説明を挿入

2.2 最適化命令

  マップ結合をトリガーするには 2 つの方法があります。1 つはユーザーが SQL ステートメントにヒントを追加する方法、もう 1 つは結合テーブル内のデータ量に基づいて Hive オプティマイザーが自動的にマップ結合をトリガーする方法です。
(1)
  マップ結合アルゴリズムを指定し、マップ結合の小さなテーブルとして ta を使用することにより、ヒント プロンプトが表示されます。(メソッドは時代遅れで非推奨です)

select /*+ mapjoin(ta) */
    ta.id,
    tb.id
from table_a ta
join table_b tb
on ta.id=tb.id;

(2)
  SQL ステートメントのコンパイル段階で共通結合アルゴリズムを実装するように Hive を自動的にトリガーし、すべての結合操作を開始します。
  後続の物理最適化段階では、Hive は、各共通結合タスクに必要なテーブルのサイズに応じて、共通結合タスクをマップ結合タスクに変換できるかどうかを判断し、要件が満たされている場合、共通結合タスクは自動的に変換されます。マップ結合タスクに変換されます。

問題があります。SQL
  のコンパイル段階では、一部の共通結合タスク (サブクエリの結合操作など) で必要なテーブルのサイズが不明であるため、この共通結合タスクをコンパイル段階でのマップ結合タスク。

解決策:
  Hive は、コンパイル フェーズで条件付きタスク (条件付きタスク (順番に実行計画)) を生成します。これには、変換後のすべての可能なマップ結合タスクと、元の共通結合タスク (バックアップ タスクとして、とにかく完了する最も安定したタスク)。最終的にどのプランが採用されるかは実行時に決定されます。
  使用するテーブルのサイズがわかっている場合は条件付きタスク(Conditional Task)を使用しない場合がありますが、サブクエリなどを使用する場合にテーブルのサイズが不明な場合はコンパイルフェーズで条件付きタスクが生成されます。

一般的な考え方:
  最適化前の実行プランは TaskA、CommonJoinTask、TaskC の順に実行され、最適化された実行プランは条件付きタスク (Conditional Task) の順に実行され、Map Join タスクがある場合はそれが最初に実行されます。 Map Join タスクがない場合 実行が成功した場合は、CommonJoinTask タスクを実行します。
ここに画像の説明を挿入
Map Join自動変換の具体的な判定ロジック(実行計画内のCommonJoinTaskをMapJoinTaskに変換できるかどうか):
ここに画像の説明を挿入
パラメータの説明:

--启动Map Join自动转换,默认开启
set hive.auto.convert.join=true;

--一个Common Join operator转为Map Join operator的判断条件,
--若该Common Join相关的表中,存在n-1张(即除大表之外的表)表的已知大小总和<=该值,
--则生成一个Map Join计划,此时可能存在多种n-1张表的组合均满足该条件,
--则hive会为每种满足条件的组合均生成一个Map Join计划,同时还会保留原有的Common Join计划作为后备(back up)计划,
--实际运行时,优先执行Map Join计划,若不能执行成功,则启动Common Join后备计划。
--即控制内存中能放下小表的大小的总阈值
set hive.mapjoin.smalltable.filesize=250000;

--开启无条件转Map Join
set hive.auto.convert.join.noconditionaltask=true;

--无条件转Map Join时的小表之和阈值,若一个Common Join operator相关的表中,存在n-1张表的大小总和<=该值,
--此时hive便不会再为每种n-1张表的组合均生成Map Join计划,同时也不会保留Common Join作为后备计划。
--而是只生成一个最优的Map Join计划。
set hive.auto.convert.join.noconditionaltask.size=10000000;

2.3 事例

1. サンプル SQL

select
    *
from order_detailod
join product_infoproduct on od.product_id = product.id
joinprovince_info province on od.province_id = province.id;

2. 最適化前、
  SQL ステートメントには 3 つのテーブルがあり、2 つの結合操作が実行されます。2 つの結合操作の関連フィールドは異なります。したがって、最適化前の実行計画には 2 つの Common Join 演算子が含まれている必要があります。つまり、2 つの MapReduce タスクによって実装されている必要があります。

実行計画:
ここに画像の説明を挿入
3. 最適化のアイデア
結合に参加する 3 つのテーブルのデータ量:
ここに画像の説明を挿入

--获取表或分区的大小:
desc formatted table_name partition(partition_col='partition_value');

Product_info と Province_info は、マップ結合の最適化のための小さなテーブルとして使用できます。
(1) オプション 1.
  条件付きタスクの進行状況の自動変換を使用して、両方の Common Join 演算子を Map Join 演算子に変換できるようにし、Common Join をバックアップ プランとして保持してコンピューティング タスクの安定性を確保します。

--启用Map Join自动转换
set hive.auto.convert.join=true;

--不使用无条件转换Map Join,即使用条件任务
set hive.auto.convert.join.noconditionaltask=false;

--调整hive.mapjoin.smalltable.filesize参数,使其大于等于product_info
set hive.mapjoin.smalltable.filesize=25285707;

実行計画:
ここに画像の説明を挿入
  hive.mapjoin.smalltable.filesize パラメーターが十分に大きいため、バックアップ タスクは実行されません。
  条件付きタスクを使用すると、両方の結合が MapJoin ですがマージされないため、実行が遅くなります。
(2) 解決策 2:
  2 つの共通結合演算子を 2 つのマップ結合演算子に直接変換します。2 つのマップ結合演算子の小さなテーブル サイズの合計が hive.auto.convert.join.noconditionaltask.size 以下であるため、を使用すると、2 つの Multiple Map Join オペレータ タスクを 1 つにマージできます。このソリューションは計算効率が最も高いですが、最も多くのメモリを必要とします。

--启用Map Join自动转换
set hive.auto.convert.join=true;

--使用无条件转换Map Join,即使不用条件任务,因为已经知道表的大小
set hive.auto.convert.join.noconditionaltask=true;

--调整hive.auto.convert.join.noconditionaltask.size参数,
--使其大于等于product_info和province_info之和
--即两小表先合并
set hive.auto.convert.join.noconditionaltask.size=25286076;

実行計画:
ここに画像の説明を挿入
  条件付きタスクを使用しない場合、メモリが小さなテーブルの合計よりも大きく設定されている限り、MapJoinTask を組み合わせて実行を高速化できます。
(3) 解決策 3:
  2 つの Common Join 演算子を Map Join 演算子に直接変換しますが、2 つの Map Join のタスクはマージしません。この方式は方式 2 よりも計算効率が低くなりますが、必要なメモリは少なくなります。

--启用Map Join自动转换
set hive.auto.convert.join=true;

--使用无条件转换Map Join,即使不用条件任务
set hive.auto.convert.join.noconditionaltask=true;

--调整hive.auto.convert.join.noconditionaltask.size参数,使其等于product_info
--即调整大小为小表里较大的表的大小
set hive.auto.convert.join.noconditionaltask.size=25285707;

実行計画:
ここに画像の説明を挿入
  2 つの共通結合演算子をマップ結合演算子に変換しますが、2 つのマップ結合のタスクはマージしません。この方式は方式 2 よりも計算効率が低くなりますが、必要なメモリは少なくなります。

3 バケットマップ結合

3.1 バケット マップ結合アルゴリズムの概要

バケット マップ結合はマップ結合アルゴリズムの改良版であり、マップ結合は小さなテーブルを結合する大きなテーブルにのみ適用できるという制限を破り、大きなテーブルが大きなテーブルを結合するシナリオ  でも使用できます。

中心となるアイデア:
  2 つのテーブルのバケット間でマップ結合操作を実行するための条件:
  (1) 結合に参加しているテーブルはすべてバケット化されたテーブルである
  (2)関連するフィールドはバケット化されたフィールドである
  (3)のバケットの数いずれかのテーブル これは、別のテーブルのバケット数の整数倍です(結合に参加している 2 つのテーブルのバケット間に明確な関係があることを確認するため)。
  2 番目のジョブのマップ側では、必要なバケットをキャッシュするだけで済み、小さなテーブルの完全なテーブル データをキャッシュする必要はありません。

原則:
ここに画像の説明を挿入
  たとえば、小さなテーブル B の場合、ハッシュ値は 0,1,2,3,4,5,6,7,8 になります。ハッシュ値はモジュロ 2 (テーブル B が 2 つのバケットに分割されているため)、テーブル B のバケット 0 に割り当てられるハッシュ値は 0、2、4、6、8、テーブル B のバケット 1 に割り当てられるハッシュ値は 0、2、4、6、8 です。表Bは1、3、5、7です。

  同じハッシュ値でも、大きなテーブル A の場合、ハッシュ値はモジュロ 4 (テーブル A が 4 つのバケットに分割されているため) となり、テーブル A のバケット 0、1、2、3 に割り当てられるハッシュ値は 0 になります。と 4 、それぞれ 8、1、5、2、6、3、7。

  したがって、大きなテーブル A のバケット 0 と 2 は小さなテーブル B のバケット 0 に関連付けられ (つまり、3 つのバケット テーブルが結合され)、大きなテーブル A のバケット 1 と 3 は小さなテーブル B のバケット 1 に接続されます。バケット協会。

  バケット マップ結合は 2 つの段階で完了します。

  第 1 段階: マップは、小さいテーブルのバケット テーブルをローカルで読み取り、バケットごとにハッシュ テーブルを作成し、小さいテーブルのルームメイトのハッシュ テーブルを HDFS メモリ分散キャッシュにアップロードします。

  第 2 段階: 大きなテーブルのデータを読み取り、それを小さなテーブルのデータに関連付けます。第 2 段階で BucketInputFormat (特にバケット テーブルの読み取りに使用される) を使用するには、バケット マップ結合を使用します。スライス戦略では 1 つのバケットと 1 つのスライスが使用されます (つまり、大きなテーブル内のバケットと同じ数のマッパーが存在します)。各マッパーはバケットを担当します。大きなテーブルの各バケットは小さなテーブルのバケットに関連付けられているため、各マッパーは必要な小さなテーブルの関連付けられたバケットをキャッシュするだけで済みます。次に、各マッパーは結合のために大きなテーブル内のバケットをスキャンします。

  これは、大きなテーブルを複数の小さなテーブルに分割し、小さなテーブルを結合することとして理解できます。

3.2 最適化命令

バケット マップ結合は自動変換をサポートしていないため、ユーザーは SQL ステートメントに次のヒントを入力し、関連パラメーターを構成する必要があります。
(1) ヒントプロンプト

select /*+ mapjoin(ta) */
    ta.id,
    tb.id
from table_a ta
join table_b tb onta.id=tb.id;

(2) 関連パラメータ

--关闭cbo优化,cbo会导致hint信息被忽略
set hive.cbo.enable=false;
--map join hint默认会被忽略(因为已经过时),需将如下参数设置为false
set hive.ignore.mapjoin.hint=false;
--启用bucket map join优化功能
set hive.optimize.bucketmapjoin = true;

3.3 事例

1. サンプル SQL

select
    *
from(
    select
        *
    from order_detail
    where dt='2020-06-14'
)od
join(
    select
        *
    from payment_detail
    where dt='2020-06-14'
)pd
onod.id=pd.order_detail_id;

2. 最適化前、
  上記の SQL には結合操作を実行するための 2 つのテーブルがあり、最適化前の実行プランには、MapReduce ジョブを通じて実装される共通の結合タスクが含まれています。
ここに画像の説明を挿入
3. 最適化のアイデア
  結合に参加している 2 つのテーブルのサイズを確認します:
ここに画像の説明を挿入
問題があります:
  2 つのテーブルは比較的大きいです。通常の Map Join アルゴリズムが使用される場合、Map 側はデータをキャッシュするためにより多くのメモリを必要とします。ただし、Map側のメモリは上限なく割り当てることができません。

解決策:
  結合に参加しているテーブルのデータ量が多すぎる場合は、バケット マップ結合アルゴリズムの使用を検討できます。

バケット マップ結合を使用する:
(1) ソース テーブルに基づいて 2 つのバケット テーブルを作成します。order_detail は
16 個のバケットに分割することをお勧めします。payment_detail は 8 つのバケットに分割することをお勧めします。バケットの数とバケットの数多重関係に注意してください。バケット フィールド (関連するフィールド バレルに分割する必要があります)

--订单表
drop table ifexists order_detail_bucketed;
create tableorder_detail_bucketed(
    id          string comment '订单id',
    user_id     string comment '用户id',
    product_id  string comment '商品id',
    province_id string comment '省份id',
    create_time string comment '下单时间',
    product_num int comment '商品件数',
    total_amount decimal(16, 2) comment '下单金额'
)
clustered by (id)into 16 buckets
row formatdelimited fields terminated by '\t';

--支付表
drop table ifexists payment_detail_bucketed;
create tablepayment_detail_bucketed(
    id              string comment '支付id',
    order_detail_id string comment '订单明细id',
    user_id         string comment '用户id',
    payment_time    string comment '支付时间',
    total_amount    decimal(16, 2) comment '支付金额'
)
clustered by(order_detail_id) into 8 buckets
row formatdelimited fields terminated by '\t';

(2) 2つのバケットにデータをインポートする

--订单表
insert overwritetable order_detail_bucketed
select
    id,
    user_id,
    product_id,
    province_id,
    create_time,
    product_num,
    total_amount   
from order_detail
wheredt='2020-06-14';

--分桶表
insert overwritetable payment_detail_bucketed
select
    id,
    order_detail_id,
    user_id,
    payment_time,
    total_amount
frompayment_detail
wheredt='2020-06-14';

データの量が多い場合、データはバケットに直接保存されるため、バケットを再作成してデータをインポートする必要はありません。
(3) パラメータの設定

--关闭cbo优化,cbo会导致hint信息被忽略,需将如下参数修改为false
set hive.cbo.enable=false;
--map join hint默认会被忽略(因为已经过时),需将如下参数修改为false
set hive.ignore.mapjoin.hint=false;
--启用bucket map join优化功能,默认不启用,需将如下参数修改为true
set hive.optimize.bucketmapjoin = true;

(4) SQL文を書き換える

select /*+ mapjoin(pd)*/
    *
from order_detail_bucketed od
join payment_detail_bucketed pd on od.id =pd.order_detail_id;

実行計画:
ここに画像の説明を挿入
  バケットマップ結合の実行計画の基本情報は通常のマップ結合と同じですが、違いを確認したい場合は、実行計画の詳細を表示できます。

explain extended select /*+ mapjoin(pd) */
    *
fromorder_detail_bucketed od
joinpayment_detail_bucketed pd on od.id = pd.order_detail_id;

  詳細な実行プランで、マップ結合演算子に「BucketMapJoin: true」と表示されている場合は、使用される結合アルゴリズムがバケット マップ結合であることを示します。

4 ソート・マージ・バケット・マップ結合

4.1 ソート・マージ・バケット・マップ結合アルゴリズムの概要

ソート マージ バケット マップ結合 (略して SMB マップ結合) は、バケット マップ結合に基づいています。
2 つのテーブルのバケット間の SMB マップ結合操作の条件:
(1) 結合に参加するテーブルはすべてバケット テーブルである
(2)バケット内のデータは順序付けされている
(3)バケット フィールド、ソート フィールド、および関連するフィールドは同じフィールドです;
(4)一方のテーブルのバケット数は、もう一方のテーブルのバケット数の整数倍です(結合に参加している 2 つのテーブルのバケット間に明確な関係があることを確認するため) 。

ソート・マージ・バケット・マップ結合とバケット・マップ結合の類似点と相違点:
(1) 類似点: 2 つのテーブルのバケット間の関連付け関係を使用して、バケット間の結合操作を実行します。
(2) 相違点: バケット間の結合操作の実装原理:
  (i) バケット マップ結合 2 つのバケット間の結合の実装原理は、ハッシュ結合アルゴリズムです。結合に参加するテーブルのハッシュ テーブルを構築し、別のテーブルをスキャンし、行ごとの照合を実行します。
  (ii) SMB マップ結合。2 つのバケット間の結合の実装原理は、ソート マージ結合アルゴリズムです。関連付けられたフィールドに従ってソートされた 2 つのテーブルで実行する必要があります。
ここに画像の説明を挿入
  2 つのテーブルの関連フィールドがソートされており、大きいテーブルの最初のキーが最初に取得されます。重複キーの場合、次のキーが現在のキーと異なることが判明した場合、それは同じキーを持つデータを意味します。が得られました。同じメソッドを使用して、小さなテーブルのデータを取得します。データを読み取る場合、各行のデータを順番に読み込むだけでよいため、バケット テーブル全体のデータをメモリに取得する必要はありません。次の行のキーが異なる限り、データが取得されました。
  2 つのテーブルのデータが取得されたら、結合操作を実行できます。
  SMB マップ結合が結合操作を実行する場合、マップ側はバケット全体のハッシュ テーブルを構築する必要も、マップ側でバケット全体のデータをキャッシュする必要もありません。各マッパーは、バケットのデータを読み取るだけで済みます。結合するには 2 つのバケットを 1 つずつ実行するだけです。そのため、SMB マップ結合にはメモリは必要ありません。

4.2 最適化命令

  並べ替え、結合、バケット マップ結合には、ヒント プロンプトと自動変換を含む 2 つのトリガー方法があります。ヒント ヒントは廃止されたため、使用はお勧めできません。
  ソート・マージ・バケット・マップ結合では、関連付けられたフィールドに従ってバケットに分割され、バケットの数が倍数であり、バケットがバケット化フィールドに従って順序付けされている限り、メモリーのサイズを考慮する必要はありません。

関連パラメータ:

--启动Sort Merge BucketMap Join优化(总开关)
sethive.optimize.bucketmapjoin.sortedmerge=true;
--使用自动转换SMB Join
sethive.auto.convert.sortmerge.join=true;

4.3 ケース

1. サンプル SQL

select
    *
from(
    select
        *
    from order_detail
    where dt='2020-06-14'
)od
join(
    select
        *
    from payment_detail
    where dt='2020-06-14'
)pd
onod.id=pd.order_detail_id;

2. 最適化前、
  上記の SQL には結合操作を実行するための 2 つのテーブルがあり、最適化前の実行プランには、MapReduce ジョブを通じて実装される共通の結合タスクが含まれています。
3. 最適化のアイデア
  結合に参加している 2 つのテーブルのサイズを確認します:
ここに画像の説明を挿入
問題があります:
  2 つのテーブルは比較的大きいです。通常の Map Join アルゴリズムが使用される場合、Map 側はデータをキャッシュするためにより多くのメモリを必要とします。ただし、Map側のメモリは上限なく割り当てることができません。

解決策:
  結合に参加するテーブルのデータ量が多すぎる場合は、バケット マップ結合アルゴリズムに加えて、SMB 結合も考慮できます。バケット マップ結合と比較して、SMB マップ結合にはバケット サイズの要件がありません。

SMB マップ結合を使用する:
(1) ソース テーブルに基づいて 2 つのバケット テーブルを作成します。order_detail は16 バケットを推奨し、payment_detail は 8 バケットを推奨します。バケット数とバケット フィールド複数の関係
に注意してください(関連するフィールド バケットに分割する必要があります)。 ) と並べ替えフィールド

--订单表
drop table ifexists order_detail_sorted_bucketed;
create table order_detail_sorted_bucketed(
    id          string comment '订单id',
    user_id     string comment '用户id',
    product_id  string comment '商品id',
    province_id string comment '省份id',
    create_time string comment '下单时间',
    product_num int comment '商品件数',
    total_amount decimal(16, 2) comment '下单金额'
)
clustered by (id) sorted by(id) into 16 buckets
row formatdelimited fields terminated by '\t';

--支付表
drop table ifexists payment_detail_sorted_bucketed;
create tablepayment_detail_sorted_bucketed(
    id              string comment '支付id',
    order_detail_id string comment '订单明细id',
    user_id         string comment '用户id',
    payment_time    string comment '支付时间',
    total_amount    decimal(16, 2) comment '支付金额'
)
clustered by (order_detail_id) sortedby(order_detail_id) into 8 buckets
row formatdelimited fields terminated by '\t';

(2) 2つのバケットにデータをインポートする

--订单表
insert overwrite table order_detail_sorted_bucketed
select
   id,
   user_id,
   product_id,
   province_id,
   create_time,
   product_num,
   total_amount   
from order_detail
where dt='2020-06-14';

--分桶表
insert overwrite table payment_detail_sorted_bucketed
select
   id,
   order_detail_id,
   user_id,
   payment_time,
   total_amount
from payment_detail
where dt='2020-06-14';

データの量が多い場合、データはバケットに直接保存されるため、バケットを再作成してデータをインポートする必要はありません。
(3) パラメータの設定

--启动Sort Merge Bucket Map Join优化
set hive.optimize.bucketmapjoin.sortedmerge=true;
--使用自动转换SMB Join
set hive.auto.convert.sortmerge.join=true;

(4) SQL文を書き換える

select
    *
from order_detail_sorted_bucketed od
join payment_detail_sorted_bucketed pd
on od.id = pd.order_detail_id;

実行計画:
ここに画像の説明を挿入

5 参加概要

5.1 マップ結合

  小さなテーブルと大きなテーブルを結合するのに適しており、SQLの場合、Map Joinが通過できる場合はMap Join、Map Joinが通過できない場合はCommon Joinが使用できます。したがって、hive-site.xml でグローバル パラメーターを構成する必要があります。

<!--启动Map Join自动转换,默认开启,全局设置为true-->
   <property>
       <name>hive.auto.convert.join</name>
       <value>true</value>
    </property>
    
<!-- 取决于map端能缓存多少东西,一般为map端总内存的1\2~2\3作为缓存 设置时为文件大小,所以需要再除以10-->
   <property>
       <name>hive.mapjoin.smalltable.filesize</name>
       <value>250000</value>
    </property>
    
<!--开启无条件转Map Join,全局设置为true-->
   <property>
       <name>hive.auto.convert.join.noconditionaltask</name>
       <value>true</value>
    </property>
    
<!-- 取决于map端能缓存多少东西,一般为map端总内存的1\2~2\3作为缓存 设置时为文件大小,所以需要再除以10-->
   <property>
       <name>hive.auto.convert.join.noconditionaltask.size</name>
       <value>100000</value>
    </property>



  1 つの SQL のみの実行が非常に遅い場合は、set を使用して、上記の分析方法に従って個別のチューニングを実行できます。

5.2 バケットマップ結合

  2 つの大きなテーブルの Map Join 操作に適しています。各テーブルをいくつのバケットに分割するかを考慮する必要があり (Map 側のメモリの量に応じて)、各ファイルのサイズは 500M を超えないことが望ましいです。また、異なるテーブル内のバケットの数は倍数である必要があります。

5.3 ソート・マージ・バケット・マップ結合

  バケット内のバケット フィールドが適切である限り、マップ結合用に大きなテーブルを最適化する場合にメモリ サイズは必要ありません。

おすすめ

転載: blog.csdn.net/qq_18625571/article/details/131197574