ElasticSearch基本使用

ElasticSearch基本使用

说明:基于 elasticsearch/elasticsearch 扩展包的 php 版使用。

文档:https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/getting-started-php.html

一、自定义服务类

php 复制代码
<?php
/**
 * Created by PhpStorm
 * User: Jason
 * Date: 2024-06-07
 * Time: 14:48
 */

require_once '../vendor/autoload.php';

use Elasticsearch\ClientBuilder;

class ES
{
    /**
     * 客户端
     *
     * @var \Elasticsearch\Client
     */
    private $client;

    /**
     * @param array $hosts
     */
    public function __construct(array $hosts = ['127.0.0.1'])
    {
        $this->client = ClientBuilder::create()->setHosts($hosts)->build();
    }

    // ================================= 索引相关操作 =================================================

    /**
     * 创建索引
     * 说明:相当于Mysql的表,只需要创建一次;原本相当于database,8.x废除了type(相当于table)
     *
     * @param string $index 索引名称
     * @param array $settings 索引设置
     * @param array $mappings 索引映射
     * @return array
     */
    public function creatIndex(string $index, array $settings = [], array $mappings = []): array
    {
        $params = [
            'index' => $index,
            'body' => [
                'settings' => $settings,
                'mappings' => $mappings,
            ]
        ];

        return $this->client->indices()->create($params);
    }

    /**
     * 删除索引
     *
     * @param $indexName string 索引名称
     * @return array
     */
    public function deleteIndex(string $indexName): array
    {
        $params = [
            'index' => $indexName,
        ];

        return $this->client->indices()->delete($params);
    }

    /**
     * 检查索引是否存在
     *
     * @param string $indexName
     * @return bool
     */
    public function existsIndex(string $indexName)
    {
        return $this->client->indices()->exists(['index' => $indexName]);
    }

    /**
     * 获取索引设置
     *
     * @param string $indexName 索引名称
     * @return array
     */
    public function getIndexSettings(string $indexName): array
    {
        $params = [
            'index' => $indexName
        ];

        return $this->client->indices()->getSettings($params);
    }

    /**
     * 关闭索引
     *
     * @param $indexName string 索引名称
     * @return array
     */
    public function closeIndex(string $indexName): array
    {
        $params = ['index' => $indexName];

        return $this->client->indices()->close($params);
    }

    /**
     * 打开索引
     *
     * @param $indexName string 索引名称
     * @return array
     */
    public function openIndex(string $indexName): array
    {
        $params = ['index' => $indexName];

        return $this->client->indices()->open($params);
    }

    /**
     * 更新索引设置
     *
     * @param string $indexName 索引名称
     * @param array $settings 新的索引设置
     * @return array 更新索引设置的响应
     */
    public function updateIndexSettings(string $indexName, array $settings): array
    {
        $params = [
            'index' => $indexName,
            'body' => ['settings' => $settings]
        ];

        return $this->client->indices()->putSettings($params);
    }

    /**
     * 更新非动态索引设置
     *
     * @param $indexName string 索引名称
     * @param array $settings 索引设置
     * @return array
     */
    public function updateNonDynamicIndexSettings(string $indexName, array $settings)
    {
        // 关闭索引
        $this->closeIndex($indexName);

        // 更新索引设置
        $response = $this->updateIndexSettings($indexName, $settings);

        // 重新打开索引
        $this->openIndex($indexName);

        return $response;
    }

    /**
     * 获取索引映射
     *
     * @param string $indexName 索引名称
     * @return array 获取索引映射的响应
     */
    public function getIndexMappings($indexName)
    {
        $params = [
            'index' => $indexName
        ];

        return $this->client->indices()->getMapping($params);
    }

    /**
     * 更新索引映射
     *
     * @param string $indexName 索引名称
     * @param array $mappings 新的索引映射
     * @return array 更新索引映射的响应
     */
    public function updateIndexMappings(string $indexName, array $mappings): array
    {
        $params = [
            'index' => $indexName,
            'body' => ['properties' => $mappings]
        ];

        return $this->client->indices()->putMapping($params);
    }

    /**
     * 刷新索引
     *
     * @param $indexName string 索引名称
     * @return array
     */
    public function refreshIndex(string $indexName): array
    {
        $params = [
            'index' => $indexName
        ];

        return $this->client->indices()->refresh($params);
    }

    // ===================================== 文档相关操作 ===============================================

