我在 S3 存储桶中有一个 SQLite 数据库 (110kb)。我想在每次运行 Python 应用程序时连接到该数据库。
一种选择是每次运行 Python 应用程序并连接它时下载数据库。但我想知道是否有办法使用S3FileSystem
和open
通过内存连接到该 SQLite 数据库。
我在Python 3.6中使用SQLite3库。
正如其他答案所示,您可能不希望将SQLite用作云中的主数据库。
但是,作为一个有趣的副项目的一部分,我编写了一个 Amazon Athena 数据源连接器,允许您从 Athena 查询 S3 中的 SQLite 数据库。为了做到这一点,我为 S3 编写了一个只读的 SQLite 接口。
SQLite有一个操作系统接口或VFS的概念。使用名为APSW的Python SQLite包装器,您可以为任意文件系统编写VFS实现。这是我在我的项目中所做的,我在下面包含了实现。
为了使用它,您将首先注册 VFS,然后使用此实现作为驱动程序创建新的 SQLite 连接。
我应该注意这根本没有优化,因此可能仍然需要从 S3 读取完整的数据库,具体取决于您的查询。但在这种特定情况下听起来不像是一个问题。
S3FS = S3VFS() # S3VFS defined below
# This odd format is used due to SQLite requirements
sqlite_uri = "file:/{}/{}.sqlite?bucket={}&immutable=1".format(
S3_PREFIX,
DATABASE_NAME,
S3_BUCKET
)
connection = apsw.Connection(sqlite_uri,
flags=apsw.SQLITE_OPEN_READONLY | apsw.SQLITE_OPEN_URI,
vfs=S3FS.vfsname
)
cursor = connection.cursor()
获得光标后,可以执行标准 SQL 语句,如下所示:
for x,y,z in cursor.execute("select x,y,z from foo"):
print (cursor.getdescription()) # shows column names and declared types
print (x,y,z)
VFS 实施(需要 APSW 库和 boto3 来实现 S3 连接)
import apsw
import sys
import boto3
VFS_S3_CLIENT = boto3.client('s3')
class S3VFS(apsw.VFS):
def __init__(self, vfsname="s3", basevfs=""):
self.vfsname=vfsname
self.basevfs=basevfs
apsw.VFS.__init__(self, self.vfsname, self.basevfs)
def xOpen(self, name, flags):
return S3VFSFile(self.basevfs, name, flags)
class S3VFSFile():
def __init__(self, inheritfromvfsname, filename, flags):
self.bucket = filename.uri_parameter("bucket")
self.key = filename.filename().lstrip("/")
print("Initiated S3 VFS for file: {}".format(self._get_s3_url()))
def xRead(self, amount, offset):
response = VFS_S3_CLIENT.get_object(Bucket=self.bucket, Key=self.key, Range='bytes={}-{}'.format(offset, offset + amount))
response_data = response['Body'].read()
return response_data
def xFileSize(self):
client = boto3.client('s3')
response = client.head_object( Bucket=self.bucket, Key=self.key)
return response['ContentLength']
def xClose(self):
pass
def xFileControl(self, op, ptr):
return False
def _get_s3_url(self):
return "s3://{}/{}".format(self.bucket, self.key)
其他答案解释了 sqlite 引擎如何注册一个替代的"文件系统",该文件系统可以在各种云存储场景中处理数据库文件。 这包括自定义解决方案和更新的云平台解决方案。 如果这些系统满足您的需求,请务必选择。
这些可能性迫使我重新思考我之前确定的"否"答案,但它们最终不会改变 sqlite 引擎的基本性质。 引擎仍然没有内置的网络/Web API,也不是为此而设计的。 这不是我的意见,而是在官方网站上说了很多。 即使使用替代文件系统,引擎仍然与数据库"文件"一起工作,因此要持久化和加载内存中的数据库仍然需要一个"文件系统"。 因此,对于大多数应用程序和大多数开发人员来说,他们想要一个与sqlite的设计目标相匹配的最佳和/或即插即用解决方案,我最初的答案是成立的。
原答案:
不可以,无法直接连接到存储在云中的 sqlite 数据库。即使要在内存中托管数据库,在加载到内存中之前仍必须完全下载数据库。 为此,仍然需要首先从基于光盘的文件加载数据库,或使用 DDL 命令直接在内存中创建数据库。 请参阅加载和保存内存中数据库。
在这种情况下,一旦数据库断开连接,就需要将其重新上传到云存储。S3FileSystem.open
只返回数据流。 所有流将允许您做的是将文件下载到本地存储,以便可以在本地打开/操作它。
如果你真的需要一个云数据库,你需要研究另一种托管数据库。
是的,使用 EFS 是可能的:
https://www.lambrospetrou.com/articles/aws-lambda-and-sqlite-over-efs/
AWS最近发布了 AWS Lambda 和 Amazon EFS 之间的集成。 它支持SQLite所需的NFSv4锁升级/降级。 这意味着SQLite引擎可以对存储在EFS文件系统上的文件进行读/写访问。
(灵感来自dacort的答案)
如果数据库只是用于读取,则有 https://github.com/michalc/sqlite-s3-query(完全披露:由我撰写)
修改其自述文件中的示例,假设您在存储桶中my-db.sqlite
my-bucket
eu-west-2
(以及环境变量中的凭证):
from sqlite_s3_query import sqlite_s3_query
with
sqlite_s3_query(url='https://my-bucket.s3.eu-west-2.amazonaws.com/my-db.sqlite') as query,
query('SELECT * FROM my_table WHERE my_column = ?', params=('my-value',)) as (columns, rows):
for row in rows:
print(row)
如果你的所有操作都限制从SQLite读取,我想这是可能的。但我不知道写作是否也可行。 就我而言,我使用的是gdal(需要libgdal),而gdal的/vsis3,/vsis3-streaming(基于/vsicurl)使您能够从云中读取SQLite和许多其他数据源。如果你想使用原始SQLite而不是基于GDAL的数据源层,你可以通过GDAL的API将它们写入本地数据库,但是,如果是这样,为什么不直接下载并读取它呢?
对我来说,由于我正在处理空间数据,并且 GDAL 的数据源提供了许多 API 来操作空间数据,因此此方法工作正常。我仍在寻找一种写入基于云的SQLite的好方法。
仅供参考,这是gdal虚拟文件系统的文档 https://gdal.org/user/virtual_file_systems.html