分页查询不知你是否真正的懂和PHP的正则的应用和一些性能优化

一、不废话太多  直接进入例子。

1  问题:

有一张收藏表,里面存储的是用户和图书ID。数据量为1亿。现在要求分页获取所有用户ID(不重复),写下你的sql语句。

  表结构大致如下:

  

          CREATE TABLE 收藏表(

             `id` bigint(20) unsigned NOT NULL auto_increment COMMENT ‘primary key‘,

             `uid` bigint(20) unsigned NOT NULL default 0 COMMENT ‘uid‘,<br>    `status` tinyint(3) unsigned NOT NULL default 0 COMMENT ‘status‘,

             `book_id` bigint(20) unsigned NOT NULL default 0 COMMENT ‘book Id‘,

             `create_time` int(11) unsigned not null default 0 COMMENT ‘create time‘,

             PRIMARY KEY (`id`),

             UNIQUE KEY `uid_book_id` (`uid`, `book_id`),<br>    KEY `uid_status` (`uid`, `status`)

          )ENGINED=Innodb Auto_increment=1 default charset=gbk COMMENT ‘用户收藏信息‘;

2   有三种给出的分页查询的方法:

最容易想到的第一种分页语句是(这也是我们最容易想到的语句):


1

2

select distinct uid from 收藏表 order by uid desc limit 0, 10;

select distinct uid from 收藏表 order by uid desc limit 11, 10;

  再高级点语句,第二种($last_min_uid表示上一次读到的最后一个uid):


1

2

select distinct uid from 收藏表 order by uid desc limit 10;

select distinct uid from 收藏表 where uid < $last_min_uid order by uid desc limit 10;

  最高级的方式

                                          1  select uid from 收藏表 group by uid order by uid desc limit 10;

                                          2  select uid from 收藏表 group by uid having uid < $last_min_uid order by uid desc limit 10;

3   我们来分析一下:

以上三种方式都可以实现分页获取到用户ID列表,那么区别是什么?我现在就把每一种跟大家分析下。

  第一种在业务场景中,会出现丢数据的情况。——这是比较严重的情况,不予采纳。

  具体的业务场景是这样的:当你读取第5页的时候,前四页的用户id列表中,假如有一页的用户ID从库中删除掉,那么你这时读到的第5页(limit 51, 10),就是原来的第6页,你会把1页的用户ID丢失掉。

  第二种的第二条语句,通过explain分析,实际并没有命中唯一索引,而只是命中了一般索引,数据查询范围在7百万级别,故explain建议我们使用group by。——这个查询会有严重的性能问题。

+----+--------------+---------------+-------+-------------------------------------------------------------+-------------+----------+-------+------------+------------------------------------------------------------------------+
| id   | select_type | table           | type  | possible_keys                                                      | key           | key_len | ref     | rows        | Extra                                                                               |
+----+--------------+---------------+-------+-------------------------------------------------------------+---------------------+---------+------+---------+---------------------------------------------------------------------+
| 1    | SIMPLE      | ubook_room | range | uid_book_id                                                       | uid_status  | 4          | NULL | 7066423  | Using where; Using index for group-by; Using temporary; Using filesort | 
+----+--------------+---------------+-------+-------------------------------------------------------------+-------------+----------+-------+------------+------------------------------------------------------------------------+

  

  第三种explain分析,数据查询范围在12万级别(跟第二种相差一个数量级),查询性能高。

+----+---------------+------------+-------+-----------------+-----------------+---------+----------+----------+-------------+
| id   | select_type  | table       | type  | possible_keys  | key               | key_len | ref       | rows      | Extra         |
+----+---------------+------------+-------+-----------------+-----------------+---------+----------+----------+-------------+
| 1    | SIMPLE       | 收藏表      | index | NULL             | uid_book_id   | 12         | NULL  | 121719   | Using index | 
+----+---------------+------------+-------+-----------------+-----------------+---------+----------+----------+-------------+

二、 PHP的正则:

正则表达式相信大家在很多种开发语言中都见到过,像javascript,java,php中。

需要知道的术语——下面的术语你知道多少?

Δ  定界符

Δ  字符域

Δ  修饰符

Δ  限定符

Δ  脱字符

Δ  通配符(正向预查,反向预查)

Δ  反向引用

Δ  惰性匹配

Δ  注释

Δ  零字符宽

定位

我们什么时候使用正则表达式呢?不是所有的字符操作都用正则就好了,php在某些方面用正则反而影响效率。当我们遇到复杂文本数据的解析时候,用正则是比较好的选择。

优点

正则表达式在处理复杂字符操作的时候,可以提高工作效率,也在一定程度节省你的代码量。

缺点

