Swoole Memory Table

由于原生PHP不支持多线程模型,所以Swoole更多时候使用多进程模型,因此代码相对更加简洁,减少了各种线程锁的阻塞和同步,但同时也带来了新的问题:数据共享。由于多线程可以直接共享进程的内存,进程之间数据的相互同步则需要依赖于共享内存。

原生PHP进程之间是不能相互通信的,在swoole_process中则是可以的,Swoole中进程之间的通信是通过管道pipe来传递的。多进程、多线程之间的数据共享则是通过内存memory来实现的。

swoole_table共享内存表是一个基于共享内存和锁实现的超高性能并发数据结构,用于解决多进程或多线程数据共享和同步加锁问题,它不消耗服务器 IO,纯内存操作,性能十分强悍。

优势

共享内存表可以直接操作系统的内存,性能强悍,单线程每秒可读写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_INTTable::TYPE_FLOATTable::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创建之前不能使用setget等数据读写操作,调用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();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值