CANOpen中一个PDO只能传输8个字节,为啥很多资料都显示一个PDO可以超过8字节?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CANOpen中一个PDO只能传输8个字节,为啥很多资料都显示一个PDO可以超过8字节?相关的知识,希望对你有一定的参考价值。

并且连从站设备的描述文件中#x1600也超过8字节。

参考技术A 图二中32是bit length,32位相当于4字节。
你图中的这个系统,我想它是不是支持CANOpen FD的。CAN总线报文从FD开始支持最大64字节的数据字段了。追问

你好,图3是某驱动器的设备描述XML文件,该图中描述的x1600有三个条目对象,该PDO的总数据长度就是10字节。驱动器PDO有这样的描述是说它支持CANOpen FD么?

追答

推测是的。因为传统上PDO由于CAN扩展帧的限制最多只能携带8字节,至少以前我看的规格书是这样的。CANOpen又不支持多报文拼接。

本回答被提问者采纳

为啥在 PHP 和 MySQL 中使用 PDO 的某些类型的准备查询很慢?

【中文标题】为啥在 PHP 和 MySQL 中使用 PDO 的某些类型的准备查询很慢?【英文标题】:Why are certain types of prepared queries using PDO in PHP with MySQL slow?为什么在 PHP 和 MySQL 中使用 PDO 的某些类型的准备查询很慢? 【发布时间】:2011-05-20 00:57:50 【问题描述】:

当使用带有prepare()/execute() 的PDO 使用具有超过10000 个键的SELECT * FROM table WHERE Id IN ( .. ) 查询时,性能比使用带有预准备语句的mysqli 或不使用预准备语句的PDO 执行相同查询要降低约10 倍。

更多奇怪的细节:

没有WHERE Id IN( ..) 子句的更典型的 SELECT 语句即使在 100K+ 行时也能正常执行。 SELECT * FROM table WHERE Id 例如很快。

prepare()/execute() 完成后性能下降 - 它完全在 PDOStatement::fetch()PDOStatement::fetchAll() 中。在所有情况下,MySQL 查询执行时间都很短——这不是 MySQL 优化的情况。

将 10K 查询拆分为具有 1K 键的 10 个查询是高效的。

使用 mysql、带有预准备语句的 mysqli 或不带预准备语句的 PDO 是高效的。

在下面的示例中,带准备的 PDO 大约需要 6 秒,而其他的大约需要 0.5 秒。

你拥有的键越多,它就会以非线性方式变得更糟。尝试 10 万个密钥。

示例代码:

// $imageIds is an array with 10K keys
$keyCount = count($imageIds);
$keys = implode(', ', array_fill(0, $keyCount, '?'));
$query = "SELECT * FROM images WHERE ImageID IN ($keys)";
$stmt = $dbh->prepare($query);
$stmt->execute($imageIds);
// until now, it's been fast.  fetch() is the slow part
while ($row = $stmt->fetch()) 
    $rows[] = $row;

【问题讨论】:

如果这是可重现的,那么您可能需要分析 PHP 以了解出现减速的原因。 从您的描述来看,这听起来像是一个后处理错误。我怀疑减速是由于处理绑定参数。尝试->debugDumpParams() 并查找is_param= 值。如果是1,那么 PDO 将遍历列表以查找要更新的绑定变量。也许使用 ->bindValue() 而不是 ->execute(ARRAY) 手动预置会有所帮助。但我怀疑 PDO 总是会遍历绑定的参数列表。不确定 is_param= 是否对此起决定性作用。 (而且懒得理解pdo_stmt.c) 请注意,在 mysqli 中使用$stmt->bind_param(str_repeat('s', count($imageIds)), ...$imageIds); 将参数绑定为字符串比将它们绑定为整数慢。两种 mysqli 方法都需要比未准备好的语句多 50% 的时间。但是 PDO 准备好的语句就像 50 倍慢(具有 10K 参数)。所以不能只是 PDO 总是将参数绑定为字符串。即使$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); 也不会改变任何东西。 PDO 发生了一些非常奇怪的事情。 你有没有弄清楚是什么原因造成的?我遇到了完全相同的问题。 相关错误报告:bugs.php.net/bug.php?id=53458 - @mario 很好地解决了这个问题。获取时间与以下两者成正比:返回的行数和绑定参数的数量。对于这种查询,应该是线性的问题变成 O(n²)。这意味着:参数增加 100 倍 => 慢 10000 倍。 【参考方案1】:

