redis数据类型分为:字符串类型、散列类型、列表类型、集合类型、有序集合类型。
redis这么火,它运行有多块?一台普通的笔记本电脑,可以在1秒钟内完成十万次的读写操作。
原子操作:最小的操作单位,不能继续拆分。即最小的执行单位,不会被其他命令插入。高并发下不存在竞态条件。
KEY的命名:一个良好的建议是article:1:title来存储ID为1的文章的标题。
一、前言。
1、获取key的列表:KEYS pattern 通配符有?*[]和转义\
2、key是否存在: EXISTS key 存在返回1,不存在返回0.
3、建立key和删除key:SET key 和 DEL key
4、根据key获取该键所存储的redis数据类型:TYPE key。返回是string、list、hash、set、zset。下面会对这5种返回的redis数据类型逐一讲解。
5、rename oldkey newkey:对key重命名,如果newkey存在则覆盖。
6、renamenx oldkey newkey:对key重命名,如果newkey存在则不覆盖。
7、randomkey:随即返回一个key
8、move key db-index:将key移动到指定的数据库中,如果key不存在或者已经在该数据库中,则返回0。成功则返回1.

二、Redis数据类型 Redis数据命令
1、Redis数据类型一字符串类型:
这个很好理解,一个key存储一个字符串。如果你要存数据呢?转换成Json或者其他的字符串序列化。

2、Redis数据命令一字符串类型:
1)赋值:SET key value。如set hello world
2)取值:GET key。如get hello。返回是world
3)自增:INCR key。就是Mysql的AUTO_INCREMENT。每次执行INCR key时,该key的值都会+1.若key不存在,则先建立一个0,然后+1,返回1。如果值不是整数则报错。该操作是原子操作。
4)自减:DECR key。将指定key的值减少1.如DECR num,就是num-1
5)自增N:INCRBY key increment用来给指定key的值加increment。如INCRBY num 5就是num+5
6)自减N:DECRBY key increment用来给指定key的值减increment。如DECRBY num 5就是num-5
7)增加浮点数:INCRBYFLOAT key increment。
8)向尾部追加:APPEND key value。如set test:key 123 append test:key 456 get test:key就是123456
9)获取长度:STRLEN key。
10)同时给多个key 赋值:MSET title 这是标题 description 这是描述 content 这是内容。
11)同时获取多个key的值:MGET title description content
12)位操作之获取:GETBIT key offset。如字符a在redis中的存储为01100001(ASCII为98),那么GETBIT key 2就是1,GET key 0就是0。
13)位操作之设置:SETBIT key offset value。如字符a在redis中的存储为01100001(ASCII为98),那么SETBIT key 6 0,SETBIT key 5 1那么get key得到的是b。因为取出的二进制为01100010。
14)位操作之统计:BITCOUNT key [start] [end]:BITCOUNT key用来获取key的值中二进制是1的个数。而BITCOUNT key start end则是用来统计key的值中在第start和end之间的子字符串的二进制是1的个数(好绕啊)。
15)位操作之位运算:BITOP operation resultKey key1 key2。operation是位运算的操作,有AND,OR,XOR,NOT。resultKey是把运算结构存储在这个key中,key1和key2是参与运算的key,参与运算的key可以指定多个。

3、Redis数据类型二散列类型:

Redis是以字典(关联数组)的形式存储的,一个key对应一个value。在字符串类型中,value只能是一个字符串。那么在散列类型,也叫哈希类型中,value对应的也是一个字典(关联数组)。那么就可以理解,Redis的哈希类型/散列类型中,key对应的value是一个二维数组。但是字段的值只可以是字符串。也就是说只能是二维数组,不能有更多的维度。

