মেমরি-দক্ষ অন্তর্নির্মিত SQLlchemy পুনরুক্তি / জেনারেটর?


90

আমার কাছে 10 মিলিয়ন ডলার রেকর্ডের মাইএসকিউএল টেবিল রয়েছে যা আমি এসকিএলএলকেমি ব্যবহার করে ইন্টারফেস করি। আমি দেখতে পেয়েছি যে এই টেবিলের বৃহত সাবসেটের ক্যোয়ারীগুলি খুব বেশি স্মৃতি গ্রাস করবে যদিও আমি ভেবেছিলাম যে আমি একটি বিল্ট-ইন জেনারেটর ব্যবহার করছি যা বুদ্ধি করে ডেটাসেটের কামড়-আকারের অংশ নিয়েছে:

for thing in session.query(Things):
    analyze(thing)

এটি এড়াতে, আমি দেখতে পাচ্ছি যে আমার নিজের পুনরুক্তি তৈরি করতে হবে যা খণ্ডগুলিতে কামড় দেয়:

lastThingID = None
while True:
    things = query.filter(Thing.id < lastThingID).limit(querySize).all()
    if not rows or len(rows) == 0: 
        break
    for thing in things:
        lastThingID = row.id
        analyze(thing)

এটি কি স্বাভাবিক বা এসএ অন্তর্নির্মিত জেনারেটর সম্পর্কিত কোনও কিছু আমি মিস করছি?

উত্তর এই প্রশ্ন নির্দেশ করে মেমরি খরচ হবে বলে আশা করা হয় না বলে মনে হয়।


আমার কাছে খুব অনুরূপ কিছু রয়েছে যা "জিনিস" দেয় s অন্যান্য সমাধানগুলির চেয়ে আরও ভাল কাজ করে
iElectric

4
এটি থিং.আইডি> লাস্টথিংআইডি নয়? এবং "সারি" কি?
synergetic

উত্তর:


118

বেশিরভাগ ডিবিএপিআই বাস্তবায়নগুলি সজ্জিত হওয়ার সাথে সাথে সারিগুলি পুরোপুরি বাফার করে so সুতরাং সাধারণত, এসকিউএলএলএমি ওআরএম এমনকি একটি ফলাফলের হোল্ড পাওয়ার আগে পুরো ফলাফল সেটটি মেমরির মধ্যে থাকে।

তবে তারপরে, Queryউপায়টি হ'ল এটি হ'ল এটি আপনার ফলাফলগুলিতে আপনাকে ফেরত দেওয়ার আগে নির্ধারিত ফলাফলটিকে সম্পূর্ণরূপে লোড করে। যৌক্তিকতার সাথে এখানে অনুসন্ধানগুলি সম্পর্কিত প্রশ্নগুলি রয়েছে যা সাধারণ নির্বাচনী বক্তব্যের চেয়ে বেশি। উদাহরণস্বরূপ, অন্যান্য টেবিলগুলিতে যোগ দেয় যা একই ফলাফলের পরিচয় একাধিকবার একই ফলাফলের পরিচয় ফেরত দিতে পারে (আগ্রহী লোডিংয়ের সাথে সাধারণ), সারিগুলির পুরো সেটটি মেমরির মধ্যে থাকা দরকার যাতে সঠিক ফলাফলগুলি অন্যথায় সংগ্রহ করা যেতে পারে এবং যেমন আংশিক জনবহুল হতে পারে।

সুতরাং Queryমাধ্যমে এই আচরণ পরিবর্তন করার জন্য একটি বিকল্প উপলব্ধ করা হয় yield_per()। এই কলটি Queryব্যাচগুলিতে সারি তৈরি করবে , যেখানে আপনি এটি ব্যাচের আকার দিন। দস্তাবেজের বিবরণ হিসাবে, এটি কেবল তখনই উপযুক্ত যদি আপনি সংগ্রহের জন্য আগ্রহী কোনও ধরণের লোড না করে থাকেন তবে এটি মূলত যদি আপনি সত্যিই জানেন যে আপনি কী করছেন। এছাড়াও, যদি অন্তর্নিহিত DBAPI প্রাক-বাফার সারিগুলি থাকে তবে এখনও সেই মেমরির ওভারহেড থাকবে তাই পদ্ধতিকে এটি ব্যবহার না করার চেয়ে সামান্য ভাল স্কেল করে।

