RandomAccessFile はファイル ハッシュの重複排除をダウンロードし、Mapreduce はファイルをダウンロードします-----ブリッジ モードはデータ クリーニングを実行して、

データのクリーニングは正当化されますか? 多次元検証グループグループ
変換
ET2オフラインエンジニア

方法 1: RandomAccessFile を使用してファイル ハッシュをダウンロードし、重複を削除する

public class App 
{ 
    public static void main( String[] args ) throws Exception { 
        //ハッシュを準備
        HashMap<String, Integer> map = new HashMap<>(); 
        //ファイルを読み込む
        RandomAccessFile raf = new RandomAccessFile("D:\ \bgdata\\bgdata01\\events.csv", "rw"); 
        //最初の行をスキップする
        raf.readLine(); 
        //データ行を読み取る
        String line=""; 
        //データを読み取るループ
        while ( (line =raf.readLine())!=null){ 
            //読み取ったデータはカンマと最初の
            文字列で区切られています。eventid=line.split(",")[0]; 
            //マップに ID が含まれているかどうかを判定します
            。 (地図 .
            }else { 
                map.put(eventid,map.get(イベントID)+1);
                //それ以外の場合は、初めて ID 値を取得して 1 に設定します。
                map.put(eventid,1); 
            } 
        } 
        //ストリーム ファイルを閉じる        
        raf.close(); 
        //出力してマップ サイズをチェックし、重複があるかどうかを確認します
        System.out.println(map.size()+" ===== ===========); 
    } 
}

短所:今回使用したファイルデータは300万で、読み込みが非常に遅く、30分くらいかかりましたo(╥﹏╥)o

次の方法を使用することをお勧めします

方法 2 では、Mapreduce を使用してファイルをダウンロードします

2.1pomファイルの設定

