ViGEmBus虚拟手柄驱动:终极游戏兼容性解决方案
2025/12/25 0:14:39
Redis 数据结构底层与 Hash 优于 JSON 的工程实践
一、Redis 对象模型与编码机制
二、各数据类型的底层实现与差异
| 数据类型 | 常见底层编码 | 关键特性 | 典型触发条件与阈值 |
|---|---|---|---|
| String | RAW / INT / EMBSTR | O(1)取长;二进制安全;EMBSTR 与对象头一次分配更省时 | INT:值为可解析的 long;EMBSTR:短字符串(如 ≤44字节,版本差异);否则 RAW |
| List | QUICKLIST(Redis 3.2+) | 双向链表 + 分段ziplist,兼顾内存与随机访问 | 历史:ziplist 小对象;现由 quicklist 统一承载 |
| Hash | LISTPACK / HASH | 小对象紧凑;大对象随机访问快 | 同时满足:字段数 ≤hash-max-listpack-entries(默认 512)且 字段/值长度 ≤hash-max-listpack-value(默认 64 字节)时用 LISTPACK,否则转 HASH |
| Set | INTSET / HASH | 整数集合更省内存;通用元素用 HASH | 同时满足:元素全为整数且数量 ≤set-max-intset-entries(默认 512)用 INTSET,否则转 HASH |
| Sorted Set | LISTPACK / SKIPLIST | 跳跃表支持O(logN)范围/排名操作 | 同时满足:元素数 ≤zset-max-listpack-entries(默认 128)且 成员长度 ≤zset-max-listpack-value(默认 64 字节)用 LISTPACK,否则转 SKIPLIST |
三、为什么经常说用 Hash 比用 JSON 好
四、代码示例对比:JSON 与 Hash 的读写与更新
// 写Useru=newUser("Alice",25,0,newBigDecimal("99.50"));jedis.set("user:1001",newObjectMapper().writeValueAsString(u));// 读Stringjson=jedis.get("user:1001");Useru2=newObjectMapper().readValue(json,User.class);// 更新(读改写,存在并发覆盖风险)u2.setLoginCount(u2.getLoginCount()+1);u2.setBalance(u2.getBalance().add(newBigDecimal("10.00")));jedis.set("user:1001",newObjectMapper().writeValueAsString(u2));// 写(可一次设置多个字段)jedis.hset("user:1001","name","Alice");jedis.hset("user:1001","age","25");jedis.hincrBy("user:1001","loginCount",1);jedis.hincrByFloat("user:1001","balance",10.00);// 读(只取需要的字段)Stringname=jedis.hget("user:1001","name");LongloginCount=Long.valueOf(jedis.hget("user:1001","loginCount"));// 批量取List<String>fields=Arrays.asList("name","age","balance");Map<String,String>profile=jedis.hmget("user:1001",fields.toArray(newString[0]));// 仅当余额充足时扣款(原子性由 Redis 保证)Stringlua="local bal = tonumber(redis.call('HGET', KEYS[1], 'balance')) "+"if bal >= tonumber(ARGV[1]) then "+" redis.call('HINCRBYFLOAT', KEYS[1], 'balance', -tonumber(ARGV[1])) "+" return 1 "+"else "+" return 0 "+"end";Longok=(Long)jedis.eval(lua,1,"user:1001","5.00");// 使用 HSCAN 分批遍历,避免一次性 HGETALL 造成阻塞ScanResult<Map.Entry<String,String>>scan;Stringcursor="0";do{scan=jedis.hscan("user:1001",cursor,newScanParams().count(50));for(Map.Entry<String,String>e:scan.getResult()){// 处理字段}cursor=scan.getCursor();}while(!"0".equals(cursor));# 需要路径查询/局部更新且希望服务端完成解析与合并JSON.SET user:1001 $'{"name":"Alice","profile":{"age":25}}'JSON.GET user:1001 $.profile.age JSON.SET user:1001 $.profile.age26五、工程实践与避坑清单