mysql读写分离

作者: 分类: php 时间: 2026-05-11 评论: 暂无评论

主从同步延迟是核心痛点
一、为什么会不同步?(延迟成因)
主从复制本质是异步:主库提交事务后立即返回客户端,从库通过 IO_THREAD 拉取 binlog 并重放(SQL_THREAD)。

延迟来源:

从库硬件差(磁盘慢、CPU弱)

主库写入压力大,binlog 产生速度超过从库重放速度

从库承担了大量复杂查询,占用资源

网络延迟

大事务(如一次删除百万行)在从库重放时阻塞

二、保证同步的方法(追求强一致)
方案 原理 优点 缺点
强制读主库 对一致性要求高的操作(如转账后立即查询余额),让代码判断路由到主库 100% 准确,无延迟 增加主库压力,无法发挥读写分离的扩展性
半同步复制 主库至少等待一个从库确认收到 binlog 后才返回客户端 基本不丢数据,延迟可控(等待一个 ack) 如果从库网络抖动,会阻塞主库写入;MySQL 5.7+ 支持 AFTER_SYNC 模式
组复制(MGR) 基于 Paxos,所有节点同步写入,强一致 真正强一致,自动故障切换 性能下降(写放大),只适合写少读多场景
等待从库位点 写入后,查询前检查从库的 Seconds_Behind_Master 或 binlog 位点,等它追上 应用层可控 增加复杂度,可能超时或死等
实际生产最常用:关键链路强制读主 + 普通查询走从库。例如:注册后立即登录 → 读主库;发布文章后作者自己刷页面 → 读主库;其他用户浏览 → 读从库。

三、同步出现延迟后如何处理?(补偿与兜底)
既然延迟无法 100% 消除,必须有应对方案:

  1. 监控与告警
    持续监控 Seconds_Behind_Master(注意它超过 MAX_ALLOWED_PACKET 会重置为 0,不够准确)。

更可靠:监控 Master_Log_File 和 Read_Master_Log_Pos vs Relay_Master_Log_File 和 Exec_Master_Log_Pos,计算位点差距。

设置阈值(如延迟 > 5秒)触发告警,自动切换读写策略。

  1. 业务容忍设计
    多数场景(评论、点赞、非实时排行榜)允许短暂不一致,用户刷新后自然可见。

前端可做“乐观展示”:用户提交动作后,前端先本地更新 UI,后台异步确认。即使从库暂时没数据,用户感受不到。

  1. 延迟补偿机制
    读取降级:检测到延迟超过阈值时,将该请求临时切到主库(需要负载均衡器或数据库中间件支持)。

缓存兜底:写操作后更新 Redis(设置较短 TTL),读请求优先查缓存,缓存未命中再查从库。

异步修复:定时任务扫描主从差异表,修复不一致数据。

  1. 从库追赶加速
    多线程并行复制(MySQL 5.7+ 的 slave_parallel_workers,8.0 默认 logical_clock 模式),大幅提升重放速度。

提升从库硬件(SSD、更多 CPU)。

避免从库执行复杂 ANALYZE、大查询(可临时关闭从库查询服务,专心同步)。

拆解大事务:DELETE ... LIMIT 1000 循环提交。

  1. 最终一致性的兜底预案
    如果延迟长期无法解决,主动进行主从切换:将原从库提升为主库,并修复数据。配合 pt-table-checksum + pt-table-sync 工具修复不一致。

ES实际需求设计展示

作者: 分类: php 时间: 2026-05-08 评论: 暂无评论

APP搜索设计案例demo

use Elasticsearch\ClientBuilder;

public static function singleton()
{
    if (empty(self::$client)) {
        $hosts        = self::getHost();
        $client       = ClientBuilder::create()->setHosts($hosts)->build();
        self::$client = $client;
    }
    return self::$client;
}

[数据同步核心代码]

