title: 一周技术小结 1
date: 2017-9-4 21:22:21
tcp 保存连接 auth 信息
在我的 服务器开发系列 1 遇到了一个问题: tcp client 在连接后, 需要先发送一个 auth msg 进行认证, 认证不通过会直接断掉, 所以需要保存当前连接的状态.
当时提供了 2 种办法:
- 用 redis 来存储
- 使用 swoole 的共享内存解决方案: swoole table
但是, 真的是自己知道的「太少」, swoole wiki 上面就有解决方案: swoole_server中内存管理机制
$serv = new swoole_server("0.0.0.0", 9999);
$serv->authFlag = []; // 认证成功后存储
$serv->on('receive', function (swoole_server $serv, $fd, $from_id, $data) {
// auth 判断
if (!isset($serv->authFlag[$fd])) {
// 需要 auth 认证
}
}
分表
有 2 张表数据增长很快, 数据量已经百万级了, 而 mysql 表的容量在千万级, 可能就满足不了服务器对性能的要求了,「分库分表」势在必行了, 先做分表方案:
$table = 'game_'. ($uid % 100); // 按照用户 id 取模, 将表分成 100 份
在分表的同时, 也会有一张表, 来存储所有数据, 方便「后台管理」等系统获取数据, 不过这张表会定期清理:
CREATE TABLE game_tmp LIKE game;
RENAME TABLE game game_201709;
RENAME TABLE game_tmp game;
并发「上机游戏」的简单实现
要解决并发导致的资源争抢问题, 很容易想到各种锁: 读写锁 / 排它锁 / 自旋锁. 但是, 其实只要保证「原子性」操作, 就可以自己来不加锁实现, 下面提供 2 种基于 redis 的方案:
public static function incr($key)
{
$res = static::getEngine()->incr($key); // redis incr command
if ($res == 1) { // 返回 1 表示此进程竞争到了此资源
return true;
}
return false; // 返回大于 1 说明没有竞争到
}
同理, 还有 redis setnx command
pr: pull request
玩 github 就会对这个词比较熟悉了, 这周遇到了 2 件事和这有关.
工作开发中使用 pr: 首先 fork 一份原项目, 在 fork 出来的项目上就可以「随意」了; 提交代码后就可以提 pr 了, 在 pr 里面可以指定 code review 的各位大大
贡献开源: 最近 swoole 用的比较多, 使用到了eaglewu/swoole-ide-helper 项目, 遇到改进的地方, 就使用 pr 来贡献一点自己的力量
mysql 问题简单排查
遇到 mysql 出问题了, 可以简单使用下面方法排查试试看:
-- mysql 上当前连接的进程
SHOW PROCESSLIST
-- information_schema 非常有用
SELECT * FROM information_schema.`PROCESSLIST`
-- 通过上面查询出来的 id 杀掉进程
kill id
用这个方法找到了外包写的问题代码.
mysql 处理 json 数据
很早就知道 mysql5.7 支持 json, 不过一直没机会使用, 这次刚好碰上了: 原来的服务器用 nodejs 写的, 在 mysql 中用了很多 json 字段, 这就造成使用时需要 json encode / json decode
, 作为服务器怎么能忍, 所以需要进行「数据迁移」.
在没有 mysql json 支持之前:
- 原表添加新字段来保存 json decode 后的数据
- 新建临时表, 用来保存 json decode 后的数据
- 跑脚本, 读取数据, json decode, 写入临时表
- 临时表和原表 join 来更新新字段
-- 临时表, 用来迁移数据
DROP TABLE IF EXISTS db_tmp;
CREATE TABLE `db_tmp` (
`id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
intv1 BIGINT NOT NULL DEFAULT 0,
intv2 BIGINT NOT NULL DEFAULT 0,
intv3 BIGINT NOT NULL DEFAULT 0,
string1 VARCHAR(255) NOT NULL DEFAULT '',
string2 VARCHAR(255) NOT NULL DEFAULT '',
string3 VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
public function gameSession()
{
$begin = microtime();
$dbTmp = 'db_tmp';
DbClient::execute("TRUNCATE $dbTmp");
$table = 'game';
$count = DbClient::findColumn("SELECT COUNT(*) FROM $table");
$id = 0;
$limit = 2000; // 一定要注意分页
while ($count>0) {
$rows = DbClient::findAll("SELECT id,record_text FROM $table WHERE id>$id LIMIT $limit");
$data = [];
foreach ($rows as $v) {
$arr = json_decode($v['record_text'], true);
$videoUrl = $arr['video_url'] ?? '';
$data[] = [$v['id'], $videoUrl];
}
$id = $v['id'];
$options = ['column_names' => ['id', 'string1']];
DbClient::insertAll($dbTmp, $data, $options);
echo "table:$table id:$id limit:$limit\n";
$count -= $limit;
}
echo "time take: " . (microtime() - $begin) . "\n";
}
但是, 我们有了 json 函数支持后:
-- mysql json function
UPDATE game SET video_url=trim(BOTH '"' FROM json_extract(record_text, '$.video_url')) WHERE record_text is NOT NULL;
而且, 实测下来, 使用 2000 来分页, 脚本需要 2 分钟, 而 json 函数不到 5s
计算机基础真差
- 说一下 top k 算法(大量数中求最大 k 个数)
- mysql 的索引是什么数据结构? 说一下 B+ Tree . explain 各字段什么意思, explain 后的语句能直接线上用么(强制指定索引)?
where a=xxx and b>xxx order by c
如何建索引? - 说一下 epoll
- 说一下 swoole 的进程模型? 网络数据如何流转的? 进程间如何通信的? 使用 unix socket 通信其实是起的 udp server, 会有什么问题?
- https 协议看过没? 说一下 https 协议以及网络数据如何交互的
再说一下以前遇到的:
- 说一下快排
- 说一下 mysql 服务器的架构, mysql 查询优化是什么?
- 说一下 select poll epoll
- 服务器大量 502 怎么办