邢栋博客

邢栋博客,Action博客,记录工作和生活中的点点滴滴

redis数据结构与对象
redis数据结构与对象

OBJECT ENCODING  KEY //查看对象的底层编码
OBJECT REFCOUNT KEY  //查看对象的引用计数
OBJECT IDLETIME KEY  //查询对象的空转时长

类型常量 对象的名称
REDIS_STRING 字符串对象   string
REDIS_LIST 列表对象 list
REDIS_HASH 哈希对象 hash
REDIS_SET 集合对象 set 
REDIS_ZSET 有序集合对象 zset

编码和底层实现

OBJECT ENCODING  KEY //查看

编码常量 编码所对应的底层数据结构
REDIS_ENCODING_INT long 类型的整数
REDIS_ENCODING_EMBSTR embstr 编码的简单动态字符串
REDIS_ENCODING_RAW 简单动态字符串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 双端链表
REDIS_ENCODING_ZIPLIST 压缩列表
REDIS_ENCODING_INTSET 整数集合
REDIS_ENCODING_SKIPLIST 跳跃表和字典

不同类型和编码的对象
类型 编码 对象
REDIS_STRING REDIS_ENCODING_INT 使用整数值实现的字符串对象。int
REDIS_STRING REDIS_ENCODING_EMBSTR 使用 embstr 编码的简单动态字符串实现的字符串对象。embstr
REDIS_STRING REDIS_ENCODING_RAW 使用简单动态字符串实现的字符串对象。raw
REDIS_LIST REDIS_ENCODING_ZIPLIST 使用压缩列表实现的列表对象。ziplist
REDIS_LIST REDIS_ENCODING_LINKEDLIST 使用双端链表实现的列表对象。linkedlist
REDIS_HASH REDIS_ENCODING_ZIPLIST 使用压缩列表实现的哈希对象。ziplist
REDIS_HASH REDIS_ENCODING_HT 使用字典实现的哈希对象。hashtable
REDIS_SET REDIS_ENCODING_INTSET 使用整数集合实现的集合对象。intset
REDIS_SET REDIS_ENCODING_HT 使用字典实现的集合对象。hashtable
REDIS_ZSET REDIS_ENCODING_ZIPLIST 使用压缩列表实现的有序集合对象。ziplist
REDIS_ZSET REDIS_ENCODING_SKIPLIST 使用跳跃表和字典实现的有序集合对象。skiplist

字符串对象编码转换
int 编码的字符串对象和 embstr 编码的字符串对象在条件满足的情况下, 会被转换为 raw 编码的字符串对象。
对于 int 编码的字符串对象来说, 如果我们向对象执行了一些命令, 使得这个对象保存的不再是整数值, 而是一个字符串值, 那么字符串对象的编码将从 int 变为 raw 。
在下面的示例中, 我们通过 APPEND 命令, 向一个保存整数值的字符串对象追加了一个字符串值, 因为追加操作只能对字符串值执行, 所以程序会先将之前保存的整数值 10086 转换为字符串值 "10086" , 然后再执行追加操作, 操作的执行结果就是一个 raw 编码的、保存了字符串值的字符串对象:


列表对象编码转换
当列表对象可以同时满足以下两个条件时, 列表对象使用 ziplist 编码:
列表对象保存的所有字符串元素的长度都小于 64 字节;
列表对象保存的元素数量小于 512 个;
不能满足这两个条件的列表对象需要使用 linkedlist 编码。
ps:
因为压缩列表比双端链表更节约内存, 并且在元素数量较少时, 在内存中以连续块方式保存的压缩列表比起双端链表可以更快被载入到缓存中;
随着列表对象包含的元素越来越多, 使用压缩列表来保存元素的优势逐渐消失时, 对象就会将底层实现从压缩列表转向功能更强、也更适合保存大量元素的双端链表上面;

哈希对象编码转换
当哈希对象可以同时满足以下两个条件时, 哈希对象使用 ziplist 编码:
哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;
哈希对象保存的键值对数量小于 512 个;
不能满足这两个条件的哈希对象需要使用 hashtable 编码。
ps:
这两个条件的上限值是可以修改的, 具体请看配置文件中关于 hash-max-ziplist-value 选项和 hash-max-ziplist-entries 选项的说明。

