PHP操作Redis常用技巧总结

一、Redis连接与认证

1
2
3
4
//连接参数:ip、端口、连接超时时间,连接成功返回true,否则返回false
$ret = $redis->connect('127.0.0.1', 6379, 30);
//密码认证:成功返回true,否则返回false
$ret = $redis->auth('123456');

二、String操作

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
//设置键值:成功返回true,否则返回false
$redis->set('mystr', 'Welcome!');
//获取键值:成功返回String类型键值,若key不存在或不是String类型则返回false
$ret = $redis->get('mystr');
//从某个key所存储的字符串的指定偏移量开始,替换为另一指定字符串,成功返回替换后新字符串的长度。
$ret = $redis->setRange('mystr', 7, ' to Beijing!');
//获取存储在指定key中字符串的子字符串。
$ret = $redis->getRange('mystr', 0, 6);
//设置新值,返回旧值:若key不存在则设置值,返回false
$ret = $redis->getSet('mystr', 'hi man');
//一次设置多个键值对:成功返回true。
$ret = $redis->mset(['name' => 'jet', 'age' => 18]);
//一次获取多个key的值:返回一个键值对数组,其中不存在的key值为false。
$ret = $redis->mget(['name', 'age']);
//设置指定key的值及其过期时间,单位:秒。
//参数:键名,过期时间,键值。成功返回true。
$ret = $redis->setex('name', 10, 'jetwu');
//以毫秒为单位设置指定key的值和过期时间。成功返回true。
$ret = $redis->psetex('name', 10, 'jetwu');
//key的值不存在时,才为其设置值。key不存在且设置成功返回true,否则返回false。
$ret = $redis->setnx('name', 'boby');
//setnx命令的批量操作。只有在给定所有key都不存在的时候才能设置成功,只要其中一个key存在,所有key都无法设置成功。
$ret = $redis->msetnx(['country' => 'China', 'city' => 'Shenzhen']);
//获取指定key存储的字符串的长度,key不存在返回0,不为字符串返回false。
$ret = $redis->strlen('name');
//将指定key存储的数字值增加1。若key不存在会先初始化为0再增加1,若key存储的不是整数值则返回false。成功返回key新值。
$ret = $redis->incr('age');
//给指定key存储的数字值增加指定增量值。
$ret = $redis->incrBy('age', 10);
//给指定key存储的数字值增加指定浮点数增量。
$ret = $redis->incrByFloat('age', 1.5);
//将指定key存储的数字值减一。
$ret = $redis->decr('age');
//将指定key存储的数字值减去指定减量值。
$ret = $redis->decrBy('age', 10);
//为指定key追加值到原值末尾,若key不存在则相对于set()函数。
$ret = $redis->append('mystr', 'haha');

三、Hash操作

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
//为hash表中的字段赋值。成功返回1,失败返回0。若hash表不存在会先创建表再赋值,若字段已存在会覆盖旧值。
$ret = $redis->hSet('user', 'realname', 'jetwu');
//获取hash表中指定字段的值。若hash表不存在则返回false。
$ret = $redis->hGet('user', 'realname');
//查看hash表的某个字段是否存在,存在返回true,否则返回false。
$ret = $redis->hExists('user', 'realname');
//删除hash表的一个字段,不支持删除多个字段。成功返回1,否则返回0。
$ret = $redis->hDel('user', 'realname');
//同时设置某个hash表的多个字段值。成功返回true。
$ret = $redis->hMset('user', ['name' => 'jet', 'age' => 18]);
//同时获取某个hash表的多个字段值。其中不存在的字段值为false。
$ret = $redis->hMget('user', ['name', 'age']);
//获取某个hash表所有的字段和值。
$ret = $redis->hGetAll('user');
//获取某个hash表所有字段名。hash表不存在时返回空数组,key不为hash表时返回false。
$ret = $redis->hKeys('user');
//获取某个hash表所有字段值。
$ret = $redis->hVals('user');
//为hash表中不存在的字段赋值。若hash表不存在则先创建,若字段已存在则不做任何操作。设置成功返回true,否则返回false。
$ret = $redis->hSetNx('user', 'realname', 'jetwu');
//获取某个hash表的字段数量。若hash表不存在返回0,若key不为hash表则返回false。
$ret = $redis->hLen('user');
//为hash表中的指定字段加上指定增量值,若增量值为负数则相当于减法操作。若hash表不存在则先创建,若字段不存在则先初始化值为0再进行操作,若字段值为字符串则返回false。设置成功返回字段新值。
$ret = $redis->hIncrBy('user', 'age', 10);
//为hash表中的指定字段加上指定浮点数增量值。
$ret = $redis->hIncrBy('user', 'age', 1.5);