public static function addAppV2($appInfo)
    {
        $isIos   = intval(stripos($appInfo['platform'], 'IOS') !== false);
        $isAz    = intval(stripos($appInfo['platform'], '安卓') !== false);
        $isH5    = intval(stripos($appInfo['platform'], 'H5') !== false);
        $isYgj   = intval(stripos($appInfo['platform'], 'YGJ') !== false);
        $isYyx   = intval(stripos($appInfo['platform'], 'PCYYX') !== false);
        $isAzH5  = intval($isAz || $isH5);
        $isIosH5 = intval($isIos || $isH5);

        $param = [
            'index' => self::APP_INDEX_V2,
            'id'    => $appInfo['id'],
            'body'  => [
                'id'              => $appInfo['id'],
                'state'           => $appInfo['state'],
                'is_ios'          => $isIos,
                'is_az'           => $isAz,
                'is_ios_h5'       => $isIosH5,
                'is_az_h5'        => $isAzH5,
                'is_ygj'          => $isYgj,
                'is_h5'           => $isH5,
                'is_pc_yyx'       => $isYyx,
                'classid'         => $appInfo['classid'],
                'app_id'          => $appInfo['app_id'],
                'is_az_h5_pc_yyx' => intval($isYyx || $isAzH5),
                'game_name'       => $appInfo['main_title'],
                'subtitle'        => $appInfo['subtitle'] ?? '',
                'title'           => $appInfo['new_title'],
                'mix_type'        => $appInfo['mix_type'] ?? 100,
            ]
        ];

        $addIndex = self::singleton()->index($param);
        return $addIndex;
    }

[搜索核心代码]
同步游戏数据到es数据库,通过function_score 设置权重

游戏名中含有 关键字 完全匹配权重设置10000
搜索词包含在标题中,且比例 ≥ 60% → 高权重
标题包含在搜索词中,且比例 ≤ 200% → 高权重

下载加权 5
日活加权 15
支付加权80