集合对象编码转换
当集合对象可以同时满足以下两个条件时, 对象使用 intset 编码:
集合对象保存的所有元素都是整数值;
集合对象保存的元素数量不超过 512 个;
不能满足这两个条件的集合对象需要使用 hashtable 编码。
ps:
第二个条件的上限值是可以修改的, 具体请看配置文件中关于 set-max-intset-entries 选项的说明。

有序集合对象编码的转换
当有序集合对象可以同时满足以下两个条件时, 对象使用 ziplist 编码:
有序集合保存的元素数量小于 128 个;
有序集合保存的所有元素成员的长度都小于 64 字节;
不能满足以上两个条件的有序集合对象将使用 skiplist 编码。
ps:
以上两个条件的上限值是可以修改的, 具体请看配置文件中关于 zset-max-ziplist-entries 选项和 zset-max-ziplist-value 选项的说明。

ps: 内容来自<redis设计与实现>这本书
redis数据类型以及常用相关命令
redis数据类型以及常用相关命令

String类型及操作
String是最简单的类型,一个key对应一个value,string类型是二进制安全的。Redis的string可以包含任何数据,比如jpg图片或者序列化的对象。

Set方法
设置key对应的值为string类型的value ,例如:我们添加一个name = xingdong的键值对 redis  127.0.0.1:6379>set name xingdong 
获取name的话 get name 就可以了
Setnx方法
如果存在则返回0,否则插入
Setex 方法
设置key对应的值为string类型的value,并指定此键值对应的有效期
Setex  haircolor 10 red
Get haircolor  10秒后过期返回(nil)
Setrange 方法
Set email xingdong@126.com
Setrange email 8 @qq.com   //第8个字符开始替换,只替换现有字符串的长度
Get emali  得到 xingdong@qq.com 
Mset方法
一次设置多个key的值,成功返回OK表示所有的值都设置了,失败并返回0表示没有任何值被设置
Mset name1 xingdong name2 action
Get name1  得到 xingdong
Get name2  得到 action
Msetnx方法
一次设置多个key 的值,成功返回ok表示所有的值都已经设置,失败返回0表示没有任何值设置,但是不会覆盖已经存在的key。
有一个设置不成功,则返回0 false
Getset方法
设置key的值,并返回key的旧值
Getrange方法
获取key 的value值的子字符串
Getrange name 0 5  
Mget方法
一次获取多个key 的值,如果对应key不存在,则对应返回nil
Meget name1 name2
Incr   (decr 对key 的值做减操作)
对key的值做加加操作,并返回新的值
Set  age  24
Incr age  得到的是25
Incrby  (decrby 减指定值)
同incr类似,加指定值,key 不存在的时候会设置key,并认为原来的value为0
Incrby age 5 得到的是 30(加5)
Append
给指定key的字符串追加value,返回新字符串值的长度
Append  name .com
Strlen
取指定key的value值的长度
Strlen name  



Hashs 类型
Hashs类型及操作
Redis hash 是一个string类型的field和value 的映射表。他的添加、删除操作都是0(1)平均。Hash特别适用于存储对象。相较于将对象的每个字段存成单个srting类型。将一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象。

Hset 方法
设置hash  field 为指定值,如果key不存在,则先创建
Hset  user:001 name xingdong
Hget  user:001 name  得到xingdong
Hsetnx方法
设置hash  field 为指定值,如果key不存在,则先创建,如果存在返回0
Hmset
同时设置 hash的多个field
Hmset user:001 name action age 24
Hmget 获得多个值
Hincrby 方法
Hexits  测试指定field 是否存在
Hexits  user:001 name 存在返回1不存在返回0
Hlen 方法 指定hash的field的数量
Hdel 方法 删除指定hash的filed
Hkeys 方法 返回hash的所有field
Hvals 方法 返回hash的所有value 
Hgetall方法 获取 某个hash中全部的field及value


