データのクリーニングは正当化されますか? 多次元検証グループグループ 変換 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' } ============ == =============================================== == ====== NUMREGIONSの説明: 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