Flink - state

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
简介:
public class StreamTaskState implements Serializable, Closeable {

    private static final long serialVersionUID = 1L;
    
    private StateHandle<?> operatorState;

    private StateHandle<Serializable> functionState;

    private HashMap<String, KvStateSnapshot<?, ?, ?, ?, ?>> kvStates;
复制代码

Flink中state分为三种,

可以看到,StreamTaskState是对三种state的封装,

1. KVState

是最基本的state,

抽象是一对,KvState和KvStateSnapshot 
通过两个接口,互相转化

复制代码
/**
 * Key/Value state implementation for user-defined state. The state is backed by a state
 * backend, which typically follows one of the following patterns: Either the state is stored
 * in the key/value state object directly (meaning in the executing JVM) and snapshotted by the
 * state backend into some store (during checkpoints), or the key/value state is in fact backed
 * by an external key/value store as the state backend, and checkpoints merely record the
 * metadata of what is considered part of the checkpoint.
 * 
 * @param <K> The type of the key.
 * @param <N> The type of the namespace.
 * @param <S> The type of {@link State} this {@code KvState} holds.
 * @param <SD> The type of the {@link StateDescriptor} for state {@code S}.
 * @param <Backend> The type of {@link AbstractStateBackend} that manages this {@code KvState}.
 */
public interface KvState<K, N, S extends State, SD extends StateDescriptor<S, ?>, Backend extends AbstractStateBackend> {

    /**
     * Sets the current key, which will be used when using the state access methods.
     *
     * @param key The key.
     */
    void setCurrentKey(K key);

    /**
     * Sets the current namespace, which will be used when using the state access methods.
     *
     * @param namespace The namespace.
     */
    void setCurrentNamespace(N namespace);

    /**
     * Creates a snapshot of this state.
     * 
     * @param checkpointId The ID of the checkpoint for which the snapshot should be created.
     * @param timestamp The timestamp of the checkpoint.
     * @return A snapshot handle for this key/value state.
     * 
     * @throws Exception Exceptions during snapshotting the state should be forwarded, so the system
     *                   can react to failed snapshots.
     */
    KvStateSnapshot<K, N, S, SD, Backend> snapshot(long checkpointId, long timestamp) throws Exception;

