前言

复习memcached中,发现很多人对set/add 语法中设置的 flags参数理解不是很透彻, 大家人云亦云。 查资料从来不对别人的材料加以自己的理解写出来,那其实有毛用。百度出来的文章如出一辙,完全是同一篇文章。总是说flags参数来表示是否压缩数据。。。。我怀疑很多人也真实人云亦云,自己没有思考过。

flags参数理解

flags这个参数其实是让客户端给自己的字符串数据打一个label标记罢了,就这么简单。由于memcached存放的数据类型只有string一种,那么memcached不知道你存的数据到底是序列化的字符串还是普通字符串,那么你设置一个flags,等你使用get拿到string的时候,也把这个flags给你,让你自己判断进行处理。目的就是为了方便各个语言版本的客户端例如 Java Python PHP等等,可以通过存储序列化后的字符串,达到存放数据的目的。 这样的话客户端可以根据自己给字符串设置的flags,判断例如是否要反序列化等等操作。 对于调用memcached的客户端库是透明的,用户无感的,感觉就是使用set/add把一个变量存进去, 后面使用get获取这个变量就行了。其实客户端库底层就是通过这个flags帮你实现的,不过各个语言的实现可能不同,但是原理是一样。
  例如php的memcache扩展,底层就帮我们把这个实现细节屏蔽了。通过add/set存对象(object),数组(array)都会自动序列化, 取数据会反序列化。 但是对于string int double bool只是就直接存了。 所以其实在memcache扩展中,我们一般情况下直接把flags设置false即可。实在是用到flags的话,只能填一些参数 如 ture false null 以及压缩常量MEMCACHE_COMPRESSED表示压缩数据,其他值可能会报warning错误或者致命错误。

原文内容

github原文: https://github.com/squarezhou/ruhoh-blog/blob/master/posts/php-memcache-extension-flags.md

###问题描述###

今天帮同事分析一段诡异的代码,代码本身很乱,加上关闭了错误输出,所以开始一直没找到问题。最后才定位到是Memcache扩展set函数flags参数设置问题,再后来结合Memcache扩展源码找出问题根本原因,并开启error_reporting验证了问题原因。
问题代码大概是这样的:

<?php
error_reporting(7);
$mc = new Memcache();
$mc->connect('127.0.0.1',11211);
$mc->set('key', 'value', true, 60);
sleep(1);
var_dump($mc->get('key'));

最终输出为bool(false),不是预期的string(5) "value"。这段代码其实有两个坑,一小一大。
小坑是同事告诉我error_reporting(7)与error_reporting(E_ALL)作用是相同的,由于我一直使用后者,也忘记了E_ALL常量的值,加上不清楚有没其他特别写法。这个坑我很快掉进去了,于是就以为所有的错误信息将会被输出。
大坑就是Memcache扩展set函数第3个参数的值,下面重点说这个。不看手册乱用害死人吧~

###Memcached的flags###

Memcached在存储数据时除了可以指定过期时间外,还支持flags参数。SET <key> <flags> <expiration time> <bytes>,第2个参数就是flags。
在Memcached 1.2.1之前为flags预留了16位,到了1.2.1以后预留了32位(4 bytes)。对于服务器端而言,并不清楚你设置这个标记的作用,只是在你取回数据的时候同时传回给客户端。因此客户端就可以利用这个值来标记数据是否是经过编码的、是压缩过的等,PHP Memcache扩展就是这么做的。

###Memcache扩展的flags###

Memcache扩展中定义了两个宏MMC_SERIALIZED和MMC_COMPRESSED,分别表示序列化和zlib压缩,但只有MMC_COMPRESSED对应值被同时定义为PHP常量。

宏定义

#define MMC_SERIALIZED 1
#define MMC_COMPRESSED 2

常量定义

REGISTER_LONG_CONSTANT("MEMCACHE_COMPRESSED", MMC_COMPRESSED, CONST_CS | CONST_PERSISTENT);

