range

If there were you, the world would be just right

一、内存回收策略

1. 删除到达时间的键对象。 
2. 内存使用达到maxmemory上限时触发内存溢出控制策略。

二、持久化

持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令) 从内 存保存到硬盘。 当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。Redis持久化 分为 RDB持久化和AOF持久化,前者将当前数据保存到硬盘,后者则是将每次执行的写命令保存到硬盘。

2.1. RDB持久化
    RDB是一种快照存储持久化方式,具体就是将Redis某一时刻的内存数据保存到硬盘的文件当中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新 加载dump.rdb文件的数据到内存当中恢复数据。触发 RDB 持久化过程分为手动触发和自动触发。

    手动触发分别对应 save 和 bgsave 命令 
        save 命令:阻塞当前 Redis 服务器,直到 RDB 过程完成为止,对于内存比较大的实例会造成长时间阻塞,线上环境不建议使用。 
        bgsave 命令:Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短。显然 bgsave 命令是针对 save 阻塞问题做的优化。因此 Redis 内部所有的涉及 RDB 的操作都采用 bgsave 的方式 
    除了执行命令手动触发之外,Redis 内部还存在自动触发RDB 的持久化机制,例如以下场景: 
        1. 使用 save 相关配置,如“save m n”。表示 m 秒内数据集存在 n 次修改时,自动触发 bgsave。 
        2. 如果从节点执行全量复制操作,主节点自动执行 bgsave 生成 RDB 文件并发送给从节点。 
        3. 执行 debug reload 命令重新加载 Redis 时,也会自动触发 save 操作。 
        4. 默认情况下执行shutdown命令时,如果没有开启 AOF 持久化功能则自动执行 bgsave

2.2 AOF持久化
    AOF(append only file)持久化:与RDB存储某个时刻的快照不同,AOF持久化方式会记录客户端对服务器的每一次写操作命令到日志当中,并将这些写操作 以 Redis协议追加保存到以后缀为aof文件末尾。
    开启 AOF 功能需要设置配置:appendonly yes,默认不开启。AOF 文件名通过 appendfilename 配置设置,默认文件名是 appendonly.aof。保存路径同 RDB 持久 化方式一致,通过 dir 配置指定

三、redis主从

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave),数据的复制是单向的,只能由主节点到 从节点。 

默认情况下,每台Redis服务器都是主节点,且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

Redis 复制问题,当主机 Master 宕机以后,我们需要人工解决切换,比如使用 slaveof no one 。实际上主从复制并没有实现 高可用。

四、哨兵机制

redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工讲从节点晋升为主节点,同时还要通知应用方更新主节点地址,那这就会存在着问题;哨兵 就是来处理这个问题的
    1. 主节点发生故障后,客户端连接主节点失败,两个从节点与主节点连接失败造成复制中断 
    2. 如果主节点无法正常确定,需要选出一个从节点升级为主节点,对它执行 slaveof no one 命令 
    3. 原来的从节点成为新的主节点后,更细应用方的主节点信息,重新启动应用方 
    4. 客户端命令另外一个从节点去复制新的主节点 
    5. 待原来的主节点恢复后,让它去复制新的主节点。

五、Redis 集群

Redis Cluster 是 Redis 的分布式解决方案,在3.0版本正式推出,有效地解决了 Redis 分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用 Cluster 架构方案达到负载均衡的目的。

使用集群好处
    1、将数据自动切分到多个节点的能力。
    2、当集群中的一部分节点失效或者无法进行通讯时,仍然可以继续处理命令请求的能力,拥有自动故障转移的能力。

创建一个新的索引库

PUT /leyou
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  }
}

查看索引信息

GET /leyou

删除索引库

DELETE /leyou

添加映射(mappings),ps:等同数据库建表

# 参数说明
PUT /索引库名/_mapping    #(自7.0起取消了Type,不需要加索引类型名,统一为_doc)
{
  "properties": {
    "字段名": {
      "type": "类型",        # 类型:可以是text、long、short、date、integer、object等
      "index": true,        # 是否索引,默认为true
      "store": true,        # 是否存储,默认为false
      "analyzer": "分词器"   # 分词器 使用ik分词器: ik_max_word 会将文本做最细粒度的拆分,ik_smart 会做最粗粒度的拆分
    }
  }
}

# 常用类型
1 String类型,分两种:
  text: 可分词,不可参与聚合
  keyword: 不可分词,数据会作为完整字段进行匹配,可以参与聚合
    
2 Numerical数值类型,分两种:
  基本数据类型:long、interger、short、byte、double、float、half_float
  浮点数的高精度类型:scaled_float 需要指定一个精度因子,比如10或100。elasticsearch会把真实值乘以这个因子后存储,取出时再还原。

3 日期类型: 
  Date elasticsearch可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为long,节省空间。