    /**
     * Disposes the key/value state, releasing all occupied resources.
     */
    void dispose();
}
复制代码
定义也比较简单,关键是snapshot接口,产生KvStateSnapshot
复制代码
public interface KvStateSnapshot<K, N, S extends State, SD extends StateDescriptor<S, ?>, Backend extends AbstractStateBackend> 
        extends StateObject {

    /**
     * Loads the key/value state back from this snapshot.
     *
     * @param stateBackend The state backend that created this snapshot and can restore the key/value state
     *                     from this snapshot.
     * @param keySerializer The serializer for the keys.
     * @param classLoader The class loader for user-defined types.
     *
     * @return An instance of the key/value state loaded from this snapshot.
     * 
     * @throws Exception Exceptions can occur during the state loading and are forwarded. 
     */
    KvState<K, N, S, SD, Backend> restoreState(
        Backend stateBackend,
        TypeSerializer<K> keySerializer,
        ClassLoader classLoader) throws Exception;
}
复制代码

KvStateSnapshot,对应于KvState,关键是restoreState接口

以具体的,FsState为例,

public abstract class AbstractFsState<K, N, SV, S extends State, SD extends StateDescriptor<S, ?>>
extends AbstractHeapState<K, N, SV, S, SD, FsStateBackend> {

可以看到AbstractFsState是继承AbstractHeapState的,因为对于FsState的状态也是cache在Heap中的,只是在snapshot的时候需要写文件

所以先看下AbstractHeapState,

复制代码
/**
 * Base class for partitioned {@link ListState} implementations that are backed by a regular
 * heap hash map. The concrete implementations define how the state is checkpointed.
 * 
 * @param <K> The type of the key.
 * @param <N> The type of the namespace.
 * @param <SV> The type of the values in the state.
 * @param <S> The type of State
 * @param <SD> The type of StateDescriptor for the State S
 * @param <Backend> The type of the backend that snapshots this key/value state.
 */
public abstract class AbstractHeapState<K, N, SV, S extends State, SD extends StateDescriptor<S, ?>, Backend extends AbstractStateBackend>
        implements KvState<K, N, S, SD, Backend>, State {

    /** Map containing the actual key/value pairs */
    protected final HashMap<N, Map<K, SV>> state; //可以看到这里,多了个namespace的概念,避免key太容易重复

    /** Serializer for the state value. The state value could be a List<V>, for example. */
    protected final TypeSerializer<SV> stateSerializer;

    /** The serializer for the keys */
    protected final TypeSerializer<K> keySerializer;

    /** The serializer for the namespace */
    protected final TypeSerializer<N> namespaceSerializer;

    /** This holds the name of the state and can create an initial default value for the state. */
    protected final SD stateDesc; //StateDescriptor,用于放一些state的信息,比如default值

    /** The current key, which the next value methods will refer to */
    protected K currentKey;

    /** The current namespace, which the access methods will refer to. */
    protected N currentNamespace = null;

    /** Cache the state map for the current key. */
    protected Map<K, SV> currentNSState;

    /**
     * Creates a new empty key/value state.
     *
     * @param keySerializer The serializer for the keys.
     * @param namespaceSerializer The serializer for the namespace.
     * @param stateDesc The state identifier for the state. This contains name
     *                           and can create a default state value.
     */
    protected AbstractHeapState(TypeSerializer<K> keySerializer,
        TypeSerializer<N> namespaceSerializer,
        TypeSerializer<SV> stateSerializer,
        SD stateDesc) {
        this(keySerializer, namespaceSerializer, stateSerializer, stateDesc, new HashMap<N, Map<K, SV>>());
    }
复制代码
 
AbstractFsState
复制代码
public abstract class AbstractFsState<K, N, SV, S extends State, SD extends StateDescriptor<S, ?>>
        extends AbstractHeapState<K, N, SV, S, SD, FsStateBackend> {

    /** The file system state backend backing snapshots of this state */
    private final FsStateBackend backend;


    public abstract KvStateSnapshot<K, N, S, SD, FsStateBackend> createHeapSnapshot(Path filePath); //

    @Override
    public KvStateSnapshot<K, N, S, SD, FsStateBackend> snapshot(long checkpointId, long timestamp) throws Exception {

        try (FsStateBackend.FsCheckpointStateOutputStream out = backend.createCheckpointStateOutputStream(checkpointId, timestamp)) { //

            // serialize the state to the output stream
            DataOutputViewStreamWrapper outView = new DataOutputViewStreamWrapper(new DataOutputStream(out)); 
            outView.writeInt(state.size());
            for (Map.Entry<N, Map<K, SV>> namespaceState: state.entrySet()) {
                N namespace = namespaceState.getKey();
                namespaceSerializer.serialize(namespace, outView);
                outView.writeInt(namespaceState.getValue().size());
                for (Map.Entry<K, SV> entry: namespaceState.getValue().entrySet()) {
                    keySerializer.serialize(entry.getKey(), outView);
                    stateSerializer.serialize(entry.getValue(), outView);
                }
            }
            outView.flush(); //真实的内容是刷到文件的

            // create a handle to the state
            return createHeapSnapshot(out.closeAndGetPath()); //snapshot里面需要的只是path
        }
    }
}
复制代码

 

对于kv state,也分为好几类,valuestate,liststate,reducestate,foldstate,

简单起见,先看valuestate

复制代码
public class FsValueState<K, N, V>
    extends AbstractFsState<K, N, V, ValueState<V>, ValueStateDescriptor<V>>
    implements ValueState<V> {

    @Override
    public V value() {
        if (currentNSState == null) {
            currentNSState = state.get(currentNamespace); //现初始化当前namespace的kv
        }
        if (currentNSState != null) {
            V value = currentNSState.get(currentKey);
            return value != null ? value : stateDesc.getDefaultValue(); //取出value,如果为null,从stateDesc中取出default
        }
        return stateDesc.getDefaultValue();
    }

    @Override
    public void update(V value) {
        if (currentKey == null) {
            throw new RuntimeException("No key available.");
        }

        if (value == null) {
            clear();
            return;
        }

        if (currentNSState == null) {
            currentNSState = new HashMap<>();
            state.put(currentNamespace, currentNSState);
        }

        currentNSState.put(currentKey, value); //更新
    }
    
    @Override
    public KvStateSnapshot<K, N, ValueState<V>, ValueStateDescriptor<V>, FsStateBackend> createHeapSnapshot(Path filePath) {
        return new Snapshot<>(getKeySerializer(), getNamespaceSerializer(), stateSerializer, stateDesc, filePath); //以文件路径,创建snapshot
    }
复制代码

 

继续看FsStateSnapshot

复制代码
public abstract class AbstractFsStateSnapshot<K, N, SV, S extends State, SD extends StateDescriptor<S, ?>> 
        extends AbstractFileStateHandle implements KvStateSnapshot<K, N, S, SD, FsStateBackend> {

    public abstract KvState<K, N, S, SD, FsStateBackend> createFsState(FsStateBackend backend, HashMap<N, Map<K, SV>> stateMap); //

    @Override
    public KvState<K, N, S, SD, FsStateBackend> restoreState(
        FsStateBackend stateBackend,
        final TypeSerializer<K> keySerializer,
        ClassLoader classLoader) throws Exception {

        // state restore
        ensureNotClosed();

        try (FSDataInputStream inStream = stateBackend.getFileSystem().open(getFilePath())) {
            // make sure the in-progress restore from the handle can be closed 
            registerCloseable(inStream);

            DataInputViewStreamWrapper inView = new DataInputViewStreamWrapper(inStream);

            final int numKeys = inView.readInt();
            HashMap<N, Map<K, SV>> stateMap = new HashMap<>(numKeys);

            for (int i = 0; i < numKeys; i++) {
                N namespace = namespaceSerializer.deserialize(inView);
                final int numValues = inView.readInt();
                Map<K, SV> namespaceMap = new HashMap<>(numValues);
                stateMap.put(namespace, namespaceMap);
                for (int j = 0; j < numValues; j++) {
                    K key = keySerializer.deserialize(inView);
                    SV value = stateSerializer.deserialize(inView);
                    namespaceMap.put(key, value);
                }
            }

            return createFsState(stateBackend, stateMap); //
        }
        catch (Exception e) {
            throw new Exception("Failed to restore state from file system", e);
        }
    }
}
复制代码

 

FsValueState内部实现的snapshot
复制代码
public static class Snapshot<K, N, V> extends AbstractFsStateSnapshot<K, N, V, ValueState<V>, ValueStateDescriptor<V>> {
    private static final long serialVersionUID = 1L;

    public Snapshot(TypeSerializer<K> keySerializer,
        TypeSerializer<N> namespaceSerializer,
        TypeSerializer<V> stateSerializer,
        ValueStateDescriptor<V> stateDescs,
        Path filePath) {
        super(keySerializer, namespaceSerializer, stateSerializer, stateDescs, filePath);
    }

    @Override
    public KvState<K, N, ValueState<V>, ValueStateDescriptor<V>, FsStateBackend> createFsState(FsStateBackend backend, HashMap<N, Map<K, V>> stateMap) {
        return new FsValueState<>(backend, keySerializer, namespaceSerializer, stateDesc, stateMap);
    }
}
复制代码

 

2. FunctionState

stateHandle对于KvState,更为通用一些

复制代码
/**
 * StateHandle is a general handle interface meant to abstract operator state fetching. 
 * A StateHandle implementation can for example include the state itself in cases where the state 
 * is lightweight or fetching it lazily from some external storage when the state is too large.
 */
public interface StateHandle<T> extends StateObject {

    /**
     * This retrieves and return the state represented by the handle.
     *
     * @param userCodeClassLoader Class loader for deserializing user code specific classes
     *
     * @return The state represented by the handle.
     * @throws java.lang.Exception Thrown, if the state cannot be fetched.
     */
    T getState(ClassLoader userCodeClassLoader) throws Exception;
}
复制代码

 

3. OperatorState,典型的是windowOperater的状态

OperatorState,也是用StateHandle作为,snapshot的抽象

 

看下这三种State如何做snapshot的

AbstractStreamOperator,看看和checkpoint相关的接口,可以看到只会snapshot KvState
复制代码
@Override
public StreamTaskState snapshotOperatorState(long checkpointId, long timestamp) throws Exception {
    // here, we deal with key/value state snapshots
    
    StreamTaskState state = new StreamTaskState();

    if (stateBackend != null) {
        HashMap<String, KvStateSnapshot<?, ?, ?, ?, ?>> partitionedSnapshots =
            stateBackend.snapshotPartitionedState(checkpointId, timestamp);
        if (partitionedSnapshots != null) {
            state.setKvStates(partitionedSnapshots);
        }
    }


    return state;
}

@Override
@SuppressWarnings("rawtypes,unchecked")
public void restoreState(StreamTaskState state) throws Exception {
    // restore the key/value state. the actual restore happens lazily, when the function requests
    // the state again, because the restore method needs information provided by the user function
    if (stateBackend != null) {
        stateBackend.injectKeyValueStateSnapshots((HashMap)state.getKvStates());
    }
}

@Override
public void notifyOfCompletedCheckpoint(long checkpointId) throws Exception {
    if (stateBackend != null) {
        stateBackend.notifyOfCompletedCheckpoint(checkpointId);
    }
}
复制代码

 

AbstractUdfStreamOperator
public abstract class AbstractUdfStreamOperator<OUT, F extends Function> extends AbstractStreamOperator<OUT> implements OutputTypeConfigurable<OUT>

这个首先继承了AbstractStreamOperator,看下checkpoint相关的接口,

复制代码
@Override
public StreamTaskState snapshotOperatorState(long checkpointId, long timestamp) throws Exception {
    StreamTaskState state = super.snapshotOperatorState(checkpointId, timestamp); //先执行super的snapshotOperatorState,即Kv state的snapshot

    if (userFunction instanceof Checkpointed) {
        @SuppressWarnings("unchecked")
        Checkpointed<Serializable> chkFunction = (Checkpointed<Serializable>) userFunction;
        
        Serializable udfState;
        try {
            udfState = chkFunction.snapshotState(checkpointId, timestamp); //snapshot,function的状态
        } 
        catch (Exception e) {
            throw new Exception("Failed to draw state snapshot from function: " + e.getMessage(), e);
        }
        
        if (udfState != null) {
            try {
                AbstractStateBackend stateBackend = getStateBackend();
                StateHandle<Serializable> handle = 
                        stateBackend.checkpointStateSerializable(udfState, checkpointId, timestamp); //调用stateBackend存储state,并返回snapshot
                state.setFunctionState(handle);
            }
            catch (Exception e) {
                throw new Exception("Failed to add the state snapshot of the function to the checkpoint: "
                        + e.getMessage(), e);
            }
        }
    }
    
    return state;
}

@Override
public void restoreState(StreamTaskState state) throws Exception {
    super.restoreState(state);
    
    StateHandle<Serializable> stateHandle =  state.getFunctionState();
    
    if (userFunction instanceof Checkpointed && stateHandle != null) {
        @SuppressWarnings("unchecked")
        Checkpointed<Serializable> chkFunction = (Checkpointed<Serializable>) userFunction;
        
        Serializable functionState = stateHandle.getState(getUserCodeClassloader());
        if (functionState != null) {
            try {
                chkFunction.restoreState(functionState);
            }
            catch (Exception e) {
                throw new Exception("Failed to restore state to function: " + e.getMessage(), e);
            }
        }
    }
}

@Override
public void notifyOfCompletedCheckpoint(long checkpointId) throws Exception {
    super.notifyOfCompletedCheckpoint(checkpointId);

    if (userFunction instanceof CheckpointListener) {
        ((CheckpointListener) userFunction).notifyCheckpointComplete(checkpointId);
    }
}
复制代码
可以看到这个operater,会snapshot kv state,和udf中的function的state

 

WindowOperator,典型的operater state
public class WindowOperator<K, IN, ACC, OUT, W extends Window>
extends AbstractUdfStreamOperator<OUT, InternalWindowFunction<ACC, OUT, K, W>>
implements OneInputStreamOperator<IN, OUT>, Triggerable, InputTypeConfigurable
复制代码
public StreamTaskState snapshotOperatorState(long checkpointId, long timestamp) throws Exception {

    if (mergingWindowsByKey != null) {
        TupleSerializer<Tuple2<W, W>> tupleSerializer = new TupleSerializer<>((Class) Tuple2.class, new TypeSerializer[] {windowSerializer, windowSerializer} );
        ListStateDescriptor<Tuple2<W, W>> mergeStateDescriptor = new ListStateDescriptor<>("merging-window-set", tupleSerializer);
        for (Map.Entry<K, MergingWindowSet<W>> key: mergingWindowsByKey.entrySet()) {
            setKeyContext(key.getKey());
            ListState<Tuple2<W, W>> mergeState = getStateBackend().getPartitionedState(null, VoidSerializer.INSTANCE, mergeStateDescriptor);
            mergeState.clear();
            key.getValue().persist(mergeState);
        }
    }

    StreamTaskState taskState = super.snapshotOperatorState(checkpointId, timestamp);

    AbstractStateBackend.CheckpointStateOutputView out =
        getStateBackend().createCheckpointStateOutputView(checkpointId, timestamp);

    snapshotTimers(out);

    taskState.setOperatorState(out.closeAndGetHandle());

    return taskState;
}

@Override
public void restoreState(StreamTaskState taskState) throws Exception {
    super.restoreState(taskState);

    final ClassLoader userClassloader = getUserCodeClassloader();

    @SuppressWarnings("unchecked")
    StateHandle<DataInputView> inputState = (StateHandle<DataInputView>) taskState.getOperatorState();
    DataInputView in = inputState.getState(userClassloader);

    restoreTimers(in);
}
相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
相关文章
|
6月前
|
SQL Java API
flink问题之state过期设置如何解决
Apache Flink是由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎。本合集提供有关Apache Flink相关技术、使用技巧和最佳实践的资源。
414 0
|
6月前
|
SQL 消息中间件 分布式数据库
Flink问题之State 0点清除如何解决
Apache Flink是由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎。本合集提供有关Apache Flink相关技术、使用技巧和最佳实践的资源。
113 0
|
21天前
|
SQL 消息中间件 分布式计算
大数据-124 - Flink State 01篇 状态原理和原理剖析:状态类型 执行分析
大数据-124 - Flink State 01篇 状态原理和原理剖析:状态类型 执行分析
57 5
|
21天前
|
存储 SQL 分布式计算
大数据-127 - Flink State 04篇 状态原理和原理剖析:状态存储 Part2
大数据-127 - Flink State 04篇 状态原理和原理剖析:状态存储 Part2
14 0
|
21天前
|
存储 消息中间件 大数据
大数据-126 - Flink State 03篇 状态原理和原理剖析:状态存储 Part1
大数据-126 - Flink State 03篇 状态原理和原理剖析:状态存储 Part1
48 0
|
21天前
|
存储 SQL 分布式计算
大数据-125 - Flink State 02篇 状态原理和原理剖析:广播状态
大数据-125 - Flink State 02篇 状态原理和原理剖析:广播状态
37 0
|
3月前
|
消息中间件 应用服务中间件 API
Flink四大基石——3.State
Flink四大基石——3.State
43 1
|
3月前
|
SQL 流计算
Flink SQL 在快手实践问题之由于meta信息变化导致的state向前兼容问题如何解决
Flink SQL 在快手实践问题之由于meta信息变化导致的state向前兼容问题如何解决
47 1
|
6月前
|
SQL 分布式数据库 Apache
Flink问题之实现state定时输出如何解决
Apache Flink是由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎。本合集提供有关Apache Flink相关技术、使用技巧和最佳实践的资源。
|
6月前
|
存储 消息中间件 资源调度
Flink state 详解
Flink state 详解
66 0