4、Redis数据命令二散列类型:
1)赋值:HSET key field value。如hset user name lane。hset user age 23
2)取值:HGET key field。如hget user name,得到的是lane。
3)同一个key多个字段赋值:HMSET key field1 value1 field2 value2...
4)同一个KEY多个字段取值:HMGET key field1 fields2...
5)获取KEY的所有字段和所有值:HGETALL key。如HGETALL user得到的是name lane age 23。每个返回都是独立的一行。
6)字段是否存在:HEXISTS key field。存在返回1,不存在返回0
7)当字段不存在时赋值:HSETNX key field value。如果key下面的字段field不存在,则建立field字段,且值为value。如果field字段存在,则不执行任何操作。它的效果等于HEXISTS + HSET。但是这个命令的优点是原子操作。再高的并发也不会怕怕。
8)自增N:HINCREBY key field increment。同字符串的自增类型,不再阐述。
9)删除字段:DEL key field1 field2...删除指定KEY的一个或多个字段。
10)只获取字段名:HKEYS key。与HGETALL类似,但是只获取字段名,不获取字段值。
11)只获取字段值:HVALS key。与HGETALL类似,但是只获取字段值,不获取字段名。
12)获取字段数量:HLEN key。

5、Redis数据类型三列表类型:
列表类型存储了一个有序的字符串列表。常用的操作是向两端插入新的元素。时间复杂度为O(1)。结构为一个链表。记录头和尾的地址。看到这里,Redis数据类型的列表类型一个重大的作用呼之欲出,那就是队列。新来的请求插入到尾部,新处理过的从头部删除。另外,比如微博的新鲜事。比如日志。列表类型就是一个下标从0开始的数组。由于是链表存储,那么越靠近头和尾的元素操作越快,越靠近中间则越慢。

6、Redis数据命令三列表类型:
1)向头部插入:LPUSH key value1 value2...。返回增加后的列表长度。
2)向尾部插入:RPUSH key value1 value2...。返回增加后的列表长度。
3)从头部弹出:LPOP key。返回被弹出的元素值。该操作先删除key列表的第一个元素,再将它返回。
4)从尾部弹出:RPOP key。返回被弹出的元素值。
5)列表元素个数:LLEN key。key不存在返回0。
6)获取列表的子列表:LRANGE start end。返回第start个到第end个元素的列表。包含start和end。支持负数索引。-1表示最后一个元素,-2表示倒数第二个元素。
7)删除列表中指定值:LREM key count value。删除key这个列表中,所有值为value的元素,只删除count。如果有count+1个,那么就保留最后一个。count不存在或者为0,则删除所有的。如果count大于0,则删除从头到尾的count个,如果count小于0,则删除从尾到头的count个。
8)获取指定索引值:LINDEX key index。如LINDEX key 0就是列表的第一个元素。index可以是负数。
9)设置索引和值:LSET key index value。这个操作只是修改指定key且指定index的值。如果index不存在,则报错。
10)保留片段,删除其它:LTRIM key start end。保留start到end之间的所有元素,含start和end。其他全部删除。
11)向列表插入元素:LINSERT key BEFORE/AFTER value1 value2。从列表头开始遍历,发现值为value1时停止,将value2插入,根据BEFORE或者AFTER插入到value1的前面还是后面。
12)把一个列表的一个元素转到另一个列表:RPOPLPUSH list1 list2。将列表list1的右边元素删除,并把该与元素插入到列表list2的左边。原子操作。

7、Redis数据类型四集合类型:
集合类型是为了方便对多个集合进行操作和运算。集合中每个元素不同且没有顺序的概念,每个元素都是且只能是一个字符串。常用操作是对集合插入、删除、判断等操作。时间复杂度尾O(1)。可以进行交集、并集、差集运算。例如文章1的有3个标签,是一个Redis数据类型集合类型存储。文章2有3个标签,有一个Redis数据类型集合类型存储。文章是1是mysql,文章2是讲redis。那么交集是不是就交出了一个数据库?(假设数据库这个tag在两篇文字都有)。集合类型在redis中的存储是一个值为空的散列表。

