邢栋博客

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

Mac下使用clion调试redis源码

1、先从github上下载源码
1)搜索 https://github.com/search?q=redis+clion
2)最好是选择别人配置好CMakeLists.txt的,不然自己配置也很麻烦


2、下载完成,开始配置
//选择一个想要调试的版本进行下载
wget https://github.com/htw0056/redis-3.0-annotated-cmake-in-clion/archive/master.zip
//解压
unzip redis-3.0-annotated-cmake-in-clion-master.zip
//重命名+拷贝
mv redis-3.0-annotated-cmake-in-clion-master redis-3.0
cp -R redis-3.0 redis-3.0-make

//编译
cd redis-3.0-make/src
make


3、打开clion软件,导入redis-3.0项目
1)搜索到saddCommand命令,打个断点,开启debug模式

image2020-12-9_20-56-1.png
4、进入到客户端服务器,执行sadd命令
1)进入到redis-3.0-make/src,开始执行命令
./redis-cli
sadd numbers 1 77777 xd
这个时候就打开clion软件一步一步往下走了

image2020-12-9_20-57-44.png

关于redis pipeline
=====为什么需要 pipeline ?=====
Redis 的工作过程是基于 请求/响应 模式的。正常情况下,客户端发送一个命令,等待 Redis 应答;Redis 接收到命令,处理后应答。请求发出到响应的时间叫做往返时间,即 RTT(Round Time Trip)。在这种情况下,如果需要执行大量的命令,就需要等待上一条命令应答后再执行。这中间不仅仅多了许多次 RTT,而且还频繁的调用系统 IO,发送网络请求。为了提升效率,pipeline 出现了,它允许客户端可以一次发送多条命令,而不等待上一条命令执行的结果。

=====实现思路=====
客户端首先将执行的命令写入到缓冲区中,最后再一次性发送 Redis。但是有一种情况就是,缓冲区的大小是有限制的:如果命令数据太大,可能会有多次发送的过程,但是仍不会处理 Redis 的应答。

=====实现原理=====
要支持 pipeline,既要服务端的支持,也要客户端支持。对于服务端来说,所需要的是能够处理一个客户端通过同一个 TCP 连接发来的多个命令。可以理解为,这里将多个命令切分,和处理单个命令一样。对于客户端,则是要将多个命令缓存起来,缓冲区满了就发送,然后再写缓冲,最后才处理 Redis 的应答。

=====Redis pipeline 的参考资料=====
https://redis.io/topics/pipelining

=====在php中使用Redis pipeline=====
<?php
 //实例化redis
$redis = new Redis();
//连接
$redis->connect('127.0.0.1', 6379);
$redis->pipeline();//开启管道
if(!$redis){
    throw new Exception('redis连接失败!',1);
}
$key = "pipeline_test";
$len = 200000;
$succ = 0;
for($i=0;$i<$len;$i++){
    $res = $redis->hset($key,"test_".$i,1);
    if($res){
        $succ ++;
    }
}
$redis->exec();

=====总结:Redis pipeline 的特性以及使用时需要注意的地方=====
pipeline 减少了 RTT,也减少了IO调用次数(IO 调用涉及到用户态到内核态之间的切换)
如果某一次需要执行大量的命令,不能放到一个 pipeline 中执行。数据量过多,网络传输延迟会增加,且会消耗 Redis 大量的内存。应该将大量的命令切分为多个 pipeline 分别执行。

redis底层数据结构总结
redis有五种对象的类型

REDIS_STRING  字符串对象
REDIS_LIST 列表对象
REDIS_HASH 哈希对象
REDIS_SET 集合对象
REDIS_ZSET 有序集合对象


底层数据结构共有八种
编码常量 编码对应的底层数据结构
1、REDIS_ENCODING_INT long类型的整数
2、REDIS_ENCODING_EMBSTR embstr编码的简单动态字符串
3、REDIS_ENCODING_RAW 简单动态字符串
4、REDIS_ENCODING_HT 字典
5、REDIS_ENCODING_LINKEDLIST 双端链表
6、REDIS_ENCODING_ZIPLIST 压缩列表
7、REDIS_ENCODING_INTSET 整数集合
8、REDIS_ENCODING_SKIPLIST 跳跃表和字典


1.字符串对象
编码可以为 int、embstr或者raw。
如果一个字符串的内容可以转换为long,那么该字符串就会被转换成为long类型,对象的ptr就会指向该long,并且对象类型也用int类型表示。
普通的字符串有两种,embstr和raw。embstr应该是Redis3.0新增的数据结构,在2.8中是没有的。如果字符串对象的长度小于39字节,就用embstr对象。否则用传统的raw对象。

2.列表对象
编码可以是ziplist或者linkedlist。
ziplist是一种压缩链表,它的好处是更能节省内存空间,因为它所存储的内容都是在连续的内存区域当中的。当列表对象元素不大,每个元素也不大的时候,就采用ziplist存储。但当数据量过大时就ziplist就不是那么好用了。因为为了保证他存储内容在内存中的连续性,插入的复杂度是O(N),即每次插入都会重新进行realloc。
linkedlist是一种双向链表。它的结构比较简单,节点中存放pre和next两个指针,还有节点相关的信息。当每增加一个node的时候,就需要重新malloc一块内存。

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


