【问题标题】:Optimize search and insert operations on large volumes of data优化对大量数据的搜索和插入操作
【发布时间】:2019-06-25 01:52:03
【问题描述】:

我正在开发一个需要处理大量数据的程序,但我想先将这些数据保存在本地存储结构中,然后再将其迁移到数据库。所以,我的问题是:保存该数据的最佳文件类型(或本地存储结构)是什么(这是结构化的,为此,我们假设它只是一个 id 和一个名称),以某种方式可以进行搜索和插入优化吗?

我的文件是 CSV 文件,因为数据是结构化的,这样可以保存相对大量的数据(在这种情况下,我需要大约 1000 到 100 000 行),但我不确定是否存在外面有什么更好的吗?我的想法是按名称的字母顺序对数据进行排序,因此在最坏的情况下,搜索操作将采用 O(n)。至于插入操作,鉴于我无法在两行之间插入一行,因此我必须在插入后覆盖整行我想要的那个。 (我也想过将整个文件读入一个列表,然后再写一遍,但如果文件太大,这不是最好的实现。

那么,谁能给我一些关于要使用的最佳文件类型的想法,以及哪种方法最适合插入和搜索优化?非常感谢!

(这是我的插入算法,但它会产生随机行为)

def writingOpt(firstName, lastName, birthdate, country):
    try:
        file = open("players.csv", "r+", newline='')
    except FileNotFoundError:
        print("File players.csv not found")
    else:
        with file:
            reader = csv.reader(file)
            writer = csv.writer(file)
            name = firstName + ' ' + lastName
            inserted = False
            previousRow = []
            previousPosition = 0

            for row in reader:
                if name < row[0]:
                    file.seek(previousPosition)

                    if not inserted:
                        previousRow = [name, birthdate, country]
                        inserted = True

                    writer.writerow(previousRow)
                    previousRow = row

                previousPosition += len(','.join(row))

【问题讨论】:

  • 使用pandas库。
  • 除非只是为了学习目的,否则你就是在浪费时间重新发明众所周知的方轮 - 这个问题已经被关系数据库解决了,你不会做任何更好或更快的事情Python 和一个 csv 文件。
  • @has 我试试看,谢谢!
  • @brunodesthuilliers 就关系数据库的概念而言,我想要完成的并不是完全“重新发明轮子”,而是以某种方式找到一种方法将我的数据临时保存在本地存储结构中,同时找到就规范化和图表而言,构建我的数据库模型的最佳方式。因为我想要的数据需要从一个结构不断变化的网站中抓取
  • 你会在 csv 中遇到同样的问题 - csv 结构也必须改变。在最坏的情况下,您可以在适当的数据库字段中提取数据的恒定部分(用于搜索的部分)并将其余部分存储为 json blob(至少在找到更好的解决方案之前)。一些 rdbms 现在已经原生支持 json 字段,但是即使您不想担心设置合适的数据库服务器,您仍然可以同时使用 sqlite - 它是本地存储,并且针对搜索和插入进行了优化。

标签: python performance csv memory optimization


【解决方案1】:

重新实现数据库的想法有利于学习,但很可能对生产代码非常不利。

数据库(尤其是关系数据库)在经过大量优化方面取得了长足的进步,而且真的很难靠近。

话虽如此,一些说明可能会有所帮助:

  • 如果可能,在内存中处理数据,写回磁盘。您将遭受所有 IO,但至少您没有在磁盘上进行搜索。如前所述,pandas 是一个很好的起点
  • 100k 对于现代数据库来说是很小的数量
  • 读取效率来自对数据进行排序和索引(现代方法中的 btree+),这使得搜索 O(logN) 而不是 O(N)。但问题是,在低级别使用 IO 非常困难,特别是如果您使用的是 CSV,那么您的“单个元素”是由换行符定义的,因此您需要自己实现高级搜索
  • 就大多数操作系统处理 IO 的方式而言,您不能“插入”数据,因为接口是顺序的。为避免插入时出现O(N),利用旧技巧——在O(N) 末尾写入new 数据,并以某种方式将旧元素标记为已删除。诀窍是能够为标记写入相同数量的字节,即每行都有布尔标志,并实现“智能”读取逻辑。

关于插入技巧,这里有一个简单的例子。假设您有按id 排序的表格,并且数据类似于

id  name    amount
1   Alice   10
2   Bob     20
3   Charlie 30

您需要更新id = 2 的名称/金额。搜索是O(logN)(如果你已经实现了正确的.seek,那么实际更新会发生什么?如果你正在写入完全相同数量的字节,你可以重写——寻找正确的位置并写入。即改变2025 完全没问题,你只写你需要的东西(不能保证,但让我们跳过低级细节)。 当您需要更改时,问题就出现了,比如将20 更改为120。在大多数情况下,您的存储抽象是顺序字节流,想象一下

id,name,amount\n1,Alice,10\n2,Bob,20\n3,Charlie,30\n  # old
id,name,amount\n1,Alice,10\n2,Bob,120\n3,Charlie,30\n # new
                                    ^ everything beyond this point
                                      needs to be re-written

所以你最终会得到O(N/2) 的平均值(很明显,与O(N) 相同)


你可以做什么:有一个“标志”显示记录现在是否有效:

valid   id  name    amount
Y       1   Alice   10
Y       2   Bob     20
Y       3   Charlie 30

当你需要更新时,通过与“有效”标志相同字节数的标志将旧行标记为“无效”,并在最后写入新行:

valid   id  name    amount
Y       1   Alice   10
N       2   Bob     20
Y       3   Charlie 30
Y       2   Bob     120

操作是O(logN)查找行(和之前一样),O(1)覆盖新标志,O(M)写入新数据(寻找到文件末尾本身并没有释放,但这是一个不同的故事)。缺点——现在你需要:

  • 实现带有回退的乐观搜索——如果你通过树或二分搜索寻找数据,你需要检查标志状态,如果数据是过时的——寻找文件末尾并读入反向
  • 随着更新的到来,未优化的“尾巴”越来越多,越来越多地将您推向O(N) 复杂性(btree 可以提供帮助,顺便说一句)。所以你最终需要压缩数据回到最佳状态——重新读取所有数据,删除过时的行,重新排序数据,然后写回磁盘。这就是 RDBMS 中通常所说的“真空”。为此,您最好跟踪“重写了多少行”与“总共有多少行”——这个比率高于某个阈值是一个真空的迹象。

【讨论】:

  • 嗯..我知道你在哪里!我也想过设计一个你提到的数据结构(B-Tree),但因为内存问题我放弃了。但我想这始终是内存和性能之间的权衡,所以我会进行一些测试,看看它是如何进行的。我不太明白最后一点,你在最后写新数据并有一个布尔标志是什么意思?你能详细说明一下吗?谢谢!
  • 写了一点关于优化的文章
【解决方案2】:

我建议您将 csv 数据存储在 pandas 数据框中,然后按字母顺序对其进行排序,然后再保存您的数据框内容将非常简单。

要处理大量数据,请参阅文档:pandas.read_csv()

这是一个代码示例:

# Instanciate your pandas dataframe reading new values  (for 1000 to 100 000 lines you shouldn't encounter any issue)
df = pd.read_csv('players.csv', low_memory=True, sep=';', ...)
# Sort on the column
df.sort('name')
# Then write your sorted data to a csv file :)
df.to_csv('players_sorted.csv', index=False, header=False, sep=';', ...)

希望对你有帮助!

【讨论】:

  • Uau,这似乎是个好主意!我可以将整个数据写入 csv,将每个注册表附加到文件中,完成后,我只需使用 pandas 数据框对其进行排序并将其写入新的 csv 文件!我一定会试一试的,谢谢!
猜你喜欢
  • 2014-02-01
  • 2018-06-11
  • 2012-08-06
  • 2014-08-23
  • 2019-06-09
  • 2012-01-05
  • 1970-01-01
  • 2010-12-25
  • 1970-01-01
相关资源
最近更新 更多