8、Redis数据命令四集合类型:
1)增加:SADD key value。
2)删除:SREM key value。
3)获取指定集合的所有元素:SMEMBERS key。
4)判断某个元素是否存在:SISMEMBER key value。
5)差集运算:SDIFF key1 key2...。对多个集合进行差集运算。
6)交集运算:SINNER key1 key2...。对多个集合进行交集运算。
7)并集运算:SUNION key1 key2...。对多个集合进行并集运算。
8)获取集合中元素个数:SCARD key。返回集合中元素的总个数。
9)对差集、交集、并集运算的结果存放在一个指定的key中:SDIFFSTORE storekey key1 key2。对key1和key2求差集,结果存放在key为storekey的集合中。SINNERSTORE和SUNIONSTORE类似。
10)获取集合中的随即元素:SRANDMEMBER key [count]。参数count可选,如果count不存在,则随即一个。count大于0,则是不重复的count个元素。count小于0,则是一共|count|个元素,可以重复。
11)随即弹出一个元素:SPOP key。随即从集合中弹出一个元素并删除,将该元素的值返回。

9、Redis数据类型五有序集合类型:
集合类型是无序的,每个元素是唯一的。那么有序集合就是有序的,每个元素是唯一的。有序集合类型和集合类型的差别是,有序集合为每个元素配备了一个属性:分数。有序集合就是根据分数来排序的。有序集合是使用散列表和跳跃表实现的。所以和列表相比,操作中间元素的速度也很快。时间复杂度尾O(log(N))。Redis数据类型中的有序集合类型比Redis数据类型中的列表类型更加耗费资源。

10、Redis数据命令五有序集合类型:
1)增加:ZADD key sorce1 value1 sorce2 value2...。
2)获取分数:ZSCORE key value。获取key的有序集合中值为value的元素的分数。
3)获取排名在某个范围内的元素列表:ZRANFGE key start stop [WITHSCORE]。获取排名在start和end之间的元素列表,包含start和end2个元素。每个元素一行。如果有WITHSCORE参数,则一行元素值,一行分数。时间复杂度为O(LOGn+m)。如果分数相同,则0<0<A<Z<a<z。
4)获取指定分数范围的元素:ZRANGEBYSCORE key min max [WITHSCORE] [LIMIT offset count]。获取分数在min和max之间的元素列表。含两头。每个元素一行。如果有WITHSCORE参数,则一行元素值,一行分数。如果min大于max则顺序反转。
5)为某个元素增加分数:ZINCRBY key increment value。指定的有序集合的值为value的元素的分数+increment。返回值后更改后的分数。
6)获取集合中元素的数量:ZCARD key。
7)获取指定分数范围内的元素个数:ZCOUNT key min max。
8)删除一个或多个元素:ZREM key value1 value2...
9)根据排名范围删除元素:ZREMRANGEBYRANK key start end。删除排名在start和end中的元素。
10)按照分数范围删除元素:ZREMRANGEBYSCORE key min max。
11)获得元素排名(正序):ZRANK key value。获取value在该集合中的从小到大的排名。
12)获得元素排名(倒序):ZREVRANK key value。获取value在该集合中从大到小的排名。
13)有序集合的交集:ZINTERSTORE storekey key1 key2...[WEIGHTS weight [weight..]] [AGGREGATE SUM|MIN|MAX]。用来计算多个集合的交集,结果存储在storekey中。返回值是storekey的元素个数。AGGREGATE为SUM则storekey集合的每个元素的分数是参与计算的集合分数和。MIN是参与计算的分数最小值。MAX是参与计算分数最大值。WEIGHTS 设置每个集合的权重,如WEIGHTS 1 0.1。那么集合A的每个元素分数*1,集合B的每个元素分数*0.1
14)有序集合的并集:ZUNIONSTORE storekey key1 kye2...[WEIGHTS weight [weight..]] [AGGREGATE SUM|MIN|MAX]