আমি খুব কমই ব্যবহার করি yield_per(); পরিবর্তে, আপনি উইন্ডো ফাংশনগুলি ব্যবহার করে উপরে প্রস্তাবিত LIMIT পদ্ধতির উন্নত সংস্করণ ব্যবহার করুন। সীমাবদ্ধ এবং অফসেট একটি বিশাল সমস্যা রয়েছে যে খুব বড় অফসেট মানগুলি কোয়েরিটিকে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে ধীরে নামিয়ে দেয় it's সারি বৃহত এবং বৃহত্তর সংখ্যা। উইন্ডো-ফাংশন পদ্ধতির সাথে, আমি "উইন্ডো" মানগুলির একটি সেট পূর্বে নিয়েছি যা আমি নির্বাচন করতে চাইলে সারণীর খণ্ডগুলি উল্লেখ করে। আমি তারপরে পৃথক নির্বাচন নির্বাচন করুন যা প্রতিটি সময়ে একবারে সেই উইন্ডোগুলির মধ্যে একটি থেকে টানা থাকে।

উইন্ডো ফাংশন পদ্ধতির উইকিতে রয়েছে এবং আমি এটি দুর্দান্ত সাফল্যের সাথে ব্যবহার করি।

এছাড়াও নোট করুন: সমস্ত ডাটাবেস উইন্ডো ফাংশন সমর্থন করে না; আপনার পোস্টগ্রেএসকিএল, ওরাকল বা এসকিউএল সার্ভার দরকার। আইএমএইচও কমপক্ষে পোস্টগ্র্যাস্কেল ব্যবহার করা অবশ্যই এর পক্ষে মূল্যবান - যদি আপনি কোনও সম্পর্কযুক্ত ডাটাবেস ব্যবহার করেন তবে আপনি সম্ভবত সেরাটি ব্যবহার করতে পারেন।


আপনি উল্লেখ করেছেন কোয়েরি পরিচয়ের তুলনা করার জন্য সমস্ত কিছুকে তত্পর করে। প্রাথমিক কী অনুসারে বাছাই করে এবং কেবল পরপর ফলাফলের তুলনা করে এড়ানো যায়?
তোবু

সমস্যাটি হ'ল যদি আপনি পরিচয় X দিয়ে একটি উদাহরণ উপস্থাপন করেন তবে অ্যাপ্লিকেশনটি এটিকে ধরে ফেলবে এবং তারপরে এই সত্তার উপর ভিত্তি করে সিদ্ধান্ত নেয় এবং এমনকি এটিকে পরিবর্তন করে। পরে, সম্ভবত (আসলে সাধারণত) এমনকি পরের সারিতেও, একই পরিচয় ফলটিতে ফিরে আসে, সম্ভবত এটির সংগ্রহে আরও সামগ্রী যুক্ত করতে contents অ্যাপ্লিকেশন তাই অসম্পূর্ণ অবস্থায় অবজেক্টটি গ্রহণ করেছে। বাছাই এখানে সহায়তা করে না কারণ বৃহত্তম সমস্যাটি আগ্রহী লোডিংয়ের কাজ - উভয় "যোগদান" এবং "সাবকিউরি" লোডিংয়ের সমস্যা রয়েছে।
zzzeek

আমি "পরবর্তী সারিতে সংগ্রহগুলি আপডেট করে" জিনিসটি বুঝতে পেরেছি, সেক্ষেত্রে সংগ্রহগুলি কখন সম্পূর্ণ হয় তা জানতে আপনাকে কেবল একটি ডিবি সারিতে এগিয়ে থাকা প্রয়োজন। আগ্রহী লোডিংয়ের বাস্তবায়নের জন্য বাছাইয়ের সাথে সহযোগিতা করতে হবে, যাতে সংগ্রহ আপডেট সর্বদা সংলগ্ন সারিগুলিতে করা হয়।
টুবু

আপনি যে প্রশ্নের উদ্রেক করছেন তা আংশিক ফলাফল সেট সরবরাহের সাথে সামঞ্জস্য রাখার বিষয়ে যখন আপনি আত্মবিশ্বাসী হন তখন উত্পাদ_প্পার () বিকল্পটি সর্বদা থাকে। সমস্ত ক্ষেত্রে এই আচরণটি সক্ষম করার চেষ্টা করে আমি বেশ কয়েক দিনের ম্যারাথন সেশন ব্যয় করেছি, সবসময় অস্পষ্ট ছিল, অর্থাৎ যতক্ষণ না আপনার প্রোগ্রামটি সেগুলির একটি ব্যবহার করে, ব্যর্থ হয়। বিশেষত, অর্ডারের উপর নির্ভর করা অনুমান করা যায় না। সর্বদা হিসাবে, আমি প্রকৃত কোড অবদানগুলিতে স্বাগত।
zzzeek

