PHP Yac cache 源码学习笔记

YAC 源码学习笔记

本文地址

http://blog.csdn.net/fanhengguang_php/article/details/54863955

config.m4

检测系统共享内存支持情况

通过autoconf语法AC_TRY_RUN AC_DEFINE 语法进行以下检查

  1. 是否支持system v ipc 共享内存
  2. 是否支持mmap MAP_ANON 共享内存
  3. 是否支持mmap(“/dev/zero”)共享内存

代码示例

AC_TRY_RUN([
#include 
#include 
#include 
#include 
#include 
#include 

int main() {
  //somthing
}
],dnl
AC_DEFINE(HAVE_SHM_IPC, 1, [Define if you have SysV IPC SHM support])
    msg=yes,msg=no,msg=no)

模块初始化MINIT阶段

读取php.ini 关于key、value 内存等设置

yac.enable = 1

yac.keys_memory_size = 4M ; 4M can get 30K key slots, 32M can get 100K key slots

yac.values_memory_size = 64M

yac.compress_threshold = -1

yac.enable_cli = 0 ; whether enable yac with cli, default 0

缓存空间空间初始化

缓存空间初始化。根据当前系统的情况, 分别选择使用以下方式进行内存的申请、初始化

  1. mmap方式
  2. shm方式
  3. filemaping方式

具体可参见yac/storage/allocator/目录下的createfilemap.c ,mmap.c, shm.c 中的create_segments 方法

初始化Yac类

注册yac类

yac类 _construct方法

Yac::__construct([string $prefix])

zend_update_property_str(yac_class_ce, getThis(), ZEND_STRL(YAC_CLASS_PROPERTY_PREFIX), prefix); 更新缓存前缀

yac类 set方法

将key => value 的映射存储到缓存中, 函数原型:

 Yac::set($key, $value[, $ttl])
 Yac::set(array $kvs[, $ttl])

$yac = new Yac();
$yac->set("foo", "bar");
$yac->set(
    array(
        "dummy" => "foo",
        "dummy2" => "foo",
        )
    );
?>

value归一化&压缩

对于不同类型的value(int, bool, long, double, string, constant等) 进行归一化。 统一归一化为字符串, 并进行压缩处理

$yac->set("foo", "bar");为例, 写cache的操作步骤如下:

  1. 将prefix 与原始key链接在一起, 作为新的key。 并判断新key是否超过48个字符,超过则失败
  2. 对于有value 为 null, bool , long, double 类型, 直接将对应的数字类型转换为字符串类型
  3. 对于value为string类型 or constant类型, 如果value字符串长度超过1<<20后, 利用fastlz方法进行字符串压缩处理
  4. 对于array 或者object类型的value, 调用php serializer 方法, 或者利用msgpack方法进行序列化, 并进行压缩处理

cache更新 yac_storage_update

yac key, value 的结构如下:

typedef struct {
    unsigned long h;   //key的hash值
    unsigned long crc;  //value的crc校验码
    unsigned int ttl;   //过期时间
    unsigned int len;   //key的长度
    unsigned int flag;  //value类型,字符串、整形、等等
    unsigned int size;  //此key 对应 的value 的长度
    yac_kv_val *val;    //value
    unsigned char key[YAC_STORAGE_MAX_KEY_LEN];
} yac_kv_key;

typedef struct { 
    unsigned long atime;
    unsigned int len;
    char data[1];
} yac_kv_val;
  1. 通过MurmurHash2 算法计算key的hash值
  2. 通过hash值找到对应的slots段的起始位置, 即:一个yac_kv_key 指针
  3. 如果此slot的value为空,那么说明之前没有存储过该key, 将执行add操作
  4. 如果value 值非空并且hash值相同&key长度相同&key完全一样, 那么说明, 此key之前已经存储过了,将执行update操作。
    • 对此slot对应的value计算crc, 与slot中的crc对比, 判断value是否合法
    • 根据原来value的存储空间大小与新value的size来重新分配内存,更新过期时间, 重新计算crc校验码
  5. 如果hash值不同或者key值不一致,说明当前slot,并不是数据存储的位置,
    • 利用times33算法计算hash值, 并根据hash值找到对应的卡槽,进行插入数据
    • 如果所有卡槽都满了, 则根据过期时间剔除一个卡槽, 将数据插入

yac类 get方法

读取key的value值函数原型:

 Yac::get(array|string $key)
php
$yac = new Yac();
$yac->set("foo", "bar");
$yac->set(
    array(
        "dummy" => "foo",
        "dummy2" => "foo",
        )
    );
$yac->get("dummy");
$yac->get(array("dummy", "dummy2"));
?>

具体实现参见static zval * yac_get_impl(zend_string *prefix, zend_string *key, uint32_t *cas, zval *rv) 方法

yac_get_impl 方法

  1. 将prefix 与key连接为新的key
  2. 调用yac_storage_find 方法取得value值, 从内存中根据key查找value, 具体做法与set方法基本一致,
    • MurmurHash2算法计算key hash值, 并找到相应的slots内存位置
    • 如果找不到再利用times33算法计算hash 在后面继续查找
    • 找到相应slot位置后, 判断value是否过期, 判断crc校验码是否一致。
  3. 根据value的flag类型将value还原为特定的类型(null, bool, long, double等等)

    switch ((flag & YAC_ENTRY_TYPE_MASK)) {
            case IS_NULL:
                if (size == sizeof(int)) {
                    ZVAL_NULL(rv);
                }
                efree(data);
                break;
            case IS_TRUE:
                if (size == sizeof(int)) {
                    ZVAL_TRUE(rv);
                }
                efree(data);
                break;
            case IS_FALSE:
                if (size == sizeof(int)) {
                    ZVAL_FALSE(rv);
                }
                efree(data);
                break;
            case IS_LONG:
                if (size == sizeof(long)) {
                    ZVAL_LONG(rv, *(long*)data);
                }
                efree(data);
                break;
  4. 对于字符串或者CONSTANT类型, 利用fastlz算法进行解压缩

    case IS_STRING:
                case IS_CONSTANT:
                    {
                        if ((flag & YAC_ENTRY_COMPRESSED)) {
                            size_t orig_len = ((uint32_t)flag >> YAC_ENTRY_ORIG_LEN_SHIT);
                            char *origin = emalloc(orig_len + 1);
                            uint32_t length;
                            length = fastlz_decompress(data, size, origin, orig_len);
                            ZVAL_STRINGL(rv, origin, length);
                            efree(origin);
                            efree(data);
                        } else {
                            ZVAL_STRINGL(rv, data, size);
                            efree(data);
                        }
                    }
                    break;
    
  5. 对于ARRAY OR OBJECT 类型进行先进性解压缩 在用unserializer yac_serializer_php_unpack 操作转换为对象或数组

    case IS_OBJECT:
    
    length = fastlz_decompress(data, size, origin, 
    rv = yac_serializer_php_unpack(data, size, &msg, rv);
    

更多内容可参考鸟哥博客 http://www.laruence.com/2013/03/18/2846.html

你可能感兴趣的:(PHP)