    /**
     * 新增文档
     *
     * @param string $indexName 索引名称
     * @param string $id 文档ID
     * @param array $body 文档内容
     * @return array
     */
    public function addDocument($indexName, string $id, array $body)
    {
        $params = [
            'index' => $indexName,
            'id' => $id, // 文档ID,可省略,默认生成随机ID
            'body' => $body
        ];

        return $this->client->index($params);
    }

    /**
     * 批量新增文档
     *
     * @param array $params
     * @return array|callable
     */
    public function batchAddDocument(array $params)
    {
        return $this->client->bulk($params);
    }

    /**
     * 获取文档
     * 说明:查询单挑记录
     *
     * @param string $indexName 索引名称
     * @param string $id 文档ID
     * @return array|callable
     */
    public function getDocument(string $indexName, string $id): callable|array
    {
        $params = [
            'index' => $indexName,
            'id' => $id,
        ];

        return $this->client->get($params);
    }

    /**
     * 更新文档
     *
     * @param string $indexName 索引名称
     * @param string $id 文档ID
     * @param array $doc 更新内容
     * @return array|callable
     */
    public function updateDocument(string $indexName, string $id, array $doc)
    {
        $params = [
            'index' => $indexName,
            'id' => $id,
            'body' => [
                'doc' => $doc, // 需要更新的内容
            ],
        ];

        return $this->client->update($params);
    }

    /**
     * 删除文档
     *
     * @param string $indexName
     * @param $id
     * @return array|callable
     */
    public function deleteDocument(string $indexName, $id)
    {
        $params = [
            'index' => $indexName,
            'id' => $id,
        ];

        return $this->client->delete($params);
    }

    // ======================================= 搜索相关操作 ================================================

    /**
     * 基础搜索方法
     *
     * @param string $indexName 索引名称
     * @param array $query 搜索查询
     * @param array|null $sort 排序参数
     * @param int|null $from 起始位置
     * @param int|null $size 返回结果数
     * @return array
     */
    public function search(string $indexName, array $query, array $sort = null, int $from = null, int $size = null)
    {
        $params = [
            'index' => $indexName,
            'body' => [
                'query' => $query
            ]
        ];

        if ($sort) {
            $params['body']['sort'] = $sort;
        }

        if (!is_null($from)) {
            $params['body']['from'] = $from;
        }

        if (!is_null($size)) {
            $params['body']['size'] = $size;
        }

        return $this->client->search($params);
    }
}

二、索引基本使用

2.1 创建索引

php 复制代码
<?php
$hosts = [
    '192.168.0.117:9200'
];

$es = new ES($hosts);

// 索引名
$userIndex = 'user';

// 设置
$settings = [
    // 分片数量: 一个索引库将拆分成多片分别存储不同的结点,默认5个
    'number_of_shards' => count($hosts),
    // 每个分片分配的副本数,replica shard是primary shard的副本,负责容错,以及承担读请求负载,如果服务器只有一台,只能设置为0
    'number_of_replicas' => 0
];

// 创建文档映射,就是文档存储在ES中的数据结构
$mappings = [
    'properties' => [
        'user_id' => [
            'type' => 'integer',
            'index' => 'true'
        ],
        'nickname' => [
            // 数据类型: text支持分词; keyword不支持分词,只能精确索引;数值类型(integer,...),日期类型(date),逻辑类型:boolean;IP类型(ip);地理坐标类型(geo_point);.....
            // keyword 排序是按照字符串的ASCII码排序的,'3'>'20'
            'type' => 'text',
            // 字段可以被索引,也就是能用来当做查询条件来查询,只能填写true和false
            'index' => 'true',
            // 索引分词器,用于字符串类型,这里使用中文分词器ik,用默认分词器可以省略
            'analyzer' => 'ik_max_word',
            // 搜索分词器,用于搜索关键词的分词器
            'search_analyzer' => 'ik_max_word',
        ],
        'age' => [
            'type' => 'integer',
            'index' => 'true',
        ],
        'create_time' => [
            'type' => 'date',
            'index' => 'true',
            'format' => 'yyyy-MM-dd HH:mm:ss'
        ],
    ]
];

$res = $es->creatIndex($userIndex, $settings, $mappings);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"acknowledged":true,"shards_acknowledged":true,"index":"user"}

2.2 判断索引是否存在

php 复制代码
<?php
$res = $es->existsIndex($userIndex);
var_dump($res); 
// 输出: true

2.3 删除索引

php 复制代码
<?php
$res = $es->deleteIndex($userIndex);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"acknowledged":true}

2.4 获取索引设置

