如何增加数据库的每秒读取查询次数?
我是数据库新手,但遇到了一个我似乎无法弄清楚的问题。如果这太长了,请提前抱歉,我正在尝试总结我所有的努力,以便您确切地知道我到目前为止所做的事情。我有一个应用程序,其中有一些逻辑,然后对数据库进行 3 次查询。第一个查询检查某个值是否存在,第二个查询是否存在另一个(相关)值,第三个查询如果不存在,则添加相关值。想象一下我对数字 2 进行查询,如果存在,我会检查 3 并在需要时添加它。我多次执行此循环(我正在查看总体查询,但我怀疑该程序的读取量大于写入量)。我曾经在程序中只使用哈希表,但当我添加多个进程时,我遇到了同步问题,因此我决定使用数据库,以便多个核心可以同时处理此问题。
起初我尝试使用mysql并使用内存存储引擎(它可以全部放入内存),制作一个复合主键来复制我在程序中的字典,对其进行索引,禁用锁定,但我只能得到大约11,000个查询/其次。
然后我尝试了redis(听说它就像memcache)并创建了与我之前相同的键/值字典(这是实际模式我可以使两列彼此唯一吗?或者使用复合主键redis?)并删除了所有 fsync 内容,因此希望它永远不会影响硬盘驱动器 I/O,但我仍然只能获得大约 30,000 个查询/秒。我通过让程序在内存驱动器等中运行来研究系统改进(我正在使用linux),但结果仍然相似。
我有一个设置脚本,并尝试使用高 cpu 实例在 ec2 上执行此操作,但结果相似(两种解决方案的查询都不会增加太多)。我有点束手无策,但不想放弃,因为我在 stackoverflow 上读到人们谈论他们如何在独立平台上获得超过 100,000k 的查询。我觉得我的数据模型非常简单(两列 INT 或者我可以将其与两个 INT 组合成一个字符串,但这似乎并没有减慢速度),一旦创建数据(并由另一个进程查询),我就有了不需要持久性(这也是我尝试不写入硬盘的原因)。我缺少什么设置可以让开发人员获得这种性能?除了创建表之外还需要特殊配置吗?或者是通过分布式数据库获得这种性能的唯一方法?我知道问题出在数据库上,因为当我在进程中关闭数据库时,我的Python应用程序在每个运行的核心上都达到了100%(尽管它什么也没写),这让我认为等待的过程(对于读取,我怀疑)是减慢速度的原因(我有足够的空闲CPU/内存,所以我想知道为什么它没有最大化,在这些工作期间我有50%的CPU和80%的内存空闲,所以我不知道发生什么事了 在)。
我有mysql、redis和hbase。希望我可以做一些事情来让这些解决方案之一像我希望的那样快速工作,但如果没有,我对任何解决方案都很好(它实际上只是分布式进程可以查询的临时哈希表)。
我能做些什么?
谢谢!
更新:根据评论中的要求,这里有一些代码(在特定的应用程序逻辑之后,似乎进展顺利):
cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s - c * x, i))
if cursor.rowcount == 1:
cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s, i+1))
if cursor.rowcount == 0:
cursor.execute (""" INSERT INTO data_table (key1, value1) VALUES ('%s', '%s')""" % (s, i+1))
conn.commit() #this maybe not needed
#print 'commited ', c
上面是在 mysql 上进行 3 次查找的代码。我还尝试进行一次大型查找(但实际上速度较慢):
cursor.execute ("""
INSERT INTO data_table (key1, value1)
SELECT '%s', '%s'
FROM dual
WHERE ( SELECT COUNT(*) FROM data_table WHERE key1='%s' AND value1='%s' )
= 1
AND NOT EXISTS
( SELECT * FROM data_table WHERE key1='%s' AND value1='%s' )
""" % ((s), (i+1), (s - c * x), (i), (s), (i+1)))
这是 mysql 上的表设计:
cursor.execute ("DROP TABLE IF EXISTS data_table")
cursor.execute ("""
CREATE TABLE data_table(
key1 INT SIGNED NOT NULL,
value1 INT SIGNED NOT NULL,
PRIMARY KEY (key1,value1)
) ENGINE=MEMORY
""")
cursor.execute("CREATE INDEX ValueIndex ON data_table (key1, value1)")
在 Redis 上,它与 3 查询结构类似(因为它是我在 mysql 上能得到的最快的,除非我不这样做)如果值存在则需要进行查找,我只是覆盖它以保存查询):
if r_server.sismember(s - c * x, i):
r_server.sadd(s, i + 1)
我的 Redis 数据结构位于链接的问题中(基本上是一个列表,3 => 1 2 3 而不是 mysql有 3 行代表 3=1, 3=2, 3=3。
希望有帮助,任何其他问题请告诉我。
I'm a rookie at databases but am having a problem that I can't seem to figure out. Sorry in advance if this is too long, I am trying to summarize all my efforts so you know exactly what I have done so far. I have an app has some logic in it and then does 3 queries to a database. First query checks if a value exists, second checks if another(related) value exists and third one, if does not exist, adds related value. Think of me doing a query on the number 2, and if it exists I check for 3 and add it if needed. I do this loop a large number of times(I am looking at overall queries but I suspect this program is more read heavy than write). I used to use just a hashtable in my program but as I added multiple proceses I had sync'ing issues, so I decided to use a database so multiple cores can work on this at the same time.
At first I tried, mysql and used a memory storage engine(it could all fit in memory), made a composite primary key to replicate the dictionary I had in my program, indexed it, disabled locking but I could only get about 11,000 queries/second from it.
I then tried redis(heard it was like memcache) and created the same key/value dict I have before(here's the actual mode Can I make two columns unique to each other? or use composite primary key's in redis? ) and removed all the fsync stuff so it hopefully never hits harddrive i/o but I still only get around 30,000 queries/second. I looked at system improvements(I'm using linux) by having the program run in a ramdrive, etc. but still similar result.
I have a setup script and tried to do this on ec2 using the high cpu instance but the result is similar(queries don't go up by much for both solutions). I'm sort of at my wits end but don't want to give up because I read of people on stackoverflow talking about how they have gotten 100,000k+ queries on a standalone. I feel my datamodel is very simple(two columns of INT or I can make it one string with both INT's combined, but that didn't seem to slow either down) and once the data is created(and queried by another process) I have no need for persistence(which is also why I am trying to not write to a harddrive). What setup am I missing that allows developers here to get that kind of performance? Is there special configuration required outside of table creation? or is the only way to get that kind of performance through distributed databases? I know the problem is with the database because when I shut down the database mid-process my python app hits 100% on each core its running(although its writing nothing), it makes me think that the process of waiting(for the reads, I suspect) is what's slowing it down(I have plenty of cpu/memory free so I'm wondering why its not max'ing out, I have 50% cpu and 80% of my memory free during these jobs so I have no idea whats going on).
I have mysql, redis and hbase. hopefully there's something I can do to get one of these solutions working as fast as I'd like but if there isn't I'm fine with any solution(its really just a temp hashtable that distributed proceses can query).
What can I do?
Thanks!
Update: as requested in comments, here's some code(after the specific application logic which seems to be going fine):
cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s - c * x, i))
if cursor.rowcount == 1:
cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s, i+1))
if cursor.rowcount == 0:
cursor.execute (""" INSERT INTO data_table (key1, value1) VALUES ('%s', '%s')""" % (s, i+1))
conn.commit() #this maybe not needed
#print 'commited ', c
above is the code with 3 lookups on mysql. I also tried to do one big lookup(but it was in fact slower):
cursor.execute ("""
INSERT INTO data_table (key1, value1)
SELECT '%s', '%s'
FROM dual
WHERE ( SELECT COUNT(*) FROM data_table WHERE key1='%s' AND value1='%s' )
= 1
AND NOT EXISTS
( SELECT * FROM data_table WHERE key1='%s' AND value1='%s' )
""" % ((s), (i+1), (s - c * x), (i), (s), (i+1)))
Here's the table design on mysql:
cursor.execute ("DROP TABLE IF EXISTS data_table")
cursor.execute ("""
CREATE TABLE data_table(
key1 INT SIGNED NOT NULL,
value1 INT SIGNED NOT NULL,
PRIMARY KEY (key1,value1)
) ENGINE=MEMORY
""")
cursor.execute("CREATE INDEX ValueIndex ON data_table (key1, value1)")
on Redis, its simlair to the 3 query structure(since it was the fastest I could get on mysql, except I do not need to do a lookup if the value exists, I just overwrite it to save a query):
if r_server.sismember(s - c * x, i):
r_server.sadd(s, i + 1)
My data structure for redis is in the linked question(basically its a list, 3 => 1 2 3 instead of mysql having 3 rows to repersent 3=1, 3=2, 3=3.
Hope that helps, any other questions please let me know.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
看看提供的代码片段,我想说这里的主要瓶颈是网络或 TCP 环回。 MySQL 和 Redis 都是同步客户端/服务器存储。每次发送查询并等待回复时,您都要为内核调度、网络延迟、CPU 缓存不良命中率等付出代价……
在 TCP 服务器上每秒运行数十万个查询的人不使用使用单个套接字来定位服务器,但使用多个连接来实现客户端并行性和/或管道化查询为了限制这种延迟的影响。
实际上,如果您有一个唯一的套接字并在没有任何管道的情况下按顺序发送查询,那么您测量的不是服务器可以实现的最大吞吐量,而是网络或 IPC 的延迟。
希望大多数 NoSQL 服务器使用的协议通常都支持管道。因此,这里有一些关于 Redis 实施的建议。
您可能需要先阅读Redis 基准测试页面。描述了对 Redis 进行基准测试时可能遇到的所有典型性能瓶颈。
以下是实现基准测试最大吞吐量的一些建议:
我已经使用hiredis(C Redis 客户端)运行了一个简单的测试来模拟您在 Xeon [电子邮件受保护]。代码可以在此处找到。
该程序实现了类似的代码,对查询进行流水线处理。它对项目进行批处理并发送一堆 sismember 命令来了解项目是否存在,然后针对必须添加的项目发送一堆 Sadd 命令。
结果:
因此,当往返未优化时,使用 unix 域套接字的影响很大,而一旦使用管道,影响就会变得非常低。大部分收益来自流水线。这就是为什么您应该首先关注软件/协议优化。
通过调整系统/网络配置可以进一步改善结果,但获得更多吞吐量的下一步通常是运行多个 Redis 实例并使用哈希机制对数据进行分片(尝试在服务器端并行化)。
Looking at the provided code snippets, I would say the main bottleneck here are the network or TCP loopback rountrips. Both MySQL and Redis are synchronous client/server stores. Each time you send a query and wait for the reply, you pay for the kernel scheduling, the network latency, CPU cache bad hit ratio, etc ...
The people who run hundreds of thousands of queries per second on TCP servers do not use a single socket to target the server, but multiple connections for client-side parallelism and/or pipeline their queries in order to limit the impact of this latency.
Actually, if you have a unique socket and send your query in sequence without any pipelining, you are not measuring the maximum throughput you can achieve with a server, but rather the latency of the network or IPCs.
Hopefully, the protocols used by most NoSQL servers usually support pipelining. So here are some advices for a Redis implementation.
You may want to read the Redis benchmark page first. All the typical performance bottlenecks you may experience when benchmarking Redis are described.
Here are a few advices to achieve maximum throughput for your benchmark:
I have run a simple test using hiredis (C Redis client) to simulate your use case on a Xeon [email protected]. Code can be found here.
The program implements a similar code, pipelining the queries. It batches items and sends a bunch of sismember commands to know if the items exist or not, and then a bunch of sadd commands for the items it has to add.
Results:
So the impact of using unix domain sockets is high when the roundtrips are not optimized, and becomes very low once pipelining is used. Most of the gain is due to pipelining. That's why you should focus on software/protocol optimizations first.
The results can be further improved by tweaking the system/network configuration, but the next step to gain more throughput is normally to run several Redis instances and shard the data using a hashing mechanism (trying to parallelize on server-side).