    <dependency> 
      <groupId>org.apache.hadoop</groupId> 
      <artifactId>hadoop-client</artifactId> 
      <version>2.6.0</version> 
    </dependency> 
    <dependency> 
      <groupId>org.apache.hadoop </groupId> 
      <artifactId>hadoop-mapreduce-client-core</artifactId> 
      <version>2.6.0</version> 
    </dependency> 
    <dependency> 
      <groupId>org.apache.hadoop</groupId> 
      <artifactId> hadoop-hdfs</artifactId> 
      <version>2.6.0</version> 
    </dependency> 
    <dependency> 
      <groupId>org.apache.hadoop</groupId> 
      <artifactId>hadoop-common</artifactId> 
      <version>2.6.0</version>
    </依存関係>

2.2 RcMapperクラスの設定

public class RcMapper extends Mapper<LongWritable,Text ,Text, IntWritable> { 
   IntWritable one= new IntWritable(1); 
    @Override 
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { 
        Stringeventid=value.toString().split(",")[0]; 
        context.write(new Text(eventid),one); 
    } 
}

2.3 RcReduce クラスの構成

public class RcReduce extends Reducer<Text, IntWritable,Text,IntWritable>{ 
    @Override 
    protected void reduce(Text key, Iterable<IntWritable> value, Context context) throws IOException, InterruptedException { 
        int count=0; 
        for (IntWritable it : 値) { 
            count+=1; 
        context.write 
        (key,new IntWritable(count)); 
    } 
}

2.4 RcCountDriver クラスの構成

public class RcCountDriver { 
    public static void main(String[] args) throws Exception { 
        //ジョブ オブジェクトをインスタンス化する
        Job job = Job.getInstance(new Configuration()); 
        //リフレクションを通じてオブジェクトを取得
        job.setJarByClass(RcCountDriver.class ); 
//
        RcMapper オブジェクトを取得するためのリフレクション
        // 対応する Text.class を設定します
        job.setMapperClass(RcMapper.class); 
        job.setMapOutputKeyClass(Text.class); 
        job.setMapOutputValueClass(IntWritable.class); 
//
        リフレクションRcReduce オブジェクトを取得します
        / /対応する Text.class を設定します
        job.setReducerClass(RcReduce.class); 
        job.setOutputKeyClass(Text.class); 
        job.setOutputValueClass(IntWritable.class);
//
        ターゲット パス ファイルを取得
        FileInputFormat.addInputPath(job,new Path("file:///D:\\bgdata\\bgdata01\\events.csv")); 
        // ダウンロード ファイルのアドレスをマークする
        FileOutputFormat.setOutputPath (job ,new Path("file:///d:/calres/cal01")); 
/
        ** 
         *ジョブはjob.waitForCompletion(true)を通じて実行されます。
         * trueは、実行の進行状況およびその他の情報が表示されることを意味します。タイムリーにユーザーに出力します。
         * false の場合は、ジョブが終了するまで待機します
         */ 
        job.waitForCompletion(true); 
    } 
}

クリックするだけでテストできます!

方法 3. Mapreduce を使用して重複を削除する

3.1 CfMapper クラスの設定

public class CfMapper extends Mapper<LongWritable, Text,Text, IntWritable> { 
    @Override 
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { 
        //取得した値はスペースで区切られます
        String[] infos = value . toString().split("\t"); 
        //2 番目のデータが 1 でない場合の操作
        if (!infos[1].equals("1")){ 
            //最初の値を取得し、2 番目の値を取得します スペースを削除します1 以外の値から
            context.write(new Text(infos[0]),new IntWritable(Integer.parseInt(infos[1].trim()))); 
        } 
} 
    }

3.2 CfCombiner クラスの構成

public class CfCombiner extends Reducer<Text, IntWritable,Text,IntWritable> { 
    @Override 
    protected void redred(Text key, Iterable<IntWritable> 値, Context context) throws IOException, InterruptedException { 
        //構成kv键值对
        context.write(key) ,values.iterator().next()); 
    } 
}

3.3 CfCountDriver クラスの構成

public class CfCountDriver { 
    public static void main(String[] args) throws Exception { 
        //ジョブ オブジェクトをインスタンス化する
        Job job = Job.getInstance(new Configuration()); 
        //リフレクションを通じてオブジェクトを取得
        job.setJarByClass(CfCountDriver.class) ); 
//
        CfMapper オブジェクトを取得するためのリフレクション
        // 対応する Text.class を設定します
        job.setMapperClass(CfMapper.class); 
        job.setMapOutputKeyClass(Text.class); 
        job.setMapOutputValueClass(IntWritable.class); 
//
        リフレクションCfCombiner オブジェクトを取得します
        / / 対応する Text.class を設定します
// job.setCombinerClass(CfCombiner.class); 
        job.setReducerClass(CfCombiner.class); 
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class); 
//
        ターゲットパスファイルを取得
        FileInputFormat.addInputPath(job,new Path("file:///d:/calres/cal01/a")); //
        ダウンロードファイルアドレスをマークする
        FileOutputFormat .setOutputPath(job,new Path("file:///d:/calres/ca102/")); 
        /** 
         *ジョブは job.waitForCompletion(true) を通じて実行されます、
         * true は、実行中の進行状況などを意味します情報は時間内に出力されます ユーザーに対して、
         * false の場合は、ジョブが終了するまで待ちます
         */ 
        job.waitForCompletion(true); 
    } 
}

理想的な構成の Kafka コード実装 (最適化: ブリッジ モード)

1 Kafka pom ファイルをインポートする

cannal はデータベースからリアルタイムでデータを取得します

2ymlの設定

読み取りデータのシリアル化モニターを最初から手動で送信する

サーバー:
  ポート: 8999 
