簡體   English   中英

有沒有辦法釋放 xarray.Dataset 的文件鎖?

[英]Is there a way to release the file lock for a xarray.Dataset?

我有一個使用netcdf4.Dataset(fn, mode=a)每 5 分鍾增加一個 NetCDF 文件fn的進程。 我還有一個使用xarray.Dataset NetCDF 文件的散景服務器可視化(我想保留它,因為它非常方便)。

問題是 NetCDF-update-process 在嘗試將新數據添加到fn時失敗,如果它在我的散景服務器進程中通過

ds = xarray.open_dataset(fn)

如果我使用選項自動autoclose

ds = xarray.open_dataset(fn, autoclose=True)

ds在散景服務器應用程序中“打開”時,用另一個進程更新fn可以工作,但是從fn提取時間片的散景圖的更新變得非常滯后。

我的問題是:在使用xarray.Dataset時,是否有另一種方法來釋放 NetCDF 文件的鎖定?

我不在乎 xarray.Dataset 的形狀是否僅在重新加載整個散景服務器應用程序后才一致更新。

謝謝!

這是一個最小的工作示例:

把它放到一個文件中並讓它運行:

import time
from datetime import datetime

import numpy as np
import netCDF4

fn = 'my_growing_file.nc'

with netCDF4.Dataset(fn, 'w') as nc_fh:
    # create dimensions
    nc_fh.createDimension('x', 90)
    nc_fh.createDimension('y', 90)
    nc_fh.createDimension('time', None)

    # create variables
    nc_fh.createVariable('x', 'f8', ('x'))
    nc_fh.createVariable('y', 'f8', ('y'))
    nc_fh.createVariable('time', 'f8', ('time'))
    nc_fh.createVariable('rainfall_amount',
                         'i2',
                         ('time', 'y', 'x'),
                         zlib=False,
                         complevel=0,
                         fill_value=-9999,
                         chunksizes=(1, 90, 90))
    nc_fh['rainfall_amount'].scale_factor = 0.1
    nc_fh['rainfall_amount'].add_offset = 0

    nc_fh.set_auto_maskandscale(True)

    # variable attributes
    nc_fh['time'].long_name = 'Time'
    nc_fh['time'].standard_name = 'time'
    nc_fh['time'].units = 'hours since 2000-01-01 00:50:00.0'
    nc_fh['time'].calendar = 'standard'

for i in range(1000):
    with netCDF4.Dataset(fn, 'a') as nc_fh:
        current_length = len(nc_fh['time'])

        print('Appending to NetCDF file {}'.format(fn))
        print(' length of time vector: {}'.format(current_length))

        if current_length > 0:
            last_time_stamp = netCDF4.num2date(
                nc_fh['time'][-1],
                units=nc_fh['time'].units,
                calendar=nc_fh['time'].calendar)
            print(' last time stamp in NetCDF: {}'.format(str(last_time_stamp)))
        else:
            last_time_stamp = '1900-01-01'
            print(' empty file, starting from scratch')

        nc_fh['time'][i] = netCDF4.date2num(
            datetime.utcnow(),
            units=nc_fh['time'].units,
            calendar=nc_fh['time'].calendar)
        nc_fh['rainfall_amount'][i, :, :] = np.random.rand(90, 90)

    print('Sleeping...\n')
    time.sleep(3)

然后,轉到例如 IPython 並通過以下方式打開不斷增長的文件:

ds = xr.open_dataset('my_growing_file.nc')

這將導致附加到 NetCDF 的進程失敗,輸出如下:

Appending to NetCDF file my_growing_file.nc
 length of time vector: 0
 empty file, starting from scratch
Sleeping...

Appending to NetCDF file my_growing_file.nc
 length of time vector: 1
 last time stamp in NetCDF: 2018-04-12 08:52:39.145999
Sleeping...

Appending to NetCDF file my_growing_file.nc
 length of time vector: 2
 last time stamp in NetCDF: 2018-04-12 08:52:42.159254
Sleeping...

Appending to NetCDF file my_growing_file.nc
 length of time vector: 3
 last time stamp in NetCDF: 2018-04-12 08:52:45.169516
