PDA

View Full Version : سوال: لطفا بگید که توی لوکال سرعت صفحه بندی سایتتون با یک میلیون مقاله چقدره؟



idocsidocs
شنبه 28 مرداد 1391, 17:12 عصر
اگر توی جدول سایتتون یک میلیون مقاله ( که هر کدوم چند صفحه هست) ذخیره کنید، توی لوکال سرعت سایتتون پایین نمی یاد؟

سایتی من توی لوکال با 500 هزار مقاله از کار افتاد !

tehro0n
شنبه 28 مرداد 1391, 19:01 عصر
اگه بخوای همه 500 کیلو رو نمایش بدی آره خوب!
تو بلاگفا هم بری بیشتر از 20 تا مقاله اگه صفحه از کار نیافته کلی طول می کشه بالا بیاد
شما باید این سلکت ها رو محدود کنی، حالا یا با زمان یا با عنوان ها و کتگوری و ..
و در نهایت هم با دستور لیمیت آخر کوئری بگی که 10 تا رو نمایش بده

idocsidocs
شنبه 28 مرداد 1391, 19:25 عصر
اگه بخوای همه 500 کیلو رو نمایش بدی آره خوب!
منظورم اینه که از یک میلیون مقاله، 20 تا رو بدست بیاریم و عنوان این 20 مقاله رو نمایش بدیدم.

بقیه رو هم صفحه بندی کنیم.

سایت شما هم با این شرایط توی لوکال کار نمی کنه؟

MMSHFE
شنبه 28 مرداد 1391, 23:52 عصر
دوست عزیز، اگه از LIMIT برای صفحه بندی استفاده کردین، نباید انتظار داشته باشین با 500 هزار رکورد، سرعت سایتتون کماکان مطلوب باشه یا حتی بالا بیاد! بهتره با WHERE محدودیت تعیین کنید. برای مثال، با این فرض که کلید اصلی جدول شما id هست و Auto Increment گذاشتین، برای صفحه بندی مطلوب میتونید اینطوری عمل کنید:

$result = mysql_query('SELECT * FROM `table` WHERE (`id`>' . ($page_number * $items_per_page) . ') ORDER BY `id` LIMIT ' . $items_per_page);
این کد رو روی همون 500 هزار رکورد امتحان کنید و راندمانش رو با کد زیر مقایسه کنید:

$result = mysql_query('SELECT * FROM `table` ORDER BY `id` LIMIT ' . ($page_number * $items_per_page) . ',' . $items_per_page);
دقت کنید که کوئری اول، با کمک WHERE رکوردهای قبل از id موردنظر رو از محدوده جستجو حذف میکنه و بعد، باقیمانده رو برحسب id مرتب کرده و 10تای اول رو جدا میکنه اما دومی، همه رکوردها رو استخراج کرده و برحسب id مرتب میکنه و بعد، با شروع از رکورد مشخص شده، به تعداد موردنظر رکورد جدا میکنه و بقیه رو از نتیجه حذف میکنه. بنابراین، میبینید که دومی، عملاً همه رکوردها رو استخراج میکنه و طبیعی هست که سرعتش کمتر باشه. بخصوص در صفحات بالاتر، این اختلاف خیلی زیاد میشه. مثلاً در صفحه 100 با تعداد 100 مطلب در هر صفحه، کوئری اول 10 هزار رکورد اصلاً پردازش نمیکنه و برای بقیه هم 100 تای اول رو به سرعت پیدا و جدا میکنه اما دومی، تمام 500 هزار رکورد رو باید استخراج کنه، بعد 10 هزار رکورد بره جلو (که با توجه به طول متغیر رکوردها در DB، خود این مسئله کار بسیار زمانبری هست) و بعد، از اونجا 100 رکورد رو جدا کنه و بقیه رو بریزه دور!
موفق باشید.

idocsidocs
یک شنبه 29 مرداد 1391, 00:37 صبح
دوست عزیز، اگه از LIMIT برای صفحه بندی استفاده کردین
مهندس با این کوئری حتی نمی شه توی 60 ثانیه تعداد کل رکوردها رو بدست آورد، چه برسه به اینکه اگر کوئری پیچیده تر بشه !

