首页 文章

将Int32转换为ushort并再次转换回来

提问于
浏览
1

我试图设计一个系统,将大于65535的整数值打包到一个ushort中 . 让我解释 .

我们有一个系统,它使用SQL Server中的IDENTITY列生成Int32值,并受到 生产环境 中客户端API的限制,该API将Int32 ID溢出到ushorts . 幸运的是,客户端在任何给定时间只有大约20个具有这些ID的事物实例 - 让我们称之为包 - 它只需要让它们在本地兄弟中独一无二 . 普遍接受的解决方案是在将它们传输到客户端之前将我们的Int32 ID转换为ushorts(并且我不是指铸造,我的意思是翻译),但是有这种方法的倒钩:

  • 由于未过期,某些ID小于65535的ID可能在任何时间仍然在给定客户端上 .

  • 我们不能有任何ID冲突 - 也就是说,如果包ID 1进入客户端,这个算法跟踪从Int32中删除65535以在应用于65536时使用ushort的次数也会导致1因此导致冲突 .

  • 我们必须能够在返回时将ushort重建为Int32 .

我们可以解决这个问题的是一个单一的有符号字节字段,它回显给我们并给出了127个值(实际上是117因为我们使用的是0-9而不是其他东西) . 我将此处称为“字段字段” .

我们已经讨论了三种不同的翻译例程:

  • Multiplicative:在字节字段中存储我们从Int32中删除65535以进行ushort的次数 . 这具有如上所述的碰撞问题 .

  • 序列化会话状态:对于每个客户端,根据有关该客户端的事实生成会话ID . 然后存储1:1的转换表,从1开始到交付的包数,这样当客户端再次访问我们的服务器时,包的库存可以转换回他们已知的数据库ID . 这有开销问题,因为我们将序列化会话状态支持到数据库,我们希望每秒支持数百到数千个事务 .

  • 多种算法方法,其中字节字段是变换算法的ID,它采用Int32并将其转换为ushort . 显然,其中很多都是简单的乘法(为了增加我们可以变换的ID的上限),但有些必须乘以较小的边界(如32768),加上/减去一个数来得到接近a兄弟姐妹中可以保证唯一的数字 . 这种方法是处理器密集型的,但应该允许我们在保持可扩展性的同时避免冲突(尽管采用这种方法,我们有一个有限的上限,在ushort问题由于升级而自行消失之前无法达到) .

所以我的问题是:是否有比上述方法更好的方法,如果没有,我应该在算法方面寻找什么(方法#3),当给定数字大于时,在1-65535之间生成一个数字0并且不能是单向哈希?

澄清:它不是ushort上限是最大的问题,它是客户端API使用ushort所以我不能将客户端上的字节字段组合起来获得更大的值(客户端API是不可升级的,但最终将逐步淘汰) .

3 回答

  • 0

    你需要多于65535多“多”?您总是可以从“字节字段”中添加几个位作为ID的高位 . 只需2位即可获得262,143,3位可以获得524,287 .

  • 1

    关于方法2:

    你的第二种方法几乎就是NAT的工作原理 . 本地网络上的每个TCP / UDP客户端最多使用65535个端口(端口0除外)和私有IP . 路由器只知道一个公共IP . 由于两个客户端可能都具有源端口300,因此不能简单地将私有IP替换为公共IP,这将导致冲突出现 . 因此它取代了IP和"translates"端口(NAT:网络地址 Translation ) . 返回时,它会将端口转换回来,并在转发包之前再次使用私有IP替换公共端口 . 除此之外你别无所求 . 然而,路由器将这些信息保存在内存中 - 并且在进行NAT时它们并不太慢(有数百台计算机的公司有时会被连接到互联网,而且在大多数情况下,减速很难显着) . 你说你想要每秒多达一千笔交易 - 但有多少客户?因为这主要定义备份映射所需的内存大小 . 如果没有太多的客户端,你可以在内存中保存带有排序表的映射,在这种情况下,速度将是最小的问题(表变大,服务器内存不足是更大的问题) .

    对我来说有点不清楚的是你曾经说过

    幸运的是,客户端在任何给定时间只有大约20个具有这些ID的事物实例 - 让我们称之为包 - 它只需要让它们具有唯一性当地的兄弟姐妹 .

    但是你说

    由于未到期,某些ID小于65535的ID可能在任何时间仍然在给定客户端上 .

    我想,你可能在第二个语句中指的是,如果客户端请求ID 65536,它可能仍然具有低于65535的ID,并且这些ID可以低至(假设)20 . 这不是客户端处理ID中的ID直接命令吧?所以你不能说,仅仅因为它现在要求65536,它可能有一些较小的值,但肯定不在1-1000范围内,对吗?它实际上可能会引用20,90,2005和41238并仍然超过65535,这就是你的意思?

    我个人喜欢你的第二种方法而不是第三种方法,因为在任何情况下都更容易避免碰撞,并且将数字翻译回来是一个简单,简单的操作 . 虽然我怀疑你的第三种方法可以长期运作 . 好的,你可能有一个字节来存储你减去2 ^ 16数字的频率 . 但是,您只能将117 * 2 ^ 16减去最大数字 . 如果数字超过这个,你会怎么做?使用不同的算法,不减去,但做什么?划分?转移位?在这种情况下,您将失去粒度,这意味着此算法不能再任何可能的数字(它将进行大跳转) . 如果只是简单地在32位上应用魔术翻译函数从其中生成16位(一个额外字节)然后再将其转换回来,那么猜测这个世界中的每种压缩方法都会使用它,无论如何32位数是什么,总是将其压缩到24位(16位一个字节) . 那将是神奇的 . 不可能将32位打包成24位,并且还将所有逻辑打包成如何将其转换回它 . 您将需要一些外部存储空间,这将我们带回您的第二种方法 . 这是唯一可行的方法,它适用于32位数范围内的每个数字 .

  • 1

    我可以想到其他一些选择:

    数据库中是否全局少于65536个条目?如果是这样,那么您可以维护一个与会话状态无关的映射表,但它是应用程序的持久性部分 .

    索引中的大多数条目是否少于50,000个?如果是这种情况,您可以直接映射这些值,并使用与会话关联的映射表示剩余的值 .

    如果持久存在此类会话数据是一个问题,并且客户端数量相当小,则可以启用客户端/会话关联,并将映射维护到服务器本地 .

    如果它不是Web应用程序,您可以在客户端上维护映射 .

    我没有看到任何避免碰撞的算法方法 - 我怀疑你总是想出一个会碰撞的例子 .

相关问题