四、List操作

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
//从list头部插入一个值。
$ret = $redis->lPush('city', 'guangzhou');
//从list尾部插入一个值。
$ret = $redis->rPush('city', 'guangzhou');
//获取列表指定区间中的元素。0表示列表第一个元素,-1表示最后一个元素,-2表示倒数第二个元素。
$ret = $redis->lrange('city', 0, -1);//查看队列所有元素
//将一个插入已存在的列表头部,列表不存在时操作无效。
$ret = $redis->lPushx('city', 'hangzhou');
//将一个或多个值插入已存在的列表尾部,列表不存在时操作无效。
$ret = $redis->rPushx('city', 'hangzhou');
//移除并返回列表的第一个元素,若key不存在或不是列表则返回false。
$ret = $redis->lPop('city');
//移除并返回列表的最后一个元素,若key不存在或不是列表则返回false。
$ret = $redis->rPop('city');
//移除并获取列表的第一个元素。如果列表没有元素则会阻塞列表直到等待超时或发现可弹出元素为止。
//参数:key,超时时间(单位:秒)
//返回值:[0=>key,1=>value],超时返回[]
$ret = $redis->blPop('city', 10);
//移除并获取列表的最后一个元素。如果列表没有元素则会阻塞列表直到等待超时或发现可弹出元素为止。
//参数:key,超时时间(单位:秒)
//返回值:[0=>key,1=>value],超时返回[]
$ret = $redis->brPop('city', 10);
//移除列表中最后一个元素,将其插入另一个列表头部,并返回这个元素。若源列表没有元素则返回false。
$ret = $redis->rpoplpush('city', 'city2');
//移除列表中最后一个元素,将其插入另一个列表头部,并返回这个元素。如果列表没有元素则会阻塞列表直到等待超时或发现可弹出元素为止。
//参数:源列表,目标列表,超时时间(单位:秒)
//超时返回false
$ret = $redis->brpoplpush('city', 'city2', 10);
//返回列表长度。
$ret = $redis->lLen('city');
//通过索引获取列表中的元素。若索引超出列表范围则返回false。
$ret = $redis->lindex('city', 0);
//通过索引设置列表中元素的值。若是索引超出范围,或对一个空列表进行lset操作,则返回false。
$ret = $redis->lSet('city', 2, 'changsha');
//在列表中指定元素前或后面插入元素。若指定元素不在列表中,或列表不存在时,不执行任何操作。
//参数:列表key,Redis::AFTER或Redis::BEFORE,基准元素,插入元素
//返回值:插入成功返回插入后列表元素个数,若基准元素不存在返回-1,若key不存在返回0,若key不是列表返回false。
$ret = $redis->lInsert('city', Redis::AFTER, 'changsha', 'nanjing');
//根据第三个参数count的值,移除列表中与参数value相等的元素。
//count > 0 : 从表头开始向表尾搜索,移除与value相等的元素,数量为count。
//count < 0 : 从表尾开始向表头搜索,移除与value相等的元素,数量为count的绝对值。
//count = 0 : 移除表中所有与value相等的值。
//返回实际删除元素个数
$ret = $redis->lrem('city', 'guangzhou', -2);
//对一个列表进行修剪,只保留指定区间的元素,其他元素都删除。成功返回true。
$ret = $redis->ltrim('city', 1, 4);

五、Set操作

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
//将一个元素加入集合,已经存在集合中的元素则忽略。若集合不存在则先创建,若key不是集合类型则返回false,若元素已存在返回0,插入成功返回1。
$ret = $redis->sAdd('myset', 'hello');
//返回集合中所有成员。
$ret = $redis->sMembers('myset');
//判断指定元素是否是指定集合的成员,是返回true,否则返回false。
$ret = $redis->sismember('myset', 'hello');
//返回集合中元素的数量。
$ret = $redis->scard('myset');
//移除并返回集合中的一个随机元素。
$ret = $redis->sPop('myset');
//返回集合中的一个或多个随机成员元素,返回元素的数量和情况由函数的第二个参数count决定:
//如果count为正数,且小于集合基数,那么命令返回一个包含count个元素的数组,数组中的元素各不相同。
//如果count大于等于集合基数,那么返回整个集合。
//如果count为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为count的绝对值。
$ret = $redis->sRandMember('myset', 2);
//移除集合中指定的一个元素,忽略不存在的元素。删除成功返回1,否则返回0。
$ret = $redis->srem('myset', 'hello');
//迭代集合中的元素。
//参数:key,迭代器变量,匹配模式,每次返回元素数量(默认为10个)
$ret = $redis->sscan('myset', $it, 'a*', 5);
//将指定成员从一个源集合移动到一个目的集合。若源集合不存在或不包含指定元素则不做任何操作,返回false。
//参数:源集合,目标集合,移动元素
$ret = $redis->sMove('myset', 'myset2', 'aaa');
//返回所有给定集合之间的差集,不存在的集合视为空集。
$ret = $redis->sDiff('myset', 'myset2', 'myset3');
//将所有给定集合之间的差集存储在指定的目的集合中。若目的集合已存在则覆盖它。返回差集元素个数。
//参数:第一个参数为目标集合,存储差集。
$ret = $redis->sDiffStore('myset3', 'myset', 'myset2');
//返回所有给定集合的交集,不存在的集合视为空集。
$ret = $redis->sInter('myset', 'myset2', 'myset3');
//将所有给定集合的交集存储在指定的目的集合中。若目的集合已存在则覆盖它。返回交集元素个数。
//参数:第一个参数为目标集合,存储交集。
$ret = $redis->sInterStore('myset4', 'myset', 'myset2', 'myset3');
//返回所有给定集合的并集,不存在的集合视为空集。
$ret = $redis->sUnion('myset', 'myset2', 'myset3');
//将所有给定集合的并集存储在指定的目的集合中。若目的集合已存在则覆盖它。返回并集元素个数。
//参数:第一个参数为目标集合,存储并集。
$ret = $redis->sUnionStore('myset4', 'myset', 'myset2', 'myset3');