示例代码存在一些重大错误。所以更准确地说。

// $imageIds is an array with 10K keys
$keyCount = count($imageIds);
$keys = implode(', ', array_fill(0, $keyCount, '?'));
$query = "SELECT * FROM images WHERE ImageID IN ($keys)";

到目前为止,上面的代码将提供类似的东西......

SELECT * FROM images WHERE ImageID IN (?, ?, ?, ?, ?, ?,...?, ?, ?, ?)

没有用于绑定的循环...应该有一个小循环,您可以在其中绑定所有传递给 MySQL 的参数。您从 prepare 转到 execute 当正确时,binding 主要是您想要的。

$stmt = $dbh->prepare($query);
$stmt->execute($imageIds);
// until now, it's been fast.  fetch() is the slow part
while ($row = $stmt->fetch()) 
    $rows[] = $row;

现在我对这部分问题有一个简单的逻辑问题......

当使用SELECT * FROM table WHERE Id IN ( .. ) 查询时 超过 10000 个键 使用 PDO 和 prepare()/execute(),性能 与使用 mysqli 执行相同查询相比,降级约 10 倍 不使用预处理语句的预处理语句或 PDO。

如果重写相同的查询,这样您就不需要传递 10000 个键作为参数,不是更好吗?

PDOMySQLi 在时间上没有大的区别。糟糕的书面查询会。如果没有得到很好的优化,非常复杂的存储过程有时可能会变得很慢。

检查另一个查询是否可以获取所需的结果。例如

创建一个名为test的小表

create table `test` (
  `id` int(10) not null,
  `desc` varchar(255)
  ); 
insert into `test` (`id`,`desc`) values (1,'a'),(10,'a1'),(11,'a2'),(12,'a3'),(13,'a4'),(14,'a5'),(15,'a6'),(2,'ab'),(20,'ab1'),(21,'ab2'),(22,'ab3'),(23,'ab4'),(24,'ab5'),(25,'ab6');

运行那些简单的查询

select * from `test` where `id` rlike '^1$';
select * from `test` where `id` rlike '^1+';
select * from `test` where `id`=1;
select * from `test` where `id` rlike '^1.$';
select * from `test` where `id` rlike '.2$';
select * from `test` where `id` rlike '^2$';
select * from `test` where `id` rlike '.(2|3)'; // Slower
select * from `test` where `id` IN (12,13,22,23); // Faster
select * from `test` where `id` IN ('12,13,22,23'); // Wrong result
select * from `test` where `id` IN ('12','13','22','23'); // Slower

在此示例中,最后 4 个查询具有相同的结果。我认为大多数情况下,如果您在SQLFiddle 上查看它,您会得到与给定标签相对应的查询时间。

【讨论】:

我不明白你的回答。 $keys = implode(', ', array_fill(0, $keyCount, '?')); 工作得很好here。 $stmt->execute($imageIds); 通常也可以正常工作。为什么要为可以通过单个命令完成的事情编写循环?查询SELECT * FROM table WHERE Id IN ( .. ) 也很好,我在处理关系时经常使用它。为什么要重写一些非常简单的东西? @PaulSpiegel 我已经编辑了我的答案,请同时检查 SQL Fiddle。你会看到某个查询得到了错误的结果,也就是说,当它在 MySQL 中声明为 int 时,你将参数作为字符串传递,你会得到一个结果,但大多数时候要慢得多。关键是如果查询很慢并不意味着驱动程序很慢。如果查询是错误的,那么肯定不是司机的错:) @PaulSpiegel 我已经编辑了我的答案,但请回答我。您已经在变量中应用了括号,如果将它们取出,它在rextester.com 中仍然得到相同的结果,这是否意味着不需要它们?关键是传递的是一系列字符串而不是整数,或者查询没有很好地编写或优化。 在字符串插值中是可选的。但有时你无法避免它。对我来说,始终使用它们是一个好习惯。 @PaulSpiegel 此外,这也是一个peculiar 问题,因为它只有一个小代码示例,其中缺少绑定参数循环的所有问题中最相关的部分。我的观点是,我真的认为 OP 应该完全改变查询,这就是为什么我做了一个小的 SQLFiddle 来支持我的观点。我没有处理过表有数百万行并且IN 子句内部有 10K 键的查询,但我真的会尝试重写我的查询以避免必须在 10k 键中搜索。也许正则表达式更合适。或许……【参考方案2】:

没有任何使用 PDO 的经验,所以对此无能为力,但这种方法的性能非常好,虽然在某些地方有点难看;)

PHP

<?php

$nums = array(); $max = 10000;

for($i=0;$i<$max*10;$i++) $nums[] = $i;

$conn = new mysqli("127.0.0.1", "vldb_dbo", "pass", "vldb_db", 3306);

$sql = sprintf("call list_products_by_id('%s',0)", implode(",",array_rand($nums, $max)));

$startTime = microtime(true);

$result = $conn->query($sql);

echo sprintf("Fetched %d rows in %s secs<br/>", 
    $conn->affected_rows, number_format(microtime(true) - $startTime, 6, ".", ""));

$result->close();
$conn->close();

?>

结果

select count(*) from product;
count(*)
========
1000000

Fetched 1000 rows in 0.014767 secs
Fetched 1000 rows in 0.014629 secs

Fetched 2000 rows in 0.027938 secs
Fetched 2000 rows in 0.027929 secs

Fetched 5000 rows in 0.068841 secs
Fetched 5000 rows in 0.067844 secs

Fetched 7000 rows in 0.095199 secs
Fetched 7000 rows in 0.095184 secs

Fetched 10000 rows in 0.138205 secs
Fetched 10000 rows in 0.134356 secs

MySQL

drop procedure if exists list_products_by_id;

delimiter #

create procedure list_products_by_id
(
in p_prod_id_csv text,
in p_show_explain tinyint unsigned
)
proc_main:begin

declare v_id varchar(10);
declare v_done tinyint unsigned default 0;
declare v_idx int unsigned default 1;

    create temporary table tmp(prod_id int unsigned not null)engine=memory; 

    -- split the string into tokens and put into a temp table...

    if p_prod_id_csv is not null then
        while not v_done do
            set v_id = trim(substring(p_prod_id_csv, v_idx, 
                if(locate(',', p_prod_id_csv, v_idx) > 0, 
                        locate(',', p_prod_id_csv, v_idx) - v_idx, length(p_prod_id_csv))));

                if length(v_id) > 0 then
                set v_idx = v_idx + length(v_id) + 1;
                        insert ignore into tmp values(v_id);
                else
                set v_done = 1;
                end if;
        end while;
    end if;

    if p_show_explain then

        select count(*) as count_of_tmp from tmp;

        explain
        select p.* from product p
        inner join tmp on tmp.prod_id = p.prod_id order by p.prod_id;

    end if;

    select p.* from product p
        inner join tmp on tmp.prod_id = p.prod_id order by p.prod_id;

    drop temporary table if exists tmp;

end proc_main #

delimiter ;

【讨论】:

嘿 f00,谢谢!唉,我已经有了 MySQL、MySQLi、PDO 没有准备好的语句等的工作函数——我想知道为什么这个特殊的用例如此混乱。出于多种原因,我更喜欢 PDO 和准备好的语句。 不用担心 :) 只是出于兴趣你得到什么样的运行时?? @donmacaskill 您的来源并未表明在 PREPARE 和 EXECUTE 之后使用 CLOSE 来释放资源。请参阅 Jon Black 的源代码结尾。【参考方案3】:

确保您告诉 PDO 该值是整数而不是字符串;如果 PDO 把它作为一个字符串,那么 MySQL 将不得不对值进行类型转换以进行比较。根据它的处理方式,它可能会导致 MySQL 避免使用索引,从而导致严重的减速。

我不完全确定这里的行为,但几年前我在 Postgres 上遇到过这个问题......

【讨论】:

+1 因为这可能是导致速度缓慢的原因。铸造将整齐地使用索引。之前在 mysql + PDO 准备语句中遇到过完全相同的问题。 上次看代码的时候,PDO MySQL驱动总是以字符串的形式传递参数。大多数数据类型参数都被忽略(PDO::PARAM_LOBPDO::PARAM_NULL 除外)。其他品牌的 RDBMS 的其他一些 PDO 驱动程序也需要它们。 请注意,mysqli 可以很好地将参数绑定为字符串。而且带有字符串列表的未准备语句也很快。

以上是关于CANOpen中一个PDO只能传输8个字节,为啥很多资料都显示一个PDO可以超过8字节?的主要内容,如果未能解决你的问题,请参考以下文章

CANopen学习

CAN与CANOpen

canOpen学习八之canOpen应用实现主从机PDO定时同步数据

CANopen 基础

CAN与CANOpen

CANopen笔记2