# 使用示例
PUT /leyou/_mapping
{
  "properties":{
    "title":{
      "type": "text",
      "analyzer": "ik_max_word"
    },
    "images":{
      "type": "keyword",
      "index": "false"
    },
    "price":{
      "type":"float"
    }
  }
}

查看索引库的索引类型

GET /leyou/_mapping

阅读剩余部分...


第一列 $1

awk '{print $1}' code_top.log | sort | uniq -c | sort -nr -k1 | head -n 100

最后一列 $NF

awk '{print $NF}' code_top.log | sort | uniq -c | sort -nr -k1 | head -n 100

查看日志中"user/login",json key=client_ip 出现次数前10

cat access.api-sdk.9ringgames.com.log |grep "user/login" | awk -F '"client_ip":' 'match($2,/\"([^\"]*)\"/,a){print a[1]}' | sort | uniq -c | sort -nr -k1 | head -n 10

说明:

awk '{ print $1}' 取数据的第1列

awk '{print $NF}' 取数据的最后1列

sort 排序

uniq -c 打印重复行出现的次数

sort -nr -k1 按照重复行出现的次数排列,-k1以第一列为标准排序

head -n 10 取10条

Htop查看内存占用大小:

执行大M,按占用内存排序

ps命令查看占用内存大小:

ps -aux | grep qx_report

通过进程的status文件查看进程占用内存大小:

cat /proc/26641/status  # VmRSS对应的值就是物理内存占用

查看内存占用前10名进程:

ps aux | sort -k4,4nr | head -n 10

批量杀死进程

ps -aux | grep qx_report | grep -v grep | cut -c 9-15 | xargs kill -15
grep -v grep 是在列出的进程中去除含有关键字"grep"的进程
"kill -15"会正常退出指定进程,-9强行杀掉
cut -c 9-15 获取进程ID部分

谷歌登陆demo

header('Content-Type:text/html; charset=utf-8');

class Google
{
    protected $setting = [
        'app_id' => 'xx',          //客户端ID
        'app_secret' => 'xx',     //客户端密钥
        'redirect_uri' => 'xx',  //回调地址
    ];

    public function __construct($pf_game_id)
    {
        $this->client_id = $this->setting['app_id'];
        $this->client_secret = $this->setting['app_secret'];
        $this->redirect_uri = $this->setting['redirect_uri']."?pf_game_id=".$pf_game_id;
    }

    public function index()
    {
        //第一步:请求CODE
        if (empty($_GET['code'])) {
            $this->getCode($this->client_id, $this->redirect_uri);
        } else {

            //用户允许授权后,将会重定向到redirect_uri的网址上,并且带上code参数
            $code = $_GET['code'];
            $postData = array(
                'code' => $code,
                'client_id' => $this->client_id,
                'client_secret' => $this->client_secret,
                'redirect_uri' => $this->redirect_uri,
                'grant_type' => 'authorization_code'
            );

            //第二步:通过code获取access_token
            $purl = 'https://accounts.google.com/o/oauth2/token';
            $token = $this->CurlSend($postData,$purl);
            if (empty($token)) {
                exit("获取token失败");
            }
            echo "<pre>";print_r($token);die;
        }
    }

    /**
     * 抓取CODE
     * @param $client_id
     * @param $redirect_uris
     */
    protected function getCode($client_id, $redirect_uris)
    {
        $redirect_uris = urlencode($redirect_uris);
        $scope = urlencode('https://www.googleapis.com/auth/androidpublisher');
        $url = "https://accounts.google.com/o/oauth2/auth?response_type=code&access_type=offline&client_id={$client_id}&redirect_uri={$redirect_uris}&state&scope={$scope}&approval_prompt=auto";
        header('Location:' . $url);
    }


    protected function CurlSend($postData, $purl)
    {
        $fields = (is_array($postData)) ? http_build_query($postData) : $postData;
        $curlHeaders = [
            'content-type: application/x-www-form-urlencoded;CHARSET=utf-8',
            'Content-Length: ' . strlen($fields),
        ];

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $purl);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_HEADER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $curlHeaders);
        curl_setopt($curl, CURLOPT_POSTFIELDS, $fields);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);

        $response = curl_exec($curl);
        $responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        curl_close($curl);

        if ($response && $responseCode == 200) {
            $json_data = json_decode($response, true);
            return $json_data;
        } else {
            return false;
        }
    }

    public function getATokenToRefToken($refresh_token){
        $url = 'https://accounts.google.com/o/oauth2/token';
        $postData = array(
            'client_id' => $this->client_id,
            'client_secret' => $this->client_secret,
            'grant_type' => 'refresh_token',
            'refresh_token' => $refresh_token
        );
        $token_data = $this->CurlSend($postData,$url);
        echo $token_data["access_token"];
    }
}

if(empty($_GET["pf_game_id"])){
    exit("pf_game_id is empty");
}
$google = new Google($_GET["pf_game_id"]);
$google->index();