List类型
List类型及操作
List是个链表结构,主要功能是push pop 获取一个范围的所有值等。操作中key理解为链表的名字。Redis 的list类型其实就是每一个子元素都是string类型的双向链表,我们可以通过push pop操作从链表中的头部或者尾部添加删除元素,这样list既可以作为栈,又可以作为队列。
栈   压入 1 2 3 输出 3 2 1
队列 压入 1 2 3 输出 1 2 3 

Lpush  在key对应的list头部添加字符串元素
Lpush  mylist ‘world’
Lpush  mylist ‘hello ’
Lange  mylist 0 -1  // 0到负1   
输出 hello world

rpush  在key对应的list尾部添加字符串元素 

Linsert在key对应list 的特定位置前或者后添加字符串
lpush mylist “world”
Insert mylist before “world”   “hello”   //在world前添加hello 
Lrange mylist 0 -1

Lset设置list中指定下标的元素值
Rpush  mylist “hello”
Lset mylist  0  “world”
Lrange  mylist  0 -1

Lrem从key对应list中删除n个和value相同的元素。(n<0从尾删除,n=0全部删除)
Rpush  mylist “one” 四次
Lrem mylist 3 “one”
Ltrim保留指定key的值范围内的数据,其余的全部删除
Ltrim mylist 1 -1  保留 1 到后面所有的
Lpop 从list的头部删除元素,并返回删除元素
Lpop  mylist
Rpop 与lpop相反
Rpoplpush从第一个list 的尾部移除元素并添加到第二个list的头部
Rpoplpush  mylist1 mylist2
Lindex 返回名称为key的list中的index位置的元素
Lindex mylist 0  
Llen 返回key对应的list类型对应的长度u


Set 类型
Setl类型和操作
Set 是集合,是string类型的无序集合。Set是通过hash table 实现的,添加  删除 和查找的复杂度都是 0(1)。对集合我们可以去并集 交集 差集。通过这些操作我们可以实现sns中的好友推荐和blog的tag功能。
Sadd 向名称为key的set中添加元素
Sadd myset ‘hello’ 重复添加的返回 0

Smembers myset 列出元素
Srem 删除名称为key的set中的元素
Srem myset ‘hello’

Spop 随机返回并删除名称为key 的set中的一个元素
Spop myset

Sdiff 返回所有指定key与第一个key的差集
Sdiff  myset1 myset2  // 以myset为标准

Sdiffstore返回所有指定key与第一个key 的差集,并将结果存为另一个key
Sdiffstore  myset1 myset2 myset3  2与3的差集存到1里面

Sinter  返回所有给定key 的交集
Sinter myset1 myset2

Sinterstore返回所有指定key与第一个key 的交集,并将结果存为另一个key
Sinterstore  myset1 myset2 myset3  2与3的交集存到1里面

Sunion 返回制定所有key 的并集
Sunion myset1 myset2

Sunionstore 返回所有给定key 的并集
Sunionstore myset1 myset2 myset3 同上

Smove 从第一个key对应的set中移除member并添加到第二个对应的set中
Smove myset1 myset2 one 将myset1中的one元素放到myset2 中去

Scard 返回名称为key的set的元素个数
Scard  myset
Sismember 测试member是否是名称为key的set的元素
Sismember myset one

Srandmember  //随机返回名称为key的set的一个元素,但不删除元素
Srandmember myset 
Sorted set类型及操作
Sorted set 是set的一个升级版本,它在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次制定后,zset会自动重新按新的值调整顺序。可以理解为有两列的mysql表,一列存value ,一列存顺序。操作中key理解为  zset的名字。

Zadd方法
向名称为key的zset中添加元素member,score用于排序,如果该元素存在,则更新其顺序
Zadd myset 1 “one”
Zadd myset 2 “two”
Zadd myset 3 “three”
Zrange myset 0 -1 withscores(不叫withscores 只列出 one two three)

Zrem 删除名称为key的zset中的元素member
Zrem myset one

Zincrby 方法
如果在名称为key 的zset中已经存在元素member,则该元素的score增加increment,则该元素的score增加increment,否则向该集合中添加该元素,其score的值为increment 

Zrank
返回名称为key的zset中的member元素的排名(按score从小到大排序)即下标
zrevrank
返回名称为key的zset中的member元素的排名(按score从大到小排序)即下标
Zrevrange  相反排序
Zrevrange myset 0 -1 