六、Zset操作

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
//将一个或多个成员元素及其分数值加入到有序集当中。如果某个成员已经是有序集的成员,则更新这个成员的分数值,并通过重新插入这个成员元素,来保证该成员在正确的位置上。分数值可以是整数值或双精度浮点数。
$ret = $redis->zAdd('scores', 98, 'English', 90, 'physics');
//返回有序集中指定区间内的成员。成员按分数值递增排序,分数值相同的则按字典序来排序。
//参数:第四个参数表示是否返回各个元素的分数值,默认为false。
$ret = $redis->zRange('scores', 0, -1, true);//查看Zset所有成员以及它们各自的分数值
//返回有序集中指定区间内的成员。成员按分数值递减排序,分数值相同的则按字典序的逆序来排序。
$ret = $redis->zReverseRange('scores', 0, -1, true);
//返回有序集中指定分数区间的成员列表,按分数值递增排序,分数值相同的则按字典序来排序。默认使用闭区间。
$ret = $redis->zRangeByScore('scores', 90, 100, ['withscores'=>true]);
//返回有序集中指定分数区间的成员列表,按分数值递减排序,分数值相同的则按字典序的逆序来排序。注意,区间表示的时候大值在前,小值在后,默认使用闭区间。
$ret = $redis->zRevRangeByScore('scores', 100, 90, ['withscores'=>true]);
//迭代有序集合中的元素。
//返回值:[元素名=>分数值,,..]
$ret = $redis->zscan('scores', $it, '', 10);
//返回指定有序集的元素数量。
$ret = $redis->zCard('scores');
//返回有序集中指定分数区间的成员数量。
$ret = $redis->zCount('scores', 90, 100);
//返回有序集中指定成员的分数值。若成员不存在则返回false。
$ret = $redis->zScore('scores', 'math');
//返回有序集中指定成员的排名,按分数值递增排序。分数值最小者排名为0。
$ret = $redis->zRank('scores', 'chemistry');
//返回有序集中指定成员的排名,按分数值递减排序。分数值最大者排名为0。
$ret = $redis->zRevRank('scores', 'chemistry');
//移除有序集中的一个或多个成员,忽略不存在的成员。返回删除的元素个数。
$ret = $redis->zRem('scores', 'chemistry', 'English');
//移除有序集中指定排名区间的所有成员。
$ret = $redis->zRemRangeByRank('scores', 0, 2);
//移除有序集中指定分数值区间的所有成员。
$ret = $redis->zRemRangeByScore('scores', 80, 90);
//对有序集中指定成员的分数值增加指定增量值。若为负数则做减法,若有序集不存在则先创建,若有序集中没有对应成员则先添加,最后再操作。
$ret = $redis->zIncrBy('scores', 2, 'Chinese');
//计算给定一个或多个有序集的交集,并将其存储到一个目的有序集中。结果集中某个成员的分数值是所有给定集下该成员分数值之和。
$ret = $redis->zinterstore('zset3', 'zset2', 'zset1');
//计算给定一个或多个有序集的并集,并将其存储到一个目的有序集中。结果集中某个成员的分数值是所有给定集下该成员分数值之和。
$ret = $redis->zunionstore('zset3', 'zset2', 'zset1');

更多关于PHP相关内容感兴趣的读者可查看本站专题:《php+redis数据库程序设计技巧总结》、《php面向对象程序设计入门教程》、《PHP基本语法入门教程》、《PHP数组(Array)操作技巧大全》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总

希望本文所述对大家PHP程序设计有所帮助。

GDI+库以及使用方法

Visual C++6.0使用GDI+的一般方法
  
1.  载解压GDI+开发包;
2.  正确设置include & lib 目录;
3.  stdafx.h 添加:
#ifndef ULONG_PTR
#define ULONG_PTR unsigned long*
#endif
#include <gdiplus.h>
4. 程序中添加GDI+的包含文件gdiplus.h以及附加的类库gdiplus.lib。
通常gdiplus.h包含文件添加在应用程序的stdafx.h文件中,而gdiplus.lib可用两种进行添加:
第一种是直接在stdafx.h文件中添加下列语句:
#pragma comment( lib, "gdiplus.lib" )
    另一种方法是: 
在VC.net中添加库文件在:项目菜单->属性->链接器->输入
 
举个例子:
 
(1)在应用程序项目的应用类中,添加一个成员变量,如下列代码: 
ULONG_PTR m_gdiplusToken;
其中,ULONG_PTR是一个DWORD数据类型,该成员变量用来保存GDI+被初始化后在应用程序中的GDI+标识,
                            以便能在应用程序退出后,引用该标识来调用Gdiplus:: GdiplusShutdown来关闭GDI+。
(2)在应用类中添加ExitInstance的重载,并添加下列代码用来关闭GDI+: 
int CGDITestApp::ExitInstance()
{
Gdiplus::GdiplusShutdown(m_gdiplusToken);
return CWinApp::ExitInstance();
}
(3)在应用类的InitInstance函数中添加GDI+的初始化代码:
注意:下面这些GDI+的初始化代码必须放在m_pMainWnd->UpdateWindow();之前。 
CWinApp::InitInstance();
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
(4)在需要绘图的窗口或视图类中添加GDI+的绘制代码。
下面分别就单文档和基于对话框应用程序为例,说明使用GDI+的一般过程和方法。
1. 在单文档应用程序中使用GDI+
在上面的过程中,我们就是以一个单文档应用程序Ex_GDIPlus作为示例的。下面列出第4步所涉及的代码: 
void CGDITestView::OnDraw(CDC* pDC)
{
         CGDITestDoc* pDoc = GetDocument();
         ASSERT_VALID(pDoc);
         // TODO: add draw code for native data here
    usingnamespace Gdiplus;
         Graphics graphics(pDC->m_hDC);
         Pen newPen(Color(255,0,0),3);
         HatchBrush newBrush(HatchStyleCross,Color(255,0,255,0),Color(255,0,0,255));//创建一个填充画刷,前景色为绿色,背景色为蓝色
         graphics.DrawRectangle(&newPen,50,50,100,60);// 在(50,50)处绘制一个长为100,高为60的矩形
         graphics.FillRectangle(&newBrush,50,50,100,60); // 在(50,50)处填充一个长为100,高为60的矩形区域
}
编译并运行,结果如图:

mysql分库分表实战及php代码操作完整实例

原文地址: http://blog.csdn.net/nuli888/article/details/52143065

当单表达到几千万时,查询一次要很久,如果有联合查询,有可能会死在那
分库分表主要就是解决这个问题,减小数据库的负担,缩短查询时间

分库:
1)按功能分
用户类库、商品类库、订单类库、日志类、统计类库…
1)按地区分
每个城市或省市一个同样的库,加上后缀或前缀如:db_click_bj、db_click_sh…