private function searchGameIdsV21()
    {
        $keyword = $this->keyword;

        $must = $mustNot = $scoreFun = [];

        $should[] = [
            'match' => [
                'game_name.keyword' => [
                    'query'     => $keyword,
                    'fuzziness' => floor(mb_strlen($keyword) / 4), // 表示容错字符
                    'boost'     => 1
                ]
            ]
        ];
        $should[] = [
            'match' => [
                'title.keyword' => [
                    'query'     => $keyword,
                    'fuzziness' => floor(mb_strlen($keyword) / 3),
                    'boost'     => 1
                ]
            ]
        ];
        $should[] = [
            'match' => [
                'cates.keyword' => [
                    'query'     => $keyword,
                    'fuzziness' => 1,
                    'boost'     => 1
                ]
            ]
        ];
        $should[] = [
            'multi_match' => [
                'query'  => $keyword,
                'fields' => [
                    'game_name', 'subtitle'
                ],
                'boost'  => 1,
            ]
        ];

        // 增加包含全量游戏名的权重
        $scoreFun[] = [
            'filter' => [
                'term' => [
                    'game_name.keyword' => $keyword,
                ]
            ],
            'weight' => 10000
        ];
        $scoreFun[] = [
            'filter' => [
                'term' => [
                    'title.keyword' => $keyword,
                ]
            ],
            'weight' => 10000
        ];
        $scoreFun[] = [
            'filter' => [
                'script' => [
                    'script' => [
                        'source' => "String title = doc['title.keyword'].value;title.contains(params.query) && (params.query.length() / (double) Math.round(doc['title_length'].value)) >= 0.6 || params.query.contains(title) && (params.query.length() / (double) Math.round(doc['title_length'].value)) <= 2",
                        'params' => [
                            'query' => $keyword
                        ]
                    ]
                ]
            ],
            'weight' => 10000
        ];
        $scoreFun[] = [
            'filter' => [
                'script' => [
                    'script' => [
                        'source' => "String title = doc['game_name.keyword'].value; title.contains(params.query) &&  (params.query.length() / (double) Math.round(doc['name_length'].value)) >= 0.6 ||  params.query.contains(title) && (params.query.length() / (double) Math.round(doc['name_length'].value)) <= 4",
                        'params' => [
                            'query' => $keyword
                        ]
                    ]
                ]
            ],
            'weight' => 10000
        ];

        // 近1日的累计实付*80%+昨日日活*15%+下载量*5%
        $scoreFun[] = [
            'script_score' => [
                'script' => [
                    'source' => "1 + Math.log1p(doc['true_down'].value)"
                ]
            ],
            'weight'       => 5,
        ];
        $scoreFun[] = [
            'script_score' => [
                'script' => [
                    'source' => "1 + Math.log1p(doc['yesterday_hy'].value)"
                ]
            ],
            'weight'       => 15,
        ];
        $scoreFun[] = [
            'script_score' => [
                'script' => [
                    'source' => "1 + Math.log1p(Math.ceil(doc['real_pay'].value))"
                ]
            ],
            'weight'       => 80,
        ];

        if (EcloudByService::isZkyCps() || AFrom::isADevice()) {
            $filter[] = [
                'match' => [
                    'is_az_h5_pc_yyx' => 1
                ]
            ];
        } else {
            $filter[] = [
                'match' => [
                    'is_ios_h5' => 1
                ]
            ];
            // iOS过滤模拟器游戏
            $mustNot[] = [
                'term' => [
                    'classid' => APP_CLASS_SIMULATOR,
                ]
            ];
        }
        $mustNot[] = [
            'term' => [
                'is_pc_yyx' => 1,
            ]
        ];

       
        // 只展示预约、运营中
        $mustNot[] = [
            'terms' => [
                'state' => [
                    XYApp::STATE_CLOSED,
                    XYApp::STATE_DISABLED,
                    XYApp::STATE_CLOSING,
                    XYApp::STATE_HX1,
                    XYApp::STATE_HX2,
                ]
            ]
        ];
        $param = [
            'index' => ElasticSearch::APP_INDEX_V3,
            'body'  => [
                'query' => [
                    'function_score' => [
                        'query'      => [
                            'bool' => []
                        ],
                        'functions'  => $scoreFun,
                        "score_mode" => "sum",
                        "boost_mode" => "sum"
                    ],
                ],

                'from' => ($this->page - 1) * $this->listRows,
                'size' => $this->listRows,
                'sort' => [
                    [
                        '_score' => ['order' => 'desc']
                    ]
                ],
            ],
        ];
        if ($must) {
            $param['body']['query']['function_score']['query']['bool']['must'] = $must;
        }
        if ($mustNot) {
            $param['body']['query']['function_score']['query']['bool']['must_not'] = $mustNot;
        }
        if ($filter) {
            $param['body']['query']['function_score']['query']['bool']['filter'] = $filter;
        }
        $param['body']['query']['function_score']['query']['bool']['should']               = $should;
        $param['body']['query']['function_score']['query']['bool']['minimum_should_match'] = 1;

        $esData   =self::singleton()->search($param);

记录几个特殊排序的例子

作者: 分类: php 时间: 2026-05-07 评论: 暂无评论

在 MySQL 中按照 status 字段的特定顺序 1, 2, 0 排序(审核通过 → 驳回 → 待审核)。
在 MySQL 中有几种方式实现自定义排序:

  1. order by FIELD(status, 1, 2, 0)
  2. order by CASE status WHEN 1 THEN 1 WHEN 2 THEN 2 WHEN 0 THEN 3 END

win8 10 11 激活方式

作者: 分类: php 时间: 2026-04-30 评论: 暂无评论

irm https://get.activated.win | iex

管理员启动 选 3 1

https://github.com/massgravel/Microsoft-Activation-Scripts

闲谈

作者: 分类: php 时间: 2026-03-05 评论: 暂无评论

属性计算复杂,要筛选,可以考虑异步计算好数据,塞进表里

Top ↑