我们在使用正则表达式的时候,复杂的正则表达式会加大代码的复杂度,让人很难理解。所以我们有的时候需要在正则表达式内部添加注释。

通用模式

¤ 定界符,通常使用 "/"做为定界符开始和结束,也可以使用"#"。

  什么时候使用"#"呢?一般是在你的字符串中有很多"/"字符的时候,因为正则的时候这种字符需要转义,比如uri。

使用"/"定界符的代码如下.

$regex = ‘/^http:\/\/([\w.]+)\/([\w]+)\/([\w]+)\.html$/i‘;

$str = http://www.youku.com/show_page/id_ABCDEFG.html;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

preg_match中的$matches[0]将包含与整个模式匹配的字符串。

使用"#"定界符的代码如下.这个时候对"/"就不转义!

$regex = ‘#^http://([\w.]+)/([\w]+)/([\w]+)\.html$#i‘;

$str = http://www.youku.com/show_page/id_ABCDEFG.html;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

¤ 修饰符:用于改变正则表达式的行为。

我们看到的(‘/^http:\/\/([\w.]+)\/([\w]+)\/([\w]+)\.html/i‘)中的最后一个"i"就是修饰符,表示忽略大小写,还有一个我们经常用到的是"x"表示忽略空格。

贡献代码:

$regex = ‘/HELLO/‘;

$str = ‘hello word‘;

$matches = array();

if(preg_match($regex, $str, $matches)){

    echo ‘No i:Valid Successful!‘,"\n";

}

if(preg_match($regex.‘i‘, $str, $matches)){

    echo ‘YES i:Valid Successful!‘,"\n";

}

¤ 字符域:[\w]用方括号扩起来的部分就是字符域。

¤ 限定符:如[\w]{3,5}或者[\w]*或者[\w]+这些[\w]后面的符号都表示限定符。现介绍具体意义。

{3,5}表示3到5个字符。{3,}超过3个字符,{,5}最多5个,{3}三个字符。

* 表示0到多个

+ 表示1到多个。

¤ 脱字符号

^:

> 放在字符域(如:[^\w])中表示否定(不包括的意思)——“反向选择”

>  放在表达式之前,表示以当前这个字符开始。(/^n/i,表示以n开头)。

注意,我们经常管"\"叫"跳脱字符"。用于转义一些特殊符号,如".","/"

通配符(lookarounds):断言某些字符串中某些字符的存在与否!

lookarounds分两种:lookaheads(正向预查 ?=)和lookbehinds(反向预查?<=)。

> 格式:

正向预查:(?=) 相对应的 (?!)表示否定意思

反向预查:(?<=) 相对应的 (?<!)表示否定意思

前后紧跟字符

$regex = ‘/(?<=c)d(?=e)/‘/* d 前面紧跟c, d 后面紧跟e*/

$str = ‘abcdefgk‘;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

否定意义:

$regex = ‘/(?<!c)d(?!e)/‘/* d 前面不紧跟c, d 后面不紧跟e*/

$str = ‘abcdefgk‘;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

>字符宽度:零

验证零字符代码

$regex = ‘/HE(?=L)LO/i‘;

$str = ‘HELLO‘;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

打印不出结果!

$regex = ‘/HE(?=L)LLO/i‘;

$str = ‘HELLO‘;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

能打印出结果!

说明:(?=L)意思是HE后面紧跟一个L字符。但是(?=L)本身不占字符,要与(L)区分,(L)本身占一个字符。

捕获数据

没有指明类型而进行的分组,将会被获取,供以后使用。

> 指明类型指的是通配符。所以只有圆括号起始位置没有问号的才能被捕捉。

> 在同一个表达式内的引用叫做反向引用。

> 调用格式: \编号(如\1)。

$regex = ‘/^(Chuanshanjia)[\w\s!]+\1$/‘;   

$str = ‘Chuanshanjia thank Chuanshanjia‘;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

> 避免捕获数据

格式:(?:pattern)

优点:将使有效反向引用数量保持在最小,代码更加、清楚。

>命名捕获组

格式:(?P<组名>) 调用方式 (?P=组名)

$regex = ‘/(?P<author>chuanshanjia)[\s]Is[\s](?P=author)/i‘;

$str = ‘author:chuanshanjia Is chuanshanjia‘;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

运行结果

惰性匹配(记住:会进行两部操作,请看下面的原理部分)

  格式:限定符?

原理:"?":如果前面有限定符,会使用最小的数据。如“*”会取0个,而“+”会取1个,如过是{3,5}会取3个。

先看下面的两个代码:

代码1.

<?php

$regex = ‘/heL*/i‘;

$str = ‘heLLLLLLLLLLLLLLLL‘;

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

结果1.

代码2

<?php

$regex = ‘/heL*?/i‘;