本例是Python基础示例。涉及Python基础,包括语法、字典型数据结构、类、引入库、pickle实现的存储器、异常处理等。
   示例是一个电话本。可以对电话本进行增加、删除、修改、获取列表和获取单人的。
   Python中,Pickle和cPickle都可以完成存储器的任务,不过cPickle是C语言所写,据称性能高于Pickle1000倍
   Python中的Pickle是把一个对象存入文件中。作为完全面向对象的语言,在声明/初始化一个变量的时候,比如字典,也就是关联数组,Python其实是在实例化一个字典对象。那么Pickle就可以把这个字典对象存入一个文件,读出来的时候不但这个字典是完整的数据,而且可以继续使用这个字典对象的方法。
   Python是用缩进来时别语句块的。因为我是在VIM下写好复制出来的,所以在博客看到的可能缩进会有问题。

#引入pickle库。cPickle比Pickle快1000倍
import cPickle as pickle
#import Pickle as pickle

#电话本类
class Address:
    #初始化
    def __init__(self):
        #把数据存到那个文件里
	    self.filename = 'list.data'
	    f = file(self.filename)
        #如果文件是新建的或者是空内容的,则初始化为一个空的字典(关联数组)
        try:
            self.lists = pickle.load(f)
        except:
            print 'Address Book is empty.initializing.....'
            self.lists = {}
	    f.close()
    #添加联系人
    def add(self, name, age, mobile, mail):
	    newUser = {'name':name, 'age':age, 'mobile':mobile, 'mail':mail}
	    self.lists[name] = newUser
    #删除联系人
    def delete(self, name):
        if name in self.lists:
            del self.lists[name]
            print 'delete ', name
        else:
            print 'No exists ', name
    #获取列表
    def getList(self):
        print 'Address Book List:'
        print self.lists
    #获取指定姓名的联系人
    def getOne(self, name):
        if name in self.lists:
            print self.lists[name]
        else:
            print 'Not Exists:', name
    #修改联系人
    def edit(self, name, key, value):
	    self.lists[name][key] = value
    #类运行结束,执行特殊方法__del__,也就是析构函数
    def __del__(self):
        f = file(self.filename, 'w')
        pickle.dump(self.lists, f)
        f.close()

#初始化电话本类
obj = Address()
#添加一个联系人
obj.add('lane', 23, 18500000000, 'lixuan868686@163.com')
#获取所有联系人的列表
obj.getList()
#获取lane这个人的联系方式
obj.getOne('lane')
#获取xiaoming这个人的联系方式
obj.getOne('xiaoming')
//修改lane这个人的年龄为24
obj.edit('lane', 'age', '24')

访问者模式,是设计模式中最难的一种模式。访问者模式适用于数据结构相对稳定的系统。访问者模式对数据结构和作用于结构上的操作之间进行了一次解耦合。访问者模式的目的是把处理从数据结构分离出来。
访问者模式的适用场景:所开发的系统具有比较问题的数据结构,又有抑郁变化的算法。因为访问者模式使得算法操作的增加和扩展变得容易。优点是增加新的操作更加容易,因为增加新操作就是意味着增加新的访问者,访问者模式将有关的行为集中到一个对象中。缺点显而易见了,就是改变数据结构变得下个对困难。
访问者模式:表示一个作用于某对象的结构中的各个元素的操作。它使你可以在不改变各元素的前提下定义作用于这些元素的新操作。
场景:人类分为男女,对于人类这个系统,分类是非常固定的,一个元素是男,一个元素是女(人妖滚粗)。男女对同一件事情往往有不同的观点。以PHP为代码环境,模拟设计模式之访问者模式的代码实现。(暂时没有想到好的例子,就从《大话设计模式》中访问者模式摘了一段)