php 复制代码
<?php
$res = $es->getIndexSettings($userIndex);
var_dump(json_encode($res,JSON_UNESCAPED_UNICODE)); 
// 输出: {"user":{"settings":{"index":{"routing":{"allocation":{"include":{"_tier_preference":"data_content"}}},"refresh_interval":"1s","number_of_shards":"1","provided_name":"user","creation_date":"1719907928617","number_of_replicas":"0","uuid":"7d3Mojg6SL6q_7Q0gamMog","version":{"created":"8503000"}}}}}

2.5 更新索引设置

php 复制代码
<?php
$settings = [
    'index.refresh_interval' => '1s' // 刷新时间间隔
];
$res = $es->updateNonDynamicIndexSettings($userIndex, $settings);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"acknowledged":true}

2.6 获取索引映射

php 复制代码
<?php
$res = $es->getIndexMappings($userIndex);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"user":{"mappings":{"properties":{"age":{"type":"integer"},"create_time":{"type":"date","format":"yyyy-MM-dd HH:mm:ss"},"nickname":{"type":"text","analyzer":"ik_max_word"},"user_id":{"type":"integer"}}}}}

2.7 更新索引映射

php 复制代码
<?php
$mappings = [
    'phone' => [
        'type' => 'keyword',
        'index' => 'true'
    ]
];
$res = $es->updateIndexMappings($userIndex, $mappings);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"acknowledged":true}

2.8 刷新索引

php 复制代码
<?php
$res = $es->refreshIndex($userIndex);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出:  {"_shards":{"total":1,"successful":1,"failed":0}}

三、文档基本使用

3.1 新增文档

php 复制代码
<?php
$res = $es->addDocument($userIndex, '1', [
    'user_id' => 1,
    'nickname' => '爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)',
    'age' => mt_rand(10, 40),
    'create_time' => \Carbon\Carbon::now()->toDateTimeString(),
    'phone' => '13612348888'
]);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"_index":"user","_id":"1","_version":1,"result":"created","_shards":{"total":1,"successful":1,"failed":0},"_seq_no":0,"_primary_term":5}

3.2 批量新增文档

php 复制代码
<?php
$documents = [
    [
        'user_id' => 2,
        'nickname' => '高颜值情侣玻璃浮雕吸管杯2个-xh',
        'age' => 12,
        'create_time' => '2024-06-29 12:00:05',
        'phone' => '13575861234',
    ],
    [
        'user_id' => 3,
        'nickname' => '网红爆款护手霜6支香味随机-K',
        'age' => 23,
        'create_time' => '2024-06-28 03:18:08',
        'phone' => '19512345286',
    ],
    [
        'user_id' => 4,
        'nickname' => '【可拆卸一拖四线】充电宝迷你自带线大容量数显快充移动电源',
        'age' => 34,
        'create_time' => '2024-07-02 03:18:08',
        'phone' => '19538883347',
    ],
];

$params = [];
foreach ($documents as $document) {
    $params['body'][] = [
        'index' => [
            '_index' => $userIndex,
            '_id' => strval($document['user_id'])
        ]
    ];

    $params['body'][] = [
        'user_id' => $document['user_id'],
        'nickname' => $document['nickname'],
        'age' => $document['age'],
        'create_time' => $document['create_time'],
        'phone' => $document['phone'],
    ];
}
var_dump(json_encode($es->batchAddDocument($params), JSON_UNESCAPED_UNICODE)); 
// 输出: {"errors":false,"took":15,"items":[{"index":{"_index":"user","_id":"2","_version":1,"result":"created","_shards":{"total":1,"successful":1,"failed":0},"_seq_no":3,"_primary_term":5,"status":201}},{"index":{"_index":"user","_id":"3","_version":1,"result":"created","_shards":{"total":1,"successful":1,"failed":0},"_seq_no":4,"_primary_term":5,"status":201}},{"index":{"_index":"user","_id":"4","_version":1,"result":"created","_shards":{"total":1,"successful":1,"failed":0},"_seq_no":5,"_primary_term":5,"status":201}}]}

3.3 获取文档

php 复制代码
<?php
var_dump(json_encode($es->getDocument($userIndex, '9'), JSON_UNESCAPED_UNICODE)); 
// 输出: {"_index":"user","_id":"1","_version":3,"_seq_no":2,"_primary_term":5,"found":true,"_source":{"user_id":1,"nickname":"爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)","age":29,"create_time":"2024-07-02 17: 15: 16","phone":"13612348888"}}

3.4 更新文档

php 复制代码
<?php
$res = $es->updateDocument($userIndex, '1', [
    'nickname' => '9999Jason的爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)',
    'age' => 30
]);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"_index":"user","_id":"1","_version":2,"result":"updated","_shards":{"total":1,"successful":1,"failed":0},"_seq_no":7,"_primary_term":5}

