PHP中静态变量作用域浅谈
最近看代码,发现有个代码写的有意思,在函数内用static修饰局部变量。当时心想,难道是跟global一样,扩大作用域?又看到同一个类里面,两个方法都用static修饰了同样的变量,我明白了,事情肯定没有我想象的这么简单,所以我写了下面的这个方法来测试下。
<?php
class MyTest
{
public static function func1($arg1)
{
static $result = [];
$p = serialize($arg1);
var_dump($p);
if (isset($result[$p])) {
return $result[$p];
}
$result[$p] = date('Y-m-d H:i:s') . " by func1 rand:".rand(0,100);
return $result[$p];
}
public static function func2($arg1)
{
static $result = [];
$p = serialize($arg1);
var_dump($p);
if (isset($result[$p])) {
return $result[$p];
}
$result[$p] = date('Y-m-d H:i:s') . " by func2 rand:".rand(0,100);
return $result[$p];
}
}
$arg = ['1' => '2'];
var_dump(MyTest::func1($arg));
var_dump(MyTest::func2($arg));
var_dump(MyTest::func1($arg));
var_dump(MyTest::func2($arg));
这个输出应该是如何呢?大家不妨想一想。同样的static $result 结果会相同吗?
好了我们一起来看输出。
string(18) "a:1:{i:1;s:1:"2";}"
string(36) "2020-04-28 03:30:04 by func1 rand:45"
string(18) "a:1:{i:1;s:1:"2";}"
string(36) "2020-04-28 03:30:04 by func2 rand:40"
string(18) "a:1:{i:1;s:1:"2";}"
string(36) "2020-04-28 03:30:04 by func1 rand:45"
string(18) "a:1:{i:1;s:1:"2";}"
string(36) "2020-04-28 03:30:04 by func2 rand:40"
从上面可以看出,static $result的作用域还是自己的函数内。但是在类实例化的生命周期内,当函数返回结果时,其生命周期并没有释放。
那么我们可以利用这个特性来缓存函数操作的结果,而又不需要定义类的成员变量。
记一下RabblitMQ的安装和RPC的工作模式