SELECT `id` FROM `tb1`

MMSHFE
یک شنبه 29 مرداد 1391, 01:02 صبح
میشه کوئری خودتون رو بگذارین تا ببینیم مشکل کجاست؟ تعداد رکوردها رو چطور بدست میارین؟ چطور صفحه بندی کردین؟
برای تعداد رکوردها اینطوری عمل کنید:
SELECT COUNT(*) AS `total` FROM `tb1`

idocsidocs
یک شنبه 29 مرداد 1391, 09:44 صبح
مهندس با روشی که توضیح دادید الان همه چیز به خوبی اجرا می شه و با 600،000 کوئری، کارهای خیلی سریع (کمتر از یک ثانیه) انجام می شه.

اما توی گروه بندی هنوز مشکل وجود داره. موقع گروه بندی کوئری به این شکل در می یاد:

$result = mysql_query('SELECT * FROM `table` WHERE (`id`>' . ($page_number * $items_per_page) . ' && `gr` LIKE '% gr1 %') ORDER BY `id` LIMIT ' . $items_per_page);

با این کوئری ارور نمایش داده شد و هیچ کدوم از ردیف های جدول بازیابی نشد. چه راه حلی برای این موضوع دارید؟

یه مسئله دیگه هم اینه که اگر بخوایم این کوئری رو روی 2 جدول اجرا کنیم، یعنی از یه جدول مقالات رو بخونیم و از یه جدول دیگه تعداد نظرات داده شده برای هر مقاله رو بخونیم، انگار سرعت نسبت به حالت اول کند تر می شه. راهی برای این موضوع وجود نداره؟

MMSHFE
یک شنبه 29 مرداد 1391, 12:13 عصر
اینو امتحان کنید:


<?php
$result = mysql_query(
"SELECT * FROM
(SELECT * FROM `table` WHERE `gr` LIKE '%{$gr1}%') AS `temp`
WHERE (`id`>" . ($page_number * $items_per_page) . ')
ORDER BY `id` LIMIT ' . $items_per_page
);
?>

یعنی اینکه اول بر اساس گروه انتخاب کنید و بعد توی اون گروه، صفحه بندی کنید.

idocsidocs
دوشنبه 30 مرداد 1391, 13:29 عصر
یعنی اینکه اول بر اساس گروه انتخاب کنید و بعد توی اون گروه، صفحه بندی کنید.
این کار توی سرعت تاثیر داره؟

MMSHFE
دوشنبه 30 مرداد 1391, 14:04 عصر
خوب طبیعتاً بله چون مطالب گروههای دیگه، همون اول از فهرست جستجو حذف میشن. البته کمی در این مورد باید با دقت عمل کنید چون id دیگه مثل قبل ترتیبی نیست. مثلاً ممکنه خبر 1 و 2 و 7 و 25 و 39 مربوط به گروه 2 باشه که در اینجا اگه با WHERE محدودیت اعمال کنید، به مشکل بر میخورین. مثلاً توی صفحه دوم اگه بخواین 10 عنصر نشون بدین، شرط WHERE میشه: WHERE `id`>10 و بعد LIMIT 10 میگذارین و درنتیجه 2 خبر رو میبینید درصورتی که این اخبار مربوط به صفحه اول هست! برای رفع چنین مشکلاتی بهتره از جداول موقت که با موتوی MEMORY میسازین استفاده کنید. مثلاً یه همچین دستوری اینجا خیلی خوب جواب میده (با فرض اینکه فیلدهای جدول اخبار شما - جدول اصلی - به ترتیب id و title و body و gr باشه) :


<?php
// Create a unique table name for each user
$name = md5(session_id() . '_' . time());

// Drop previous temp table if exists
mysql_query("DROP TABLE IF EXISTS `{$name}`;");

// Create new temp table in memory
mysql_query("CREATE TABLE IF NOT EXISTS `{$name}` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`news_id` int(11) NOT NULL,
`title` varchar(255) COLLATE utf8_bin NOT NULL,
`body` text COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `title` (`title`)
) ENGINE=MEMORY DEFAULT CHARSET=utf8 COLLATE=utf8_bin;");

// Sort news table by id
mysql_query("ALTER TABLE `news` ORDER BY `id`");

// Extract news of a specific group and insert them into temp table
mysql_query("
INSERT INTO `{$name}` (`news_id`, `title`, `body`)
SELECT `id`,`title`,`body` FROM `news` WHERE `gr` LIKE '%{$group}%'
");

// Fetch the result from temp table
$result = mysql_query("SELECT * FROM `{$name}` WHERE (`id`>" . ($page_number * $items_per_page) . ') ORDER BY `id` LIMIT ' . $items_per_page);

// Drop temp table
mysql_query("DROP TABLE IF EXISTS `{$name}`;");
?>

همونطور که میبینید، اول یک اسم یکتا با ترکیب هش session_id کاربر و timestamp جاری برای هر جدول موقت هر کاربر ساخته میشه تا اخباری که کاربران مختلف میبنن یا اخبار مختلفی که یک کاربر توی صفحات مختلف داره نگاه میکنه، با هم قاطی نشه.
بعد چک میشه ببینیم جدول موقت با این اسم داریم یا نه و اگه بود، حذفش میکنیم.
بعد جدولی با همون نام و صرفاً فیلدهایی که لازم داریم (id خبر و عنوان و متن) ساخته میشه و بعد، این اطلاعات برحسب گروه انتخاب شده، از جدول اصلی استخراج شده و در این جدول درج میشه. نکته اصلی در اینجا اینه که فیلد id در جدول موقت مقدار نمیگیره و خودش Auto Increment شماره میندازه. درنتیجه id در اینجا مرتب شده هست و پرش توش نیست و id اصلی اخبار اینجا توی فیلد news_id هست که اگه لازمش داشته باشین، از این فیلد بخونیدش.
نهایتاً اطلاعات صفحه بندی شده از این جدول موقت استخراج میشه و بعد، جدول موقت رو حذف میکنیم.
مزیت اصلی این روش، موتور MEMORY هست که توی RAM ایجاد میشه و سرعت فوق العاده ای داره (نسبت به دیسک) و درنتیجه راندمان بالایی داریم (یکبار اطلاعات از دیسک خونده میشه و بعد، با RAM سروکار داریم). تنها عیب موتور MEMORY پاک شدنش با Restartشدن سیستم هست که اونهم اینجا مهم نیست چون هدف ما، موقت بودن جدول هست و نمیخوایم برای نگهداری طولانی مدت ازش استفاده کنیم.
امیدوارم خوب توضیح داده باشم و مشکلتون برطرف بشه.
موفق باشید.

Unique
دوشنبه 30 مرداد 1391, 16:17 عصر
ببخشین میام وسط بحثتون اما چند تا موضوع که شاید کمکی بکنه :

اولا راهی که جناب MMSHFE کردن درسته اما میشه کل اطلاعات را هم توی جدول temp نریخت ! فق id برای autoincrement و news_id کافیه ! چون بعدش میشه راحت با join اطلاعات را در آورد ! در ثانی limit باز هم برای این جدول 500000 تایی که حالا میشه 60000 تا بیشتر یا کمتر بازم هم بده ! میشه با => و =< خیلی راحت بدون Limit اطلاعات را استخراج کرد !

ثانیا این روش بد نیست اما راه حل دیگه اینه که شما یک فیلد داشته باشین به نام news_sort ! حالا هر وقت یک رکورد حذف یا اضافه میشه شما یک temp table با autoincerement به تعداد رکورد های جدول اصلی میسازین و با یک update از temp table روی news_sort از جدول اصلی sort را درست میکنید ! اینطوری نیاز نیست برای هر کاربر این کار را انجام بدین ! limit هم دیگه نمیخواد !

MMSHFE
دوشنبه 30 مرداد 1391, 18:17 عصر
درسته، راه حل زیاده. اما یکی از تکنیکهایی که اخیراً دیدم زیاد استفاده میشه، همین استفاده از جداول موقت توی RAM هست. بالأخره این موتور یه کاربردهایی داشته که تولید شده. حالا بحث بهینگی و پرهیز از افزونگی اگه باشه، حرف شما کاملاً متینه و صرفاً news_id کفایت میکنه اما هدف من این بود که وقتی یکبار از دیسک خوندیم، دیگه به دیسک مراجعه نکنیم تا عنوان و متن اخبار رو استخراج کنیم. ضمناً اگه دقت کنید، توی جدول تولیدشده در RAM اول با WHERE محدودیت اعمال شده و بعد از LIMIT استفاده کردیم. یعنی در اینجا عملاً OFFSET نداریم و چیزی که باعث کاهش سرعت میشه، همین OFFSET هست نه خود LIMIT
ضمناً توی همون فیلد news_sort هم که گفتین، یکسری دردسرها داریم. مثلاً اونجا هم اگه رکوردی حذف بشه، بین news_sortها هم Gap خواهیم داشت و این مسئله، استفاده از WHERE رو برای Pagination مشکل میکنه. مگه اینکه با هر حذف، دوباره news_id از یک شماره گذاری بشه که این مسئله، عملاً مصداق از چاله درآمدن و به چاه افتادن هست!

Unique
دوشنبه 30 مرداد 1391, 20:09 عصر
مگه اینکه با هر حذف، دوباره news_id از یک شماره گذاری بشه که این مسئله، عملاً مصداق از چاله درآمدن و به چاه افتادن هست!
البته نیاز نیست که news_id را هر بار دوباره از نو شماره بدیم ! چون ممکنه reference های اون توی جداول دیگه را از دست بدیم و مجبوریم همه را update کنیم ! منظورم news_sort بود ! از جایی که این جداول به این خاطر بزرگ میشوند که حذف زیاد اتفاق نمیفته ! پس فرآیند زیر نیاز به تکرار زیاد نداره ! گرچه زمان زیادی حتی برای 10 میلیون رکورد هم نمیگیره ! یعنی در قیاس به paging و limit و اینا به نظر من بهتره :


//newstemp [id (autoincrement),newsid]
insert into newstemp select news_id from news;
update news inner join newstemp on newsid = news_id set news_sort = id;

اما شخصا برای جداول بزرگ از این سیستم استفاده نمیکنم ! یعنی اصلا نیاز نیست به کاربر paging نشون بدم ! کاربری که میخواد بین صفحات پرش کنه معلومه خودش را الکی داره درگیر میکنه ! اکثرا 2 یا 3 صفحه اول را میبینند برای مطالب جدید ! اگه دنبال چیز خاصی باشه جستجو میکنه ! 500000 رکورد با نمایش 50 تا در هر صفحه یعنی 10000 تا صفحه اگه گرو ه های paging را 10 بگیریم ! خودش گیج خواهد شد ! پس چیکار کنیم ! کاری که همه سایت های جدید انجام میدهند یعنی میایم 100 تا یا همون 50 تا را نشون میدیم ! و از بعدی و قبلی استفاده میکنیم ! اینجوری آخرین رکوردی که گرفتم را میگذاری توی بعدی و اولی را میگذاریم توی قبلی و برای اسکریپت میفرستیم ! دیگه هیچ نیازی به offset نیست که سرعت را بگیره !

tehro0n
پنج شنبه 02 شهریور 1391, 15:56 عصر
من که در phpMyAdmin که تست می گیرم اگه از where استفاده کنم حالا یا برای بازه زمانی و یا محدود کردن چیزهای دیگه مقدار زمان برای محاسبش بیشتر از بدون where است، شاید به دلیل این که شرط نداره!

idocsidocs
چهارشنبه 08 شهریور 1391, 09:54 صبح
مهندس الان با کوئری توی صفحه بندی مشکل ایجاد می شه.

$result = mysql_query('SELECT * FROM `table` WHERE (`id`>' . ($page_number * $items_per_page) .) ORDER BY `id` LIMIT ' . $items_per_page);

فرض کنید کلا 100 مقاله داریم و توی هر صفحه 10 مقاله نمایش می دیم، و مقالات 30 تا 50 حذف شدن.

با این کوئری باید صفحه بندی بصورت 10 تا 10 تا افزایش پیدا کنه و صفحات 30 تا 50 هم توی صفحه بندی قرار می گیرن که این باعث می شه کار صفحه بندی با دقت انجام نشه.

برای حل این مشکل باید چیکار کرد؟

MMSHFE
چهارشنبه 08 شهریور 1391, 19:11 عصر
من که در phpMyAdmin که تست می گیرم اگه از where استفاده کنم حالا یا برای بازه زمانی و یا محدود کردن چیزهای دیگه مقدار زمان برای محاسبش بیشتر از بدون where است، شاید به دلیل این که شرط نداره!
باید هم اینطور باشه. به شما اگه بگن تمام بچه های توی محوطه مدرسه رو صدا بزن سریعتر اینکار رو انجام میدین یا اگه بگن برو هرکی اسمش علی هست پیدا کن و بردار بیار (با فرض اینکه از بلندگو استفاده نکنید!)

MMSHFE
چهارشنبه 08 شهریور 1391, 19:12 عصر
...فرض کنید کلا 100 مقاله داریم و توی هر صفحه 10 مقاله نمایش می دیم، و مقالات 30 تا 50 حذف شدن...
پاسخ 10 همین تاپیک رو ملاحظه کنید.

idocsidocs
چهارشنبه 08 شهریور 1391, 21:29 عصر
پاسخ 10 همین تاپیک رو ملاحظه کنید.
با این روش کمی مشکل دارم !
چون فرض کنید یک میلیون رکورد داریم، 1000 کاربر همزمان هم داریم. برای هر کاربر باید یه جدول موقت ایجاد بشه و این یک میلیون رکورد رو توی جدول موقت ذخیره کنیم.

راه دیگه ای بغیر از جدل موقت وجود نداره؟

MMSHFE
جمعه 10 شهریور 1391, 09:20 صبح
دوست عزیز، این 1000 جدول که هرکدوم 1 میلیون رکورد دارن، همه توی RAM هستن و پردازش اونها در حد میلیونیم ثانیه! راه حل زیاده ولی فکر میکنم این روش از همه بهینه تر باشه چون فیلد id دیتابیس شما مرتب نیست و یکسری رکوردها حذف شده و درنتیجه با WHERE نمیتونید به درستی عمل انتخاب رو انجام بدین.

idocsidocs
جمعه 10 شهریور 1391, 10:55 صبح
دوست عزیز، این 1000 جدول که هرکدوم 1 میلیون رکورد دارن، همه توی RAM هستن و پردازش اونها در حد میلیونیم ثانیه!



یعنی سرعت جدولهای MEMORY بیشتر از myisam هست یا دلیل دیگه ای داره؟

راه حل زیاده ولی فکر میکنم این روش از همه بهینه تر باشه چون فیلد id دیتابیس شما مرتب نیست و یکسری رکوردها حذف شده و درنتیجه با WHERE نمیتونید به درستی عمل انتخاب رو انجام بدین.
مهندس دارم سی ام اسی که نوشتم رو کلا تغییر می دم و می خوام همه چیز استاندارد باشه. می شه یه لینک معتبر معرفی کنید که این روش رو تایید کرده باشه؟
سی ام اس های رایگان از این روش استفاده می کنن؟

MMSHFE
جمعه 10 شهریور 1391, 11:30 صبح
یعنی سرعت جدولهای MEMORY بیشتر از myisam هست یا دلیل دیگه ای داره؟
جدولهای MEMORY توی RAM ایجاد میشن و سایر جداول توی DISC و قطعاً میدونید سرعت RAM حداقل 200 برابر دیسک هست!

مهندس دارم سی ام اسی که نوشتم رو کلا تغییر می دم و می خوام همه چیز استاندارد باشه. می شه یه لینک معتبر معرفی کنید که این روش رو تایید کرده باشه؟
سی ام اس های رایگان از این روش استفاده می کنن؟
لینک خود dev.mysql.com (http://dev.mysql.com/doc/refman/5.6/en/storage-engines.html) کفایت میکنه؟ :چشمک:

idocsidocs
جمعه 10 شهریور 1391, 12:26 عصر
جدولهای MEMORY توی RAM ایجاد میشن و سایر جداول توی DISC و قطعاً میدونید سرعت RAM حداقل 200 برابر دیسک هست!
اطلاعاتی که توی جدولهای MEMORY ذخیره می شه تا چه مدت توی رم باقی می مونه؟


لینک خود dev.mysql.com (http://dev.mysql.com/doc/refman/5.6/en/storage-engines.html) کفایت میکنه؟ :چشمک:
طبق توضیحات صفحه ای که مشخص کردید memory قابلیت Full-text search indexes نداره. اگه بخوام نتایج جستجو رو صفحه بندی کنم باید چیکار کنم؟

MMSHFE
شنبه 11 شهریور 1391, 01:59 صبح
اطلاعاتی که توی جدولهای MEMORY ذخیره می شه تا چه مدت توی رم باقی می مونه؟
تا وقتی که سرور Restart نشده باشه یا برقش قطع نشه! مثل اطلاعات RAM سیستم خودتون. مگه اینکه خودتون زودتر جدول موقت رو DROP کنید.

طبق توضیحات صفحه ای که مشخص کردید memory قابلیت Full-text search indexes نداره. اگه بخوام نتایج جستجو رو صفحه بندی کنم باید چیکار کنم؟
اگه هدفتون از Full-text Search صرفاً سرعتش باشه، توی RAM مهم نیست چون سرعت درهرحال خیلی بالاتر از دیسک هست. اما اگه مزایای دیگه اون مثل یافتن عبارات نزدیک به عبارت جستجو شده و مرتب سازی برحسب میزان شباهت و... هست، اونوقت باید قید MEMORY ENGINE رو بزنید. صفحه بندی به اون شکلی که مدنظر شماست، فکر نکنم نیازی به Full-text Search داشته باشه.

idocsidocs
شنبه 11 شهریور 1391, 09:23 صبح
تا وقتی که سرور Restart نشده باشه یا برقش قطع نشه! مثل اطلاعات RAM سیستم خودتون. مگه اینکه خودتون زودتر جدول موقت رو DROP کنید.
یعنی اگر جدول رو پاک نکنیم و سرور خاموش نشه اطلاعات این جدولها توی رم باقی می مونه؟ این کار توی بازدیدهای بالا باعث کند شدن سایت نمی شه؟

MMSHFE
چهارشنبه 15 شهریور 1391, 23:41 عصر
دقیقاً این مشکل وجود داره و باید در استفاده از موتور MEMORY حواستون جمع باشه که بعد از انجام کار، حتماً DROP بشه.

idocsidocs
پنج شنبه 16 شهریور 1391, 00:35 صبح
دقیقاً این مشکل وجود داره و باید در استفاده از موتور MEMORY حواستون جمع باشه که بعد از انجام کار، حتماً DROP بشه.

مهندس هنوز هم با این روش مشکل دارم.

فرض کنید 1000 تا کاربر همزمان بیاد و برای صفحه بندی اطلاعات یه جدول حجیم رو بیرون بکشیم و توی مموری دوباره یه جدول دیگه ایجاد کنیم، مصرف حافظه رم خیلی بالا می ره.

از طرفی ممکنه توی بازدید های بالا خطایی رخ بده و چندتا از این جدولها حذف نشن و... کلا یکمی با این روش راحت نیستم.

کلا نمی دونم که از این به بعد این روش رو بکار ببرم یا نه. نظر شما چیه؟ البته با چند نفر صحبت کردم که اونها با این روش موافق نبودن ولی نظر شما یه چیز دیگست:چشمک:


بنظرتون سایت برنامه نویس که از اسکریپت vb استفاده کرده چطور صفحه بندی رو انجام می ده؟

MMSHFE
جمعه 17 شهریور 1391, 03:42 صبح
راستش تابحال فرصت نکردم vB رو چک کنم و ببینم چطور صفحه بندی رو انجام میده ولی روشی که گفتم خیلی سریع انجام میشه چون همه کارها توی RAM هست. ضمناً به چه دلیلی ممکنه جدول DROP نشه؟

idocsidocs
جمعه 17 شهریور 1391, 11:02 صبح
DROP نشدن مهم نیست. مهم اینه که اگه یه جدول حجیم داشته باشیم و برای هر کاربر با هربار صفحه بندی بخوایم یه جدول حجیم دیگه توی رم درست کنیم، بنظرم کار درستی نیست.