<3> Filter Stream & Data Stream & Streams in memory

[b]六、Filter Streams[/b]
Filter input Streams从[b]预先[/b]存在的输入流(比如FileInputStream)中读取数据,在把数据发送给客户端程序之前可以对数据进行[b]预处理[/b]。Filter output Streams把数据写到预先存在的输出流(比如FileOutputStream)里,可以在把数据写到底层的输出流[b]之前[/b]再加工数据。[color=red]一个底层的stream之上可以叠加多个filter[/color]。Filter Stream可以用来完成加密,压缩,缓存等操作。

Filter一词来源于滤水器(water filter)。 滤水器位于水管和水源之间,[b]stream filter位于数据源和最终的目的地之间[/b],在此间可以使用特定的算法处理数据。

[b]6.1 The Filter Stream Classes[/b]
java.io.FilterInputStream和java.io.FilterOutputStream是各具体filter stream实现类的父类。
public class FilterInputStream extends InputStream 
public class FilterOutputStream extends OutputStream

这两个类都有一个[b]protected[/b]的构造方法,必须指明要进行预处理的底层的stream(specify the underlying stream from which the filter stream reads or writes data)。
protected FilterInputStream(InputStream in)
protected FilterOutputStream(OutputStream out)
指定的底层stream会被分别保存到protected的成员变量里:
protected InputStream in
protected OutputStream out
[b][color=red]因为这两个类的构造方法是protected的,所以只能由子类来创建具体的对象(Filter Stream classes 的实现实际上是装饰器模式的应用)。[/color][/b]

FilterOutputStream的close()方法实现如下:
public void close() throws IOException 
{
out.close();
}

[color=red]因此,关闭一个filter stream也就是关闭了底层实际的stream,无论是Filter inputstream还是OutputStream,也无论嵌套几层Filter stream都是这样。[/color]

[b]6.2 The Filter Stream Subclasses[/b]
java.io包里有很多有用的filter stream classes. BufferedInputStream和BufferedOutputStream类会把读和写的数据首先放到缓存里。因此,应用程序可以不用调用底层数据流的native方法就可以读或写数据。很多情况下这可以提高性能。

java.io.PrintStream类(System.out和System.err就是这个类型的)可以简单的打印出原始类型值,对象和字符串。这个类使用平台默认的字符编码来转换字符成bytes。我们也能用PrintStream当做过滤器作用在其他outputStream上,比如作用在FileOutputStream上就可以简单的输出文本到文件中。

DataInputStream和DataOutputStream类可以用机器独立(machine-independent way)的方式来读和写java原始数据类型。(Big-endian for integer types, IEEE-754 for floats and doubles, UTF-8 for Unicode)。

[b]6.3 Buffered Streams[/b]
Buffered input Streams会读比原始需要要多的数据到一个缓存里([b]一个内部的byte数组[/b])。当调用Stream的read()方法时,首先会从缓存里获取数据而不是底层的stream, 当缓存的数据用完时,buffered stream会再次从底层stream里读一块数据填满缓存。同样地,buffered output stream在一个内部byte数组里存储数据,[b]直到缓存满了或显式调用flush()方法数据才会一下子输出到底层output stream.[/b]

BufferedInputStream和BufferedOutputStream分别有两个构造方法:
public BufferedInputStream(InputStream in) 
public BufferedInputStream(InputStream in, int size)
public BufferedOutputStream(OutputStream out)
public BufferedOutputStream(OutputStream out, int size)

第一个参数是底层实际的stream, 第二个参数size就是缓存的大小。[b]如果不指定缓存size, 默认使用2048字节的缓存[/b]。缓存大小最好根据平台来定,或者最好跟硬盘的block大小相关。 小于512字节可能会太小,大于4096可能会太多。对于不可靠的网络连接,最好使用较小的缓存大小:
URL u = new URL("http://java.developer.com"); 
BufferedInputStream bis = new BufferedInputStream(u.openStream(), 256);


[b]七、Data Streams[/b]
Data stream能读和写string,integer, float和其他数据类型数据。java.io.DataInputStream和java.io.DataOutputStream类以一种平台独立的方式读和写原始数据类型(boolean, int,double等)。

[b]7.1 The Data Stream Classes[/b]
java.io.DataInputStream和java.io.DataOutputStream类是FilterInputStream和FilterOutputStream的子类:
public class DataInputStream extends FilterInputStream implements DataInput 
public class DataOutputStream extends FilterOutputStream implements DataOutput