<?php
class Action{
    public function getManView(Man $manObj){}
    public function getWomanView(Woman $manObj){}
}
class Person{
    public function accept(Action $actionObj){}
}
class Success extends Action{
    public function getManView(Man $manObj){
        echo sprintf('%s成功时,背后多半有一个伟大的女人<br>', $manObj->getName());
    }
    public function getWomanView(Woman $womanObj){
        echo sprintf('%s成功时,背后多半有一个不成功的女人<br>', $womanObj->getName());
    }
}
class Failing extends Action{
    public function getManView(Man $manObj){
        echo sprintf('%s失败时,闷头喝酒,谁也不用劝<br>', $manObj->getName());
    }
    public function getWomanView(Woman $womanObj){
        echo sprintf('%s失败时,眼泪汪汪,谁也劝不住<br>', $womanObj->getName());
    }
}
class Love extends Action{
    public function getManView(Man $manObj){
        echo sprintf('%s恋爱时,凡事不懂也要装懂<br>', $manObj->getName());
    }
    public function getWomanView(Woman $womanObj){
        echo sprintf('%s恋爱时,凡事懂也要装不懂<br>', $womanObj->getName());
    }
}
class Man extends Person{
    private $name = '男人';
    public function getName(){
        return $this->name;
    }
    public function accept(Action $actionObj){
        $actionObj->getManView($this);
    }
}
class Woman extends Person{
    private $name = '女人';
    public function getName(){
        return $this->name;
    }
    public function accept(Action $actionObj){
        $actionObj->getWomanView($this);
    }
}
class ObjectStructure{
    private $elementList;
    public function add(Person $elementObj){
        $this->elementList[] = $elementObj;
    }
    public function display(Action $visitorObj){
        foreach($this->elementList as $element){
            $element->accept($visitorObj);
        }
    }
}
//客户端/接口
$o = new ObjectStructure();
$o->add(new Man());
$o->add(new Woman());

$successObj = new Success();
$o->display($successObj);

$failingObj = new Failing();
$o->display($failingObj);

$loveObj = new Love();
$o->display($loveObj);

这里用到一个双分派技术。客户端将状态(成功、失败、恋爱)作为参数传递给男人,这是第一次分派。男人类调用作为参数的“具体状态中的方法-男人的观点”,同时将自身传递给状态的对象,这是第二次分派。双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型。双分派的好处是,如果要增加结婚类,只需要增加如下:

class Marry extends Action{
    public function getManView(Man $manObj){
        echo sprintf('%s结婚时,有妻徒刑<br>', $manObj->getName());
    }
    public function getWomanView(Woman $womanObj){
        echo sprintf('%s结婚时,婚姻保险<br>', $womanObj->getName());
    }
}

除此之外,客户端在需要的时候调用即可。不需要动其他的代码,增加新算法,只需要扩展一个新类。完美体现开放-封闭原则。