1. 什么是Rabbitmq
Rabbitmq 是一款由erlang开发的基于AMQP协议的消息队列。官网地址https://www.rabbitmq.com 有六种工作模式。详细的教程在本文就不再赘述,可以直接去官网阅读。
2. 安装RabbitMq
安装环境CentOS7,步骤如下:
- 从源码安装
#下面是从源码master分支安装示例
git clone https://github.com/erlang/otp.git
cd otp
./otp_build autoconf
./configure
make
make install
安装完成之后,记得修改/etc/profile,设置erlang的路径
vim /etc/profile
export ERLANG="/usr/local/lib/erlang/bin/"
export PATH=/usr/local/php/bin:/usr/local/nginx/sbin:$PATH:$ERLANG
source /etc/profile
- 安装socat
sudo yum install socat
- 下载Rabbitmq-server安装包
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.2/rabbitmq-server-3.8.2-1.el7.noarch.rpm
rpm -ivh --nodeps rabbitmq-server-3.8.2-1.el7.noarch.rpm
一定要注意加上--nodeps, 不然会提示erlang依赖版本问题
- 启动服务端
rabbitmq-plugins list #查看组件是否正常显示
rabbitmq-plugins enable rabbitmq_management # 加载web管理组件
rabbitmq-server #启动服务端
在浏览器里输入http://localhost:15672,看后台是否出来,默认账号密码都是guest
3. RPC下如何工作
在上面的web页可以添加用户,由于默认的guest是不允许在外网访问,所以我们新增了yhw这个用户
从官网下载教程代码。到php目录下执行composer install。修改好配置之后我们启动rpc_server.php和rpc_client.php即可。
最后附上修改的测试代码。
rpc_server.php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
$connection = new AMQPStreamConnection('10.211.55.3', 5672, 'yhw', 'yhw');
$channel = $connection->channel();
$channel->queue_declare('rpc_queue', false, false, false, false);
function fib($n)
{
if ($n == 0) {
return 0;
}
if ($n == 1) {
return 1;
}
return fib($n - 1) + fib($n - 2);
}
// 我增加的测试函数
function sayHello($n)
{
return sprintf(
"%s Hello gentleman, I am rabbitmq, working on RPC mode. You typed %d, the result is %d",
date('Y-m-d H:i:s'),
$n,
fib($n)
);
}
echo " [x] Awaiting RPC requests\n";
$callback = function ($req) {
$n = intval($req->body);
echo ' [.] fib(', $n, ")\n";
$msg = new AMQPMessage(
(string)sayHello($n),
array('correlation_id' => $req->get('correlation_id'))
);
$req->delivery_info['channel']->basic_publish(
$msg,
'',
$req->get('reply_to')
);
$req->delivery_info['channel']->basic_ack(
$req->delivery_info['delivery_tag']
);
};
$channel->basic_qos(null, 1, null);
$channel->basic_consume('rpc_queue', '', false, false, false, false, $callback);
while ($channel->is_consuming()) {
$channel->wait();
}
$channel->close();
$connection->close();
?>
rpc_client.php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
class FibonacciRpcClient
{
private $connection;
private $channel;
private $callback_queue;
private $response;
private $corr_id;
public function __construct()
{
$this->connection = new AMQPStreamConnection(
'10.211.55.3',
5672,
'yhw',
'yhw'
);
$this->channel = $this->connection->channel();
list($this->callback_queue, ,) = $this->channel->queue_declare(
"",
false,
false,
true,
false
);
$this->channel->basic_consume(
$this->callback_queue,
'',
false,
true,
false,
false,
array(
$this,
'onResponse'
)
);
}
public function onResponse($rep)
{
if ($rep->get('correlation_id') == $this->corr_id) {
$this->response = $rep->body;
}
}
public function call($n)
{
$this->response = null;
$this->corr_id = uniqid();
// 此处为调用参数,以及回复
$msg = new AMQPMessage(
(string) $n,
array(
'correlation_id' => $this->corr_id,
'reply_to' => $this->callback_queue
)
);
$this->channel->basic_publish($msg, '', 'rpc_queue');
while (!$this->response) {
$this->channel->wait();
}
return $this->response;
}
}
$fibonacci_rpc = new FibonacciRpcClient();
$n = (int)$argv[1];
while(1){
$response = $fibonacci_rpc->call($n);
echo ' [.] Got ', $response, "\n";
usleep(100);
}
?>
稍微改动下,客户端的代码,多起几个php脚本,看执行结果是否能回复正确。看下面截图结果完全正确。


