Tôi là nhà phát triển cao cấp trên ứng dụng Phần mềm dưới dạng dịch vụ được sử dụng bởi nhiều khách hàng khác nhau. Phần mềm của chúng tôi chạy trên một cụm máy chủ ứng dụng Apache / PHP, được cung cấp bởi phụ trợ MySQL. Trong một phiên bản cụ thể của phần mềm, mã PHP để truy vấn danh sách tên danh mục sẽ hết thời gian khi khách hàng có hơn 29 danh mục . Tôi biết điều này vô nghĩa; Không có gì đặc biệt về số 30 sẽ phá vỡ điều này và các khách hàng khác có hơn 30 danh mục, tuy nhiên, vấn đề có thể tái tạo 100% khi cài đặt này có 30 danh mục trở lên và biến mất khi có ít hơn 30 danh mục.
Bảng trong câu hỏi là:
CREATE TABLE IF NOT EXISTS `categories` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(64) NOT NULL,
`title` varchar(128) NOT NULL,
`parent` int(10) unsigned NOT NULL,
`keywords` varchar(255) NOT NULL,
`description` text NOT NULL,
`status` enum('Active','Inactive','_Deleted','_New') NOT NULL default 'Active',
`style` enum('_Unknown') default NULL COMMENT 'Autoenum;',
`order` smallint(5) unsigned NOT NULL,
`created_at` datetime NOT NULL,
`modified_at` datetime default NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`),
KEY `parent` (`parent`),
KEY `created_at` (`created_at`),
KEY `modified_at` (`modified_at`),
KEY `status` (`status`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='R2' AUTO_INCREMENT=33 ;
Mã trong câu hỏi truy vấn đệ quy bảng để tìm nạp tất cả các danh mục. Nó phát hành một
SELECT * FROM `categories` WHERE `parent`=0 ORDER BY `order`,`name`
Và sau đó lặp lại truy vấn này cho mỗi hàng được trả về, nhưng sử dụng WHERE parent=$category_id
mỗi lần. (Tôi chắc chắn quy trình này có thể được cải thiện, nhưng đó có lẽ là một câu hỏi khác)
Theo như tôi có thể nói, truy vấn sau sẽ bị treo mãi mãi:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Tôi có thể thực hiện truy vấn này trong máy khách mysql trên máy chủ hoàn toàn tốt và tôi có thể thực hiện nó trong PHPMyAdmin mà không gặp vấn đề gì.
Lưu ý rằng đó không phải là truy vấn cụ thể mà là vấn đề. Nếu tôi DELETE FROM categories WHERE id=22
thì một truy vấn khác tương tự như câu hỏi trên sẽ bị treo. Ngoài ra, truy vấn trên trả về hàng 0 khi tôi chạy thủ công .
Tôi nghi ngờ rằng bảng có thể bị hỏng, và tôi đã thử REPAIR TABLE
và OPTIMIZE TABLE
nhưng những vấn đề được báo cáo này cũng không giải quyết được vấn đề. Tôi bỏ bảng và tạo lại, nhưng vấn đề trở lại. Đây chính xác là cùng một cấu trúc bảng và mã PHP mà các khách hàng khác đang sử dụng không có vấn đề gì với bất kỳ ai khác, kể cả những khách hàng có hơn 30 danh mục.
Mã PHP không được đệ quy mãi mãi. (Đây không phải là vòng lặp vô hạn)
Máy chủ MySQL đang chạy Linux CentOS với cộng đồng mysqld Ver 5.0.92 cho pc-linux-gnu trên i686 (Phiên bản cộng đồng MySQL (GPL))
Tải trên máy chủ MySQL thấp: tải trung bình: 0,58, 0,75, 0,73, Cpu: 4,6% chúng tôi, 2,9% sy, 0,0% ni, 92,2% id, 0,0% wa, 0,0% hi, 0,3% si, 0,0% st. Trao đổi không đáng kể đang được sử dụng (448k)
Làm thế nào tôi có thể khắc phục sự cố này? Bất kỳ đề xuất như những gì có thể xảy ra?
CẬP NHẬT: Tôi TRUNCE
chỉnh sửa bảng và chèn 30 hàng dữ liệu giả:
INSERT INTO `categories` (`id`, `name`, `title`, `parent`, `keywords`, `description`, `status`, `style`, `order`, `created_at`, `modified_at`) VALUES
(1, 'New Category', '', 0, '', '', 'Inactive', NULL, 1, '2011-10-25 12:06:30', '2011-10-25 12:06:34'),
(2, 'New Category', '', 0, '', '', 'Inactive', NULL, 2, '2011-10-25 12:06:39', '2011-10-25 12:06:40'),
(3, 'New Category', '', 0, '', '', 'Inactive', NULL, 3, '2011-10-25 12:06:41', '2011-10-25 12:06:42'),
(4, 'New Category', '', 0, '', '', 'Inactive', NULL, 4, '2011-10-25 12:06:46', '2011-10-25 12:06:47'),
(5, 'New Category', '', 0, '', '', 'Inactive', NULL, 5, '2011-10-25 12:06:49', NULL),
(6, 'New Category', '', 0, '', '', 'Inactive', NULL, 6, '2011-10-25 12:06:51', '2011-10-25 12:06:52'),
(7, 'New Category', '', 0, '', '', 'Inactive', NULL, 7, '2011-10-25 12:06:53', '2011-10-25 12:06:54'),
(8, 'New Category', '', 0, '', '', 'Inactive', NULL, 8, '2011-10-25 12:06:56', '2011-10-25 12:06:57'),
(9, 'New Category', '', 0, '', '', 'Inactive', NULL, 9, '2011-10-25 12:06:59', '2011-10-25 12:06:59'),
(10, 'New Category', '', 0, '', '', 'Inactive', NULL, 10, '2011-10-25 12:07:01', '2011-10-25 12:07:01'),
(11, 'New Category', '', 0, '', '', 'Inactive', NULL, 11, '2011-10-25 12:07:03', '2011-10-25 12:07:03'),
(12, 'New Category', '', 0, '', '', 'Inactive', NULL, 12, '2011-10-25 12:07:05', '2011-10-25 12:07:05'),
(13, 'New Category', '', 0, '', '', 'Inactive', NULL, 13, '2011-10-25 12:07:06', '2011-10-25 12:07:07'),
(14, 'New Category', '', 0, '', '', 'Inactive', NULL, 14, '2011-10-25 12:07:08', '2011-10-25 12:07:09'),
(15, 'New Category', '', 0, '', '', 'Inactive', NULL, 15, '2011-10-25 12:07:11', '2011-10-25 12:07:12'),
(16, 'New Category', '', 0, '', '', 'Inactive', NULL, 16, '2011-10-25 12:07:13', '2011-10-25 12:07:14'),
(17, 'New Category', '', 0, '', '', 'Inactive', NULL, 17, '2011-10-25 12:09:41', '2011-10-25 12:09:42'),
(18, 'New Category', '', 0, '', '', 'Inactive', NULL, 18, '2011-10-25 12:09:47', NULL),
(19, 'New Category', '', 0, '', '', 'Inactive', NULL, 19, '2011-10-25 12:09:48', NULL),
(20, 'New Category', '', 0, '', '', 'Inactive', NULL, 20, '2011-10-25 12:09:48', NULL),
(21, 'New Category', '', 0, '', '', 'Inactive', NULL, 21, '2011-10-25 12:09:49', NULL),
(22, 'New Category', '', 0, '', '', 'Inactive', NULL, 22, '2011-10-25 12:09:50', NULL),
(23, 'New Category', '', 0, '', '', 'Inactive', NULL, 23, '2011-10-25 12:09:51', NULL),
(24, 'New Category', '', 0, '', '', 'Inactive', NULL, 24, '2011-10-25 12:09:51', NULL),
(25, 'New Category', '', 0, '', '', 'Inactive', NULL, 25, '2011-10-25 12:09:52', NULL),
(26, 'New Category', '', 0, '', '', 'Inactive', NULL, 26, '2011-10-25 12:09:53', NULL),
(27, 'New Category', '', 0, '', '', 'Inactive', NULL, 27, '2011-10-25 12:09:54', NULL),
(28, 'New Category', '', 0, '', '', 'Inactive', NULL, 28, '2011-10-25 12:09:55', NULL),
(29, 'New Category', '', 0, '', '', 'Inactive', NULL, 29, '2011-10-25 12:09:56', NULL),
(30, 'New Category', '', 0, '', '', 'Inactive', NULL, 30, '2011-10-25 12:09:57', NULL);
Không có cha mẹ nào cả , tất cả các hạng mục đều ở cấp cao nhất. vấn đề vẫn còn đó. Truy vấn sau đây, được thực thi bởi PHP, không thành công:
SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`
Đây là EXPLAIN
:
mysql> EXPLAIN SELECT * FROM `categories` WHERE `parent`=22 ORDER BY `order`,`name`;
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
| 1 | SIMPLE | categories | ref | parent | parent | 4 | const | 1 | Using where; Using filesort |
+----+-------------+------------+------+---------------+--------+---------+-------+------+-----------------------------+
1 row in set (0.00 sec)
CẬP NHẬT # 2: Bây giờ tôi đã thử tất cả các cách sau:
- Tôi đã sao chép bảng và dữ liệu này sang một trang web khác có cùng phần mềm. Vấn đề không theo bảng. Nó dường như bị giới hạn trong cơ sở dữ liệu này.
- Tôi đã thay đổi chỉ số như câu trả lời của gbn đề nghị. Vấn đề vẫn còn.
- Tôi bỏ bảng và tạo lại dưới dạng
InnoDB
bảng và chèn cùng 30 hàng kiểm tra ở trên. Vấn đề vẫn còn.
Tôi nghi ngờ nó phải là một cái gì đó với cơ sở dữ liệu này ...
CẬP NHẬT # 3: Tôi đã bỏ hoàn toàn cơ sở dữ liệu và tạo lại nó dưới một tên mới, nhập dữ liệu của cô ấy. Vấn đề vẫn còn.
Tôi đã thấy rằng câu lệnh PHP thực sự bị treo là một lời kêu gọi mysql_query()
. Tuyên bố sau này không bao giờ được thực hiện.
Trong khi cuộc gọi bị treo, MySQL liệt kê các luồng như đang ngủ!
mysql> show full processlist;
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
| 5560 | root | localhost | problem_db | Query | 0 | NULL | show full processlist |
----- many rows which have no relevancy; only rows from this customer's app are shown ------
| 16341 | shared_db | oak01.sitepalette.com:53237 | shared_db | Sleep | 308 | | NULL |
| 16342 | problem_db | oak01.sitepalette.com:60716 | problem_db | Sleep | 307 | | NULL |
| 16344 | shared_db | oak01.sitepalette.com:53241 | shared_db | Sleep | 308 | | NULL |
| 16346 | problem_db | oak01.sitepalette.com:60720 | problem_db | Sleep | 308 | | NULL |
+-------+------------------+-----------------------------+----------------------+---------+------+-------+-----------------------+
CẬP NHẬT # 4: Tôi đã thu hẹp nó thành sự kết hợp của hai bảng, categories
bảng chi tiết ở trên và một media_images
bảng có 556 hàng. Nếu media_images
bảng chứa ít hơn 556 hàng hoặc categories
bảng chứa ít hơn 30 hàng, vấn đề sẽ biến mất. Nó giống như đó là một loại giới hạn MySQL mà tôi đang gặp ở đây ...
CẬP NHẬT # 5: Tôi vừa thử chuyển cơ sở dữ liệu sang một máy chủ MySQL khác và vấn đề đã biến mất ... Vì vậy, nó liên quan đến máy chủ cơ sở dữ liệu sản xuất của tôi ...
CẬP NHẬT # 6: Đây là mã PHP có liên quan bị treo mỗi lần:
public function find($type,$conditions='',$order='',$limit='')
{
if($this->_link == self::AUTO_LINK)
$this->_link = DFStdLib::database_connect();
if(is_resource($this->_link))
{
$q = "SELECT ".($type==_COUNT?'COUNT(*)':'*')." FROM `{$this->_table}`";
if($conditions)
{
$q .= " WHERE $conditions";
}
if($order)
{
$q .= " ORDER BY $order";
}
if($limit)
{
$q .= " LIMIT $limit";
}
switch($type)
{
case _ALL:
DFSkel::log(DFSkel::LOG_DEBUG,"mysql_query($q,$this->_link);");
$res = @mysql_query($q,$this->_link);
DFSkel::log(DFSkel::LOG_DEBUG,"res = $res");
Mã này đang được sản xuất và hoạt động tốt trên tất cả các cài đặt khác. Chỉ cần một lần cài đặt, nó bị treo $res = @mysql_query($q,$this->_link);
. Tôi biết bởi vì tôi thấy mysql_query
trong nhật ký gỡ lỗi, chứ không phải res =
, và khi tôi strace
xử lý PHP, nó bị treo ởread(
CẬP NHẬT # bất cứ điều gì-nó-là-I-ghét-this- & (# ^ & -issue! Điều này bây giờ đã bắt đầu xảy ra với hai khách hàng của tôi. Tôi chỉ bắn lên tcpdump
và nó trông giống như phản hồi từ MySQL không bao giờ được gửi hoàn toàn. Luồng TCP dường như bị treo trước khi có thể gửi phản hồi đầy đủ của MySQL. (Tuy nhiên tôi vẫn đang điều tra)
CẬP NHẬT # I-have-gone-hoàn toàn-crazy-but-it-works-now-kinda: Ok, điều này vô nghĩa, nhưng tôi đã tìm ra giải pháp. Nếu tôi chỉ định một địa chỉ IP thứ hai cho eth2
giao diện của máy chủ MySQL và sử dụng một IP cho lưu lượng NFS và IP thứ hai cho MySQL, thì vấn đề sẽ biến mất. Giống như tôi bằng cách nào đó ... quá tải địa chỉ IP nếu cả hai lưu lượng truy cập NFS + MySQL đều đi đến IP đó. Nhưng điều đó không có ý nghĩa gì vì bạn không thể "quá tải" địa chỉ IP. Bảo vệ một giao diện chắc chắn, nhưng đó là cùng một giao diện.
Có ai biết cái quái gì đang diễn ra ở đây không? Đây có lẽ là một câu hỏi unix.SE hoặc ServerFault tại thời điểm này ... (Ít nhất là nó hoạt động ngay bây giờ ...)
CẬP NHẬT # why-oh-why: Vấn đề này vẫn đang xảy ra. Nó bắt đầu xảy ra ngay cả khi sử dụng hai IP khác nhau. Tôi có thể tiếp tục tạo IP riêng mới, nhưng rõ ràng có gì đó không đúng.