3.5 删除文档

php 复制代码
<?php
$res = $es->deleteDocument($userIndex, '9');
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE));
// 输出: {"_index":"user","_id":"9","_version":3,"result":"deleted","_shards":{"total":1,"successful":1,"failed":0},"_seq_no":8,"_primary_term":5}

四、文档搜索

4.1 词条匹配查询(精确匹配查询): 类似 where age = xx

php 复制代码
<?php
$query = [
    'term' => [
        'age' => 34
    ]
];
$res = $es->search($userIndex, $query);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"took":2,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":2,"relation":"eq"},"max_score":null,"hits":[{"_index":"user","_id":"4","_score":null,"_source":{"user_id":4,"nickname":"【可拆卸一拖四线】充电宝迷你自带线大容量数显快充移动电源","age":34,"create_time":"2024-07-02 03: 18: 08","phone":"19538883347"},"sort":[1719890288000,4]}]}}

4.2 多条件查询 : 类似 where xxx and xxx

php 复制代码
<?php
$query = [
    'bool' => [
        'must' => [
            [
                'term' => [
                    'age' => 29
                ],
            ],
            [
                'term' => [
                    'phone' => '13612348888'
                ],
            ]
        ]
    ]
];
$res = $es->search($userIndex, $query);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); // 输出: {"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":2.3862944,"hits":[{"_index":"user","_id":"1","_score":2.3862944,"_source":{"user_id":1,"nickname":"爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)","age":29,"create_time":"2024-07-02 17:15:16","phone":"13612348888"}}]}}

4.3 通配符查询

说明:对于keyword等不支持分词的类型,模糊查询使用通配符(wildcard)查询;

php 复制代码
<?php
$query = [
    'wildcard' => [
        "nickname" => '*肉松*' // 使用 * 作为通配符,* 可以匹配任意字符序列(包括空字符序列)。例如,*elastic* 可以匹配 elastic 、my elastic search 、elasticity 等
    ]
];
$res = $es->search($userIndex, $query);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":2,"relation":"eq"},"max_score":1,"hits":[{"_index":"user","_id":"1","_score":1,"_source":{"user_id":1,"nickname":"爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)","age":29,"create_time":"2024-07-02 17: 15: 16","phone":"13612348888"}},{"_index":"user","_id":"5","_score":1,"_source":{"user_id":5,"nickname":"爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)","age":34,"create_time":"2024-07-03 09: 28: 29","phone":"19575621495"}}]}}

4.4 模糊查询

说明:对于text等支持分词的类型,推荐使用fuzzy进行模糊查询

php 复制代码
<?php
$query = [
    'fuzzy' => [
        "nickname" => [
            'value' => '肉松',
            'fuzziness' => 1 // fuzziness 参数指定了允许的模糊程度。值越大,允许的差异越大。例如,fuzziness: 2 可能允许一些字符的替换、插入或删除
        ]
    ]
];
$res = $es->search($userIndex, $query);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":2,"relation":"eq"},"max_score":0.8915597,"hits":[{"_index":"user","_id":"1","_score":0.8915597,"_source":{"user_id":1,"nickname":"爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)","age":29,"create_time":"2024-07-02 17: 15: 16","phone":"13612348888"}},{"_index":"user","_id":"5","_score":0.8915597,"_source":{"user_id":5,"nickname":"爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)","age":34,"create_time":"2024-07-03 09: 28: 29","phone":"19575621495"}}]}}

4.5 前缀查询

说明: 前缀查询通常适用于keyword类型的字段,这种类型的字段不会进行分词处理,会将整个值作为一个词条来建立索引

php 复制代码
<?php
$query = [
    'prefix' => [
        "phone" => '195'
    ]
];
$res = $es->search($userIndex, $query);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":3,"relation":"eq"},"max_score":1,"hits":[{"_index":"user","_id":"3","_score":1,"_source":{"user_id":3,"nickname":"网红爆款护手霜6支香味随机-K","age":23,"create_time":"2024-06-28 03: 18: 08","phone":"19512345286"}},{"_index":"user","_id":"4","_score":1,"_source":{"user_id":4,"nickname":"【可拆卸一拖四线】充电宝迷你自带线大容量数显快充移动电源","age":34,"create_time":"2024-07-02 03: 18: 08","phone":"19538883347"}},{"_index":"user","_id":"5","_score":1,"_source":{"user_id":5,"nickname":"爆款煎饼(传统双蛋煎饼+肉松+优质火腿片+配菜+薄脆)","age":34,"create_time":"2024-07-03 09: 28: 29","phone":"19575621495"}}]}}