解释器模式,作为PHPer应该非常非常非常熟悉的一种,尽管不知道它叫做解释器模式,但是肯定使用过它。在解释器模式的最佳应用,就是大量优秀的模板引擎。解释器模式解决了一种特定的类型的问题发生的频率足够高,那么就可能值得将该问题的各个势力表述为一个简单的语言中的句子。这就构建了一个解释器,解释器他哦各国解释这些句子来解决问题。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
比如:模板引擎smart;比如论坛的UBB代码,就是用[url=http://www.lanecn.com]LaneBlog[/url]来表示<a href="http://www.lanecn.com/>LaneBlog</a>;还比如正则表达式等
场景:a表示你,b表示好,c表示世界。1表示我说,2表示你说。以PHP为代码环境,模拟设计模式之解释器模式的代码实现。

<?php
class Content{
    private $content = '';
    public function get(){
        return $this->content;
    }
    public function set($content){
        $this->content = $content;
    }
}
class Expression{
    public function interpret($contentObj){
        $content = $contentObj->get();
        if(!empty($content)){
            $lenth = strlen($content);
            for($i=0; $i<$lenth; $i++){
                if(is_numeric($content[$i])){
                    Number::excute($content[$i]);
                }else if(is_string($content[$i])){
                    String::excute($content[$i]);
                }
            }
        }
    }
}
class Number{
    public static function excute($value){
        $data = '';
        switch($value){
            case 1:
                $data = '我说:';
                break;
            case 2:
                $data = '你说:';
                break;
            default:
                break;
        }
        echo $data;
    }
}
//a表示你,b表示好,c表示世界。1表示我说,2表示你说
class String{
    public static function excute($value){
        $data = '';
        switch($value){
            case 'a':
                $data = '你';
                break;
            case 'b':
                $data = '好';
                break;
            case 'c':
                $data = '世界';
                break;
            default:
                break;
        }
        echo $data;
    }
}
//客户端/接口
$contentObj = new Content();
$str = '1abc';
$contentObj->set($str);
echo '解密' . $str . ':<br>';
$expression = new Expression();
$expression->interpret($contentObj);
echo '<br>';
$str = '2abc';
$contentObj->set($str);
echo '解密' . $str . ':<br>';
$expression = new Expression();
$expression->interpret($contentObj);

享元模式解决了大量几乎相似的对象的这种情况。设计模式中的享元模式使程序运行时更加节省服务器资源。享元模式是一种非常好的设计模式。如果一个应用程序使用了大量的对象,而大量的这些对象对服务器资源造成了很大的开销和压力时,就应该考虑使用享元模式。
享元模式:运用共享技术有效的支持大量的细粒度对象。
比如:围棋只有黑白两种棋子,用一个对象生成黑棋子,一个对象生成白棋子,是要一份代码共享给所有的黑棋子共同使用呢,还是每个黑棋子独立一个对象。这就是享元模式,共享对象以达到节省开销的目的。
场景:阿里云旗下的万网提供快速建站的服务,它是给每个用户独立生成一个网站所有的源代码,还是说同类型的网站共享一份代码?答案是后者(示例仅为说明享元模式,并不代表万网的真实实现方式)。以PHP为代码环境,模拟设计模式之享元模式的代码实现。

<?php
class User{
    private $name;
    public function __construct($name){
        $this->setName($name);
    }
    public function setName($name){
        $this->name = $name;
    }
    public function getName(){
        return $this->name;
    }
}
abstract class Website{
    private $name;
    public function __construct($name){
        $this->setName($name);
    }
    public function setName($name){
        $this->name = $name;
    }
    public function getName(){
        return $this->name;
    }
}
class ConcreteWebsite extends Website{
    public function __construct($name){
        parent::__construct($name);
    }
    public function useWebsite($userObj){
        echo '网站名称:' . $this->getName() . '。 所属用户' . $userObj->getName() . '<br>';
    }
}
class WebsiteFactory{
    private $userWebsiteList = array();
    public function getWebsite($key, $name){
        if(!isset($this->userWebsiteList[$key])){
            $this->userWebsiteList[$key] = new ConcreteWebsite($name);
        }
        return $this->userWebsiteList[$key];
    }
}
//客户端/接口
//网站工厂
$websiteFactory = new WebsiteFactory();

//采用万网提供的第一套模板并起名
$website = $websiteFactory->getWebsite('1', 'LaneBlog');
$website->useWebsite(new User('小轩'));

//采用万网提供的第一套模板并起名
$website = $websiteFactory->getWebsite('1', 'Lane博客');
$website->useWebsite(new User('小明'));

//采用万网提供的第一套模板并起名
$website = $websiteFactory->getWebsite('1', 'LixuanBlog');
$website->useWebsite(new User('小红'));

//采用万网提供的第二套模板并起名
$website = $websiteFactory->getWebsite('2', '论坛');
$website->useWebsite(new User('小白'));

根据结果可以看到,多个用户,前三个用户使用的是同一套系统。节省开销。至于名称,从库里读出来即可。这里完全不需要。