Sleeping...

---------------------------------------------------------------------------
IOError                                   Traceback (most recent call last)
<ipython-input-17-9950ca2e53a6> in <module>()
     37 
     38 for i in range(1000):
---> 39     with netCDF4.Dataset(fn, 'a') as nc_fh:
     40         current_length = len(nc_fh['time'])
     41 

netCDF4/_netCDF4.pyx in netCDF4._netCDF4.Dataset.__init__()

netCDF4/_netCDF4.pyx in netCDF4._netCDF4._ensure_nc_success()

IOError: [Errno -101] NetCDF: HDF error: 'my_growing_file.nc'

如果使用

ds = xr.open_dataset('my_growing_file.nc', autoclose=True)

沒有錯誤,但是通過xarray的訪問時間當然會變慢,這正是我的問題,因為我的儀表板可視化變得非常滯后。

我可以理解這可能不是xarray的預期用途,如果需要,我將回netCDF4提供的較低級別的接口(希望它支持並發文件訪問,至少對於讀取而言),但我想保留xarray以方便使用。

我在這里回答我自己的問題是因為我找到了一個解決方案,或者更好地說,是用 Python 中的 NetCDF 文件鎖定解決這個問題的方法。

如果您希望在文件中持續增長數據集,同時保持其開放以進行實時可視化,那么一個好的解決方案是使用zarr而不是 NetCDF 文件。

幸運的是,由於最近合並了 PRxarray現在還可以輕松地允許使用append_dim關鍵字參數沿選定維度將數據附加到現有 zarr 文件中。

在我的問題中使用 zarr 而不是 NetCDF 的代碼如下所示:


import dask.array as da
import xarray as xr
import pandas as pd
import datetime
import time

fn = '/tmp/my_growing_file.zarr'

# Creat a dummy dataset and write it to zarr
data = da.random.random(size=(30, 900, 1200), chunks=(10, 900, 1200))
t = pd.date_range(end=datetime.datetime.utcnow(), periods=30, freq='1s')
ds = xr.Dataset(
    data_vars={'foo': (('time', 'y', 'x'), data)},
    coords={'time': t},
)
#ds.to_zarr(fn, mode='w', encoding={'foo': {'dtype': 'int16', 'scale_factor': 0.1, '_FillValue':-9999}})
#ds.to_zarr(fn, mode='w', encoding={'time': {'_FillValue': -9999}})
ds.to_zarr(fn, mode='w')

# Append new data in smaller chunks
for i in range(100):
    print('Sleeping for 10 seconds...')
    time.sleep(10)

    data = 0.01 * i + da.random.random(size=(7, 900, 1200), chunks=(7, 900, 1200))
    t = pd.date_range(end=datetime.datetime.utcnow(), periods=7, freq='1s')
    ds = xr.Dataset(
        data_vars={'foo': (('time', 'y', 'x'), data)},
        coords={'time': t},
    )
    print(f'Appending 7 new time slices with latest time stamp {t[-1]}')
    ds.to_zarr(fn, append_dim='time')

然后您可以打開另一個 Python 進程,例如 IPython 並執行

 ds = xr.open_zarr('/tmp/my_growing_file.zarr/')   

一遍又一遍,而不會使編寫器進程崩潰。

我在這個例子中使用了 xarray 版本 0.15.0 和 zarr 版本 2.4.0。

一些補充說明:

請注意,此示例中的代碼故意附加在 zarr 文件中不均勻划分塊大小的小塊中,以查看這如何影響塊。 從我的測試中,我可以說最初選擇的 zarr 文件的塊大小被保留了下來,這很棒!

另請注意,代碼在附加時會生成警告,因為datetime64數據由xarray編碼並存儲為整數,以符合 NetCDF 的 CF 約定。 這也適用於 zarr 文件,但目前似乎_FillValue不會自動設置。 只要您的時間數據中沒有NaT ,這應該無關緊要。

免責聲明:我還沒有嘗試過使用更大的數據集和一個長期運行的文件增長過程,所以我無法評論最終性能下降或如果 zarr 文件或其元數據以某種方式從這個過程中碎片化可能發生的其他問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM