关于php:内存耗尽cakephp数据库查询

Memory exhausted cakephp DB query

我是CakePHP 2.4的新手,希望有人可以为我解决问题。

我的项目中存在内存用尽的问题-我已经对其进行调试,并使所有内容都可以使用2 GB的内存-但我的网络托管公司仅允许使用1 GB。

所以我想知道是什么使MySQL占用了如此多的RAM-当我在订单中添加优惠券时发生的情况-优惠券包含了我店中的所有产品。

我在导致问题的行和一长串数组(大约1200个A4页)之前放了debug-我发现很多东西,我猜这就是蛋糕中的mysql.php产生以下错误的原因:

1
2
Fatal error: Allowed memory size of 188743680 bytes exhausted
(tried to allocate 32 bytes) in lib/Cake/Model/Datasource/Database/Mysql.php on line 277

有没有办法解决这个问题-因此通话变小或内存增加。

编辑:我认为不是问题的Coupon控制器/模型是订单模型导致其获取所有产品的问题-已放置以下代码:

希望有人来看看它,给我一个提示,让我知道如何使它工作。
我试过调试内存使用情况,它仅使用约3 MB-但是添加优惠券时,它只会淹没内存并永远运行。

订购模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
<?php
App::uses('AppModel', 'Model');

class Order extends AppModel
{
    public $displayField = 'id';

    private $shipping_price = 39;
    private $free_shipping_threshold = 500;
    private $tax_rate = 0.20;

    public $validate = array(
        'state' => array(
            'rule'    => array('inList', array('unplaced', 'received', 'packed', 'resolved')),
            'allowEmpty' => false,
            'message' => 'Du skal v?|lge et stadie.'
         ),
        'shipping_price' => array(
            'decimal' => array(
                'rule' => 'decimal',
                'allowEmpty' => false,
                'message' => 'Du skal angive en pris.',
            ),
            'notNegative' => array(
                'rule'    => array('comparison', '>=', 0),
                'message' => 'En pris kan ikke v?|re negativ.'
            )
        ),
    );

    public $hasMany = array(
        'OrderItem'
    );

    public $belongsTo = array(
        'Customer',
        'Coupon'
    );

    private function purgeInactives($order_id)
    {
        // Find the order
        $order = $this->find('first', array('conditions' => array('Order.id' => $order_id), 'recursive' => 2));

        // Run through all order items in the order
        foreach ($order['OrderItem'] as $key => $order_item) {

        // Check if the order was a yarn batch
            if ($order_item['yarn_batch_id'] != 0) {
            // Check if the item is active
                if (!$order_item['YarnBatch']['is_active']) {
                    $this->OrderItem->delete($order_item['id']);
                    SessionComponent::setFlash(' Der blev fjernet en varer fra din indk??bskurv da det er ikke l?|ngere er aktivt. '.SessionComponent::read('Message.warning.message'), null, array(), 'warning');
                } // Check if there are any of this item left in stock
                else if ($order_item['YarnBatch']['stock_quantity'] < 1) {
                    $this->OrderItem->delete($order_item['id']);
                    SessionComponent::setFlash(' En af varene i din indk??bskurv er ikke l?|ngere p?¥ lager. '.SessionComponent::read('Message.warning.message'), null, array(), 'warning');
                }
            } // Check if the order was a needle variant
            else if ($order_item['needle_variant_id'] != 0) {
            // Check if the item is active
                if (!$order_item['NeedleVariant']['is_active']) {
                    $this->OrderItem->delete($order_item['id']);
                    SessionComponent::setFlash(' Der blev fjernet en varer fra din indk??bskurv da det er ikke l?|ngere er aktivt. '.SessionComponent::read('Message.warning.message'), null, array(), 'warning');
                } // Check if there are any of this item left in stock
                else if ($order_item['NeedleVariant']['stock_quantity'] < 1) {
                    $this->OrderItem->delete($order_item['id']);
                    SessionComponent::setFlash(' En af varene i din indk??bskurv er ikke l?|ngere p?¥ lager. '.SessionComponent::read('Message.warning.message'), null, array(), 'warning');
                }
            } // Check if the order was a recipe
            else if ($order_item['recipe_id'] != 0) {
            // Check if the item is active
                if (!$order_item['Recipe']['is_active']) {
                    $this->OrderItem->delete($order_item['id']);
                    SessionComponent::setFlash(' Der blev fjernet en varer fra din indk??bskurv da det er ikke l?|ngere er aktivt. '.SessionComponent::read('Message.warning.message'), null, array(), 'warning');
                }
            } else if ($order_item['color_sample_id'] != 0) {
            // Check if the item is active
                if (!$order_item['ColorSample']['is_active']) {
                    $this->OrderItem->delete($order_item['id']);
                    SessionComponent::setFlash(' Der blev fjernet en varer fra din indk??bskurv da det er ikke l?|ngere er aktivt. '.SessionComponent::read('Message.warning.message'), null, array(), 'warning');
                }
            } else {
                $this->OrderItem->delete($order_item['id']);
            }
        }
        // If the order have a coupon and it is no long active
        // if ($order['Coupon']['id'] !=0 && !$order['Coupon']['is_active']) {
        //     SessionComponent::setFlash(' Kuponen i din indk??bskurv blev fjernet fordi den ikke l?|ngere er gyldig. '.SessionComponent::read('Message.warning.message'), null, array(), 'warning');
        //     $this->Order->set($order);
        //     $this->Order->saveField('coupon_id', null);
        // }

    }

    private function resetOrderItemsPrices($order_id)
    {
        $order_items = $this->OrderItem->find('all', array('conditions' => array('OrderItem.order_id' => $order_id)));

        foreach ($order_items as $key => $order_item) {
            $this->OrderItem->set($order_item);
            $this->OrderItem->resetPrice();
        }
    }

    public function refresh()
    {
        // Remove edited or removed items
        $this->purgeInactives($this->id);
        $this->resetOrderItemsPrices($this->id);

        $order = $this->find('first', array('conditions' => array('Order.id' => $this->id), 'recursive' => 2));


        // Items order by prices, highest first
        $order_items = Set::sort($order['OrderItem'], '{n}.price', 'desc');
        $order_price = 0;
        $order_saving = 0;
        $total_amount_of_items = 0;

        $is_only_color_samples = true;

        // Run through all items
        foreach ($order_items as $key => $order_item) {
            $new_total_item_price = $order_item['price'];
            if ($order['Coupon']['id'] != 0) {
                if ($order_item['yarn_batch_id'] != 0) {
                    // There exist an item that is not a color sample
                    $is_only_color_samples = false;
                    foreach ($order['Coupon']['YarnBatch'] as $key => $yarn_batch_on_coupon) {

                        if ($yarn_batch_on_coupon['id'] == $order_item['yarn_batch_id']) {
                            // Calcualte savings
                            $calculated_savings = $this->calculateSaving(
                                $yarn_batch_on_coupon['price'],
                                $order_item['amount'],
                                $order['Coupon']['percentage_discount'],
                                $order['Coupon']['actual_discount'],
                                $order['Coupon']['item_amount']
                            );

                            // Was this a better soloution
                            if ($order_item['saving'] < $order_item['price'] - $calculated_savings['new_total_item_price']) {
                                $new_total_item_price = $calculated_savings['new_total_item_price'];
                                $best_coupon['amount_left'] = $calculated_savings['coupon_item_amount_left'];
                                $best_coupon['id'] = $order['Coupon']['id'];
                            }
                        } else {
                            continue;
                        }

                    }
                } else if ($order_item['needle_variant_id'] != 0) {
                    // There exist an item that is not a color sample
                    $is_only_color_samples = false;

                    if (!$order_item['NeedleVariant']['is_active']) {
                        foreach ($order['Coupon']['YarnBatch'] as $key => $yarn_batch_on_coupon) {
                            if ($needle_variant_on_coupon['id'] == $order_item['needle_variant_id']) {
                                // Calcualte savings
                                $calculated_savings = $this->calculateSaving(
                                    $needle_variant_on_coupon['price'],
                                    $order_item['amount'],
                                    $order['Coupon']['percentage_discount'],
                                    $order['Coupon']['actual_discount'],
                                    $order['Coupon']['item_amount']
                                );

                                // Was this a better soloution
                                if ($order_item['saving'] < $order_item['price'] - $calculated_savings['new_total_item_price']) {
                                    $new_total_item_price = $calculated_savings['new_total_item_price'];
                                    $best_coupon['amount_left'] = $calculated_savings['coupon_item_amount_left'];
                                    $best_coupon['id'] = $order['Coupon']['id'];
                                }
                            } else {
                                continue;
                            }
                        }
                    }
                } else if ($order_item['recipe_id'] != 0) {
                    // There exist an item that is not a color sample
                    $is_only_color_samples = false;
                }

                $order_item['saving'] = $order_item['price'] - $new_total_item_price;

            }

                // Calucalte the prices of item
                $order_saving += $order_item['saving'];
                $order_price += $order_item['price'] - $order_item['saving'];

                // Store prices on item
                $this->OrderItem->set($order_item);
                $this->OrderItem->saveField('saving', $order_item['saving']);
                $this->OrderItem->saveField('price', $order_item['price'] - $order_item['saving']);

            // Count up the counter
            $total_amount_of_items += $order_item['amount'];
        }
        $shipping_price = 0;

        // Calculate shipping prince and remainder (if it is only color samples keep it at 0)
        if ($order_price < $this->free_shipping_threshold) {
            $shipping_price = $this->shipping_price;
        }

        $free_shipping_remainder = $this->free_shipping_threshold - $order_price;



        $order_price = $order_price + $shipping_price;

        // Calculate taxing
        $tax = $order_price * $this->tax_rate;
        $sub_total = $order_price - $tax;

        // Store order data
        $this->saveField('shipping_price', $shipping_price);
        $this->saveField('free_shipping_remainder', $free_shipping_remainder);
        $this->saveField('amount', $total_amount_of_items);
        $this->saveField('saving', $order_saving);
        $this->saveField('price', $order_price);
        $this->saveField('tax', $tax);
        $this->saveField('sub_total', $sub_total);
        $this->saveField('modified', date('Y-m-d H:i:s'));

    }

    private function calculateSaving($item_price, $item_amount, $coupon_saving_percent, $coupon_saving_actual, $coupon_item_amount)
    {
        $new_item_price = 0;
        $new_total_item_price = 0;
        $coupon_item_amount_left = 0;

        // Check if the percentage discount is null, then use actual discount otherwise use percentage
        if ($coupon_saving_percent == null) {
            $new_item_price = $item_price - $coupon_saving_actual;
            $new_item_price = $new_item_price < 0 ? 0 : $new_item_price;
        } else {
            $new_item_price = $item_price - ($item_price * $coupon_saving_percent/100 );
        }

        // Check if there is enough to get savings on all of this particular item
        if ($item_amount <= $coupon_item_amount) {
            $new_total_item_price = $new_item_price * $item_amount;
            $coupon_item_amount_left = $coupon_item_amount - $item_amount;
        } else {
            $new_total_item_price = $new_item_price * $coupon_item_amount + $item_price * ($item_amount - $coupon_item_amount);
            $coupon_item_amount_left = $coupon_item_amount;
        }

        return array('new_total_item_price' => $new_total_item_price, 'coupon_item_amount_left' => $coupon_item_amount_left);
    }

    public function updateStockQuantityAfterPurchase()
    {
        // Find the order
        $order = $this->find('first', array('conditions' => array('Order.id' => $this->id), 'recursive' => 2));

        // Run through all order items in the order
        foreach ($order['OrderItem'] as $key => $order_item) {
        // Check if the order was a yarn batch
            if ($order_item['yarn_batch_id'] != 0) {
            // Find the item
                $yarn_batch = $this->OrderItem->YarnBatch->find('first', array('conditions' => array('YarnBatch.id' => $order_item['yarn_batch_id'])));

                // Calculate new stock amount
                $new_stock_quantity = $yarn_batch['YarnBatch']['stock_quantity'] - $order_item['amount'];

                $this->OrderItem->YarnBatch->id = $yarn_batch['YarnBatch']['id'];
                $this->OrderItem->YarnBatch->saveField('stock_quantity', $new_stock_quantity);
            } // Check if the order was a needle variant
            else if ($order_item['needle_variant_id'] != 0) {
            // Find the item
                $needle_variant = $this->OrderItem->NeedleVariant->find('first', array('conditions' => array('NeedleVariant.id' => $order_item['needle_variant_id'])));

                // Calculate new stock amount
                $new_stock_quantity = $needle_variant['NeedleVariant']['stock_quantity'] - $order_item['amount'];

                $this->OrderItem->NeedleVariant->id = $needle_variant['NeedleVariant']['id'];
                $this->OrderItem->NeedleVariant->saveField('stock_quantity', $new_stock_quantity);
            }
        }

        // If the order have a coupon and it is no long active
        if ($order['Coupon']['id'] != 0 && !$order['Coupon']['is_active']) {
            SessionComponent::setFlash(' Kuponnen i din indk??bskurv blev fjernet fordi den ikke l?|ngere er gyldig. '.SessionComponent::read('Message.warning.message'), null, array(), 'warning');
            $this->Order->set($order);
            $this->Order->saveField('coupon_id', null);
        }

    }
}


188743680字节为180兆,因此您不会占用1GB的内存。但是,很可能已将PHP配置为使用最大180Meg。

在具有2Gb内存的机器上运行它的事实是无关紧要的,在另一台机器上将php.ini memory_limit设置为什么。

如果您有权访问php.ini文件,则可以通过将memory_limit = 180M修改为更大的数字来更改此限制。

但是我要说的是,您的问题很可能与您处理结果集的方式有关,因此,最好看看重构查询和处理查询结果的PHP。 >


可以完成的两件事:

1)默认情况下,查询和结果集将返回Entities对象。您可以通过禁用水合来检索基本数组:

1
2
3
$query->enableHydration(false);
// Prior to 3.4.0
$query->hydrate(false);

https://book.cakephp.org/3.0/zh-CN/orm/retrieving-data-and-resultsets.html#Cake\\\\ORM\\\\Table::find

2)但是,如果您的代码实现(即代码设计)不允许这样做,则可以禁用查询缓冲以流式传输结果:

1
2
3
$query->enableBufferedResults(false);
// Prior to 3.4.0
$query->bufferResults(false);

https://book.cakephp.org/3.0/zh-CN/orm/retrieving-data-and-resultsets.html#working-with-result-sets