[b]7.1.1 The DataInput and DataOutput Interfaces [/b]
java.io.DataInput接口声明了15个读各种各样数据的方法:
public abstract boolean readBoolean() throws IOException 
public abstract byte readByte() throws IOException
public abstract int readUnsignedByte() throws IOException
public abstract short readShort() throws IOException
public abstract int readUnsignedShort() throws IOException
public abstract char readChar() throws IOException
public abstract int readInt() throws IOException
public abstract long readLong() throws IOException
public abstract float readFloat() throws IOException
public abstract double readDouble() throws IOException
public abstract String readLine() throws IOException
public abstract String readUTF() throws IOException
public void readFully(byte[] data) throws IOException
public void readFully(byte[] data, int offset, int length) throws IOException
public int skipBytes(int n) throws IOException


类似地,java.io.DataOutput接口声明了14个方法,大部分是跟DataInput接口对应的:
public abstract void write(int b) throws IOException 
public abstract void write(byte[] data) throws IOException
public abstract void write(byte[] data, int offset, int length) throws IOException
public abstract void writeBoolean(boolean v) throws IOException
public abstract void writeByte(int b) throws IOException
public abstract void writeShort(int s) throws IOException
public abstract void writeChar(int c) throws IOException
public abstract void writeInt(int i) throws IOException
public abstract void writeLong(long l) throws IOException
public abstract void writeFloat(float f) throws IOException
public abstract void writeDouble(double d) throws IOException
public abstract void writeBytes(String s) throws IOException
public abstract void writeChars(String s) throws IOException
public abstract void writeUTF(String s) throws IOException


注意这里的writerBytes()和writeChars()方法跟DataInput的readBytes()和readChars()方法不是对应匹配的。下面是DataInput和DataOutput的方法的对应关系:

[img]http://dl2.iteye.com/upload/attachment/0098/1675/cbb4d983-a485-3f98-bdd5-f3ff4fd149bb.bmp[/img]
[img]http://dl2.iteye.com/upload/attachment/0098/1673/29bc96f7-daa4-3416-96c4-82859cf67868.bmp[/img]

[b]7.1.2 Constructors[/b]
DataInputStream和DataOutputStream的构造方法如下:
public DataInputStream(InputStream in) 
public DataOutputStream(OutputStream out)

使用示例:
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt")); 
DataOutputStream dos = new DataOutputStream(new FileOutputStream("output.dat"));


[b]八、Streams in Memory[/b]
前面介绍的输入输出主要是在java程序和外部程序之间。本章再扩展三个类似的方法。[b]Sequence input streams能把好几个[/b]input streams串在一起好像是一个stream一样;
[b] Byte array streams[/b]能把数据存储在byte arrays里也能从byte arrays里读数据;
最后,[b]piped input and output streams[/b]可以让一个线程的输出称为一个线程的输入。

[b]8.1 Sequence input Streams[/b]
java.io.SequenceInputStream类以一定的顺序连接多个input stream:
public class SequenceInputStream extends InputStream
从SequenceInputStream里读数据会先从第一个stream里读取所有数据,然后读取第二个stream的所有数据,以此类推直到最后一个stream的所有数据,这个类有两个构造方法:
public SequenceInputStream(Enumeration e) 
public SequenceInputStream(InputStream in1, InputStream in2)


