将Int32转换为ushort并再次返回
我正在尝试设计一种系统,用于将大于65535的整数值打包到ushort中。让我解释。
我们有一个系统,该系统使用SQL Server的IDENTITY列生成Int32值,并且受生产中客户端API的限制,该API将我们的Int32 ID溢出到ushorts。幸运的是,客户端只有大约20个左右带有这些ID的事物实例,我们可以在任何给定时间将它们称为包,并且只需要它们在本地同级中具有唯一性即可。普遍接受的解决方案是在将Int32 ID传输给客户端之前,将我们的Int32 ID转换为ushorts(不是我的意思是转换,我的意思是翻译),但是这种方法有些倒钩:
- 由于未到期,某些小于65535的ID可能随时在给定的客户端上运行。
- 我们不能有任何ID冲突-即,如果程序包ID 1到达客户端,则跟踪应用于Int32的65535从int32中删除65535次以使ushort生效的算法也将导致1,从而导致冲突。
- 返回时,我们必须能够将ushort重构到Int32中。
我们可以用来解决此问题的是一个带符号的字节字段,该字段将被回显给我们,并为我们提供127个值(实际上是117,因为我们将0-9用作其他值)。从现在开始,我将其称为"字节字段"。
我们讨论了三种不同的翻译例程:
- 可乘:在字节字段中存储从Int32中删除65535的次数,以生成一个ushort。如上所述,这具有碰撞问题。
- 序列化会话状态:对于每个客户端,根据有关该客户端的事实生成一个会话ID。然后存储一个1:1转换表,从1到交付的软件包数,以便当客户端再次访问我们的服务器时,可以将软件包清单转换回它们的已知数据库ID。这有开销的问题,因为我们要将序列化的会话状态备份到数据库,并且希望每秒支持数百到数千个事务。
- 多种算法方法,其中字节字段是采用Int32并将其转换为ushort的转换算法的ID。显然,其中许多将是简单的乘法(以增加我们可以转换的ID的上限),但其中一些必须与较小的边界(例如32768)相乘,并加上/减去一个数字,以使其接近于可以保证兄弟姐妹中唯一的数字。这种方法需要占用大量处理器,但是应该可以避免冲突,同时保持可扩展性(尽管采用这种方法,我们的上限有限,在升级之前,ushort问题不会自行解决)。
所以我的问题是:有没有比我上面的方法更好的方法,如果没有,我应该寻找什么算法(对于方法3)在给定数字大于1到65535之间时生成一个数字? 0,并且一定不能是单向哈希吗?
澄清:不是ushort上限是最大的问题,而是客户端API使用ushort,因此我无法结合客户端上的byte字段来获取更大的值(客户端API不可升级,但最终将淘汰存在)。
解决方案
我们需要比65535多"多"的东西吗?我们总是可以只将"字节字段"中的几位添加为ID的高位。仅2位将使我们达到262,143,3位将使我们达到524,287.
我可以想到其他一些选择:
全球数据库中是否少于65536个条目?如果是这样,那么我们可以维护一个与会话状态无关的映射表,但是它是应用程序的持久部分。
索引中的大多数条目是否少于50,000个?如果是这种情况,我们可以直接映射这些值,并对其余会话使用与会话关联的映射。
如果持久化此类会话数据是一个问题,并且客户端数量相当少,则可以启用客户端/会话亲缘关系并在服务器本地维护映射。
如果不是Web应用程序,则可以在客户端本身上维护地图。
我看不到任何可以避免冲突的算法,我怀疑我们总能提出一个可能会发生冲突的示例。
关于方法2:
第二种方法几乎就是NAT的工作方式。本地网络上的每个TCP / UDP客户端最多使用65535个端口(端口0除外)和一个专用IP。路由器仅知道一个公共IP。由于两个客户端可能都具有源端口300,因此不能仅将私有IP替换为公共IP,这将导致出现冲突。因此,它将替换IP并"转换"端口(NAT:网络地址转换)。返回时,它会将端口转换回去,并在将包转发回之前,再次用专用IP替换公共端口。除此之外,我们什么也不会做。但是,路由器会将这些信息保留在内存中,并且在进行NAT时并不会太慢(有时将拥有数百台计算机的公司NAT到Internet,并且在大多数情况下,速度的下降几乎不会引起明显影响)。我们说我们每秒要进行数千笔交易,但是会有多少个客户?因为这主要将定义备份映射所需的内存大小。如果没有太多的客户端,则可以将带有排序表的映射保留在内存中,在这种情况下,速度将是最小的问题(表变大,而服务器内存不足则是更大的问题)。
我有点不清楚的是你曾经说过
Fortunately the client only has about 20 or so instances of things with these IDs - let's call them packages - at any given time and it only needs to have them unique amongst local siblings.
但是然后你说
Some IDs less than 65535 may still be in play on a given client at any time due to non-expiration.
我猜想,第二条语句可能意味着,如果客户端请求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位+ 1个字节)。那太神奇了。不可能将32位压缩为24位,也不能压缩所有逻辑以及如何将其转换回它。我们将需要一些外部存储,这使我们回到了第二种方法。这是唯一可行的方法,并且适用于32位数字范围内的每个数字。