+
80
-

swoole内存表table有啥用?

swoole内存表table有啥用?

网友回复

+
0
-
原生PHP进程之间是不能相互通信的,在swoole_process中则是可以的,Swoole中进程之间的通信是通过管道pipe来传递的。多进程、多线程之间的数据共享则是通过内存memory来实现的。 swoole_table共享内存表是一个基于共享内存和锁实现的超高性能并发数据结构,用于解决多进程或多线程数据共享和同步加锁问题,它不消耗服务器 IO,纯内存操作,性能十分强悍。 swoole table优势 共享内存表可以直接操作系统的内存,性能强悍,单线程每秒可读写200万每次,主要用于进程间的数据通信。 共享内存表应用代码无需加锁,内置行锁自旋锁,所有操作都是多进程或多线程安全,用户层完全不需要考虑数据同步问题。 共享内存表支持多进程,内存表可以用于多进程之间共享数据。 共享内存表使用行锁而非全局锁,仅当两个进程在同一CPU时间并发读取同一条数据时才会发生抢锁。 共享内存表不受PHP的memory_limit最大内存的控制 适用场景:用于解决多进程或多线程数据共享和同步加锁问题,进程结束后共享内存表会自动释放。

示例代码

<?php
use Swoole\Table;

//创建共享内存表对象
$size = 1024;//2的n次方
$table = new swoole_table($size);

//共享内存表添加字段
$table->column("id", Table::TYPE_INT, 11);
$table->column("name", Table::TYPE_STRING, 20);
$table->column("money", Table::TYPE_FLOAT, 10);
$table->create();

//共享内存表添加行
$table->set("alice", ["id"=>1, "name"=>"alice", "money"=>100.5]);
//使用数组方式添加行
$table["ben"] =["id"=>2, "name"=>"ben", "money"=>200.1];

//获取行返回数组
$val = $table->get("alice");
var_dump($val);
//获取行返回对象
$val = $table["ben"];
var_dump($val);

//共享内存表内字段值增加
$table->incr("alice", "money", 100);
var_dump($table["alice"]);

//共享内存表中字段值减少
$table->decr("ben", "money", 50);
var_dump($table["ben"]);

echo json_encode($table).PHP_EOL;

方法解析

创建对象construct

创建共享内存表

function Table->__construct(int $size, float $conflict_propertion = 0.2);

共享内存表实际上是一个开链法实现的哈希表,内存是由哈希键Key与具体数据组成的数组,如果哈希冲突即不同的键值对一个同一个哈希,那么就会从内存池pool中分配出一个元素作为数组元素的链表尾。 内存表占用的内存总数 = ( 结构体长度 + 哈希键长度64byte + 行尺寸$size ) * 预留作为哈希冲突的百分比 * 列尺寸 参数1:int $size $size表示创建共享内存表时设置的最大行数,必须是2的次方,如果不是底层会自动调整为接近的一个数字,若小于1024则默认为1024,即1024为最小值。若机器内存不足内存表会创建失败。 共享内存表底层是建立在共享内存之上的哈希表HashTable(数据结构),最大行数$size决定了哈希表的总行数。由于内存表是在共享内存之上,所以无法动态扩容,因此$size必须在创建前提前设置好。 参数2:float $conflict_propertion 哈希冲突率

如果哈希冲突超过最大比例,共享内存表将不再允许添加新的行元素。共享内存表能存储的总数据行数取决于数据的哈希键冲突率,默认如果冲突率超过20%,预留的哈希hash冲突内存块容量不足,会报Unable to allocate memory无法分配内存的错误,并返回false表示存储失败。

<?php
use Swoole\Table;

//创建共享内存表对象
$size = 1024;//2的n次方
$table = new swoole_table($size);
$table->column("data", Table::TYPE_STRING, 1);
$table->create();

echo json_encode($table).PHP_EOL;// {"size":1024,"memorySize":117548}

添加列数据column

共享内存表新增一列 bool Table->column(string $name, int $type, int $size = 0)

