有没有办法释放xarray.Dataset的文件锁?

问题描述 投票:4回答:1

我有一个过程,可以生长一个NetCDF文件。fn 每5分钟使用 netcdf4.Dataset(fn, mode=a). 我也有一个虚化服务器可视化的NetCDF文件,使用一个 xarray.Dataset (我想保留,因为它很方便)。

问题是 是NetCDF-update-process在尝试添加新数据到 fn 如果它在我的bokeh服务器进程中打开了,通过

ds = xarray.open_dataset(fn)

如果我使用选项 autoclose

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

更新 fnds 是 "打开 "在虚化服务器应用程序的工作,但更新到虚化的数字,其中拉时间片从 fn,变得非常滞后。

我的问题是: 在使用NetCDF文件时,是否有另一种方法可以解除NetCDF文件的锁。xarray.Dataset?

我不会在意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 concurrency netcdf python-xarray
1个回答
0
投票

我在这里回答我自己的问题,因为我找到了一个解决方案,或者更好的说,一个解决这个问题的方法,在Python中NetCDF的文件锁。

一个好的解决方案是使用 扎尔 而不是NetCDF文件,如果你想在一个文件中持续增长一个数据集,同时保持它的开放性,例如实时可视化。

幸运的是 xarray 现在也可以轻松地将数据添加到现有的zarr文件中,沿着选定的维度使用 append_dim 关键词争论感谢 合并后的公关公司.

在我的问题中,使用 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 verion 0.15.0和zarr 2.4.0版本。

一些补充说明。

请注意,这个例子中的代码故意以小块的方式追加,不均匀地划分zarr文件中的分块大小,看看这对分块有什么影响。从我的测试来看,我可以说zarr文件最初选择的chunk大小被保留了下来,这很好!

另外,请注意,代码在追加时产生了一个警告,原因是 datetime64 数据以整数形式编码和存储,由 xarray 以符合 NetCDF 的 CF 惯例。这也适用于zarr文件,但是目前看来 _FillValue 是不会自动设置的。只要你没有 NaT 在你的时间数据中,这应该没有问题。

声明:我还没有用一个更大的数据集和一个长期运行的过程来增长文件,所以我不能评论最终的性能下降或其他问题,如果zarr文件或它的元数据以某种方式从这个过程中得到碎片,可能会发生。

© www.soinside.com 2019 - 2024. All rights reserved.