示例:
try
{
URL u1 = new URL("http://java.sun.com/");
URL u2 = new URL("http://www.altavista.com");
SequenceInputStream sin = new SequenceInputStream(u1.openStream(),
u2.openStream());
}
catch (IOException e)
{ //...


[b]8.2 Byte Array Streams[/b]
有时候使用stream的方法操作byte arrays里的数据很方便。比如,我们可能接收到一个原始字节的数组,里面的数据需要解释成double类型。最快的方式是使用DataInputStream,但是在创建DataInputStream之前,我们首先需要创建一个原始的byte流才能接收字节数据,这正是java.io.ByteArrayInputStream类提供的能力。

[b]8.2.1 Byte Array Input Streams[/b]
ByteArrayInputStream类使用InputStream的方法从byte array里读数据:
public class ByteArrayInputStream extends InputStream
此类有两个构造方法,参数就是数据源:
public ByteArrayInputStream(byte[] buffer) 
public ByteArrayInputStream(byte[] buffer, int offset, int length)


[b]8.2.2 Byte Array Output Streams[/b]
ByteArrayOutputStream类使用java.io.OutputStream的方法输出数据到byte array.
public class ByteArrayOutputStream extends OutputStream
它有两个构造方法:
public ByteArrayOutputStream() 
public ByteArrayOutputStream(int size)

无参构造方法使用一个32字节的buffer.使用toByteArray()方法能返回包含要输出数据的byte数组:
public synchronized byte[] toByteArray()

这个类也有toString()方法能把bytes转成string。无参的版本使用平台默认的编码,第二个方法可以自己指定编码。
public String toString() 
public String toString(String encoding) throws UnsupportedEncodingException


比如,一个把double转成bytes的方法就是用DataOutputStream过滤类嵌套在ByteArrayOutputStream外面,然后输出double到byte array:
ByteArrayOutputStream bos = new ByteArrayOutputStream(1024); 
DataOutputStream dos = new DataOutputStream(bos);

for (int r = 1; r < 1024; r++)
{
dos.writeDouble(r * 2.0 * Math.PI);
}


[b]8.3 Communicating Between Threads with Piped Streams[/b]
java.io.PipedInputStream类和java.io.PipedOutputStream类提供了方便的方法来把stream在线程之间传递。
[img]http://dl2.iteye.com/upload/attachment/0098/1677/bd436f3c-d22d-30c9-8459-916eb8f29dd1.bmp[/img]

public class PipedInputStream extends InputStream
public class PipedOutputStream extends OutputStream


PipedInputStream有两个构造方法:
public PipedInputStream()
public PipedInputStream(PipedOutputStream source) throws IOException
无参的构造方法创建一个还没有和一个piped outputStream关联的piped InputStream。第二个构造方法创建一个已经和一个output stream连接的piped input Stream。
同样,PipedOutputStream也有两个构造方法:
public PipedOutputStream(PipedInputStream sink) throws IOException
public PipedOutputStream()

使用示例:
PipedOutputStream pout = new PipedOutputStream(); 
PipedInputStream pin = new PipedInputStream(pout);
PipedInputStream pin = new PipedInputStream();
PipedOutputStream pout = new PipedOutputStream(pin);


或者创建两个都没有关联的输入输出之后再连接:
PipedInputStream pin = new PipedInputStream(); 
PipedOutputStream pout = new PipedOutputStream();
pin.connect(pout);


参考:
《java I/O》 Elliotte Rusty Harold
&lt;think&gt;好的,我现在要回答用户关于ETL是什么的问题。首先,我需要明确ETL的定义,确保自己理解正确。ETL是Extract, Transform, Load的缩写,也就是数据抽取、转换和加载的过程。这是数据仓库和数据处理中常见的流程。 接下来,我需要拆解这三个步骤,分别解释每个部分的作用。提取(Extract)是从不同数据源获取数据,比如数据库、文件、API等。转换(Transform)包括清洗数据、格式化、合并、去重、计算等处理。加载(Load)则是将处理后的数据存储到目标数据库或数据仓库中。 然后,我应该考虑用户可能的背景。如果用户是技术背景,可能需要更详细的技术细节,比如使用的工具或技术;如果是非技术用户,可能需要更通俗的比喻或例子。这里用户的问题比较基础,所以保持简明扼要,用易懂的语言解释。 还需要考虑ETL的应用场景,比如商业智能、数据分析、数据迁移等,这样用户能理解其实际用途。同时,可能提到ETL的工具,比如Informatica、Talend、AWS Glue等,但不需要深入,点到为止即可。 另外,可能需要对比ELT(Extract, Load, Transform),说明两者的不同,但根据用户的问题,可能不需要展开,除非用户后续提问。这里先专注于ETL本身的解释。 要检查是否有常见的误解需要澄清,比如ETL不只是三个步骤,而是一个完整的数据处理流程,强调其系统性和自动化的重要性。确保没有遗漏关键点,比如数据质量的重要性,转换过程中的验证步骤等。 最后,组织语言结构清晰,分点或分段解释,使用例子帮助理解,比如零售公司整合销售数据的例子。保持回答简洁,同时覆盖ETL的核心概念、步骤、应用和工具,确保用户能全面理解。&lt;/think&gt;ETL 是数据工程领域的核心流程,全称为 **Extract(抽取)- Transform(转换)- Load(加载)**,主要用于实现异构数据源的整合与标准化处理。以下是技术层面的深度解析: --- ### **1. 三阶段技术实现** #### **① Extract 数据抽取** - **技术方式**: - 全量抽取:`SELECT * FROM source_table` - 增量抽取:通过时间戳(`WHERE update_time &gt; last_extract`)、CDC(Change Data Capture)或日志解析 - API调用:`GET /api/v1/data?since=2023-07-20T00:00:00Z` - **典型数据源**: - 关系型数据库:MySQL(`mysqldump`)、Oracle(`SQL*Loader`) - 文件系统:CSV(`pandas.read_csv()`)、JSON(`json.load()`)、Parquet(`pyarrow.parquet.read_table()`) - 云端服务:AWS S3(`boto3.client(&#39;s3&#39;).get_object()`)、Salesforce API #### **② Transform 数据转换** - **核心操作**(以SQL/Python为例): ```python # 数据清洗 df.drop_duplicates(subset=[&#39;user_id&#39;], keep=&#39;last&#39;) df.fillna({&#39;age&#39;: df[&#39;age&#39;].median()}, inplace=True) # 数据标准化 df[&#39;price&#39;] = df[&#39;price&#39;].apply(lambda x: x * exchange_rate) # 维度建模 fact_table = pd.merge(orders, products, on=&#39;product_id&#39;, how=&#39;left&#39;) ``` - **典型转换类型**: - 结构转换:`pivot_table()`(行转列) / `melt()`(列转行) - 计算字段:`df[&#39;BMI&#39;] = df[&#39;weight&#39;] / (df[&#39;height&#39;]**2)` - 数据脱敏:`df[&#39;phone&#39;] = df[&#39;phone&#39;].str.replace(r&#39;\d{4}$&#39;, &#39;****&#39;)` #### **③ Load 数据加载** - **写入策略**: - 全量覆盖:`TRUNCATE TABLE target_table; INSERT INTO...` - 增量追加:`INSERT INTO target_table SELECT ... WHERE NOT EXISTS(...)` - 合并更新(UPSERT):`MERGE INTO target_table USING ... ON ... WHEN MATCHED THEN UPDATE...` - **目标系统**: - 数据仓库:Redshift(`COPY`命令)、BigQuery(`bq load`) - 数据湖:HDFS(`hdfs dfs -put`)、Delta Lake(`df.write.format(&quot;delta&quot;)`) --- ### **2. 现代技术演进** #### **① ELT 模式** - 传统ETL &rarr; 现代ELT(利用云数仓计算能力): ```sql /* Snowflake ELT示例 */ COPY INTO raw_sales FROM @s3_stage; INSERT INTO transformed_sales SELECT UUID_STRING() AS sale_id, PARSE_JSON(raw_data):customer-&gt;&gt;&#39;name&#39; AS customer_name, DATE_TRUNC(&#39;month&#39;, TO_DATE(raw_data:date)) AS sale_month FROM raw_sales; ``` #### **② 流式ETL** - 实时处理框架: - Apache Kafka(消息队列) + Kafka Streams(流处理): ```java KStream&lt;String, String&gt; stream = builder.stream(&quot;input-topic&quot;); stream.filter((k, v) -&gt; v != null) .mapValues(v -&gt; v.toUpperCase()) .to(&quot;output-topic&quot;); ``` - Flink SQL: ```sql CREATE TABLE orders ( order_id STRING, amount DECIMAL(10,2), proc_time AS PROCTIME() ) WITH (&#39;connector&#39; = &#39;kafka&#39;...); INSERT INTO hourly_sales SELECT TUMBLE_START(proc_time, INTERVAL &#39;1&#39; HOUR), SUM(amount) FROM orders GROUP BY TUMBLE(proc_time, INTERVAL &#39;1&#39; HOUR); ``` --- ### **3. 企业级工具对比** | 工具类型 | 代表产品 | 关键技术特征 | |----------------|-----------------------------------|---------------------------------------| | 开源框架 | Apache NiFi、Airflow | 可视化流水线设计,Python DAG定义 | | 云原生服务 | AWS Glue、Azure Data Factory | Serverless执行,按DPU计费,内置连接器 | | 传统ETL工具 | Informatica PowerCenter | 元数据驱动,企业级调度系统 | | 代码优先工具 | dbt(Data Build Tool) | SQL-centric,版本控制集成 | --- ### **4. 关键性能指标(KPIs)** - **数据质量**:完整性(`COUNT(NULL)`)、一致性(跨系统校验) - **处理效率**:吞吐量(MB/s)、延迟(端到端处理时间) - **资源利用率**:CPU/Memory消耗(`top`命令监控)、云成本(AWS Glue DPU-hours) 通过合理设计ETL流水线,可使数据准备时间从传统数天的批处理缩短至分钟级的实时流处理,典型应用场景包括金融风控实时反欺诈(`Flink CEP`模式识别)和电商实时推荐系统(`Kafka &rarr; Spark ML &rarr; Redis`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值