Zrangebyscore方法 返回集合中score在给定区间的元素
Zrangebyscore  myset 2 3 withscores  返回 2 到3的元素

Zcount 返回集合中score在给定区间的数量
Zcount myset
Zcard 返回集合中score所有的数量
Zcard myset 
Zremrangebyrank 删除集合中排名在给定区间的元素
Zremrangebyrank myset 1 1(按照索引)
Zremrangebyscore  删除集合中score在给定区间的元素



其他常用命令
Key * 返回满足给定pattern的所有key /    key sess*
Exists 确认一个key 是否存在 /  exists  name
Del 删除一个键  del name
Expire 设置一个key过期的时间 expire name 10 
Ttl name  查看还有多少时间过期

//数据库形式
默认的是select 0  编号0-15
Select 0  Set age 30  Get age 得到值Move age 1  Get age没有值 
Select 1   Get age / 得到值

Persist 移除给定key 的过期时间 Persist name
Randomkey  随机返回key空间的一个key  randomkey 
Rename 重命名 key  rename  age  newgae
Type 返回键的数据类型 type name

Ping 测试连接是否存活  ping
Echo 在命令行打印一些内容 echo name
Quit 退出连接  exit
Dbsize 返回当前数据库中key 的数目 dbsize 
Info 返回服务器的信息和统计
Config get 实时传储收到的请求config get dir   config get *
Flushdb  删除当前选择数据库中的所有key
Flushall  删除所有数据库的所有key


mysql5.6太占内存了

刚在阿里云买的服务器,512内存的,装上mysql5.6,就这样了,哎

QQ截图20151009093717.png

mysql order by varchar类型字段 排序
今天写一个sql语句 最后 order by fld_sort;
出来的顺序竟然是 1,11,2
看下原来fld_sort的字段竟然是 varchar类型
最后写成 order by fld_sort+0; 可以解决
不过感觉如果单独对列表进行排序,排序的字段最好是 写成 int 类型
php用redis存储session数据(附一个简单的类)
php用redis存储session数据(附一个简单的类)
在php.ini中配置
session.save_handler = Redis
session.save_path = "tcp://localhost:6379"
或者在php文件中配置的,
ini_set('session.save_handler','Redis');
ini_set('session.save_path','tcp://localhost:6379');

<?php
class RedisSession{

private $redis;
private $sessionSavePath;
private $sessionName;
private $sessionExpireTime=30;

public function __CONSTRUCT(){
$this->redis = new Redis();
$this->redis->connect('127.0.0.1',6379);
$retval = session_set_save_handler(
array($this,'open'), 
array($this,'close'), 
array($this,'read'), 
array($this,'write'), 
array($this,'destroy')
array($this,'gc')
);
session_start();
}

public function open($path,$name){
return true;
}

public function close(){
return true;
}

public function read($id){
$value = $this->redis->get($id);
if($value){
return $value;
}else{
return '';
}
}

public function write($id,$data){
if($this->redis->set($id,$data)){
$this->redis->expire($id,$this->sessionExpireTime);
return true;
}
return false;
}

public function destroy($id){
if($this->redis->delete($id)){
return true;
}
return false;
}

public function gc($maxlifetime){
return true;
}

public function __DESTRUCT(){
session_write_close();

}

}


?>

在新建两个文件,test1.php
<?php
include('RedisSession.php');
new RedisSession();
$_SESSION['name'] = 'xingdong';
?>

test2.php

<?php
include('RedisSession.php');
new RedisSession();
echo $_SESSION['name'];
?>

关于redis内存淘汰策略
当redis使用的内存数大于可使用的内存数时,进行内存淘汰。
相对于Memcached来说(只有LRU淘汰算法),redis的淘汰算法比较丰富,主要有3种:
1.随机淘汰算法:从数据库中随机删除一个key
2.LRU淘汰算法:从数据库中删除一个最近最少访问的key
3.TTL淘汰算法:从数据库中删除一个最快过期的key
通过maxmemory-policy配置项指定使用的淘汰算法,至于使用哪种淘汰算法,应根据自己的需求设定。