4
যেহেতু আমি পোস্টগ্র্যাগগুলি ব্যবহার করছি দেখে মনে হচ্ছে পুনরাবৃত্তযোগ্য পঠনযোগ্য কেবল পঠনযোগ্য লেনদেন ব্যবহার করা সম্ভব এবং সেই লেনদেনে সমস্ত উইন্ডোড ক্যোয়ারী চালানো সম্ভব।
স্কেচেন

24

আমি কোনও ডাটাবেস বিশেষজ্ঞ নই, তবে এসকিউএলএলচেমিকে সাধারণ পাইথন বিমূর্ত স্তর হিসাবে ব্যবহার করার সময় (অর্থাত্, ওআরএম কোয়েরি অবজেক্টটি ব্যবহার না করে) আমি মেমরির ব্যবহার বিস্ফোরিত না করে 300M-সারি টেবিলটি জিজ্ঞাসা করার একটি সন্তোষজনক সমাধান নিয়ে এসেছি ...

এখানে একটি ছদ্মবেশী উদাহরণ:

from sqlalchemy import create_engine, select

conn = create_engine("DB URL...").connect()
q = select([huge_table])

proxy = conn.execution_options(stream_results=True).execute(q)

তারপরে, আমি fetchmany()অসীম লুপটিতে ফলাফলগুলি পুনরাবৃত্তি করতে SQLAlchemy পদ্ধতিটি ব্যবহার করি while:

while 'batch not empty':  # equivalent of 'while True', but clearer
    batch = proxy.fetchmany(100000)  # 100,000 rows at a time

    if not batch:
        break

    for row in batch:
        # Do your stuff here...

proxy.close()

এই পদ্ধতিটি আমাকে কোনও বিপজ্জনক স্মৃতি ওভারহেড ছাড়াই সমস্ত ধরণের ডেটাগ্রাফিকেশন করার অনুমতি দেয়।

NOTE stream_resultsPostgres এবং সাথে কাজ করে pyscopg2অ্যাডাপ্টারের, কিন্তু আমি অনুমান এটা কোনো DBAPI সাথে কাজ করবে না, কিংবা কোনো ডাটাবেসের ড্রাইভারের সাথে ...

এই ব্লগ পোস্টে একটি আকর্ষণীয় ইউজকেস রয়েছে যা আমার উপরের পদ্ধতিটিকে অনুপ্রাণিত করে।


4
যদি কেউ পোস্টগ্রিজ বা মাইএসকিএল (সহ pymysql) এ কাজ করে থাকে তবে এটি গ্রহণযোগ্য উত্তর আইএমএইচও হওয়া উচিত।
ইউকি ইনুই

4
আমার জীবন বাঁচাচ্ছিল, আমার প্রশ্নগুলি ধীরে ধীরে চলছে was আমি উপরেরটি পাইডবিসি-তে চালিত করেছি (এসকিউএল সার্ভার থেকে পোস্টগ্রিসে) এবং এটি স্বপ্নের মতো চলছে।
এড বেকার

এটি আমার পক্ষে সেরা পদ্ধতির ছিল। আমি যখন ওআরএম ব্যবহার করছি, তখন এসকিউএলকে আমার উপভাষার (পোস্টগ্রিস) সংকলন করতে হবে এবং তারপরে উপরে বর্ণিত সংযোগ (সেশন থেকে নয়) সরাসরি চালিত করতে হবে। "অন্যদিকে" কীভাবে এই সংকলনটি আমি এই অন্যান্য প্রশ্নের মধ্যে খুঁজে পেয়েছি stackoverflow.com/questions/4617291 । বেগ উন্নত ছিল বড়। JOINS থেকে সাবকিউরিয়সে পরিবর্তন করাও পারফরম্যান্সে একটি বড় বৃদ্ধি ছিল। এছাড়াও sqlalchemy_mixins ব্যবহার করার পরামর্শ দিন, স্মার্ট_কোয়ারি ব্যবহার করে সর্বাধিক দক্ষ ক্যোয়ারী তৈরি করতে অনেক সহায়তা করেছে। github.com/absent1706/sqlalchemy-mixins
গুস্তাভো

14