看到这里,其实我们隐约可以感觉到Memcache扩展是不支持在PHP中启用数据序列化的,事实正是如此。
当flags参数为MMC_COMPRESSED(2)时,扩展会对数据进行zlib压缩后再发送给服务端;在取回数据后再根据flags参数,对数据解压并返回给PHP。判断使用位与(&)。

存储压缩

if (flags & MMC_COMPRESSED) {unsigned long data_len;if (!mmc_compress(&data, &data_len, value, value_len TSRMLS_CC)) {/* mmc_server_seterror(mmc, "Failed to compress data", 0); */return -1;}/* was enough space saved to motivate uncompress processing on get */if (data_len < value_len * (1 - pool->min_compress_savings)) {value = data;value_len = data_len;}else {flags &= ~MMC_COMPRESSED;efree(data);data = NULL;}
}

取回解压

if ((*flags & MMC_COMPRESSED) && data_len > 0) {char *result_data;unsigned long result_len = 0;if (!mmc_uncompress(&result_data, &result_len, data, data_len)) {mmc_server_seterror(mmc, "Failed to uncompress data", 0);if (key) {efree(*key);}efree(data);php_error_docref(NULL TSRMLS_CC, E_NOTICE, "unable to uncompress data");return 0;}efree(data);data = result_data;data_len = result_len;
}

但数据序列化却不一样,是否序列化不受flags参数控制。只有值类型不是string、long、double、bool类型,即array、object等类型时,才会对值序列化,而无视flags参数,当然这里序列化后会更新flags值确保取回时反序列化。但在取回数据时,扩展会严格根据flags值判断是否对数据反序列化。

存储序列化