$str = ‘heLLLLLLLLLLLLLLLL‘;

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

结果2

代码3,使用“+”

<?php

$regex = ‘/heL+?/i‘;

$str = ‘heLLLLLLLLLLLLLLLL‘;

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

结果3

代码4,使用{3,5}

<?php

$regex = ‘/heL{3,10}?/i‘;

$str = ‘heLLLLLLLLLLLLLLLL‘;

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

结果4

正则表达式的注释

格式:(?# 注释内容)

用途:主要用于复杂的注释

贡献代码:是一个用于连接MYSQL数据库的正则表达式

$regex = ‘/

    ^host=(?<!\.)([\d.]+)(?!\.)                 (?#主机地址)

\|

    ([\[email protected]#$%^&*()_+\-]+)                       (?#用户名)

\|

    ([\[email protected]#$%^&*()_+\-]+)                       (?#密码)

(?!\|)$/ix‘;

$str = ‘host=192.168.10.221|root|123456‘;

$matches = array();

if(preg_match($regex, $str, $matches)){

    var_dump($matches);

}

echo "\n";

特殊字符

特殊字符 解释
* 0到多次
+ 1到多次还可以写成{1,}
? 0或1次
. 匹配除换行符外的所有单个的字符
\w [a-zA-Z0-9_]
\s 空白字符(空格,换行符,回车符)[\t\n\r]
\d [0-9]

三、PHP一些性能优化问题

使用缓存

利用缓存模块(如Memcache)或者模板系统(如Smarty)进行缓存处理。我们可以缓存数据库结果和提取页面结果的方式来提升网站性能。

使用输出缓冲区

当你的脚本尝试着渲染的时候,php会使用内存缓存区保存所有的数据。缓存区可能让你的页面看起来很慢,原因是缓冲区填满所有要响应的数据之后再把结果响应给用户。幸运的是,你能够做一下改变,迫使php强行在缓冲区填满之前把数据响应给用户,这样就会让你的网站看起来更快一些。

避免写幼稚的setters和getters

当你写php类的时候,你可以直接操作对象属性,这样能帮助你节省时间和提升你的脚本性能。而不是那种让人感到幼稚可笑的setters和getters。

下面是一些案例:dog类通过使用setName()和getName()方式来操作name属性。


1

2

3

4

5

6

7

8

9

10

11

class dog {

  public $name ‘‘;

  public function setName($name) {

    $this->name = $name;

  }

  public function getName() {

    return $this->name;

  }

}

  

注意:setName()和getName()除了存储和返回name属性外,没做任何工作。


1

2

3

$rover new dog();

$rover->setName(‘rover‘);

echo $rover->getName();

  

直接设置和访问name属性,性能能提升100%,而且也能缩减开发时间!


1

2

3

$rover new dog();

$rover->name = ‘rover‘;

echo $rover->name;

  

没有原因不要copy变量

有时初级phper,为了使代码更加“干净”,常常把已经定义的变量重新赋值给另一个变量。这实际上就导致了双重内存的消耗(当改变变量的时候),这就导致脚本的性能下降。比如一个用户把一个512KB的变量在额外插入给另一个变量,那么就会导致1MB的内存被消耗掉。


1

2

$description strip_tags($_POST[‘description‘]);

echo $description;

  

上面的代码没有任何原因,复制了一遍变量。你仅需要使用内联的方式简单输出变量,而不用额外的消耗内存。


1

echo strip_tags($_POST[‘description‘]);

  

避免循环做SQL操作

经常犯的错误是把一个SQL 操作放置到一个循环中,这就导致频繁的访问数据库,更重要的是,这会直接导致脚本的性能低下。以下的例子,你能够把一个循环操作重置为一个单一的SQL语句。


1

2

3

4

foreach ($userList as $user) {

  $query ‘INSERT INTO users (first_name,last_name) VALUES("‘ $user[‘first_name‘] . ‘", "‘ $user[‘last_name‘] . ‘")‘;

  mysql_query($query);

}

  过程:


1

INSERT INTO users (first_name,last_name) VALUES("John""Doe")

  

替换这种循环方案,你能够拼接数据成为一个单一的数据库操作。


1

2

3

4

5

6

$userData array();

foreach ($userList as $user) {

    $userData[] = ‘("‘ $user[‘first_name‘] . ‘", "‘ $user[‘last_name‘] . ‘")‘;

 }

$query ‘INSERT INTO users (first_name,last_name) VALUES‘ . implode(‘,‘$userData);

mysql_query($query);

  过程:


1

INSERT INTO users (first_name,last_name) VALUES("John""Doe"),("Jane""Doe")...

  

其他资源

谢谢大家!希望这些能对大家有帮助

时间: 11-15

分页查询不知你是否真正的懂和PHP的正则的应用和一些性能优化的相关文章

EntityFramework之原始查询及性能优化(六)

前言 在EF中我们可以通过Linq来操作实体类,但是有些时候我们必须通过原始sql语句或者存储过程来进行查询数据库,所以我们可以通过EF Code First来实现,但是SQL语句和存储过程无法进行映射,于是我们只能手动通过上下文中的SqlQuery和ExecuteSqlCommand来完成. SqlQuery sql语句查询实体 通过DbSet中的SqlQuery方法来写原始sql语句返回实体实例,如果是通过Linq查询返回的那么返回的对象将被上下文(context)所跟踪. 首先给出要操作的

Mybatis+SpringMVC实现分页查询(附源码)

Maven+Mybatis+Spring+SpringMVC实现分页查询(附源码) 一.项目搭建 关于项目搭建,小宝鸽以前写过一篇Spirng+SpringMVC+Maven+Mybatis+MySQL项目搭建,这篇文章提供了详细的搭建过程,而且提供了源码下载,接下来的将在这个源码的基础上继续开发.所以建议各位猿友可以把猿友下载一下. 二.分页插件的介绍 博主采用的插件是PageHelper这个插件,使用起来十分方便.该插件支持以下数据库: Oracle Mysql MariaDB SQLite

Oracle分页查询

一.利用rownum,无order by(最优方案) 如下例查询出来5003行数据,然后扔掉了前面5000行,返回后面的300行.经过测试,此方法成本最低,只嵌套一层,速度最快!即使查询的数据量再大,也几乎不受影响,速度依然. SELECT * FROM (SELECT ROWNUM AS rowno, t.* FROM XXX t WHERE hire_date BETWEEN TO_DATE ('20060501', 'yyyymmdd') AND TO_DATE ('20060731',

QBC查询、离线条件查询(DetachedCriteric)和分页查询模版

一.QBC检索步骤 QBC检索步骤: 1.调用Session的createCriteria()方法创建一个Criteria对象. 2.设定查询条件.Expression类提供了一系列用于设定查询条件的静态方法, 这些静态方法都返回Criterion实例,每个Criterion实例代表一个查询条件. Criteria的add()方法用于加入查询条件. 3.调用Criteria的list()方法执行查询语句.该方法返回List类型的查询结果,在 List集合中存放了符合查询条件的持久化对象. 比较运

Linq高级查询与分页查询

Linq高级查询 以~开头: r=>r.Name.StartsWith("李"); 以~结尾: r=>r.Name.EndsWith("光"); 包含(模糊查询): r=>r.Name.Contains("四"); 数据总个数: Con.Goods.Count();||Con.Users.ToList().count; 最大值: Con.Goods.ToList().Max(r=>r.Price); 最小值: Con.Go

bos 第4 (区域excel批量导入、区域通用分页查询、分区的添加、分区多条件分页查询、分区导出excel)

BOS项目笔记 第4天 今天内容安排: 1.区域批量导入功能 jQuery OCUpload(一键上传插件).apache POI.pinyin4j 2.实现区域的分页查询 3.对分页代码重构 4.添加分区(combobox下拉框) 5.分区的组合条件分页查询 6.分区数据导出功能 1. 区域数据批量导入功能 1.1 一键上传插件使用 ajax不能做文件上传. 第一步:在jsp页面中引入插件的js文件 <script type="text/javascript" src=&quo

mybatis中分页查询

1 如果在查询方法中有多个参数,可以使用map对象将所有数据都存储进去.比如分页查询,需要用到两个参数,可以将这两个参数包装到map中. 例子:分页查询 dao层方法 public List<Student> getStudentPage(int pstart, int pnumber) throws Exception{ SqlSession sqlSession = MybatisUtil.getSqlSession(); Map<String,Integer> map = n

HBase多条件及分页查询的一些方法

HBase是Apache Hadoop生态系统中的重要一员,它的海量数据存储能力,超高的数据读写性能,以及优秀的可扩展性使之成为最受欢迎的NoSQL数据库之一.它超强的插入和读取性能与它的数据组织方式有着密切的关系,在逻辑上,HBase的表数据按RowKey进行字典排序, RowKey实际上是数据表的一级索引(Primary Index),由于HBase本身没有二级索引(Secondary Index)机制,基于索引检索数据只能单纯地依靠RowKey.也只有使用RowKey查询数据才能得到非常高

.net淘宝客基础api 分页查询

1 using System; 2 using System.Collections.Generic; 3 using System.Configuration; 4 using System.Linq; 5 using System.Web; 6 using Top.Api; 7 using Top.Api.Request; 8 using Top.Api.Response; 9 10 namespace MvcWebApp.DataBase 11 { 12 public class TaoD