3.哈希对象
编码可以是ziplist或者hashtable。
ziplist中的哈希对象是按照key1,value1,key2,value2这样的顺序存放来存储的。当对象数目不多且内容不大时,这种方式效率是很高的。
hashtable的是由dict这个结构来实现的,dict是一个字典,其中的指针dicht ht[2] 指向了两个哈希表。dicht[0]是用于真正存放数据,dicht[1]一般在哈希表元素过多进行rehash的时候用于中转数据。
dictht中的table用语真正存放元素了,每个key/value对用一个dictEntry表示,放在dictEntry数组中


4.集合对象
编码可以是intset或者hashtable。
intset是一个整数集合,里面存的为某种同一类型的整数,支持如下三种长度的整数:
#define INTSET_ENC_INT16 (sizeof(int16_t))  
#define INTSET_ENC_INT32 (sizeof(int32_t))  
#define INTSET_ENC_INT64 (sizeof(int64_t))  
intset是一个有序集合,查找元素的复杂度为O(logN),但插入时不一定为O(logN),因为有可能涉及到升级操作。比如当集合里全是int16_t型的整数,这时要插入一个int32_t,那么为了维持集合中数据类型的一致,那么所有的数据都会被转换成int32_t类型,涉及到内存的重新分配,这时插入的复杂度就为O(N)了。是intset不支持降级操作。

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


5.有序集合对象
有序集合的编码可能两种,一种是ziplist,另一种是skiplist与dict的结合。
ziplist作为集合和作为哈希对象是一样的,member和score顺序存放。按照score从小到大顺序排列。
skiplist是一种跳跃表,它实现了有序集合中的快速查找,在大多数情况下它的速度都可以和平衡树差不多。但它的实现比较简单,可以作为平衡树的替代品。
当有序集合对象可以同时满足以下两个条件时, 对象使用 ziplist 编码:
有序集合保存的元素数量小于 128 个;
有序集合保存的所有元素成员的长度都小于 64 字节;
不能满足以上两个条件的有序集合对象将使用 skiplist 编码。
ps:
以上两个条件的上限值是可以修改的, 具体请看配置文件中关于 zset-max-ziplist-entries 选项和 zset-max-ziplist-value 选项的说明。
php使用redis替换文件存储session(session_set_save_handler)
<?php
class MySessionHandler implements SessionHandlerInterface
{
    private $redis;
    private $sessionsavepath;
    private $sessionname;
    public function __construct()
    {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1',6379);
    }
    public function close()
    {
        return true;
    }
    public function destroy($session_id)
    {
        if($this->redis->delete($session_id)){
            return true;
        }
        return false;
    }
    public function gc($maxlifetime)
    {
        return true;
    }
    public function open($sessionsavepath, $name)
    {
        return true;
    }
    public function read($session_id)
    {
        if($value = $this->redis->get($session_id)){
            return $value;
        }else{
            return false;
        }
    }
    public function write($session_id, $session_data)
    {
        if($this->redis->set($session_id,$session_data,60)){
            return true;
        }else{
            return false;
        }
    }

}

$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();

//$_SESSION['sess_name'] = 'xd';
var_dump($_SESSION);
redis查看当前redis-server启动使用的配置文件

redis查看当前redis-server启动使用的配置文件


redis-cli info | grep config 

windows下把redis加入服务

加入服务,开机自启动
redis-server.exe --service-install redis.windows.conf

启动
redis-server.exe --service-start
停止
redis-server.exe --service-stop
卸载
redis-server.exe --service-uninstall

安装多个实例
redis-server.exe --service-install –service-name redisService1 –port 10001
redis-server.exe --service-start –service-name redisService1
redis-server.exe --service-install –service-name redisService2 –port 10002
redis-server.exe --service-start –service-name redisService2
redis-server.exe --service-install –service-name redisService3 –port 10003
redis-server.exe --service-start –service-name redisService3

关于redis 的排序
redis的sort命令可以对列表键、集合键或者有序集合键的值进行排序。

SORT命令的实现
SORT <key>
SORT 命令的排序操作由快速排序算法实现。
例子
rpush numbers 5 3 4 1 2
lrange numbers 0 -1 //无排序
sort numbers  //排序 正序

ALPHA选择的实现
SORT <key> ALPHA
例子
sadd alphabet a b c d e f g
smembers alphabet //无排序
sort alphabet ALPHA//排序

ASC选项和DESC选项的实现
SORT <key> [ASC|DESC]

BY选项的实现
BY选项默认假设权重键保存的值为数字值,如果权重键保存的是字符串的话,那么就需要在使用BY选项的同时,配合使用ALPHA选项。
zadd test-result 3.0 jack 3.5 peter 4.0 tom
zrange test-result 0 -1
mset peter_number 1 tom_number 2 jack_number 3
sort test-result BY *_number