default: {zval value_copy, *value_copy_ptr;/* FIXME: we should be using 'Z' instead of this, but unfortunately it's PHP5-only */value_copy = *value;zval_copy_ctor(&value_copy);value_copy_ptr = &value_copy;PHP_VAR_SERIALIZE_INIT(value_hash);php_var_serialize(&buf, &value_copy_ptr, &value_hash TSRMLS_CC);PHP_VAR_SERIALIZE_DESTROY(value_hash);if (!buf.c) {/* something went really wrong */zval_dtor(&value_copy);php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to serialize value");RETURN_FALSE;}flags |= MMC_SERIALIZED;zval_dtor(&value_copy);result = mmc_pool_store(pool, command, command_len, key_tmp, key_tmp_len, flags, expire,buf.c, buf.len TSRMLS_CC);
}

取回反序列化

/* not found */
if (result == 0) {ZVAL_FALSE(*return_value);
}
/* read "END" */
else if ((response_len = mmc_readline(mmc TSRMLS_CC)) < 0 || !mmc_str_left(mmc->inbuf, "END", response_len, sizeof("END")-1)) {mmc_server_seterror(mmc, "Malformed END line", 0);result = -1;
}
else if (flags & MMC_SERIALIZED ) {result = mmc_postprocess_value(return_value, value, value_len TSRMLS_CC);
}
else {ZVAL_STRINGL(*return_value, value, value_len, 0);
}

OK,问题来了:如果值类型为string、long、double、bool其中之一,并且在set时设置了flags为1、true、所有可以转换为1或&1>0的值时,扩展在存储时不会序列化,但在取回时尝试反序列化,这时会抛出一个"unable to unserialize data"的Notice。如果代码关闭了Notice输出或错误输出,那就像上面的代码一样,除了输出bool(false)什么也看不到。

###解决方案###

  • 使用Memcached扩展(无flags参数);
  • 修改扩展源码,在判断序列化前使用flags &= ~MMC_SERIALIZED强制取消序列化标识;
  • 封装或继承set函数,对上面4种类型判断自动修改flags值。

全网最详细解释memcached中的flags含义相关推荐

  1. [转]详细解释数据挖掘中的 10 大算法

    在一份调查问卷中,三个独立专家小组投票选出的十大最有影响力的数据挖掘算法,今天我打算用简单的语言来解释一下. 一旦你知道了这些算法是什么.怎么工作.能做什么.在哪里能找到,我希望你能把这篇博文当做一个 ...

  2. 详细解释C# 中Path.Join与Path.Combine的区别

    C# 中Path.Join与Path.Combine的区别 public static void Main(){var path1 = "C:/Program Files/";va ...

  3. oracle中decode函数详细解释,oracle中的decode函数

    decode函数能够实现逻辑的if--else选择判断,但是只能进行等值判断,就像switch--case. 基本语法格式如下:DECODE(col/expression , search1 , re ...

  4. linux中ping命令详解,linux中的ping命令的详细解释

    linxu下的ping命令的主要功能就是确定网络状态,下面由秋天网 Qiutian.ZqNF.Com小编为大家整理了linux的ping命令的详细解释的相关知识,希望对大家有帮助! 一.linux中的 ...

  5. linux中useradd的文件路径,linux的useradd命令详细解释

    Linux中的useradd命令顾名思义就是添加用户的命令.下面由学习啦小编为大家整理了Linux的useradd命令的详细解释的相关知识,希望对大家有帮助! linux的useradd命令的详细解释 ...

  6. 英语中的介词详细解释

    英语中的介词详细解释 xixi2015-01-05 10:02:58 转载于:https://www.cnblogs.com/lyhabctranslate/p/4202991.html

  7. theano中的Rop和Lop的详细解释

    #------------------------------------------序------------------------------------------------------- ...

  8. Spring中IOC和AOP的详细解释(转)

    原文链接:Spring中IOC和AOP的详细解释 我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂 ...

  9. linux 的ss命令,Linux中的ss命令的详细解释

    linxu下的ss其实Socket Statistics的缩写.下面由学习啦小编为大家整理了linux的ss命令的详细解释的相关知识,希望对大家有帮助! 一.Linux中的ssh命令的详细解释 1.命 ...

最新文章

  1. 通过define _CRTDBG_MAP_ALLOC宏来检测windows上的code是否有内存泄露
  2. Android -- Annotation(注解)原理详解及常见框架应用
  3. CSS盒模型及边距问题
  4. linux编程 fmemopen函数打开一个内存流 使用FILE指针进行读写访问
  5. 如何回答十个最棘手的面试问题(下)
  6. DeepLearning based on PaddlePaddle系列二
  7. javascript中的内存管理
  8. c语言常量的正确表示const,C语言中的const和free用法详解
  9. 模拟微信自动化发送(微信公众号文章自动点击)
  10. C#获取当前堆栈的各调用方法列表
  11. Dns-prefetch DNS 预解析优化页面加载速度
  12. 中专生计算机教案,[定稿]计算机基础教案中专V8.1(全文完整版)
  13. haproxy之安装与配置详解
  14. KR C C90,C99的改进
  15. JDK各版本新特性(完整版)
  16. 使用Zbar进行二维码识别 中文字符解码 RawBytes
  17. gmap实现地图的旋转
  18. 夜神模拟器 Nox Player 雷电模拟器 掉线 连不上 运行不显示的解决方案
  19. Android虚拟机的安装
  20. 以太坊源码分析:fetcher模块和区块传播

热门文章

  1. 实现不同编程语言间的自由代码转换需要注意些什么?【经验分享】
  2. 【题解】CF#24 D-Broken Robots
  3. Python采集烤肉店数据,又是一个外包项目,努力挣钱,实现吃肉自由
  4. Mari投射(映射)XYZ纹理(置换贴图)画不上去
  5. ROC曲线的AUC(以及其他评价指标的简介)知识整理
  6. 南航大学生华为俱乐部签约成立
  7. 千峰JAVA逆战班Day49
  8. hon linux lol中文语音包,致lol转来的童鞋们:HON和英雄联盟的不同点
  9. 杯香茗氤氲着淡淡的清香
  10. 在易趣的日子 (02) 当忽悠成为一种时尚