redis 确定驱逐某个键值对后,会删除这个数据并,并将这个数据变更消息发布到本地(AOF 持久化)和从机(主从连接)。


具体到实践:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据


mongodb的备份和修复
mongodb的备份和修复
//备份
mongodump -d test -o backup  //把test数据库备份到backup文件夹下
//恢复
mongorestore -d foo --drop backup/test/  把备份的test数据库恢复到数据库foo中

//备份前加锁
use admin 
db.runCommand({"fsync":1,"lock":1})//返回{"info":"...","ok":1}
//解锁
db.$cmd.sys.unlock.findOne();//返回{"ok":1,"info":"unlock requested"}
db.cuurentOp();//为了确保已经解锁,返回{"inprog":[]}

//修复
未能正常停止MongoDb后应该修复数据库。要是未正常停止,下次启动服务器备份时MongoDb会提示
修复所有数据库最简单的方式就是 加上--repair;mongod --repair 来启动服务。
//修复数据库还能起到压缩数据的作用。闲置的空间在修复后被重新回收。
use test
db.repairDatabases()
通过驱动程序的话是:
{"repairDatabases":1}

MongoDB之MapReduce(转)
MongoDB MapReduce学习笔记
语法:
db.runCommand(
 { mapreduce : 字符串,集合名,
   map : 函数,见下文
   reduce : 函数,见下文
   [, output : 字符串,统计结果保存的集合。]
   [, query : 文档,会在发往map函数前,先用指定条件过滤文档]
   [, sort : 文档,会在发往map函数前,先给文档排序]
   [, limit : 整数,发往map函数的文档数量上限]
   [, keeptemp: 布尔值,链接关闭时临时结果集合是否保存]
   [, finalize : 函数,将reduce的结果送给这个函数,做最后的处理]
   [, scope : 文档,js代码中要用到的变量]
   [, jsMode : 布尔值,是否减少执行过程中BSON和JS的转换,默认true] //注:false时 BSON-->JS-->map-->BSON-->JS-->reduce-->BSON,可处理非常大的mapreduce,
   //true时BSON-->js-->map-->reduce-->BSON
   [, verbose : 布尔值,是否产生更加详细的服务器日志,默认true]
 }

);


测试数据:

2012020914555740.jpg


第一步是写映射(Map)函数,可以简单的理解成分组吧~
var m=function(){
    emit(this.age,this.name);
}

emit的第一个参数是key,就是分组的依据,这是自然是age了,后一个是value,可以是要统计的数据,下面会说明,value可以是JSON对象。
这样m就会把送过来的数据根据key分组了,可以想象成如下结构:

第一组
{key:0,values: ["name_6","name_12","name_18"]
第二组
{key:1,values: ["name_1","name_7","name_13","name_19"]
......
组中的key其实就是age的值了,values是个数组,数组内的成员都有相同的age!!。

第二步就是简化了,编写reduce函数
var r=function(key,values){
    var ret={age:key,names:values};
    return ret;
}
reduce函数会处理每一个分组,参数也正好是我们想像分组里的key和values。
这里reduce函数只是简单的把key和values包装了一下,因为不用怎么处理就是我们想要的结果了,然后返回一个对象。对象结构正好和我们想象的相符!:
{age:对应的age,names:[名字1,名字2..]}

最后,还可以编写finalize函数对reduce的返回值做最后处理:
var f=function(key,rval){
    if(key==0){
        rval.msg="a new life,baby!";
    }
    return rval
}

这里的key还是上面的key,也就是还是age,rval是reduce的返回值,所以rval的一个实例如:{age:0,names:["name_6","name_12","name_18"]},
这里判断 key 是不是 0 ,如果是而在 rval 对象上加 msg 属性,显然也可以判断 rval.age==0,因为 key 和 rval.age 是相等的嘛!!
这里其他的选项就不说了,一看就知道。

执行:
db.runCommand({
    mapreduce:"t",//集合名字
    map:m,
    reduce:r,
    finalize:f,
    out:"t_age_names"
    }
)

2012020915382353.jpg



优惠券
最新微语