私は次のスクリプトを書いています:
- データベースから URL のリストを取得します (約 10000 の URL)
- すべてのページをダウンロードしてデータベースに挿入します
- コードを解析する
- if(何らかの条件)は、データベースに他の挿入を行います
ハイパースレッディングを備えた Xeon クアッドコアを使用しているため、合計 8 つのスレッドを使用でき、Linux (64 ビット) を使用しています。
ページを取得し、解析し、データベースとやり取りするためにcStringIO
、バッファとして使用しています。pycurl
BeautifulSoup
MySQLdb
以下のコードを単純化しようとしました (try/except、解析操作などをすべて削除しました)。
import cStringIO, threading, MySQLdb.cursors, pycurl
NUM_THREADS = 100
lock_list = threading.Lock()
lock_query = threading.Lock()
db = MySQLdb.connect(host = "...", user = "...", passwd = "...", db = "...", cursorclass=MySQLdb.cursors.DictCursor)
cur = db.cursor()
cur.execute("SELECT...")
rows = cur.fetchall()
rows = [x for x in rows] # convert to a list so it's editable
class MyThread(threading.Thread):
def run(self):
""" initialize a StringIO object and a pycurl object """
while True:
lock_list.acquire() # acquire the lock to extract a url
if not rows: # list is empty, no more url to process
lock_list.release()
break
row = rows.pop()
lock_list.release()
""" download the page with pycurl and do some check """
""" WARNING: possible bottleneck if all the pycurl
connections are waiting for the timeout """
lock_query.acquire()
cur.execute("INSERT INTO ...") # insert the full page into the database
db.commit()
lock_query.release()
"""do some parse with BeautifulSoup using the StringIO object"""
if something is not None:
lock_query.acquire()
cur.execute("INSERT INTO ...") # insert the result of parsing into the database
db.commit()
lock_query.release()
# create and start all the threads
threads = []
for i in range(NUM_THREADS):
t = MyThread()
t.start()
threads.append(t)
# wait for threads to finish
for t in threads:
t.join()
multithreading
一部のリクエストがタイムアウトで失敗する場合に待機する必要がないように使用します。その特定のスレッドは待機しますが、他のスレッドは自由に他の URL を続行できます。
これは、スクリプトだけを実行している間のスクリーンショットです。5 つのコアがビジーで、他のコアはビジーではないようです。質問は次のとおりです。
- スレッドの数と同じ数のカーソルを作成する必要がありますか?
- クエリの実行をロックする必要は本当にありますか? スレッドが cur.execute() を実行し、db.commit()を実行せず、別のスレッドが実行を実行し、別のクエリでコミットした場合はどうなりますか?
- Queueクラスについて読みましたが、正しく理解できたかどうかわかりません: lock + extract a url + releaseを実行する代わりに使用できますか?
multithreading
I/O (ネットワーク) のボトルネックに悩まされることはありますか? 100 スレッドの場合、速度は最大 500Kb/s を超えませんが、接続はより高速になります。に移行するmultiprocess
と、この面で改善が見られますか?- 同じ質問ですが、MySQL の場合: 私のコードを使用すると、こちら側にボトルネックがある可能性がありますか? これらすべてのロック + クエリの挿入 + 解放は、何らかの方法で改善できますか?
- 進むべき道が の場合、
multithreading
100 は多数のスレッドですか? つまり、I/O 要求 (または DB クエリ) を実行するスレッドが多すぎると、これらの操作が相互に排除されて役に立たないということですか? それとも、スレッド数が多いということは、ネットワーク速度が向上するということですか?