分表:
1、横向分表 解决表记录太大问题
1)按某个字段分,
如:discuz的附件表分成10个附件分表pre_forum_attachment_0到pre_forum_attachment_9,还有1个附件索引表pre_forum_attachment存储tid和附件id关系
根据主题的tid最后一位来决定附件要保存在哪个分表,
2)按日期分表
一些日志、统计类的可以按年、月、日、周分表
如:点击量统计click_201601、click_201602
3)使用mysql的merge
先把分表创建好,然后创建总表指定engine= MERGE UNION=(table1,table2) INSERT_METHOD = LAST;
2、纵向分表 解决列过多问题
1)经常组合查询的列放在一个表,常用字段的表可考虑Memory引擎
2)不经常使用的字段单独成表
3)把text、blob等大字段拆分放在附表
如:phpcms的文章表分成主表v9_news和从表v9_news_data,主表存标题、关键字、浏览量等,从表存具体内容、模板等

很多主流mvc框架都提供了切换数据库访问方法

thinkphp切换数据库

$this->db(1,”mysql://root:123456@localhost:3306/test”)->query(“查询sql”);//数据库连接信息可放在配置文件
$this->db(1)->query(“查询sql”);//下次直接使用编号1定位
分表的话 在查询前先根据分表规则把表名查出

这里用两台机子简单以同个业务库分库,同个表分表,演示插入、查询如何定位库和表并最终成功执行

两台机子:

server1:192.168.1.198

server2:192.168.1.199

两台机子都执行下面操作

1、先创建10个数据库,每个数据库10个表

当然也可以改成百库百表,也可手动创建,我为了方便写了个脚本批量创建

create.php

  1. <?php
  2. ini_set(‘memory_limit’‘-1’);
  3. $con=mysql_connect(“192.168.1.198”,“open”,“123456”);
  4. if($con){
  5.     for($i=0;$i<10;$i++){//10个库
  6.         $sql=“drop database cloude_{$i};”;//删库 谨慎
  7.         mysql_query($sql);
  8.         $sql=“create database cloude_{$i} default character set utf8 collate utf8_general_ci;”;
  9.         $do=mysql_query($sql,$con)or die(mysql_error());
  10.         if($do){
  11.             mysql_select_db(“cloude_{$i}”,$con);
  12.             mysql_query(“set name gtf8”);
  13.             for($j=0;$j<10;$j++){        //10个表
  14.                 $sql=“drop table if exists user_{$j};”;
  15.                 mysql_query($sql);
  16.                 $sql=”create table user_{$j}
  17.                 (
  18.                     id char(36) not null primary key,
  19.                     name char(15) not null default ,
  20.                     password char(32) not null default ,
  21.                     sex char(1) not null default ‘男’
  22.                 )engine=InnoDB;”;
  23.                 $do=mysql_query($sql,$conor die(mysql_error());
  24.                 if($do){
  25.                     //echo “create table user_{$j} successful! <br/>”;
  26.                 }else{
  27.                     //echo “create error!”;
  28.                 }
  29.             }
  30.         }
  31.     }
  32. }else{
  33.     echo “connect error!!!!”;
  34. }

2、分库分表路由实现
Config.PHP

  1. <?php
  2. class Config{
  3.     public $dsn;
  4.     public $user;
  5.     public $password;
  6.     public $dbname//分库分表后得到的数据库名
  7.     public $table//分库分表后得到的表名
  8.     private static $config;//mysql配置数组
  9.     private static $configFile = ‘mysql.php’//配置文件路径 
  10.     public function __construct($dbname$table$id = 0){
  11.         if (is_null(static::$config)) {
  12.             $config = include(static::$configFile);
  13.             static::$config = $config;
  14.         }
  15.         $config = static::$config;
  16.         if (isset($config[‘shared’]) && isset($config[‘shared’][$dbname])) {
  17.             $dbconfig = $config[‘shared’][$dbname];
  18.             $id = is_numeric($id) ? (int)$id : crc32($id);
  19.             $database_id = ($id / $dbconfig[‘database_split’][0]) % $dbconfig[‘database_split’][1];
  20.             $table_id = ($id / $dbconfig[‘table_split’][0]) % $dbconfig[‘table_split’][1];
  21.             foreach ($dbconfig[‘host’as $key => $conf) {
  22.                 list($from$to) = explode(‘-‘$key);
  23.                 if ($from <= $database_id && $database_id <= $to) {
  24.                     $the_config = $conf;
  25.                 }
  26.             }
  27.             $this->dbname = $dbname . ‘_’ . $database_id;
  28.             $this->table = $table . ‘_’ . $table_id;
  29.         } else {
  30.             $this->dbname = $dbname;
  31.             $this->table = $table;
  32.             $the_config = $config[‘db’][$dbname];
  33.         }
  34.         $c = $the_config;
  35.         if (isset($c[‘unix_socket’]) && $c[‘unix_socket’]) {
  36.             $this->dsn = sprintf(‘mysql:dbname=%s;unix_socket=%s’$this->dbname, $c[‘unix_socket’]);
  37.         } else {
  38.             $this->dsn = sprintf(‘mysql:dbname=%s;host=%s;port=%s’$this->dbname, $c[‘host’], $c[‘port’]);
  39.         }
  40.         $this->user = $c[‘user’];
  41.         $this->password = $c[‘password’];
  42.     }
  43. }

3、数据库配置文件
mysql.php

  1. <?php
  2. $default = array(
  3.     ‘unix_socket’ => null,
  4.     ‘host’ => ‘192.168.1.198’,
  5.     ‘port’ => ‘3306’,
  6.     ‘user’ => ‘open’,
  7.     ‘password’ => ‘123456’,
  8. );
  9. $db_199 = array(
  10.     ‘unix_socket’ => null,
  11.     ‘host’ => ‘192.168.1.199’,
  12.     ‘port’ => ‘3306’,
  13.     ‘user’ => ‘open’,
  14.     ‘password’ => ‘123456’,
  15. );
  16. $config = array(
  17.     // 不进行分库分表的数据库  
  18.     ‘db’ => array(
  19.         ‘hadoop’ => $default,
  20.     ),
  21.     // 分库分表  
  22.     ‘shared’ => array(
  23.         ‘cloude’ => array(
  24.             ‘host’ => array(
  25.                 /** 
  26.                  * 编号为 0 到 4 的库使用的链接配置 
  27.                  */
  28.                 ‘0-4’ => $default,
  29.                 /** 
  30.                  * 编号为 5 到 9 的库使用的链接配置 
  31.                  */
  32.                 ‘5-9’ => $db_199,
  33.             ),
  34.             // 分库分表规则  
  35.             /** 
  36.              * 下面的配置对应10库10表
  37.              * 如果根据 uid 进行分表,假设 uid 为 224,对应的库表为: 
  38.              *  (224 / 1) % 10 = 4 为编号为 4 的库 
  39.              *  (224 / 10) % 10 = 1 为编号为 2 的表 
  40.              */
  41.             ‘database_split’ => array(1, 10),
  42.             ‘table_split’ => array(10, 10),
  43.         ),
  44.     ),
  45. );
  46. return $config;

4、模型类操作数据库
Model.php

  1. <?php
  2. require_once ‘Config.php’;//引入配置信息
  3. class Model{
  4.     public $config;     //数据库配置
  5.     public $connection//pdo
  6.     protected $dbnamePrefix//库前缀如cloude_50 前缀为cloude 
  7.     protected $tablePrefix;  //表前缀
  8.     protected $dbname;  //分库分表后对应的库
  9.     protected $table;   //分库分表后对应的库表
  10.     public function __construct($id){
  11.         $this->config = new Config($this->dbnamePrefix, $this->tablePrefix, $id);                  //根据id找到对应库和表
  12.         $this->connection = new Pdo($this->config->dsn, $this->config->user, $this->config->password);//实例化pdo  
  13.         $this->connection->exec(“set names utf8”);
  14.         $this->dbname = $this->config->dbname;
  15.         $this->table = $this->config->table;
  16.     }
  17.     public function update(array $dataarray $where = array()){
  18.     }
  19.     public function select(array $condition){
  20.         $sqlwhere=;
  21.         if(!empty($condition)){
  22.             foreach ($condition as $field => $value) {
  23.                 $where[] = ‘`’.$field.‘`=’.“‘”.addslashes($value).“‘”;
  24.             }
  25.             $sqlwhere .= ‘ ‘.implode(‘ and ‘$where);
  26.         }
  27.         $sql=“select * from “.$this->dbname.‘.’.$this->table;
  28.         if($sqlwhere){
  29.             $sql.=” where $sqlwhere”;
  30.         }
  31.         $res=$this->connection->query($sql);
  32.         $data[‘data’]=$res->fetchAll(PDO::FETCH_ASSOC);
  33.         $data[‘info’]=array(“dsn”=>$this->config->dsn,“dbname”=>$this->dbname,“table”=>$this->table,“sql”=>$sql);
  34.         return $data;
  35.     }
  36.     public function insert(array $arrData) {
  37.         $name = $values = ;
  38.         $flag = $flagV = 1;
  39.         $true = is_array( current($arrData) );//判断是否一次插入多条数据
  40.         if($true) {
  41.             //构建插入多条数据的sql语句
  42.             foreach($arrData as $arr) {
  43.                 $values .= $flag ? ‘(‘ : ‘,(‘;
  44.                 foreach($arr as $key => $value) {
  45.                     if($flagV) {
  46.                         if($flag$name .= “$key”;
  47.                         $values .= “‘$value'”;
  48.                         $flagV = 0;
  49.                     } else {
  50.                         if($flag$name .= “,$key”;
  51.                         $values .= “,’$value'”;
  52.                     }
  53.                 }
  54.                 $values .= ‘) ‘;
  55.                 $flag = 0;
  56.                 $flagV = 1;
  57.             }
  58.         } else {
  59.             //构建插入单条数据的sql语句
  60.             foreach($arrData as $key => $value) {
  61.                 if($flagV) {
  62.                     $name = “$key”;
  63.                     $values = “(‘$value'”;
  64.                     $flagV = 0;
  65.                 } else {
  66.                     $name .= “,$key”;
  67.                     $values .= “,’$value'”;
  68.                 }
  69.             }
  70.             $values .= “) “;
  71.         }
  72.         $sql = “insert into “.$this->dbname.‘.’.$this->table.” ($name) values $values”;
  73.         if( ($rs = $this->connection->exec($sql) ) > 0 ) {
  74.             return array(“dsn”=>$this->config->dsn,“dbname”=>$this->dbname,“table”=>$this->table,“sql”=>$sql);
  75.         }
  76.         return false;
  77.     }
  78.     public function query($sql){
  79.         return $this->connection->query($sql);
  80.     }
  81. }

5、测试
使用主键id作为分表字段,那最好就不要使用自增了,可使用uuid

User.php

  1. <?php
  2. require ‘Config.php’;
  3. require ‘Model.php’;
  4. class User extends Model
  5. {
  6.     protected $dbnamePrefix = ‘cloude’;
  7.     protected $tablePrefix = ‘user’;
  8. }
  9. //生成唯一uuid
  10. function create_uuid($prefix = “”){    //可以指定前缀
  11.     $str = md5(uniqid(mt_rand(), true));
  12.     $uuid  = substr($str,0,8) . ‘-‘;
  13.     $uuid .= substr($str,8,4) . ‘-‘;
  14.     $uuid .= substr($str,12,4) . ‘-‘;
  15.     $uuid .= substr($str,16,4) . ‘-‘;
  16.     $uuid .= substr($str,20,12);
  17.     return $prefix . $uuid;
  18. }
  19. $userId=create_uuid();
  20. $user = new User($userId);
  21. $data=array(‘id’=>$userId,‘name’=>‘大明’.$userId,‘password’=>’14e1b600b1fd579f47433b88e8d85291′,‘sex’=>‘男’);
  22. if($result=$user->insert($data)){
  23.     echo ‘插入成功:’,‘<pre/>’;
  24.     print_r($result);
  25. }
  26. $condition=array(“id”=>$userId);
  27. $list=$user->select($condition);
  28. if($list){
  29.     echo ‘查询成功:’,‘<pre/>’;
  30.     print_r($list);
  31. }


6、结果

插入成功会返回插入到哪个库哪个表,查询成功返回从哪个库哪个表查的

分库分表注意事项:
1、维度问题
假如用户购买了商品,需 要将交易记录保存取来,如果按照用户的纬度分表,则每个用户的交易记录都保存在同一表中,所以很快很方便的查找到某用户的购买情况,但是某商品被购买的情 况则很有可能分布在多张表中,查找起来比较麻烦。反之,按照商品维度分表,可以很方便的查找到此商品的购买情况,但要查找到买人的交易记录比较麻烦。
所以常见的解决方式有:
通过扫表的方式解决,此方法基本不可能,效率太低了。
记录两份数据,一份按照用户纬度分表,一份按照商品维度分表。
通过搜索引擎解决,但如果实时性要求很高,又得关系到实时搜索
2、避免分表join操作 因为关联的表有可能不在同一数据库中
3、避免跨库事务
避免在一个事务中修改db0中的表的时候同时修改db1中的表,一个是操作起来更复杂,效率也会有一定影响
4、分表宜多不宜少;这样做主要是为了尽量避免后期可能遇到的二次拆分
5、尽量把同一组数据放到同一DB服务器上
例如将卖家a的商品和交易信息都放到db0中,当db1挂了的时候,卖家a相关的东西可以正常使用。也就是说避免数据库中的数据依赖另一数据库中的数据

PHP实现一致性hash

随着memcache、Redis以及其它一些内存K/V数据库的流行,一致性哈希也越来越被开发者所了解。因为这些内存K/V数据库大多不提供分布式支持(本文以redis为例),所以如果要提供多台redis server来提供服务的话,就需要解决如何将数据分散到redis server,并且在增减redis server时如何最大化的不令数据重新分布,这将是本文讨论的范畴。
取模算法
取模运算通常用于得到某个半开区间内的值:m % n = v,其中n不为0,值v的半开区间为:[0, n)。取模运算的算法很简单:有正整数k,并令k使得k和n的乘积最大但不超过m,则v的值为:m – kn。比如1 % 5,令k = 0,则k * 5的乘积最大并不超过1,故结果v = 1 – 0 * 5 = 1。
我们在分表时也会用到取模运算。如一个表要划分三个表,则可对3进行取模,因为结果总是在[0, 3)之内,也就是取值为:0、1、2。
但是对于应用到redis上,这种方式就不行了,因为太容易冲突了。
哈希(Hash)
Hash,一般翻译做“散列”,也有直接音译为”哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。
简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
目前普遍采用的哈希算法是time33,又称DJBX33A (Daniel J. Bernstein, Times 33 with Addition)。这个算法被广泛运用于多个软件项目,Apache、Perl和Berkeley DB等。对于字符串而言这是目前所知道的最好的哈希算法,原因在于该算法的速度非常快,而且分类非常好(冲突小,分布均匀)。
PHP内核就采用了time33算法来实现HashTable,来看下time33的定义:
  1. hash(i) = hash(i-1) * 33 + str[i]
有了定义就容易实现了:
  1. <?php
  2. function myHash($str) {
  3.     // hash(i) = hash(i-1) * 33 + str[i]
  4.     $hash = 0;
  5.     $s    = md5($str);
  6.     $seed = 5;
  7.     $len  = 32;
  8.     for ($i = 0; $i < $len; $i++) {
  9.         // (hash << 5) + hash 相当于 hash * 33
  10.         //$hash = sprintf(“%u”, $hash * 33) + ord($s{$i});
  11.         //$hash = ($hash * 33 + ord($s{$i})) & 0x7FFFFFFF;
  12.         $hash = ($hash << $seed) + $hash + ord($s{$i});
  13.     }
  14.     return $hash & 0x7FFFFFFF;
  15. }
  16. echo myHash(“test”); //输出 786776064
利用取模实现
现在有2台redis server,所以需要计算键的hash并跟2取模。比如有键key1和key2,代码如下:
  1. <?php
  2. function myHash($str) {
  3.     // hash(i) = hash(i-1) * 33 + str[i]
  4.     $hash = 0;
  5.     $s    = md5($str);
  6.     $seed = 5;
  7.     $len  = 32;
  8.     for ($i = 0; $i < $len; $i++) {
  9.         // (hash << 5) + hash 相当于 hash * 33
  10.         //$hash = sprintf(“%u”, $hash * 33) + ord($s{$i});
  11.         //$hash = ($hash * 33 + ord($s{$i})) & 0x7FFFFFFF;
  12.         $hash = ($hash << $seed) + $hash + ord($s{$i});
  13.     }
  14.     return $hash & 0x7FFFFFFF;
  15. }
  16. echo “key1: ” . (myHash(“key1”) % 2) . “\n”;
  17. echo “key2: ” . (myHash(“key2”) % 2) . “\n”;
对于key1和key2来说,同时存储到一台服务器上,这似乎没什么问题,但正因为key1和key2是始终存储到这台服务器上,一旦这台服务器下线了,则这台服务器上的数据全部要重新定位到另一台服务器。对于增加服务器也是类似的情况。而且重新hash(之前跟2进行hash,现在是跟3进行hash)之后,结果就变掉了,导致大多数数据需要重新定位到redis server。
在服务器数量不变的时候,这种方式也是能很好的工作的。
一致性哈希
由于hash算法结果一般为unsigned int型,因此对于hash函数的结果应该均匀分布在[0,2^32-1]区间,如果我们把一个圆环用2^32 个点来进行均匀切割,首先按照hash(key)函数算出服务器(节点)的哈希值, 并将其分布到0~2^32的圆环上。
用同样的hash(key)函数求出需要存储数据的键的哈希值,并映射到圆环上。然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器(节点)上。如图所示:
key1、key2、key3和server1、server2通过hash都能在这个圆环上找到自己的位置,并且通过顺时针的方式来将key定位到server。按上图来说,key1和key2存储到server1,而key3存储到server2。如果新增一台server,hash后在key1和key2之间,则只会影响key1(key1将会存储在新增的server上),其它不变。
上图这个圆环相当于是一个排好序的数组,我们先通过代码来看下key1、key2、key3、server1、server2的hash值,然后再作分析:
  1. <?php
  2. function myHash($str) {
  3.     // hash(i) = hash(i-1) * 33 + str[i]
  4.     $hash = 0;
  5.     $s    = md5($str);
  6.     $seed = 5;
  7.     $len  = 32;
  8.     for ($i = 0; $i < $len; $i++) {
  9.         // (hash << 5) + hash 相当于 hash * 33
  10.         //$hash = sprintf(“%u”, $hash * 33) + ord($s{$i});
  11.         //$hash = ($hash * 33 + ord($s{$i})) & 0x7FFFFFFF;
  12.         $hash = ($hash << $seed) + $hash + ord($s{$i});
  13.     }
  14.     return $hash & 0x7FFFFFFF;
  15. }
  16. //echo myHash(“却道天凉好个秋~”);
  17. echo “key1: ” . myHash(“key1”) . “\n”;
  18. echo “key2: ” . myHash(“key2”) . “\n”;
  19. echo “key3: ” . myHash(“key3”) . “\n”;
  20. echo “serv1: ” . myHash(“server1”) . “\n”;
  21. echo “serv2: ” . myHash(“server2”) . “\n”;
现在我们根据hash值重新画一张在圆环上的分布图,如下所示:
key1、key2和key3都存储到了server1上,这是正确的,因为是按顺时针来定位。我们想像一下,所有的server其实就是一个排好序的数组(降序):[server2, server1],然后通过计算key的hash值来得到处于哪个server上。来分析下定位过程:如果只有一台server,即[server],则直接定位,取数组的第一个元素。如果有多台server,则要先看通过key计算的hash值是否落在[server2, server1, …]这个区间上,这个直接跟数组的第一个元素和最后一个元素比较就知道了。然后就可以通过查找来定位了。
利用一致性哈希实现
下面是一个实现一致性哈希的例子,仅仅实现了addServer和find。其实对于remove的实现跟addServer是类似的。代码如下:
  1. <?php
  2. function myHash($str) {
  3.     // hash(i) = hash(i-1) * 33 + str[i]
  4.     $hash = 0;
  5.     $s    = md5($str);
  6.     $seed = 5;
  7.     $len  = 32;
  8.     for ($i = 0; $i < $len; $i++) {
  9.         // (hash << 5) + hash 相当于 hash * 33
  10.         //$hash = sprintf(“%u”, $hash * 33) + ord($s{$i});
  11.         //$hash = ($hash * 33 + ord($s{$i})) & 0x7FFFFFFF;
  12.         $hash = ($hash << $seed) + $hash + ord($s{$i});
  13.     }
  14.     return $hash & 0x7FFFFFFF;
  15. }
  16. class ConsistentHash {
  17.     // server列表
  18.     private $_server_list = array();
  19.     // 延迟排序,因为可能会执行多次addServer
  20.     private $_layze_sorted = FALSE;
  21.     public function addServer($server) {
  22.         $hash = myHash($server);
  23.         $this->_layze_sorted = FALSE;
  24.         if (!isset($this->_server_list[$hash])) {
  25.             $this->_server_list[$hash] = $server;
  26.         }
  27.         return $this;
  28.     }
  29.     public function find($key) {
  30.         // 排序
  31.         if (!$this->_layze_sorted) {
  32.             asort($this->_server_list);
  33.             $this->_layze_sorted = TRUE;
  34.         }
  35.         $hash = myHash($key);
  36.         $len  = sizeof($this->_server_list);
  37.         if ($len == 0) {
  38.             return FALSE;
  39.         }
  40.         $keys   = array_keys($this->_server_list);
  41.         $values = array_values($this->_server_list);
  42.         // 如果不在区间内,则返回最后一个server
  43.         if ($hash <= $keys[0] || $hash >= $keys[$len – 1]) {
  44.             return $values[$len – 1];
  45.         }
  46.         foreach ($keys as $key=>$pos) {
  47.             $next_pos = NULL;
  48.             if (isset($keys[$key + 1]))
  49.             {
  50.                 $next_pos = $keys[$key + 1];
  51.             }
  52.             if (is_null($next_pos)) {
  53.                 return $values[$key];
  54.             }
  55.             // 区间判断
  56.             if ($hash >= $pos && $hash <= $next_pos) {
  57.                 return $values[$key];
  58.             }
  59.         }
  60.     }
  61. }
  62. $consisHash = new ConsistentHash();
  63. $consisHash->addServer(“serv1”)->addServer(“serv2”)->addServer(“server3”);
  64. echo “key1 at ” . $consisHash->find(“key1”) . “.\n”;
  65. echo “key2 at ” . $consisHash->find(“key2”) . “.\n”;
  66. echo “key3 at ” . $consisHash->find(“key3”) . “.\n”;
  1. $ php -f test.php
  2. key1 at server3.
  3. key2 at server3.
  4. key3 at serv2.
即使新增或下线服务器,也不会影响全部,只要根据hash顺时针定位就可以了。

阿里云mysql5.6日志数据的恢复

Centos7使用yum安装MySQL5.6
2017年12月04日 15:29:36
阅读数:318

我要在自己的阿里云服务器上搭建hadoop+hive+mysql+tomcat环境,以下为mysql的安装记录

→_→大家都知道,centos自带的repo是不会自动更新每个软件的最新版本,所以无法通过yum方式安装MySQL的高级版本。所以,即使我使劲用yum -y install mysql mysql-server mysql-devel,也是没有人会鸟我的。
所以,正确的安装mysql5姿势是要先安装带有可用的mysql5系列社区版资源的rpm包

[root@iZ28gvqe4biZ ~]# rpm -Uvh http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
获取http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
准备中…                          ################################# [100%]
正在升级/安装…
1:mysql-community-release-el7-5    ################################# [100%]

这个时候查看当前可用的mysql安装资源:

[root@iZ28gvqe4biZ ~]# yum repolist enabled | grep “mysql.*-community.*”
mysql-connectors-community/x86_64 MySQL Connectors Community                  17
mysql-tools-community/x86_64      MySQL Tools Community                       31
mysql56-community/x86_64          MySQL 5.6 Community Server                 199

一般来说,只要安装mysql-server跟mysql-client
这个时候我们可以直接使用yum的方式安装MySQL了

[root@iZ28gvqe4biZ ~]# yum -y install mysql-community-server

mysqlbinlog –base64-output=decode-rows -v mysql-bin.000202.tar > row.sql
这样就可以恢复日志了。

使用Magic Packet进行网络唤醒(WOL)

现在几乎所有的电脑都支持从网络唤醒,这意味着你可以通过网络控制远程一台计算机开机。

目前,远程唤醒数据包的发送,大多使用AMD公司的Magic Packet。Magic Packet虽然只是AMD公司所开发的一项技术,但是受到了几乎所有网卡制造商的支持,因此,大多数流行网卡都能与之很好地兼容。最常使用的工具是Magic Packet Utility 1.00.005。

使用WOL需要被唤醒机满足以下条件:

  1. 使用ATX电源;
  2. 同时主板要提供对网络唤醒的硬件和软件支持(在BIOS中开启);
  3. 网卡支持WOL。

需要说明的是:如果是集成网卡,只要主板支持就行了;如果是PCI网卡,在主板上和网卡上会有三针的WOL跳线插座,需要将其连接好。

有些网卡还提供了对Wake up功能的设置项(例如集成在驱动的设置中),可以选择唤醒包的类型等。

发送Magic Packet唤醒数据包只需要知道被唤醒计算机网卡的MAC地址,这可以使用getmac命令,或者ipconfig命令结合all参数来实现。

使用Magic Packet Utility进行网络唤醒时,每次都必须输入手工被唤醒计算机的MAC地址,十分麻烦。还有一款名为WOL – Magic Packet Sender的软件,也使用Magic Packet包进行网络唤醒,但是功能更多,可以对配置文件进行管理。

Magic Packet的包格式很简单,首先是六个FF,然后是重复十六次待唤醒电脑的MAC。比如上图的封包,就应该是这样:

FF FF FF FF FF FF 20 12 04 24 13 43 20 12 04 24 13 43 ……

理论上可以在任意网络封包中打包Magic Packet,不过一般选择UDP或IPX。

下面使用C#语言编写了一个简单的小程序,使用UDP协议向局域网内广播Magic Packet包。

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
private static void WakeUp(byte[] mac)
{
    System.Net.Sockets.UdpClient client = new System.Net.Sockets.UdpClient();
    //指定目标地址为广播地址,端口号任意值即可;
    client.Connect(System.Net.IPAddress.Broadcast, 0);
    byte[] packet = new byte[17 * 6];
    for (int i = 0; i &lt; 6; i++)
    {
        packet[i] = 0xff;
    }
    for (int i = 1; i &lt;= 16; i++)
    {
        for (int j = 0; j &lt; 6; j++)
        {
            packet[i * 6 + j] = mac[j];
        }
    }
    //发送UDP数据包,返回发送的字节数到bytesSent
    int bytesSent = client.Send(packet, packet.Length);
    if (bytesSent != packet.Length)
    {
        MessageBox.Show("发送可能失败,请重试!");
    }
}

该程序可以在博主的SkyDrive网盘下载。
最后讨论两个小问题:

一、跨局域网的问题

如果被唤醒机器和发送唤醒包的机器在同一个网段内,用client.Connect(IPAddress.Broadcast,port)是可以的。如果不是处于同一个网段内,那么就要用被唤醒机器网段的广播地址,比如A机器是192.168.1.2,B机器IP地址是192.168.11.10,用A远程唤醒B,就要用B网段的广播地址(192.168.11.255),即

1
2
IPAddress ip = new IPAddress(new byte[]{192,168,11,255});
client.Connect(ip,port)

前提是计算机A和计算机B的网络地址是可连通的。

二、从外网唤醒路由器内局域网中的机器

如果被唤醒的计算机位于使用NAT功能的路由器组建的局域网内,从外网上是看不到这台机器的,因此也无法将唤醒包送达被唤醒的计算机。

解决方法估计只有端口转发了,我觉得在路由器上设置将发到WAN地址为一个特定的UDP端口的数据包想LAN端广播即可,比如设定 UDP Port 9 至 LAN broadcast address,估计直接转发给被唤醒计算机的IP地址也没有问题。

opencv 1.0 各种人脸识库介绍

opencv 1.0 各种人脸识别库介绍(原创)

1 haarcascade_frontalface_alt2.xml

Tree-based 20×20 gentle adaboost frontal face detector.Created by Rainer Lienhart.

2 haarcascade_frontalface_alt.xml

Stump-based 20×20 gentle adaboost frontal face detector.Created by Rainer Lienhart

3 haarcascade_frontalface_alt_tree.xml

Stump-based 20×20 gentle adaboost frontal face detector.This detector uses tree of stage classifiers instead of a cascadeCreated by Rainer Lienhart.

4 haarcascade_frontalface_default.xml Stump-based 24×24 discrete(?) adaboost frontal face detector.Created by Rainer Lienhart