spring: 
  application: 
    name: userinterest 
  kafka: 
    bootstrap-servers: 192.168.64.210:9092 #acks=0: 成功または失敗に関係なく、送信は 1 回だけです。確認は必要ありません
      #acks=1: つまり、リーダーがメッセージを受信したことを確認するだけで済みます#acks=all または -1: ISR + リーダーは必ず
    コンシューマー
      を受信します: # オフセット offset を自動的にコミットするかどうか
      Enable-auto-commit: false #Earliest: 送信レコードなし、消費は最初から開始
      #latest: 送信レコードなし、消費は次の最新のメッセージから開始
      auto-offset-reset: 最古      #Key エンコードおよびデコード方法
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer   #値のエンコードおよびデコード方法
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer 
      #リスナーの設定
     
     
     

    
    listener: 
      #enable.auto.commit の値がfalse に設定されている場合、値は有効になります; true の場合、値は有効になりません
      # Manual_immediate: 
      ACKを送信するには、Acknowledgment.acknowledge() を手動で呼び出す必要があります-モード: 手動_即時

3. ブリッジモードテンプレートの設定

3.1 ブリッジモードインターフェースクラス

public Interface FillHbaseData<T> extends FillData<Put,T> { 
    List<Put> fillData(List<T> lis); 
} 
/

** 
 * さまざまなユーザー データと受信エンティティ クラス モデルに従ってデータ形式を変換します Interface 
 * @param < T> 
 */
パブリック インターフェイス StringToEntity<T> { 
    List<T> change(String line); 
}

3.2 ブリッジモード実装クラス

# EventAttendessFillDataImpl类
public class EventAttendessFillDataImplimplements FillHbaseData<EventAttendees> {
    @Override
    public List<Put> fillData(List<EventAttendees> lst) {
        List<Put> put = new ArrayList<>(); 
        lst.stream().forEach(ea->{
            Put put = new Put((ea.getEventid() + ea.getUserid() + ea.getAnswer()).getBytes());
            put.addColumn("base" .getBytes(),"eventid".getBytes(),ea.getAnswer().getBytes());
            put.addColumn("base".getBytes(),"userid".getBytes(),ea.getAnswer(). getBytes());
            put.addColumn("base".getBytes(),"answer".getBytes(),ea.getAnswer(). getBytes()); 
            put.add(put); 
        });
        リターンプット。
    } 
} 
# EventFillDataImpl类
public class EventFillDataImplimplements FillHbaseData<Events> { 
    @Override 
    public List<Put> fillData(List<Events> lis) { 
        List<Put> put = new ArrayList<>(); 
        lis.stream().forEach( 
                event -> { 
                    Put put = new Put(event.getEventid().getBytes()); 
                    put.addColumn("base".getBytes(),"userid".getBytes(),even .getUserid().getBytes()); 
                    put.addColumn("base".getBytes(),"starttime".getBytes(),event.getStarttime().getBytes()); 
                    put.addColumn("base".

                    put.addColumn("base".getBytes(),"zip".getBytes(),event.getZip().getBytes()); 
                    put.addColumn("base".getBytes(),"state".getBytes(),event.getState().getBytes()); 
                    put.addColumn("base".getBytes(),"country".getBytes(),event.get Country().getBytes()); 
                    put.addColumn("base".getBytes(),"lat".getBytes(),event.getLat().getBytes()); 
                    put.addColumn("base".getBytes(),"lng".getBytes(),event.getLng().getBytes()); 
                    置きます。
getBytes()); 
        }); 
        null を返します。
    } 
} 
# EventAttendeesChangeImpl类

public class EventAttendeesChangeImplimplements StringToEntity<EventAttendees> { 
/