4.6 联合查询 - and

SQL: where age in (23,34) and create_time between 2024-07-01 00:00:00 and 2024-07-02 23:59:59

php 复制代码
<?php
$query = [
    'bool' => [
        // must(且): 数组里面的条件都要满足,该条数据才会被选择
        'must' => [
            [
                'terms' => [
                    'age' => [23, 34] // 类似于 where in
                ]
            ],
            [
                'range' => [ // 类似 between
                    'create_time' => [
                        'gte' => '2024-07-01 00:00:00',
                        'lte' => '2024-07-02 23:59:59',
                    ]
                ]
            ]
        ]
    ]
];
$res = $es->search($userIndex, $query);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE)); 
// 输出: {"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":2,"hits":[{"_index":"user","_id":"4","_score":2,"_source":{"user_id":4,"nickname":"【可拆卸一拖四线】充电宝迷你自带线大容量数显快充移动电源","age":34,"create_time":"2024-07-02 03: 18: 08","phone":"19538883347"}}]}}

4.7 联合查询 - or

SQL: where (age <=18 or age >=34) and between 2024-07-01 00:00:00 and 2024-07-02 23:59:59

php 复制代码
<?php
$query = [
    'bool' => [
        "must" => [
            [
                'range' => [
                    'create_time' => [
                        'gte' => '2024-06-29 00:00:00',
                        'lte' => '2024-07-02 04:00:00',
                    ]
                ]
            ]
        ],
        // should(或): 数组里面的条件满足其中一个,该条数据被选择
        "should" => [
            [
                'range' => [
                    'age' => [
                        'lte' => 18,
                    ]
                ]
            ],
            [
                'range' => [
                    'age' => [
                        'gte' => 34,
                    ]
                ]
            ]
        ]
    ]
];
$res = $es->search($userIndex, $query);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE));
 // 输出: {"took":2,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":2,"relation":"eq"},"max_score":2,"hits":[{"_index":"user","_id":"2","_score":2,"_source":{"user_id":2,"nickname":"高颜值情侣玻璃浮雕吸管杯2个-xh","age":12,"create_time":"2024-06-29 12: 00: 05","phone":"13575861234"}},{"_index":"user","_id":"4","_score":2,"_source":{"user_id":4,"nickname":"【可拆卸一拖四线】充电宝迷你自带线大容量数显快充移动电源","age":34,"create_time":"2024-07-02 03: 18: 08","phone":"19538883347"}}]}}

4.8 联合查询 - or and or

SQL: where (age >= 34 or age <= 18) and (create_time >= '2024-07-02 00:00:00' or create_time <= '2024-06-30 00:00:00')

php 复制代码
<?php
# 联合查询
$query = [
    'bool' => [
        'must' => [
            [
                'bool' => [
                    'should' => [
                        [
                            'range' => [
                                'age' => [
                                    'lte' => 18,
                                ]
                            ]
                        ],
                        [
                            'range' => [
                                'age' => [
                                    'gte' => 34,
                                ]
                            ]
                        ]
                    ]
                ]
            ],
            [
                'bool' => [
                    'should' => [
                        [
                            'range' => [
                                'create_time' => [
                                    'lte' => '2024-06-30 00:00:00',
                                ]
                            ]
                        ],
                        [
                            'range' => [
                                'create_time' => [
                                    'gte' => '2024-07-02 00:00:00',
                                ]
                            ]
                        ]
                    ]
                ]
            ]
        ]
    ]
];
$res = $es->search($userIndex, $query);
var_dump(json_encode($res, JSON_UNESCAPED_UNICODE));
相关推荐
BingoGo13 分钟前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack20 分钟前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820719 小时前
PHP 扩展——从入门到理解
php
鹏仔先生2 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
大志哥1232 天前
ES和Logstash日志链路系统上线后遭遇切片爆炸(解决)
大数据·elasticsearch
云水一下2 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
xingpanvip2 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
TableRow2 天前
参数化搜索的实现原理:从多维索引到查询优化
elasticsearch·全文检索
酉鬼女又兒2 天前
零基础入门计算机网络运输层:端到端通信核心作用、端口号分类规则、复用分用工作机制及UDP与TCP协议全方位对比详解
网络·网络协议·tcp/ip·计算机网络·考研·udp·php
dog2502 天前
不要再继续优化 TCP
网络协议·tcp/ip·php