LIMIT选项的实现
通过limit选项,可以让sort命令只返回一部分以及排序的元素。
limit选项的格式为 LIMIT <offset> <count>
<offset> 参数表示要跳过的已经排序的元素数量
<count> 参数表示跳过给定数量的已排序元素之后,要返回的已排序元素数量。

GET选项的实现
通过使用get选项,我们可以让sort命令在对键进行排序之后,根据被排序的元素,以及get选项所指定的模式,查找并返回某些键的值
例子
sadd students "peter" "jack" "tom" //
set peter-name "peter white"
set jack-name "jack snow"
set tom-name "tom smith"
sort students ALPHA GET *_name //
返回结果
1)"jack snow"
2)"peter white"
3)"tom smith"

SORTE 选项的实现
通过sorte选项,我们可以将排序结果保存在指定的键里面,并在需要时重用这个排序结果。
例子
sort students ALPHA sorte sorted_students
注意:如果sorted_students这个键存在,则会被删除,然后重新设置sorted_students的空白键


多个选项的执行顺序
1.排序(命令会使用ALPHA、[ASC|DESC]、BY选项)
2.限制(limit)
3.获取外部键(get选项)
4.保存排序结果集(sorte选项 )
5.向客户端返回排序结果集

选择的摆放顺序
除了 GET 选项之外, 调整选项的摆放位置不会影响 SORT 命令的排序结果。

内容来自:<redis设计与实现>这本书


关于redis的事务
redis的事务

实现
redis> MULTI
OK
redis> SET name "xingdong"
QUEUED
redis> GET name
QUEUED
redis> SET author 'Action'
QUEUED
redis> GET author
QUEUED
redis> EXEC
1) OK
2) "xingdong"
3) OK
4) "Action"

ps:事务队列以先进先出的方法保存入队的命令。

watch命令监视数据库键
实现
redis> watch name author
OK
带有 WATCH 命令的事务会将客户端和被监视的键在数据库的 watched_keys 字典中进行关联, 当键被修改时, 程序会将所有监视被修改键的客户端的 REDIS_DIRTY_CAS 标志打开。
只有在客户端的 REDIS_DIRTY_CAS 标志未被打开时, 服务器才会执行客户端提交的事务, 否则的话, 服务器将拒绝执行客户端提交的事务。

判断事务是否安全
判断事务是否安全,服务器会会根据这个客户端是否打开了REDIS_DIRTY_CAS标识来决定是否执行事务


事务的ACID性质
1.原子性
对于redis的事务功能来说,事务队列中的命令要么就是全部执行,要么就是一个都不执行。
redis的事务和传统的关系型数据库事务的最大区别在于,Redis不支持事务回滚机制。即使事务队列中的某个命令在执行期间出现了错误,整个事务也会继续执行下去,直到将事务队列中的所有命令都执行完毕为止。
2.一致性
事务具有一致性指的是,如果数据库在执行事务之前是一致的,那么在事务执行之后,无论事务是否执行成功,数据库也应该仍然是一致的。
a.入队错误
如果一个事务在入队命令的过程中,出现了命令不存在,或者命令的格式不正确等情况,那么redis将拒绝执行这个事务。
ps:redis2.6.5以前的入队错误处理,这种情况是被忽略的。
例子:
MUTI
set msg 'hello'
xingdong// ****
get msg
EXEC
此时服务器会拒绝执行入队过程中出现的事务。

b.执行错误
执行过程中发生的错误都是一些不能再入队时被服务器发现的错误,这些错误只会在命令实现执行时被触发。
即使在事务的过程中发生了错误,服务器也不会中断事务的执行,它会继续执行事务中余下的命令,并且已经执行的命令不会被出错的命令影响。
例子
set msg 'hello'
MULTI
sadd fruit 'apple' 'banana' 'cherry'
rpush msg 'see you' 'bye' //********
sadd language 'php' 'python'
EXEC
这里的出错命令不会对数据库做任何修改,但不会对事务的一致性产生认识影响。


c.隔离性
事务的隔离性指的是,即使数据库中有多个事务并发执行,各个事务之前也不会互相影响,并且在并发状态下执行的事务和串行执行的事务产生的结果完全相同。
因为redis是使用单线程的方式来执行事务(以及事务队列中的命令),并且服务器保证,在执行事务期间不会对事务进行中断,因此,redis 的事务总是以串行的方式运行的,并且事务也总是具有隔离性的。

d.耐久性
redis的耐久性
当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时,程序总会在执行命令之后调用同步sync函数,将命令数据真正的保存到硬盘里面,只有这种配置下的事务具有耐久性的。(并且no-appendfsync-on-rewrite配置是关闭的)

下面这种做法可以保证事务的耐久性,不过效果太低,不具有实用性。
redis> MULTI
OK
redis> SET name "xingdong"
QUEUED
redis> SAVE
QUEUED
redis> EXEC
1) OK
2) OK


内容来自:<redis设计与实现>这本书



优惠券
最新微语