java CRC16算法,分解一个大Key实例。和各种hash算法代码

本文探讨了在 Redis 集群中通过数据分片(sharding)而非一致性哈希来实现大规模用户数据的高效存储策略。介绍了如何利用 CRC16 校验和算法将一亿个用户均匀分布于有限数量的哈希槽中,以此减轻单个节点的压力并提高系统的整体性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package org.rui.hi;  
  2.   
  3. /** 
  4.  * 测试: 解决场景:把1亿的用户 存储在一个队列里,过大。用sharding 摸拟redis 集群 sharding Redis 
  5.  * 集群使用数据分片(sharding)而非一致性哈希(consistency hashing)来实现: 一个 Redis 集群包含 16384 
  6.  * 个哈希槽(hash slot), 数据库中的每个键都属于这 16384 个哈希槽的其中一个, 集群使用公式 CRC16(key) % 16384 来计算键 
  7.  * key 属于哪个槽, 其中 CRC16(key) 语句用于计算键 key 的 CRC16 校验和 。 
  8.  *  
  9.  * @author ruiliang 
  10.  * 
  11.  */  
  12. public class HashDist {  
  13.     public final static int maxInt = 1000;// 00000;//1亿  
  14.     public final static int USER_KEY_SLOT_COUNT = 20// 定议分配存储用户的Slot位  
  15.                                                         // ,如果存储有压力,可调大槽位  
  16.   
  17.     public static void main(String[] args) {  
  18.   
  19.         // int 不用crc16  
  20.         for (int i = 1; i < maxInt; i++) {  
  21.             // 根据玩家id 分布指定到Slot位  
  22.             int ranint = i % USER_KEY_SLOT_COUNT;  
  23.             String key = "key:" + ranint;  
  24.             System.out.println("key:" + key);  
  25.             // redisList.lpush(randomKey, String.valueOf(playerId));  
  26.         }  
  27.   
  28.         /** 
  29.          * crc16 redis 集群也是用这种方式分配key 
  30.          */  
  31.   
  32.         String a = "a,b,c,d,e,f,g,g,g";  
  33.         for (String j : a.split(",")) {  
  34.             int solt = CRCJava.crc16(j.getBytes()) % USER_KEY_SLOT_COUNT;  
  35.             String key = "key:" + solt;  
  36.             System.out.println("crc%solt=key:" + key);  
  37.         }  
  38.   
  39.         // redisList.lpush(randomKey, String.valueOf(playerId));  
  40.   
  41.     }  
  42.   
  43. }  
  44. /** 
  45.  * output: ... 
  46.  *  
  47.  * key:key:0 key:key:1 key:key:2 key:key:3 key:key:4 key:key:5 key:key:6 
  48.  * key:key:7 key:key:8 key:key:9 key:key:10 key:key:11 key:key:12 key:key:13 
  49.  * key:key:14 key:key:15 key:key:16 key:key:17 key:key:18 key:key:19 key:key:0 
  50.  * key:key:1 key:key:2 key:key:3 key:key:4 key:key:5 key:key:6 key:key:7 
  51.  * key:key:8 key:key:9 key:key:10 key:key:11 key:key:12 key:key:13 key:key:14 
  52.  * key:key:15 key:key:16 key:key:17 key:key:18 key:key:19 key:key:0 key:key:1 
  53.  * key:key:2 key:key:3 key:key:4 key:key:5 key:key:6 key:key:7 key:key:8 
  54.  * key:key:9 key:key:10 key:key:11 key:key:12 key:key:13 key:key:14 key:key:15 
  55.  * key:key:16 key:key:17 key:key:18 key:key:19 key:key:0 key:key:1 key:key:2 
  56.  * key:key:3 key:key:4 key:key:5 key:key:6 key:key:7 key:key:8 key:key:9 
  57.  * key:key:10 key:key:11 key:key:12 key:key:13 key:key:14 key:key:15 key:key:16 
  58.  * key:key:17 key:key:18 key:key:19 key:key:0 key:key:1 key:key:2 key:key:3 
  59.  * key:key:4 key:key:5 key:key:6 key:key:7 key:key:8 key:key:9 key:key:10 
  60.  * key:key:11 key:key:12 key:key:13 key:key:14 key:key:15 key:key:16 key:key:17 
  61.  * key:key:18 key:key:19 key:key:0 key:key:1 key:key:2 key:key:3 key:key:4 
  62.  * key:key:5 key:key:6 key:key:7 key:key:8 key:key:9 key:key:10 key:key:11 
  63.  * key:key:12 key:key:13 key:key:14 key:key:15 key:key:16 key:key:17 key:key:18 
  64.  * key:key:19 crc%solt=key:key:11 crc%solt=key:key:8 crc%solt=key:key:17 
  65.  * crc%solt=key:key:10 crc%solt=key:key:19 crc%solt=key:key:16 
  66.  * crc%solt=key:key:5 crc%solt=key:key:5 crc%solt=key:key:5 
  67.  */  