আমি এসকিউএএলএলচেমির সাথে দক্ষ ট্র্যাভারসাল / পেজিংয়ের সন্ধান করছি এবং এই উত্তরটি আপডেট করতে চাই।

আমি মনে করি আপনি কোনও ক্যোয়ারির সুযোগটি সঠিকভাবে সীমাবদ্ধ করতে স্লাইস কলটি ব্যবহার করতে পারেন এবং আপনি দক্ষতার সাথে এটি পুনরায় ব্যবহার করতে পারেন।

উদাহরণ:

window_size = 10  # or whatever limit you like
window_idx = 0
while True:
    start,stop = window_size*window_idx, window_size*(window_idx+1)
    things = query.slice(start, stop).all()
    if things is None:
        break
    for thing in things:
        analyze(thing)
    if len(things) < window_size:
        break
    window_idx += 1

এটি খুব সহজ এবং দ্রুত বলে মনে হচ্ছে। আমি নিশ্চিত নই যে এটি .all()প্রয়োজনীয়। আমি প্রথম কল করার পরেও গতিতে অনেক উন্নতি লক্ষ্য করেছি।
hamx0r

@ hamx0r আমি বুঝতে পারি এটি একটি পুরানো মন্তব্য তাই কেবল উত্তরসূরির জন্য রেখে দেওয়া। ছাড়া .all()কিছু পরিবর্তনশীল একটি ক্যোয়ারী যে lên না অবলম্বন পাওয়া () হল
ডেভিড

9

জোয়েলের উত্তরের চেতনায় আমি নিম্নলিখিতটি ব্যবহার করি:

WINDOW_SIZE = 1000
def qgen(query):
    start = 0
    while True:
        stop = start + WINDOW_SIZE
        things = query.slice(start, stop).all()
        if len(things) == 0:
            break
        for thing in things:
            yield thing
        start += WINDOW_SIZE

জিনিসগুলি = ক্যোয়ারী.স্লাইস (শুরু করুন, থামান)। সমস্ত () শেষে ফিরে আসবে [] এবং লুপ কখনই ভাঙবে না
মার্টিন রেগলি

4

সীমাবদ্ধ / অফফেসটি ব্যবহার করা খারাপ, কারণ আপনাকে আগে সমস্ত {OFFSET} কলামগুলি খুঁজে বের করতে হবে, সুতরাং বৃহত্তর অফসেট হবে - আপনি যে দীর্ঘতর অনুরোধ পাবেন। আমার জন্য উইন্ডোযুক্ত ক্যোয়ারী ব্যবহার করা বৃহত পরিমাণে ডেটা সহ বড় টেবিলে খারাপ ফলাফল দেয় (আপনি খুব বেশি সময়ের জন্য প্রথম ফলাফলের জন্য অপেক্ষা করেন, এটি খণ্ডিত ওয়েব প্রতিক্রিয়ার জন্য আমার ক্ষেত্রে ভাল নয়)।

এখানে https://stackoverflow.com/a/27169302/450103 দেওয়া সেরা পন্থা । আমার ক্ষেত্রে আমি কেবল ডেটটাইম ফিল্ডে সূচক ব্যবহার করে এবং ডেটটাইম> = পূর্ববর্তী_সাময়ের সাথে পরবর্তী জিজ্ঞাসাটি আনতে সমস্যার সমাধান করেছি। বোকা, কারণ আমি আগে থেকে বিভিন্ন ক্ষেত্রে সেই সূচকটি ব্যবহার করেছিলাম, তবে ভেবেছিলাম যে সমস্ত ডেটা উইন্ডোড কোয়েরি আনার জন্য আরও ভাল হবে। আমার ক্ষেত্রে আমি ভুল ছিল।


3

আফাইক, প্রথম রূপটি এখনও টেবিল থেকে সমস্ত টিপল পায় (একটি এসকিউএল কোয়েরি সহ) তবে পুনরাবৃত্তির সময় প্রতিটি সত্তার জন্য ORM উপস্থাপনা তৈরি করে। সুতরাং এটি পুনরাবৃত্ত হওয়ার আগে সমস্ত সত্তার একটি তালিকা তৈরির চেয়ে বেশি দক্ষ তবে আপনাকে এখনও সমস্ত (কাঁচা) ডেটা মেমরিতে আনতে হবে।

সুতরাং, বিশাল টেবিলগুলিতে লিমিট ব্যবহার করা আমার কাছে ভাল ধারণা বলে মনে হচ্ছে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.