swoole_table->column(string $name, int $type, int $size = 0)

参数列表

string $name 表示字段的名称

int $type 表示字段类型,可支持3种类型分别是Table::TYPE_INT、Table::TYPE_FLOAT、Table::TYPE_STRING。

int $size 表示字符串字段的最大长度单位字节,字符串类型Table::TYPE_STRING的字段必须指定$size。

字段类型

Table::TYPE_INT 整型默认4字节,可设置1、2、4、8共四种长度。

Table::TYPE_STRING 字符型必须指定,设置的字符串不得超过设置值。

Table::TYPE_FLOAT 浮点型,占用8字节的内存。

整型溢出 由于Swoole底层使用有符号整型,如果传入的数值超过溢出边界就可能会发生溢出。 整数类型安全值的范围

int8 1byte -127 ~127

int16 2byte -32767~327767

int32 4byte -2147483647 ~ 2147483647

int64 8byte 不会溢出

内存对齐 从Swoole4.3版本开始,底层对内存长度做了对齐处理。字符串长度必须是8的整数倍,如果长度为18字节会自动对其到24字节。注意非x86环境则内存对齐。

创建共享内存表create

当定义好表的结构后执行create会向操作系统申请内存并创建共享内存表。需要注意的是,调用create创建之前不能使用set、get等数据读写操作,调用create创建之后不能使用column添加新字段。如果 系统内存不足则会申请失败此时create将返回false,若申请成功create将会返回true。 Table使用共享内存来保存数据,在创建子进程之前,务必要执行create执行创建。服务器中使用共享内存表执行create创建操作必须在服务器执行start启动方法之前。 当使用create方法创建共享内存表后,可以读取$table->memorySize属性来获取实际占用的内存尺寸,单位为字节。

function Table->create():bool

设置行数据set

共享内存表使用键值对的方式存取数据

Table->set(string $key, array $value):bool

参数列表 参数1:string $key 设置行数据对应的键名,相同的键名对应同一行数据,如果set设置了同一个键名则会覆盖上一次的数据。 参数2:array $value 设置行数据对应的键值,必须是一个数组,必须与字段定义的名称$name完全相同。 返回值 设置成功返回true,设置失败返回false,失败的原因可能是由于哈希冲突过多导致动态空间无法分配内存,可以调大构造方法的$conflict_propertion参数。 如果传入字符串的长度超过列定义的最大尺寸,底层会自动截断。

获取行数据get

获取单行数据

array Table->get(string $key, string $field = null)

参数列表中string $key表示查询数据行的键名,必须为字符串类型。 如果带查询的键名不存在,则get方法将会返回false。若成功则返回的结果为数组类型。

检查键名是否存在exist

exist方法用于检查共享内存表中是否存在某个键名key,若存在则返回true,否则返回false。

bool swoole_table->exists(string $key)

条目数量count

count方法用于获取共享内存表table中存在的条目数量

int function Table->count()

删除键值对del

bool Table->del(string $keyy)

del方法用于删除共享内存表$table中指定键名$key的行,如果键名$key对应的是数据不存在则返回false,否则成功返回true。此处需要注意的是键名$key是非二进制安全的,所以必须为字符串类型,因此不得传入二进制数据。

原子递增incr

function Table->incr(string $key, string $column. mixed $incrby = 1): int

incr表示原子递增操作,参数string $key表示指定数据的键名,如果键名对应的行不存在则默认值为0。

int column 表示指定的列表即字段名,仅支持浮点型和整型字段。

int $incrby表示则增量默认为1,若列为整型则增量必须为整型,如果列为浮点型则增量必须为浮点型。

返回最终的结果数值

原子自减decr

function Table->decr(string $key, string $column, mixed $decrby = 1) int | bool

参数列表

string $key表示指定数据的键名,如果键名对应的行不存在,底层会首先将该行数据初始化为0。

string $column表示指定的列名,仅支持浮点型和整型字段。

mixed $decrby表示减量默认为1,如果列为整型则减量必须也为整型,同样如果列为浮点型则减量也必须为浮点型。