聊一聊快排算法
曾经有一个人让我写下快排算法,我给他写出来了,然后就被Diss了,怎么申请这么多临时变量。来,各位看官一起看看本菜🐔的作业。各位看官也可以动手写一写。
快速排序的逻辑 — 来自百度百科
快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
<?php
$input = [100, 23, 452, 234, 56223, 6234, 134, 51, 234];
$ret = quickSort($input);
printf("%s\n", json_encode($ret));
function quickSort($input)
{
$len = count($input);
if ($len <= 1) {
return $input;
}
$temp = $input[0];
$left = $right = [];
for ($i = 1; $i < $len; $i++) {
if ($temp >= $input[$i]) {
$left[] = $input[$i];
} else {
$right[] = $input[$i];
}
}
$left = quickSort($left);
$right = quickSort($right);
return array_merge($left, [$temp], $right);
}
基本上也是符合快速排序的标准,不过确实$left和$right这两个属于数组copy,会需要大量的内存空间的。但是既然说到利用了中间变量来存放数组,那么是否有别的方式呢?
答案肯定是有的了,既然不能用中间变量存数组,那么我可以使用交换的方式,因为反正都是比大小,我们通过一个临时变量将原数组的数据挖一个出来,然后再去比较,可以看下面的示例。
[100, 23, 452, 234, 56223, 6234, 134, 51, 234]
第一趟,我先取100出来,然后从右向左扫描,比他小的(51<100)放入他的坑,那么结果如下:
[---, 23, 452, 234, 56223, 6234, 134, 51, 234]
从左扫发现51,填入左边的坑。
[51, 23, 452, 234, 56223, 6234, 134, ---, 234]
然后从51的下个值往右扫,发现452>100,那么把452填入上面挖的坑
[51, 23, ---, 234, 56223, 6234, 134, 452, 234]
然后从452往左扫描,发现没有小于100的。那么100 就只能去填坑了。自此我们做了一个循环。
[51, 23, 100 234, 56223, 6234, 134, 452, 234]
那么下一个循环我们怎么开始呢?因为100的右边肯定是大于它的,100的左边肯定是小于它的。PS.这不是废话吗?上面的快排就是这样要求的。
我们就从51,到100 这三个元素之间继续排序。
[51,23,100]
[--,23,100]
[23,--,100]
[23,50,100]
最左边的完成了,那么我们来看看最右边的
[234,56223,6234, 134, 452, 234]
.... 各位看官自己实现下看看?
好了直接上代码
<?php
function quickSort(&$input, $low, $high)
{
if ($high <= $low) return;
$i = $low;
$j = $high;
$key = $input[$low];
while ($i < $j) {
while ($i < $j && $input[$j] >= $key) {
$j--;
}
if ($i < $j) {
$input[$i] = $input[$j];
$i++;
}
while ($i < $j && $input[$i] < $key) {
$i++;
}
if ($i < $j) {
$input[$j] = $input[$i];
$j--;
}
// printf("find i(%d),j(%d)\n", $i, $j);
}
$input[$i] = $key;
quickSort($input, $low, $i - 1);
quickSort($input, $i + 1, $high);
// return $input;
}
为了节省递归参数赋值,我们传了引用进去,更加节省内存空间。那么具体效果如何呢,我们来看看跟内置函数sort的对比结果。
排序个数:100000
内置排序耗时:27.215004 ms
手写排序耗时:104.742050 ms
结果抽查 1
排序个数:200000
内置排序耗时:61.784983 ms
手写排序耗时:241.871119 ms
结果抽查 1
排序个数:300000
内置排序耗时:87.085009 ms
手写排序耗时:448.505878 ms
结果抽查 1
排序个数:400000
内置排序耗时:120.175838 ms
手写排序耗时:618.201971 ms
结果抽查 1
排序个数:500000
内置排序耗时:149.783850 ms
手写排序耗时:796.765804 ms
结果抽查 1
排序个数:600000
内置排序耗时:182.029963 ms
手写排序耗时:1063.417196 ms
结果抽查 1
排序个数:700000
内置排序耗时:209.767103 ms
手写排序耗时:1333.762884 ms
结果抽查 1
排序个数:800000
内置排序耗时:241.870165 ms
手写排序耗时:1633.098841 ms
结果抽查 1
排序个数:900000
内置排序耗时:272.398949 ms
手写排序耗时:1975.782871 ms
结果抽查 1
排序个数:1000000
内置排序耗时:299.492121 ms
手写排序耗时:2303.519964 ms
结果抽查 1
可以看出虽然我们采用了传引用的方法,在大量的数据排序的时候,自己写的还是没有内置的厉害。基本上内置的算法只需要自己写的1/5的时间。
那么是否别的语言也是这样的呢?我们来看看golang吧。
排序个数: 100000
内置排序耗时: 16.072056ms
手写排序耗时: 6.95578ms
结果抽查: true
排序个数: 200000
内置排序耗时: 31.831613ms
手写排序耗时: 14.822814ms
结果抽查: true
排序个数: 300000
内置排序耗时: 46.872946ms
手写排序耗时: 24.190585ms
结果抽查: true
排序个数: 400000
内置排序耗时: 66.008501ms
手写排序耗时: 33.844555ms
结果抽查: true
排序个数: 500000
内置排序耗时: 77.824994ms
手写排序耗时: 43.05986ms
结果抽查: true
排序个数: 600000
内置排序耗时: 106.42095ms
手写排序耗时: 53.728681ms
结果抽查: true
排序个数: 700000
内置排序耗时: 117.377987ms
手写排序耗时: 65.855924ms
结果抽查: true
排序个数: 800000
内置排序耗时: 125.352682ms
手写排序耗时: 80.507389ms
结果抽查: true
排序个数: 900000
内置排序耗时: 140.563745ms
手写排序耗时: 92.550294ms
结果抽查: true
排序个数: 1000000
内置排序耗时: 162.15016ms
手写排序耗时: 100.330999ms
结果抽查: true
可以看到golang手写的居然比内置的快。100万个int排序,golang内置需要162ms,手写的居然只要100ms,php内置需要299ms,而手写居然需要2300多ms。
看来php的执行效率确实低,不过惊讶的是php内置函数排序居然只比golang慢一倍。
电脑配置
CPU:2.8 GHz 双核Intel Core i5
MEM:8 GB 1600 MHz DDR3
PHP version:7.4.2
Golang version :go1.13.7
最后贴上golang代码
package main
import (
"fmt"
"math/rand"
"sort"
"time"
)
func quickSortCustom(nums *[]int, start int, end int) {
// 起点和终点重合的时候,退出
if start >= end {
return
}
i, j := start, end
//左节点的坑挖出来,备用
mid := (*nums)[start]
for {
// 左节点位移到跟右节点重合时退出for循环
if i >= j {
break
}
// 右节点开始向左查找,直到比基准值小
for {
if j > i && (*nums)[j] >= mid {
j--
} else {
break
}
}
// 把右节点的坑,填入左节点。现在右节点j空出来了
if i < j {
(*nums)[i] = (*nums)[j]
i++
}
// 左节点开始向右寻找,直到找到比基准值大的
for {
if j > i && (*nums)[i] < mid {
i++
} else {
break
}
}
//把左节点的值填入右节点的坑,(上面的右节点j是空的没有变过)。此时左节点i空出来了
if i < j {
(*nums)[j] = (*nums)[i]
j--
}
}
// 把最开始挖出来的坑放到新坑里面
(*nums)[i] = mid
quickSortCustom(nums, start, i-1)
quickSortCustom(nums, i+1, end)
}
type IntSlice []int
func (s IntSlice) Less(i, j int) bool { return s[i] < s[j] }
func (s IntSlice) Len() int { return len(s) }
func (s IntSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func main() {
startTime := time.Now()
for i := 100000; i <= 1000000; i += 100000 {
numsIn := make(IntSlice, 0)
numsMy := make([]int, 0)
for n := 0; n < i; n++ {
//rand.Seed(int64(n))
x := rand.Intn(10000)
numsIn = append(numsIn, x)
numsMy = append(numsMy, x)
}
l := len(numsMy)
//fmt.Println("原始:", numsMy, "排序个数:", l)
fmt.Println("排序个数:", l)
// ---------------- 内置排序 开始----------------
startTime = time.Now()
sort.Ints(numsIn)
cost := time.Since(startTime)
fmt.Println("内置排序耗时:", cost)
// ---------------- 内置排序 结束----------------
// ---------------- 手写排序 开始----------------
startTime = time.Now()
quickSortCustom(&numsMy, 0, l-1)
cost = time.Since(startTime)
fmt.Println("手写排序耗时:", cost)
// ---------------- 手写排序 结束----------------
fmt.Println("结果抽查:", numsMy[l/2] == numsIn[l/2])
//fmt.Println("内置:", numsIn)
//fmt.Println("手写:", numsMy)
fmt.Println(" ")
}
//nums := []int{8081, 7887, 1847, 4059, 2081, 1318, 4425, 2540, 456, 3300}
//fmt.Println("原始的数据:", nums)
//quickSortCustom(&nums, 0, len(nums)-1)
//fmt.Println("自己排序后:", nums)
//
//nums2 := IntSlice{8081, 7887, 1847, 4059, 2081, 1318, 4425, 2540, 456, 3300}
////fmt.Println("排序前:",nums2)
//sort.Stable(nums2)
//fmt.Println("内置排序后:", nums2)
}
MySQL 分组之后如何取Top(N)?
最近碰到一个有意思的问题,因为MySQL里没有top n的用法,所以如果要实现取数据的前几操作只能通过排序之后加limit限制数量,但是这种用法又跟group 冲突。这篇文章就是来分析下分组取topN的解题思路。
现在创建一个测试表。用户的商品消费数据(测试表就不建立索引了)
CREATE TABLE `tb_user_consume` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(11) unsigned DEFAULT NULL,
`goods_id` int(11) unsigned DEFAULT NULL,
`goods_name` varchar(50) CHARACTER SET utf8mb4 DEFAULT NULL,
`price` decimal(10,2) unsigned DEFAULT NULL,
`num` int(10) unsigned DEFAULT NULL,
`total` decimal(10,2) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tb_user_consume`(`user_id`,`goods_id`,`goods_name`,`price`,`num`,`total`)
VALUES
(100,1,"1号商品",1.3,10, price*num),
(100,2,"2号商品",1.15,12, price*num),
(100,3,"3号商品",3.4,5, price*num),
(100,4,"4号商品",18.99,2, price*num),
(100,5,"5号商品",7.4,9, price*num),'
(101,1,"1号商品",1.3,13, price*num),
(101,2,"2号商品",1.15,12, price*num),
(101,3,"3号商品",3.4,20, price*num),
(101,4,"4号商品",18.99,8, price*num),
(101,5,"5号商品",7.4,7, price*num),'
(102,1,"1号商品",1.3,21, price*num),
(102,2,"2号商品",1.15,3, price*num),
(102,3,"3号商品",3.4,51, price*num),
(102,4,"4号商品",18.99,23, price*num),
(102,5,"5号商品",7.4,22, price*num),'
(103,1,"1号商品",1.3,2, price*num),
(103,2,"2号商品",1.15,7, price*num),
(103,3,"3号商品",3.4,9, price*num),
(103,4,"4号商品",18.99,22, price*num),
(103,5,"5号商品",7.4,99, price*num),'
(104,1,"1号商品",1.3,77, price*num),
(104,2,"2号商品",1.15,54, price*num),
(104,3,"3号商品",3.4,23, price*num),
(104,4,"4号商品",18.99,23, price*num),
(104,5,"5号商品",7.4,44, price*num)
;
假如现在有一个需求是,筛选出用户消费商品总价最高的前三个商品。
粗一看,这个需求也没有什么实现上的难度,就是根据用户分组,取出表里total最高的三行记录就可以了。
对没有错,需求就是这么简单,解题思路也不难,那么我们开始着手编码了。
第一步,做一个子查询,
取出表里total最高的三行记录
sql写起来也很简单,如下所示
SELECT * FROM `tb_user_consume` WHERE user_id = 100 ORDER BY total DESC LIMIT 3;
第二步,按照用户分组
取出所有用户
SELECT * FROM `tb_user_consume` ORDER BY total DESC LIMIT 3 GROUP BY user_id;
看这个好像是满足了需求,别急,我们运行一下。
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘GROUP BY user_id’ at line 1
报错了,很明显上面的sql有语法错误。limit 只能用在查询语句的最后面。
那么我们要怎么去实现这个需求呢?用单一的子查询好像都没法直接的按照用户分组来取数据。
一般到这种时候,我们很可能就直接用代码来解决了。
先取出所有用户列表。 SELECT DISTINCT(user_id) AS uid FROM
tb_user_consume;然后遍历用户列表,按照上面的查询语句查出所有的用户前三total信息 SELECT * FROM
tb_user_consumeWHERE user_id = 100 ORDER BY total DESC LIMIT 3;
这种方法不是不可取,在表里的数据不多的时候,用这个也能完成需求,抛去执行效率不说,我们就说开发效率,又是写代码,又是写sql。还要去联调,是不是很费时费力?
那么到底能不能通过sql语句直接查询出来呢?
首先我们要想,上面不能实现的痛点在哪里?没有办法先limit 3,对不对?那我们能不能通过排序筛选的方式来实现,排序后达到三个的数量我们就停止。
按照机器的思维应该是,先order by user_id, 然后 order by total desc。
SELECT * FROM
tb_user_consumeORDER BYuser_id,totalDESC;
在这个结果集里当user_id 输出3个记录行就停止。 本文的重点来了 怎么实现这个呢?
通过谷歌(其实是百度)发现mysql里有一个 case when的条件判断。正好满足我们的需求。【 在这个结果集里当user_id 输出3个记录行就停止 】 美哉!开撸。
SELECT @rnd :=
CASE
WHEN @userid = `user_id` THEN
@rnd := @rnd+1
ELSE 1
END rnd, @userid := `user_id`, user_id,total,goods_id,goods_name,price
FROM `tb_user_consume`,
(SELECT @rnd := 1,
@userid:=0) b
ORDER BY `user_id` ,`total` DESC
像这样我们就可以以rnd变量来标记我们的结果集排序结果了。这样我们把它作为一个子查询在外面加上限制条件就拿到指定的行数。
最终的sql如下:
SELECT *
FROM
(SELECT @rnd :=
CASE
WHEN @userid = `user_id` THEN
@rnd := @rnd+1
ELSE 1
END rnd, @userid := `user_id`, user_id,total,goods_id,goods_name,price
FROM `tb_user_consume`,
(SELECT @rnd := 1,
@userid:=0) b
ORDER BY `user_id` ,`total` DESC) aa
WHERE rnd <=3;
最终的查询展示结果:
+------+----------------------+---------+--------+----------+------------+-------+
| rnd | @userid := `user_id` | user_id | total | goods_id | goods_name | price |
+------+----------------------+---------+--------+----------+------------+-------+
| 1 | 100 | 100 | 66.60 | 5 | 5号商品 | 7.40 |
| 2 | 100 | 100 | 37.98 | 4 | 4号商品 | 18.99 |
| 3 | 100 | 100 | 17.00 | 3 | 3号商品 | 3.40 |
| 1 | 101 | 101 | 151.92 | 4 | 4号商品 | 18.99 |
| 2 | 101 | 101 | 68.00 | 3 | 3号商品 | 3.40 |
| 3 | 101 | 101 | 51.80 | 5 | 5号商品 | 7.40 |
| 1 | 102 | 102 | 436.77 | 4 | 4号商品 | 18.99 |
| 2 | 102 | 102 | 173.40 | 3 | 3号商品 | 3.40 |
| 3 | 102 | 102 | 162.80 | 5 | 5号商品 | 7.40 |
| 1 | 103 | 103 | 732.60 | 5 | 5号商品 | 7.40 |
| 2 | 103 | 103 | 417.78 | 4 | 4号商品 | 18.99 |
| 3 | 103 | 103 | 30.60 | 3 | 3号商品 | 3.40 |
| 1 | 104 | 104 | 436.77 | 4 | 4号商品 | 18.99 |
| 2 | 104 | 104 | 325.60 | 5 | 5号商品 | 7.40 |
| 3 | 104 | 104 | 100.10 | 1 | 1号商品 | 1.30 |
+------+----------------------+---------+--------+----------+------------+-------+
参考文章: 我的mysql如何分组取top10?
<?php
$host = '127.0.0.1';
$dbname = 'yang';
$port = 3306;
$db = new PDO("mysql:host=$host;dbname=$dbname;port=$port", 'root', '12345');
$goods_info = [
['id' => 1, 'name' => '1号商品', 'price' => 1.30],
['id' => 2, 'name' => '2号商品', 'price' => 1.15],
['id' => 3, 'name' => '3号商品', 'price' => 3.40],
['id' => 4, 'name' => '4号商品', 'price' => 18.99],
['id' => 5, 'name' => '5号商品', 'price' => 7.40],
];
function insert($goods_info, PDO &$db, $start)
{
$sql = 'insert into tb_user_consume(user_id,goods_id,goods_name,price,num,total) values';
for ($i = $start; $i < $start + 50000; $i++) {
foreach ($goods_info as $info) {
$num = mt_rand(0, 1000);
$total = $num * $info['price'];
$sql .= sprintf("(%d,%d,\"%s\",%f,%d,%f),", $i, $info['id'], $info['name'], $info['price'], $num, $total);
}
}
$sql = substr($sql, 0, -1);
//echo $sql;
$db->prepare($sql)->execute();
}
// 批量添加测试数据
//for ($j = 1000000; $j < 2000000; $j += 50000) {
// insert($goods_info,$db,$j);
//}
// 执行时间
$start = time();
select($db);
echo "cost:".(time()-$start)."\n";
function select(PDO &$db){
for ($i = 1000000; $i < 2000000; $i++) {
$sql = 'SELECT * FROM `tb_user_consume` WHERE user_id = '.$i.' ORDER BY total DESC LIMIT 3; ';
$ret = $db->query($sql)->fetchAll();
//var_dump($ret);
}
}