    ** 
     * データはイベント ID として入力されます はい、招待される可能性があります いいえ
     * 例:123,112233,34343,234234,45454,112233,23232,234234,3434343,34343 
     * データを変換しますformat の場合、123 112233 はい、123 34343 はい、123 234234 かもしれません... 
     * @param line 
     * @return 
     */ 
    @Override 
    public List<EventAttendees> change(String line) { 
        String[] infos = line.split(" ," , -1); 
        List<EventAttendees> eas = new ArrayList<>(); 
        //まず「はい」と答えた人をすべて数えます
        if (infos[1].trim().equals("")&&infos[1]! = null){ 
            Arrays.asList(infos[1].split(" ")).stream().forEach(
                    yes->{ 
                     EventAttendees ea = EventAttendees.builder() 
                             .eventid(infos[0]).userid(yes).answer("yes") 
                             .build(); 
                        eas.add(ea); 
                    }); 
        } 
        //先计計算すべての回答かもしれない人
        if (infos[2].trim().equals("")&&infos[2]!=null){ 
            Arrays.asList(infos[2].split(" ")) .stream().forEach( 
                    may->{ 
                        EventAttendees ea = EventAttendees.builder() 
                                .eventid(infos[0]).userid(maybe).answer("maybe") 
                                .build();
                        eas.add(ea); 
                    }); 
        } 
        //まず、招待されたと答えた人をすべて計算します
        if (infos[3].trim().equals("")&&infos[3]!=null){ 
            Arrays.asList( infos [3].split(" ")).stream().forEach( 
                    invited->{ 
                        EventAttendees ea = EventAttendees.builder() 
                                .eventid(infos[0]).userid(invited).answer("invited") 
                                . build(); 
                        eas.add(ea); 
                    }); 
        } 
        //まず、「いいえ」と答えた人をすべて計算します
        if (infos[4].trim().equals("")&&infos[4]!=null) {
            Arrays.asList(infos[4].split(" ")).stream().forEach( 
                    no->{ 
                        EventAttendees ea = EventAttendees.builder() 
                                .eventid(infos[0]).userid(no).answer( "いいえ") 
                                .build(); 
                        eas.add(ea); 
                    }); 
        ea
        を返します。
    } 
} 
# EventsChangeImpl类
public class EventsChangeImplimplements StringToEntity<Events> { 
    @Override 
    public List<Events> change(String line) { 
        String[] infos = line.split(",", -1); 
        List<Events> events=new ArrayList<>();

        イベント イベント = Events.builder().eventid(infos[0]).userid(infos[1]).starttime(infos[2]) .city(infos[3]).state(infos[4]). 
                zip (infos[5]).country(infos[6]) 
                .lat(infos[7]).lng(infos[8]).build(); 
        events.add(event);
        イベント
を返す 
    } 
} 
# UserFriendsChangeImpl类
/** 
 * 将将123123, 123435 435455 345345 => 123123, 123435 123123,435455 123123, 345345 * 
 /
パブリック
クラス UserFriendsChangeImpl は StringToEntity<User を実装しますFriends> { 
    @Override 
    public List<UserFriends> change(String line) { 
        String[] infos = line.split(",");

        List<UserFriends> ufs = new ArrayList<>(); 
        Arrays.asList((infos[1]).split(" ")).stream().forEach( 
                fid->{ 
                    UserFriends uf = UserFriends.builder().userid(infos[0]).friendid(fid). build(); 
                    ufs.add(uf); 
                } 
        ); 
        UFS を返します。
    } 
}

3.3 ブリッジモードの抽象クラス

/** 
 * #3.3.1 AbstractDataChanage 抽象类
 * 桥梁モード中の抽象角色
 */ 
public abstract class AbstractDataChanage<E,T>implements DataChanage<T> { 
protected
    FillData<E,T> fillData; 
    protected StringToEntity<T> stringToEntity; 
public

    AbstractDataChanage(FillData<E, T> fillData, StringToEntity<T> stringToEntity) { 
        this.fillData = fillData; 
        this.stringToEntity = stringToEntity; 
    } 
    @Override
 
