我是MySQL的新手,我对内存有疑问。
我有一个200mb的表(MyISAM,2,000,000行),我尝试将所有这些表加载到 记忆。
我使用python(实际上是python中的MySQLdb)和sql:SELECT * FROM table
。
然而,从我的linux“top”我看到这个python进程使用了50%的内存(总共6GB)
我很好奇为什么它只为一个200 mb的表使用大约3GB的内存。 提前谢谢!
答案 0 :(得分:2)
你正在做的事情本身没有任何错误。如果内存使用量随着查询的大小而扩大,那么就会发生以下几种情况之一:
请注意,底层库可能会缓存一定数量的数据,因此您可能会看到大量内存使用,但除非配置出现灾难性错误,否则它不应为3GB。
这是一些简单的SQLite代码,可以重现您正在做的事情。运行时,它会创建一个包含一千五百万行的普通表,对于我正在使用的版本,它在磁盘上大约为180 MB。然后选择所有这些数据,丢弃结果,然后睡觉,以便检查结果。在我的系统上生成的进程仅使用15 MB。
(请注意,我使用单独的调用运行create_db
和read_db
次传递;创建数据库需要一些时间。)
SQLite可以处理这个问题,任何生产服务器支持的数据库,如MySQL和Postgresql也应该能够处理。 SELECT结果是数据流,数据库应该能够轻松处理无限大小的流。
import sqlite3
def create_db(conn):
c = conn.cursor()
c.execute('create table test (i integer)')
conn.commit()
max_val = 15000000
chunk = 1000000
for start in xrange(0, max_val, chunk):
print "%i ..." % start
for i in xrange(start, start + chunk):
c = conn.cursor()
c.execute('insert into test (i) values (?)', (i,))
conn.commit()
def read_db(conn):
c = conn.cursor()
c.execute('select * from test')
for x in xrange(15000000):
c.fetchone()
print "Done"
# Sleep forever, to examine memory usage:
while True:
time.sleep(1)
def go():
conn = sqlite3.connect('test.db')
# Pick one:
create_db(conn)
# read_db(conn)
if __name__ == "__main__":
go()
这不能回答你的问题,但是我想明确表示你正在做的事情没有任何问题 - 你不应该手动查询,尽管最后这可能是你的解决方法需要。
答案 1 :(得分:1)
在几乎任何脚本语言中,变量总是会占用比实际内容所暗示的内存更多的内存。 INT可能是32或64位,这表明它需要4或8个字节的内存,但它需要16或32个字节(从我的帽子中提取数字),因为语言解释器必须将各种元数据附加到该值上。方式。
数据库可能只需要200兆字节的原始存储空间,但一旦您考虑了元数据,它肯定会占用更多。
答案 2 :(得分:0)
这几乎肯定是一个糟糕的设计。
你一下子对内存中的所有数据做了什么?
如果是一个用户,为什么不缩小尺寸以便支持多个用户?
如果您正在中间层进行计算,是否可以将工作转移到数据库服务器,这样您就不必将所有数据都带入内存?
你知道你可以做到这一点,但更大的问题是(1)为什么? (2)你还能做什么?我们需要更多背景来回答这些问题。
答案 3 :(得分:0)
这是Marc B的答案和MySQLdb中的陷阱(不是错误)的组合。 MySQLdb中的默认光标是客户端游标,这意味着客户端库将整个结果集编组在客户端进程的内存中。见
的答案How to get a row-by-row MySQL ResultSet in python
解决此问题(主要使用服务器端游标类)。您在选择查询中返回的列越多,您可以预期的内存扩展就越大,因为每列会导致创建其他解释器元数据。 3Gb看起来很大,除非你有几十个专栏。