返回值将返回最终的结果数值,数值为0时递减会变成负数。 实例:创建swoole_table用于进程间数据共享
<?php
class Server
{
private $server;
public function __construct($host="0.0.0.0", $port = 9501, $size = 1024)
{
//创建swoole_table用于进程间数据共享
$table = new swoole_table($size);
$table->column("fd", swoole_table::TYPE_INT);
$table->column("uid", swoole_table::TYPE_INT);
$table->column("type", swoole_table::TYPE_STRING, 256);
$table->column("data", swoole_table::TYPE_STRING, 256);
$table->create();
//创建WebServer服务器对象
$this->server = new swoole_websocket_server($host, $port);
$this->server->table = $table;
//注册事件回调函数
$this->server->on("handShake", [$this, "onHandShake"]);
$this->server->on("workerStart", [$this, "onWorkerStart"]);
$this->server->on("open", [$this, "onOpen"]);
$this->server->on("message", [$this, "onMessage"]);
$this->server->on("close", [$this, "onClose"]);
//开启服务器
$this->server->start();
}
public function onHandShake($server)
{
echo "[handshake]".PHP_EOL;
}
public function onWorkerStart($server)
{
echo "[workerstart]".PHP_EOL;
}
public function onOpen($server)
{
echo "[open]".PHP_EOL;
}
public function onMessage($server)
{
echo "[message]".PHP_EOL;
}
public function onClose($message)
{
echo "[close]".PHP_EOL;
}
}
$server = new Server();

链接:https://www.jianshu.com/p/1dae870407bc
+
0
-

在多进程模式下进程之间的内存是相互隔离的,在一个工作进程中的全局变量和超全局变量,在另一个工作进程中是无法读取和操作的。

如果只有一个工作进程,则不存在进程隔离问题,可以使用全局变量和超全局变量。 要实现进程间共享数据,我们可以使用第三方的 Redis 内存数据库或 Swoole 内置的 Table 共享内存来实现。

swoole 内存表Table 的优势

性能强悍,单线程每秒可读写 200 万次;

应用代码无需加锁,Table 内置行锁自旋锁,所有操作均是多线程 / 多进程安全。用户层完全不需要考虑数据同步问题;

支持多进程,Table 可以用于多进程之间共享数据;

使用行锁,而不是全局锁,仅当 2 个进程在同一 CPU 时间,并发读取同一条数据才会进行发生抢锁。

我们来用swoole的内存表实现类似memcache的缓存服务中间件,代码如下:

<?php
// 实例化一个占用的共享内存大小为1024的内存表
$table = new Swoole\Table(1024);
// 内存表增加3列
$table->column('fd', Swoole\Table::TYPE_INT);
$table->column('reactor_id', Swoole\Table::TYPE_INT);
$table->column('data', Swoole\Table::TYPE_STRING, 64);
$table->create();

$serv = new Swoole\Server('127.0.0.1', 9501);
// 设置数据包分发策略:轮循模式
$serv->set(['dispatch_mode' => 1]);
$serv->table = $table;

$serv->on('receive', function ($serv, $fd, $reactor_id, $data) {
    $cmd = explode(" ", trim($data));

    if ($cmd[0] == 'get') {
        //get self
        if (count($cmd) < 2) {
            $cmd[1] = $fd;
        }
        $get_fd = intval($cmd[1]);
        $info = $serv->table->get($get_fd);
        $serv->send($fd, var_export($info, true) . "\n");
    } elseif ($cmd[0] == 'set') {
        // 使用连接的文件描述符作为key写入内存表
        $ret = $serv->table->set($fd, array('fd' => $fd, 'reactor_id' => $reactor_id, 'data' => $cmd[1]));
        if ($ret === false) {
            $serv->send($fd, "ERROR\n");
        } else {
            $serv->send($fd, "OK\n");
        }
    } else {
        $serv->send($fd, "command error.\n");
    }
});

$serv->start();

我知道答案,我要回答