[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package org.rui.hi;  
  2.   
  3. public class CRCJava {  
  4.   
  5.     public static void main(String[] args) {  
  6.         String a = "a,b,c,d,e,f,g";  
  7.   
  8.         for (String i : a.split(",")) {  
  9.             System.out.println(crc16(i.getBytes()));  
  10.         }  
  11.         System.out.println("----------------------------------");  
  12.         for (String i : a.split(",")) {  
  13.             System.out.println(crc162(i.getBytes()));  
  14.         }  
  15.   
  16.     }  
  17.   
  18.     /****************************************************************************** 
  19.      * Compilation: javac CRC16CCITT.java Execution: java CRC16CCITT s 
  20.      * Dependencies: 
  21.      *  
  22.      * Reads in a sequence of bytes and prints out its 16 bit Cylcic Redundancy 
  23.      * Check (CRC-CCIIT 0xFFFF). 
  24.      * 
  25.      * 1 + x + x^5 + x^12 + x^16 is irreducible polynomial. 
  26.      * 
  27.      * % java CRC16-CCITT 123456789 CRC16-CCITT = 29b1 
  28.      * 
  29.      ******************************************************************************/  
  30.   
  31.     public static int crc16(final byte[] buffer) {  
  32.   
  33.         int crc = 0xFFFF// initial value 65535  
  34.         int polynomial = 0x1021// 0001 0000 0010 0001 (0, 5, 12)  
  35.         // byte[] testBytes = "123456789".getBytes("ASCII");  
  36.         for (byte b : buffer) {  
  37.             for (int i = 0; i < 8; i++) {  
  38.                 boolean bit = ((b >> (7 - i) & 1) == 1);  
  39.                 boolean c15 = ((crc >> 15 & 1) == 1);  
  40.                 crc <<= 1;  
  41.                 if (c15 ^ bit)  
  42.                     crc ^= polynomial;  
  43.             }  
  44.         }  
  45.   
  46.         return crc &= 0xffff;  
  47.   
  48.     }  
  49.   
  50.     /** 
  51.      *  
  52.      * @param buffer 
  53.      * @return 
  54.      */  
  55.     static int crc162(final byte[] buffer) {  
  56.         int crc = 0xFFFF;  
  57.   
  58.         for (int j = 0; j < buffer.length; j++) {  
  59.             crc = ((crc >>> 8) | (crc << 8)) & 0xffff;  
  60.             crc ^= (buffer[j] & 0xff);// byte to int, trunc sign  
  61.             crc ^= ((crc & 0xff) >> 4);  
  62.             crc ^= (crc << 12) & 0xffff;  
  63.             crc ^= ((crc & 0xFF) << 5) & 0xffff;  
  64.         }  
  65.         crc &= 0xffff;  
  66.         return crc;  
  67.   
  68.     }  
  69. }  

---------------------------------------

各种hash算法

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package org.rui.hi;  
  2.   
  3. /** 
  4.  * 相关hash算法 
  5.  *  
  6.  * @author ruiliang 
  7.  * 
  8.  */  
  9. public class HashTest {  
  10.   
  11.     public static void main(String[] args) {  
  12.   
  13.         // long hash = DEKHash("123456");  
  14.         // System.out.println(hash);  
  15.         System.out.println(hashCode(0));  
  16.         System.out.println(hashCode(1));  
  17.         System.out.println(hashCode(2));  
  18.         System.out.println(hashCode(3));  
  19.         System.out.println(hashCode(123456));  
  20.         System.out.println(hashCode(123457));  
  21.         System.out.println(hashCode(123458));  
  22.   
  23.         System.out.println(PJWHash("0"));  
  24.         System.out.println(PJWHash("1"));  
  25.         System.out.println(PJWHash("2"));  
  26.         System.out.println(PJWHash("3"));  
  27.         System.out.println(PJWHash("123456"));  
  28.         System.out.println(PJWHash("123457"));  
  29.         System.out.println(PJWHash("123458"));  
  30.   
  31.     }  
  32.   
  33.     /** 
  34.      * 从Robert Sedgwicks的 Algorithms in C一书中得到了 
  35.      *  
  36.      * @param str 
  37.      * @return 
  38.      */  
  39.     public static long RSHash(String str) {  
  40.         int b = 378551;  
  41.         int a = 63689;  
  42.         long hash = 0;  
  43.         for (int i = 0; i < str.length(); i++) {  
  44.             hash = hash * a + str.charAt(i);  
  45.             a = a * b;  
  46.         }  
  47.         return hash;  
  48.     }  
  49.   
  50.     /** 
  51.      * Justin Sobel写的一个位操作的哈希函数。 
  52.      *  
  53.      * @param str 
  54.      * @return 
  55.      */  
  56.     public static long JSHash(String str) {  
  57.         long hash = 1315423911;  
  58.         for (int i = 0; i < str.length(); i++) {  
  59.             hash ^= ((hash << 5) + str.charAt(i) + (hash >> 2));  
  60.         }  
  61.         return hash;  
  62.     }  
  63.   
  64.     /** 
  65.      * PJW 该散列算法是基于贝尔实验室的彼得J温伯格的的研究。在Compilers一书中(原则,技术和工具),建议采用这个算法的散列函数的哈希方法。 
  66.      *  
  67.      * @param str 
  68.      * @return 
  69.      */  
  70.     public static long PJWHash(String str) {  
  71.         long BitsInUnsignedInt = (long) (4 * 8);  
  72.         long ThreeQuarters = (long) ((BitsInUnsignedInt * 3) / 4);  
  73.         long OneEighth = (long) (BitsInUnsignedInt / 8);  
  74.         long HighBits = (long) (0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);  
  75.         long hash = 0;  
  76.         long test = 0;  
  77.         for (int i = 0; i < str.length(); i++) {  
  78.             hash = (hash << OneEighth) + str.charAt(i);  
  79.             if ((test = hash & HighBits) != 0) {  
  80.                 hash = ((hash ^ (test >> ThreeQuarters)) & (~HighBits));  
  81.             }  
  82.         }  
  83.         return hash;  
  84.     }  
  85.   
  86.     /** 
  87.      * ELF 和PJW很相似,在Unix系统中使用的较多。 
  88.      *  
  89.      * @param str 
  90.      * @return 
  91.      */  
  92.     public static long ELFHash(String str) {  
  93.         long hash = 0;  
  94.         long x = 0;  
  95.         for (int i = 0; i < str.length(); i++) {  
  96.             hash = (hash << 4) + str.charAt(i);  
  97.             if ((x = hash & 0xF0000000L) != 0) {  
  98.                 hash ^= (x >> 24);  
  99.             }  
  100.             hash &= ~x;  
  101.         }  
  102.         return hash;  
  103.     }  
  104.   
  105.     /** 
  106.      * BKDR 这个算法来自Brian Kernighan 和 Dennis Ritchie的 The C Programming 
  107.      * Language。这是一个很简单的哈希算法,使用了一系列奇怪的数字,形式如31,3131,31...31,看上去和DJB算法很相似。 
  108.      *  
  109.      * @param str 
  110.      * @return 
  111.      */  
  112.   
  113.     public static long BKDRHash(String str) {  
  114.         long seed = 131// 31 131 1313 13131 131313 etc..  
  115.         long hash = 0;  
  116.         for (int i = 0; i < str.length(); i++) {  
  117.             hash = (hash * seed) + str.charAt(i);  
  118.         }  
  119.         return hash;  
  120.     }  
  121.   
  122.     public static long SDBMHash(String str) {  
  123.         long hash = 0;  
  124.         for (int i = 0; i < str.length(); i++) {  
  125.             hash = str.charAt(i) + (hash << 6) + (hash << 16) - hash;  
  126.         }  
  127.         return hash;  
  128.     }  
  129.   
  130.     /** 
  131.      * DJB 这个算法是Daniel J.Bernstein 教授发明的,是目前公布的最有效的哈希函数。 
  132.      *  
  133.      * @param str 
  134.      * @return 
  135.      */  
  136.     public static long DJBHash(String str) {  
  137.         long hash = 5381;  
  138.         for (int i = 0; i < str.length(); i++) {  
  139.             hash = ((hash << 5) + hash) + str.charAt(i);  
  140.         }  
  141.         return hash;  
  142.     }  
  143.   
  144.     /** 
  145.      * .DEK 由伟大的Knuth在《编程的艺术 第三卷》的第六章排序和搜索中给出。 
  146.      *  
  147.      * @param str 
  148.      * @return 
  149.      */  
  150.     public static long DEKHash(String str) {  
  151.         long hash = str.length();  
  152.         for (int i = 0; i < str.length(); i++) {  
  153.             hash = ((hash << 5) ^ (hash >> 27)) ^ str.charAt(i);  
  154.         }  
  155.         return hash;  
  156.     }  
  157.   
  158.     /** 
  159.      * jdk hash 
  160.      *  
  161.      * @param a 
  162.      * @return 
  163.      */  
  164.     public static int hashCode(int a) {  
  165.         final int prime = 48;  
  166.         int result = 1;  
  167.         result = prime * result + a;  
  168.         return result;  
  169.     }  
  170. }  

<think>嗯,用户之前问过如何在多个服务器上同时运行Python脚本处理数据,避免重复处理。现在他进一步问有没有一种算法,可以把数据分区,每个实例加载全部数据,但只处理分配给自己的那部分。看来用户可能遇到了性能问题,或者数据量太,需要分布到多个服务器处理,同时避免重复劳动。 首先,我需要确认用户的具体需求。每个实例加载全部数据,但只处理分配给自己的部分。这可能意味着数据本身很,但每个实例需要访问全部数据,但处理的时候只处理特定分区。这可能是因为数据之间有依赖关系,或者处理逻辑需要全局信息,但计算可以并行。 用户可能希望避免中央调度,比如数据库或者消息队列,而是让每个实例独立处理自己的分区。这样可能减少对中央服务的依赖,提高可靠性。但需要确保数据分区是明确的,每个实例知道自己该处理哪部分。 接下来,我需要考虑不同的数据分区算法。常见的比如哈希取模,范围分片,轮询分片,或者一致性哈希。每种方法的适用场景不同。比如哈希取模简单,但服务器数量变化时会导致数据迁移。范围分片适合有序数据,但需要预先规划。一致性哈希适合动态增减节点的情况。 用户提到每个实例加载全部数据,这可能意味着数据不是存储在中央数据库,而是每个实例都有全量数据。比如数据文件被复制到每个服务器,或者从同一个存储源加载。这种情况下,分区算法需要在代码层面实现,每个实例根据算法决定处理哪些数据。 然后,我需要考虑如何实现这些算法。比如在Python中,可以通过命令行参数传递实例编号实例数,然后根据ID的哈希值取模来决定是否处理该数据。或者根据ID的范围分配,比如实例0处理ID 1-1000,实例1处理1001-2000等。 还需要注意数据分布的均匀性。哈希取模通常能保证均匀分布,但如果有热点数据可能需要特殊处理。范围分片如果数据分布不均匀,可能导致某些实例负载过重。 用户可能关心如何动态扩展,比如增加或减少实例时,如何重新分配数据。哈希取模在实例数变化时,部分数据需要重新分配,而一致性哈希可以减少这种影响。但实现复杂度较高。 另外,幂等性也很重要,即使多个实例加载相同数据,也要确保每个数据只被处理一次。可能需要通过外部存储记录处理状态,或者在数据本身包含处理标记。 最后,需要给出具体的实现示例,让用户能够理解如何在自己的脚本中应用这些算法。比如修改脚本,添加分片逻辑,根据实例编号总数决定处理哪些数据。</think>### **分片算法实现方案(无需中央调度)** ```python import hashlib class DataPartitioner: def __init__(self, total_workers, worker_index): """ :param total_workers: 总实例数量 :param worker_index: 当前实例编号 (0 ~ total_workers-1) """ self.total_workers = total_workers self.worker_index = worker_index def is_my_task(self, data_id): """判断数据是否属于当前实例""" # 方法1:取模分片(适合数值型ID) return data_id % self.total_workers == self.worker_index # 方法2:哈希分片(适合任意ID类型) # hashed = int(hashlib.md5(str(data_id).encode()).hexdigest(), 16) # return hashed % self.total_workers == self.worker_index # 方法3:范围分片(需预知ID范围) # total_ids = max_id - min_id + 1 # chunk_size = total_ids // self.total_workers # return min_id + self.worker_index*chunk_size <= data_id < min_id + (self.worker_index+1)*chunk_size ``` --- ### **完整工作流程** ```python # 所有服务器加载相同数据文件 all_data = load_all_data() # 假设返回 {id: content} 的字典 # 启动参数指定实例编号(0/1/2...) import sys total_workers = 3 # 总服务器数量 worker_index = int(sys.argv[1]) # 启动命令: python script.py 0 partitioner = DataPartitioner(total_workers, worker_index) for data_id, content in all_data.items(): if partitioner.is_my_task(data_id): process_data(content) print(f"实例{worker_index} 处理数据 {data_id}") ``` --- ### **关键算法对比** | 算法类型 | 实现方式 | 优点 | 缺点 | 适用场景 | |---------------|--------------------------|--------------------------|--------------------------|---------------------| | **取模分片** | `id % N == index` | 实现简单 | 增删节点时需全量重分布 | ID均匀分布的数值型数据 | | **哈希分片** | `hash(id) % N == index` | 数据分布均匀 | 计算哈希消耗CPU | 非数值型或离散ID | | **范围分片** | 按ID区间划分 | 支持范围查询 | 需预知数据分布 | 有序连续ID | | **一致性哈希** | 虚拟节点环 | 动态扩缩容影响小 | 实现复杂 | 集群规模频繁变化 | --- ### **动态扩容解决方案** ```python # 使用一致性哈希算法(示例代码) from hashlib import sha256 from bisect import bisect class ConsistentHasher: def __init__(self, nodes=None, replicas=100): self.replicas = replicas # 虚拟节点数 self.ring = [] self.node_map = {} if nodes: for node in nodes: self.add_node(node) def add_node(self, node): """添加新节点""" for i in range(self.replicas): key = f"{node}_{i}".encode() hash_val = int(sha256(key).hexdigest(), 16) self.ring.append(hash_val) self.node_map[hash_val] = node self.ring.sort() def get_node(self, data_id): """获取数据对应的节点""" if not self.ring: return None key = str(data_id).encode() hash_val = int(sha256(key).hexdigest(), 16) idx = bisect(self.ring, hash_val) % len(self.ring) return self.node_map[self.ring[idx]] # 初始化含3个节点的环 hasher = ConsistentHasher(['node0', 'node1', 'node2']) # 查询数据所属节点 if hasher.get_node(data_id) == current_node_name: process_data(content) ``` --- ### **生产环境最佳实践** 1. **数据预热**:所有节点加载数据时校验checksum ```python import zlib def verify_data(data): crc = zlib.crc32(str(data).encode()) if crc != expected_crc: # 从中央存储获取校验值 raise ValueError("数据损坏") ``` 2. **负载均衡**:动态调整虚拟节点数量 ```python # 根据节点性能设置不同权重 high_perf_node_replicas = 200 # 高性能节点更多虚拟节点 low_perf_node_replicas = 50 ``` 3. **跨机房容灾**:给节点增加位置标记 ```python hasher.add_node("node3@dc2") # 不同机房节点 ``` 4. **监控告警**: ```python # 统计各节点处理量 from collections import defaultdict counter = defaultdict(int) for data_id in all_ids: node = hasher.get_node(data_id) counter[node] += 1 print("负载分布:", dict(counter)) ``` --- ### **常见问题应对** 1. **数据倾斜**: - 使用双重哈希:`hash(hash(id)) % N` - 添加随机后缀:`hash(f"{id}_{salt}") % N` 2. **节点失效**: ```python # 自动故障转移 def handle_node_failure(failed_node): new_hasher = ConsistentHasher([n for n in nodes if n != failed_node]) # 重新分配失效节点的数据 ``` 3. **数据变更**: ```python # 版本号校验 if data['version'] != get_central_version(data['id']): reload_data(data['id']) # 重新加载最新数据 ``` --- 通过这种分片算法,可以实现: 1. **无中心化架构**:每个实例自主判断任务归属 2. **数据本地性**:适合需要全量数据的计算场景 3. **线性扩展**:通过增加实例数量提升吞吐量 实际部署时建议优先使用**哈希分片**(方法2),既能处理各种数据类型,又能保证较好的负载均衡性。对于需要动态扩容的场景,可以采用**一致性哈希**方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值