从非常大(密码)列表聚合和删除重复项的有效方法

时间:2014-06-05 07:13:59

标签: python sql sqlite passwords large-files

上下文:

  • 我正在尝试将大量单独的密码列表文本文件合并到一个文件中,以便在基于字典的密码破解中使用。

  • 每个文本文件都以行分隔(每行一个密码),目前有82个单独的文件。大多数(66)文件在1-100Mb文件大小范围内,12个是100-700Mb,3个是2Gb,1个(最有问题的)是11.2Gb。

  • 总的来说,我估计需要处理17.5亿个非唯一密码;其中我估计~4.5亿(%25)将是重复的,最终需要丢弃。

  • 我试图在一台可以免费使用6Gb以上RAM的设备上进行此操作(即已经消耗2Gb的8Gb)。

问题:

我需要一种方法来a)将所有这些密码聚合在一起并且b)删除精确的副本,在我的RAM内存约束内并且在一个合理的范围内(约7天,理想情况下要少得多,但我真的不在乎,如果这需要数周时间,然后我再也不需要再次运行它了。时间窗口。

我是一名称职的Python程序员,因此已经多次给它一个破解。我最成功的尝试是使用sqlite3将处理过的密码存储在硬盘上。然而,这意味着通过对每个已完成的文件进行散列并在每次打开新文件时维护/比较这些文件,来跟踪处理实例之间已经完成哪些文件(我已经多次取消并重新启动以进行更改)是非常繁琐的。但是对于非常大的文件,任何进度都会丢失。

我一次处理大约10亿行(最多)行的文本文件,以防止内存耗尽,而不会在很长一段时间内没有反馈。我知道我可以,因为我在24小时的运行时间内实现了大约4.5Gb的数据库文件大小,所以我可以充分填充我的数据库所以我估计剩下运行它最多需要4天才能完成所有工作,但我不知道是否/如何最有效地读/写它也没有任何关于如何解决重复删除的好主意(这样做,因为我填充数据库或之后进行额外的传递..在我不了解的数据库配置中,有没有更快的方法来查找唯一性?)。


我今天在这里的请求是关于如何实现我的巨大,独特的密码列表(理想情况下使用Python)的编程和优化方法的建议/解决方案。如果我已经脱离标记,我完全愿意采取完全不同的方法。


有两件好事:

  • 一种在将来添加更多密码而无需重建整个列表的方法;以及

  • 数据库<所有这一切都结束时20Gb,这样就不会感到非常痛苦。


解决方案

基于CL的解决方案,最终比我想象的更优雅,我想出了一个稍微修改过的方法。

按照CL的建议,我设置了一个sqlite3数据库并将文本文件送入Python脚本,然后输出命令将它们插入到数据库中。直接蝙蝠这个〜做了〜工作但非常(不可行)慢。

我通过一些简单的数据库优化解决了这个问题,这些优化更容易实现,而且从下面包含的基于CL的框架代码构建的核心Python脚本中完成所有操作更加清晰。原始代码生成 sooooooo 许多I / O操作的事实导致我的(Win7)操作系统上的一些有趣的东西导致BSOD和丢失的数据。我通过将整个密码文件插入一个SQL事务加上几个pragma更改来解决这个问题。最后,代码以大约30,000次插入/秒运行,这不是最好的,但对于我的目的来说肯定是可以接受的。

在最大的文件中可能会出现这种情况,但是如果是这样的话,我只需将文件分成较小的1Gb部分并单独使用它们。

import sys
import apsw

i = 0
con = apsw.Connection("passwords_test.db")
cur = con.cursor()

cur.execute("CREATE TABLE IF NOT EXISTS Passwords(password TEXT PRIMARY KEY) WITHOUT ROWID;")
cur.execute("PRAGMA journal_mode = MEMORY;")
cur.execute("PRAGMA synchronous = OFF;")

cur.execute("BEGIN TRANSACTION")
for line in sys.stdin:
    escaped = line.rstrip().replace("'", "''")
    cur.execute("INSERT OR IGNORE INTO Passwords VALUES(?);", (escaped,))
    i += 1
    if i % 100000 == 0: # Simple line counter to show how far through a file we are
        print i

cur.execute("COMMIT")
con.close(True)

然后从命令行运行此代码:

insert_passwords.py < passwordfile1.txt

自动化:

for %%f in (*.txt) do (
insert_passwords.py < %%f
)

总而言之,数据库文件本身并没有增长得太快,插入速度已经足够,我可以在一塌糊涂的情况下中断/恢复操作,重复的值被准确地丢弃,并且当前的限制因素是查找数据库的速度而不是CPU或磁盘空间。

1 个答案:

答案 0 :(得分:3)

将密码存储在SQL数据库中时,能够检测重复项需要索引。 这意味着密码存储在表格和索引中两次。

但是,SQLite 3.8.2或更高版本支持WITHOUT ROWID tables(称为&#34;聚集索引&#34;或&#34;索引组织表&#34;在其他数据库中),这避免了主键的单独索引。

没有包含SQLite 3.8.2的Python版本。 如果您没有使用APSW,您仍然可以使用Python来创建SQL命令:

  1. 安装最新的sqlite3命令行shell(download page)。
  2. 创建数据库表:

    $ sqlite3 passwords.db
    SQLite version 3.8.5 2014-06-02 21:00:34
    Enter ".help" for usage hints.
    sqlite> CREATE TABLE MyTable(password TEXT PRIMARY KEY) WITHOUT ROWID;
    sqlite> .exit
    
  3. 创建一个Python脚本来创建INSERT语句:

    import sys
    print "BEGIN;"
    for line in sys.stdin:
        escaped = line.rstrip().replace("'", "''")
        print "INSERT OR IGNORE INTO MyTable VALUES('%s');" % escaped
    print "COMMIT;"
    

    (如果副本违反主键的唯一约束,INSERT或IGNORE语句将不会插入行。)

  4. 通过将命令传递到数据库shell中来插入密码:

    $ python insert_passwords.py < passwords.txt | sqlite3 passwords.db
    
  5. 无需拆分输入文件;更少的交易有更少的开销。