    public abstract List<T>change(String line); 
public
    abstract void fill(ConsumerRecord<String,
/** 
 * #3.3.2 DataChanage インターフェイス
 * データ変換インターフェイス kafka データは共通のデータ形式に変換されます
 * (redis hbase oracle 複数のデータベースがある場合、埋め込みインターフェイスを記述する必要があります) 
 * @param <T> 
 * /
public
Interface DataChanage<T > { 
    List<T>change(String line); 
}
 
# 3.3.3 DataChangeFillHbaseDatabase クラス
public class DataChangeFillHbaseDatabase<T> extends AbstractDataChanage<Put,T> { 
public
    DataChangeFillHbaseDatabase( 
            FillData<Put,T> fillData, 
            StringToEntity<T > stringToEntity) 
    { 
        super(fillData,stringToEntity); 
    } 
    @Override



    public List<T>change(String line){ 
return
        stringToEntity.change(line); 
    } 
    @Override
 
    public void fill(ConsumerRecord<String,String> record){ 
        //kafka によって取得された ConsumerRecord を読み取り、それをstring 
        List< Put> put = fillData.fillData(change(record.value())); 
        //対応する hbase データベースにコレクションを入力します
    } 
} 
#
 3.3.4 インターフェイス FillData 
public interface FillData<T,E> { 
    List< T> fillData(List<E> lst); 
}

3.4 ブリッジモード

  

概要 工場
a、工場abc、3製品、
工場b、工場abc、3製品

4. エンティティクラスを作成する

@Data 
@AllArgsConstructor 
@NoArgsConstructor 
@Builder 
public class EventAttendees { 
    private String イベント ID; 
    プライベート文字列ユーザーID; 
    プライベート文字列の回答。
} 
@Data
 
@AllArgsConstructor 
@NoArgsConstructor 
@Builder 
public class Events { 
    private String イベント ID; 
    プライベート文字列ユーザーID; 
    プライベート文字列の開始時刻。
    プライベートストリングシティ。
    プライベート文字列の状態。
    プライベート文字列 zip; 
    プライベート文字列の国。
    プライベート文字列緯度。
    プライベート文字列 lng; 
} 
@データ

@AllArgsConstructor 
@NoArgsConstructor 
@Builder 
public class UserFriends { 
    private String userid; 
    プライベート文字列の友人ID; 
}

5.設定クラスの書き込み

@Configuration 
public class HbaseConfig { @Bean
 
        public org.apache.hadoop.conf.Configuration hbaseConfiguration(){ 
            org.apache.hadoop.conf.Configuration cfg= HBaseConfiguration.create(); 
            cfg.set(HConstants.ZOOKEEPER_QUORUM,"192.168.64.210:2181"); 
            return cfg; 
        }
   @Bean
 @Scope("プロトタイプ") 
        public Connection getConnection() { 
            Connection connection=null; 
            {
                接続 = ConnectionFactory.createConnection(hbaseConfiguration());を試してください。
            } catch (IOException e) { 
                e.printStackTrace();
       
      
       
            接続を返します
            。
        } @Bean
 
        public Supplier<Connection> hbaseConSupplier(){ 
            return ()->{ 
                return getConnection(); 
            }; 
        } 
}
       

5.1 データ重複の解決 hbase

hbase の基になる kv k は行キーであり、行キーはユーザー名 + 友人です。
5.2
データ量が多すぎますか? 30w以上、数十g、3000wのデータ、数g程度、
サブデータベース、テーブル、垂直パーティショニング 10g 
hbase関数 プリパーティション関数
oracleパーティション ハッシュパーティション レンジパーティション リストパーティション

5.2 サービスクラスの作成

#5.2.1 
/**
 * 変換された List<Put> データ セットを受け入れて Hbase データベースに入力します
 */
@Component

public class HbaseWriter {
    @Resource
    private Connection hbaseConnection;
public
    void write(List<Put> Puts,String tableName ){
        try {
            Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
            table.put(puts);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
# 5.2.2 
@Component
public class KafkaReader {
@KafkaListener
    (groupId = "cm",topics = {"events_raw"})

    public void readEventToHbase(ConsumerRecord<String ,String > record, Acknowledgment ack){ 
        AbstractDataChanage<Put,Events> eventsHandler = new DataChangeFillHbaseDatabase<Events>( 
                new EventFillDataImpl(), 
                new EventsChangeImpl() 
        ); 
        eventsHandler.fill(レコード); 
        ack.acknowledge(); 
    } 
    @KafkaListener(groupId = "cm",topics = {"event_attendees_raw"}) 
    public void readEventToHbase1(ConsumerRecord<String ,String > Record, Acknowledgment ack){ 
        AbstractDataChanage<Put,
                new EventAttendeesChangeImpl() 
)
        ; 
        eventsHandler.fill(レコード); 
        ack.acknowledge(); 
    } 
    @KafkaListener(groupId = "cm",topics = {"user_friends_raw"}) 
    public void readEventToHbase2(ConsumerRecord<String ,String > record, Acknowledgment ack){ 
        AbstractDataChanage<Put, UserFriends> eventsHandler = new DataChangeFillHbaseDatabase<UserFriends>( 
                new UserFriendsFillDataImpl ()、
                新しい UserFriendsChangeImpl() 
)
        ; 
        eventsHandler.fill(レコード); 
        ack.acknowledge(); 
    } 
}

6 hbase データベースに列クラスターを作成する

#必要なリージョン数と分割アルゴリズムに基づいて分割を自動的に計算します
create 'userfriends','base',{ NUMREGIONS   => 3, SPLITALGO  =>'HexStringSplit' } 
============ ==
=============================================== == ====== NUM​​REGIONSの説明: 
hbase
のデフォルトの HFile サイズは 10G (hbase.hregion.max.filesize=10737418240=10G)
ソース
データは Hive です: 推奨されるパーティション数 ≈ HDFS サイズ/10G * 10 * 1.2 
HexStringSplit
、UniformSplit、DecimalStringSplit 説明: 
UniformSplit
(小さなスペース占有、完全にランダムな行キー プレフィックス •••••••): 可能なキーのスペースを均等に分割する集合体。これは、キーがほぼ一貫したランダム バイト (ハッシュなど) である場合にお勧めします。行は 00 => FF の範囲の生のバイト値で、同じ memcmp() 順序を維持するために右に 0 が埋め込まれます。これは byte[] 環境にとって自然なアルゴリズムであり、スペースを節約しますが、読みやすさの点で必ずしも最も単純であるとは限りません。

HexStringSplit (多くのスペースを必要とし、rowkey はプレフィックスとしての 16 進数の文字列です。•••••••): HexStringSplit は、領域境界を選択するための典型的な RegionSplitter.SplitAlgorithm です。HexStringSplit 領域境界の形式は、MD5 チェックサムまたはその他の均一に分散された 16 進値の ASCII 表現です。Row は、「00000000」 => 「FFFFFFFF」の範囲の 16 進数でエンコードされた Long 値で、辞書編集上バイナリと同じ順序になるように左に 0 が埋め込まれています。この分割アルゴリズムは 16 進文字列をキーとして使用するため、シェルでの読み書きは簡単ですが、より多くのスペースを必要とし、直感的ではない可能性があります。
DecimalStringSplit: rowkey はプレフィックスとしての 10 進数の文字列です
===================================== === ============================= 
create
'eventAttendees','base' 
cat
events.csv.COMPLETED | head -2 
cd
/opt/data/attendees 
cat event_attendees_raw |head -2 
create
'events' 'base'

6.1 hbase起動コマンド

#hbaseStart
 start-hbase.sh
またはスクリプトを開始してください。スクリプトは次のとおりです#ブラウザアドレスhttp://192.168.64.210: 60010 /master-status

おすすめ

転載: blog.csdn.net/just_learing/article/details/126273880