java生成唯一有序序列号_分布式唯一 ID 之 Snowflake 算法

SegmentFault 社区专栏:全栈修仙之路作者:semlinker


No.1

Snowflake 简介

1.1 什么是 Snowflake

Snowflake is a service used to generate unique IDs for objects within Twitter (Tweets, Direct Messages, Users, Collections, Lists etc.). These IDs are unique 64-bit unsigned integers, which are based>s is due to the way Javascript and other languages that consume JSON evaluate large integers. If you come across a scenario where it doesn’t appear that id and id_str match, it’s due to your environment having already parsed the id integer, munging the number in the process. —— developer.twitter.comSnowflake(雪花)是一项服务,用于为 Twitter 内的对象(推文,直接消息,用户,集合,列表等)生成唯一的 ID。这些 IDs 是唯一的 64 位无符号整数,它们基于时间,而不是顺序的。完整的 ID 由时间戳,工作机器编号和序列号组成。当在 API 中使用 JSON 数据格式时,请务必始终使用 id_str 字段而不是 id,这一点很重要。这是由于处理 JSON 的 Javascript 和其他语言计算大整数的方式造成的。如果你遇到 id 和 id_str 似乎不匹配的情况,这是因为你的环境已经解析了 id 整数,并在处理的过程中仔细分析了这个数字。在 JavaScript 中,Number 基本类型可以精确表示的最大整数是 2^53。因此如果直接使用 Number 来表示 64 位的 Snowflake ID 肯定是行不通的。所以 Twitter 工程师们让我们务必使用 id_str 字段即通过字符串来表示生成的 ID。当然这个问题不仅仅存在于使用 Snowflake ID 的场景,为了解决 JavaScript 不能安全存储和操作大整数的问题,BigInt 这个救星出现了,它是一种内置对象,可以表示大于 2^53 的整数,甚至是任意大的整数。BigInt 现在处在 ECMAScript 标准化过程中的第三阶段。当它进入第四阶段草案,也就是最终标准时,BigInt 将成为 Javacript 中的第二种内置数值类型。BigInt 可能会成为自 ES2015 引入 Symbol 之后,增加的第一个新的内置类型。

1.2 Snowflake 算法

下图是 Snowflake 算法的 ID 构成图:9ccbd11db61a69cdf0d50faca5fef246.png•  1 位标识部分,该位不用主要是为了保持 ID 的自增特性,若使用了最高位,int64_t 会表示为负数。在 Java 中由于 long 类型的最高位是符号位,正数是 0,负数是 1,一般生成的 ID 为正整数,所以最高位为 0;•  41 位时间戳部分,这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差值(当前时间减去固定的开始时间),这样可以使产生的 ID 从更小值开始;41 位的时间戳可以使用 69 年,(1L << 41) / (1000L 60 60 24 365) = (2199023255552 / 31536000000) ≈ 69.73 年;•  10 位工作机器 ID 部分,Twitter 实现中使用前 5 位作为数据中心标识,后 5 位作为机器标识,可以部署 1024 (2^10)个节点;•  12 位序列号部分,支持同一毫秒内同一个节点可以生成 4096 (2^12)个 ID;Snowflake 算法生成的 ID 大致上是按照时间递增的,用在分布式系统中时,需要注意数据中心标识和机器标识必须唯一,这样就能保证每个节点生成的 ID 都是唯一的。我们不一定需要像 Twitter 那样使用 5 位作为数据中心标识,另 5 位作为机器标识,可以根据我们业务的需要,灵活分配工作机器 ID 部分。比如:若不需要数据中心,完全可以使用全部 10 位作为机器标识;若数据中心不多,也可以只使用 3 位作为数据中心,7 位作为机器标识。

No.2

Snowflake 解惑

以下问题来源于漫漫路博客 - “Twitter-Snowflake,64 位自增ID算法详解” 评论区

2.1 既然是 64 位,为何第一位不使用?

首位不用主要是为了保持 ID 的自增特性,若使用了最高位,int64_t 会表示为负数。在 Java 中由于 long 类型的最高位是符号位,正数是 0,负数是 1,一般生成的 ID 为正整数,所以最高位为 0。

2.2 怎么生成 41 位的时间戳?

41 位的时间戳,这个是毫秒级的时间,一般实现上不会存储当前的时间戳,而是时间戳的差值(当前时间减去固定的开始时间)。41 位只是预留位(主要目的是约定使用年限,固定的开始时间),不用的位数填 0 就好了。

2.3 工作机器 id 如果使用 MAC 地址的话,怎么转成 10 bit?

网络中每台设备都有一个唯一的网络标识,这个地址叫 MAC 地址或网卡地址,由网络设备制造商生产时写在硬件内部。MAC 地址则是 48 位的(6 个字节),通常表示为 12 个 16 进制数,每 2 个 16 进制数之间用冒号隔开,如08:00:20:0A:8C:6D 就是一个 MAC 地址。具体如下图所示,其前 3 字节表示OUI(Organizationally Unique Identifier),是 IEEE (电气和电子工程师协会)区分不同的厂家,后 3 字节由厂家自行分配。117ab623d8eb78c11a764cc020a54bba.png(图片来源 - 百度百科)很明显 Mac 地址是 48 位,而我们的工作机器 ID 部分只有 10 位,因此并不能直接使用 Mac 地址作为工作机器 ID。若要选用 Mac 地址的话,还需使用一个额外的工作机器 ID 分配器,用来实现 ID 与 Mac 地址间的唯一映射。

2.4 怎么生成 12 bit 的序列号?

序列号不需要全局维护,在 Java 中可以使用 AtomicInteger(保证线程安全)从 0 开始自增。当序列号超过了 4096,序列号在这一毫秒就用完了,等待下一个毫秒归 0 重置就可以了。

No.3

Snowflake 优缺点

理论上 Snowflake 方案的 QPS 约为 409.6w/s(1000 * 2^12),这种分配方式可以保证在任何一个 IDC 的任何一台机器在任意毫秒内生成的 ID 都是不同的。

3.1 优点

•  毫秒数在高位,自增序列在低位,整个 ID 都是趋势递增的。趋势递增的目的是:在 MySQL InnoDB 引擎中使用的是聚集索引,由于多数 RDBMS 使用 B-tree 的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。•  不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成 ID 的性能也是非常高的。•  可以根据自身业务特性分配 bit 位,非常灵活。

3.2 缺点

•  强依赖机器时钟,如果机器上时钟回拨,会导致发号重复或者服务会处于不可用状态。除了时钟回拨问题之外,Snowflake 算法会存在并发限制,当然对于这些问题,以本人目前的 Java 功力根本解决不了。但这并不影响我们使用它。在实际项目中我们可以使用基于 Snowflake 算法的开源项目,比如百度的 UidGenerator 或美团的 Leaf。下面我们简单介绍一下这两个项目,感兴趣的小伙伴可以自行查阅相关资料。

3.3 UidGenerator

UidGenerator 是 Java 实现的,基于 Snowflake 算法的唯一 ID 生成器。UidGenerator 以组件形式工作在应用项目中,支持自定义 workerId 位数和初始化策略,从而适用于 docker 等虚拟化环境下实例自动重启、漂移等场景。在实现上,UidGenerator 通过借用未来时间来解决 sequence 天然存在的并发限制;采用 RingBuffer 来缓存已生成的 UID,并行化 UID 的生产和消费,同时对 CacheLine 补齐,避免了由 RingBuffer 带来的硬件级「伪共享」问题。最终单机 QPS 可达 600 万。依赖版本:Java8 及以上版本,MySQL(内置 WorkerID 分配器,启动阶段通过 DB 进行分配;如自定义实现,则 DB非必选依赖)。

3.4 Leaf

Leaf 最早期需求是各个业务线的订单 ID 生成需求。在美团早期,有的业务直接通过 DB 自增的方式生成 ID,有的业务通过 Redis 缓存来生成 ID,也有的业务直接用 UUID 这种方式来生成 ID。以上的方式各自有各自的问题,因此我们决定实现一套分布式 ID 生成服务来满足需求。具体 Leaf 设计文档见:Leaf 美团分布式ID生成服务。目前 Leaf 覆盖了美团点评公司内部金融、餐饮、外卖、酒店旅游、猫眼电影等众多业务线。在 4C8G VM 基础上,通过公司 RPC 方式调用,QPS 压测结果近5w/s,TP999 1ms。

No.4

Snowflake 算法实现

在前面 Snowflake 知识的基础上,现在我们来分析一下 Github 上 beyondfengyu 大佬基于 Java 实现的 SnowFlake,完整代码如下:
/**
 * twitter的snowflake算法 -- java实现
 * 
 * @author beyond
 * @date 2016/11/26
 */public class SnowFlake {/**
     * 起始的时间戳
     */private final static long START_STMP = 1480166465631L;/**
     * 每一部分占用的位数
     */private final static long SEQUENCE_BIT = 12; //序列号占用的位数private final static long MACHINE_BIT = 5;   //机器标识占用的位数private final static long DATACENTER_BIT = 5;//数据中心占用的位数/**
     * 每一部分的最大值
     */private final static long MAX_DATACENTER_NUM = -1L ^ (-1L <    private final static long MAX_MACHINE_NUM = -1L ^ (-1L <    private final static long MAX_SEQUENCE = -1L ^ (-1L </**
     * 每一部分向左的位移
     */private final static long MACHINE_LEFT = SEQUENCE_BIT;private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;private long datacenterId;  //数据中心private long machineId;     //机器标识private long sequence = 0L; //序列号private long lastStmp = -1L;//上一次时间戳public SnowFlake(long datacenterId, long machineId) {if (datacenterId > MAX_DATACENTER_NUM || datacenterId 0) {throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }if (machineId > MAX_MACHINE_NUM || machineId 0) {throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }this.datacenterId = datacenterId;this.machineId = machineId;
    }/**
     * 产生下一个ID
     *
     * @return
     */public synchronized long nextId() {long currStmp = getNewstmp();if (currStmp             throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
        }if (currStmp == lastStmp) {//相同毫秒内,序列号自增
            sequence = (sequence + 1) & MAX_SEQUENCE;//同一毫秒的序列数已经达到最大if (sequence == 0L) {
                currStmp = getNextMill();
            }
        } else {//不同毫秒内,序列号置为0
            sequence = 0L;
        }
        lastStmp = currStmp;return (currStmp - START_STMP) <//时间戳部分
                | datacenterId <//数据中心部分
                | machineId <//机器标识部分
                | sequence;                             //序列号部分
    }private long getNextMill() {long mill = getNewstmp();while (mill <= lastStmp) {
            mill = getNewstmp();
        }return mill;
    }private long getNewstmp() {return System.currentTimeMillis();
    }public static void main(String[] args) {
        SnowFlake snowFlake = new SnowFlake(2, 3);for (int i = 0; i 1 <12); i++) {
            System.out.println(snowFlake.nextId());
        }
    }
}
在详细分析之前,我们先来回顾一下 Snowflake 算法的 ID 构成图:9ccbd11db61a69cdf0d50faca5fef246.png

4.1 ID 位分配

首位不用,默认为 0。41bit(第2-42位)时间戳,是相对时间戳,通过当前时间戳减去一个固定的历史时间戳生成。在 SnowFlake 类定义了一个 long 类型的静态变量 START_STMP,它的值为 1480166465631L:
/**
 * 起始的时间戳:Sat Nov 26 2016 21:21:05 GMT+0800 (中国标准时间)
 */private final static long START_STMP = 1480166465631L;
接着继续定义三个 long 类型的静态变量,来表示序列号和工作机器 ID 的占用位数:
/**
 * 每一部分占用的位数
 */private final static long SEQUENCE_BIT = 12; //序列号占用的位数private final static long MACHINE_BIT = 5;   //机器标识占用的位数private final static long DATACENTER_BIT = 5;//数据中心占用的位数
此外还定义了每一部分的最大值,具体如下:
/**
 * 每一部分的最大值
 */private final static long MAX_DATACENTER_NUM = -1L ^ (-1L <// 31private final static long MAX_MACHINE_NUM = -1L ^ (-1L <// 31private final static long MAX_SEQUENCE = -1L ^ (-1L <// 4095

4.2 构造函数

SnowFlake 类的构造函数,该构造函数含有 datacenterId 和 machineId 两个参数,它们分别表示数据中心 id 和机器标识:
private long datacenterId;  //数据中心private long machineId;     //机器标识public SnowFlake(long datacenterId, long machineId) {if (datacenterId > MAX_DATACENTER_NUM || datacenterId 0) {throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
        }if (machineId > MAX_MACHINE_NUM || machineId 0) {throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
        }this.datacenterId = datacenterId;this.machineId = machineId;
}

4.3 生成 id

在 SnowFlake 类的实现中,在创建完 SnowFlake 对象之后,可以通过调用 nextId 方法来获取 ID。有的小伙伴可能对位运算不太清楚,这里先简单介绍一下 nextId 方法中,所用到的位运算知识。按位与运算符(&)参加运算的两个数据,按二进制位进行 “与” 运算,它的运算规则:
0&0=0; 0&1=0; 1&0=0; 1&1=1;
即两位同时为 1,结果才为 1,否则为 0。•  清零:如果想将一个单元清零,只需要将它与一个各位都为零的数值相与即可。•  取一个数指定位的值:若需获取某个数指定位的值,只需把该数与指定位为 1,其余位为 0 所对应的数相与即可。按位或运算(|)参加运算的两个对象,按二进制位进行 “或” 运算,它的运算规则:
0|0=0; 0|1=1; 1|0=1; 1|1=1;
即仅当两位都为 0 时,结果才为 0。左移运算符 <<将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补 0)。若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以 2。在了解完位运算的相关知识后,我们再来看一下 nextId 方法的具体实现:
/**
 * 产生下一个ID
 *
 * @return
 */public synchronized long nextId() {// 获取当前的毫秒数:System.currentTimeMillis(),该方法产生一个当前的毫秒,这个毫秒// 其实就是自1970年1月1日0时起的毫秒数。long currStmp = getNewstmp();// private long lastTimeStamp = -1L; 表示上一次时间戳// 检测是否出现时钟回拨if (currStmp      throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
  }// 相同毫秒内,序列号自增if (currStmp == lastStmp) {// private long sequence = 0L; 序列号// MAX_SEQUENCE =      4095 111111111111// MAX_SEQUENCE + 1 = 4096 1000000000000
     sequence = (sequence + 1) & MAX_SEQUENCE;// 同一毫秒的序列数已经达到最大if (sequence == 0L) {// 阻塞到下一个毫秒,获得新的时间戳
           currStmp = getNextMill();
       }
     } else {//不同毫秒内,序列号置为0
            sequence = 0L;
   }
   lastStmp = currStmp;// MACHINE_LEFT = SEQUENCE_BIT; -> 12// DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; -> 17// TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT; -> 22return (currStmp - START_STMP) <//时间戳部分
                | datacenterId <//数据中心部分
                | machineId <//机器标识部分
                | sequence;                             //序列号部分
}
现在我们来看一下使用方式:
public static void main(String[] args) {
  SnowFlake snowFlake = new SnowFlake(2, 3);for (int i = 0; i 1 <12); i++) {
    System.out.println(snowFlake.nextId());
  }
}
现在我们已经可以利用 SnowFlake 对象生成唯一 ID 了,那这个唯一 ID 有什么用呢?这里举一个简单的应用场景,即基于 SnowFlake 的短网址生成器,其主要思路是使用 SnowFlake 算法生成一个整数,然后对该整数进行 62 进制编码最终生成一个短地址 URL。对短网址生成器感兴趣的小伙伴,可以参考徐刘根大佬在码云上分享的工具类。最后我们来简单总结一下,本文主要介绍了什么是 Snowflake(雪花)算法、Snowflake 算法 ID 构成图及其优缺点,最后详细分析了 Github 上 beyondfengyu 大佬基于 Java 实现的 SnowFlake。在实际项目中,建议大家选用基于 Snowflake 算法成熟的开源项目,如百度的 UidGenerator 或美团的 Leaf。

参考资源:

•  Twitter-ids:https://developer.twitter.com/en/docs/basics/twitter-ids•  MDN - BigInt:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt•  Leaf:美团分布式ID生成服务开源:https://tech.meituan.com/2019/03/07/open-source-project-leaf.html•  漫漫路 - Twitter-Snowflake,64位自增ID算法详解:https://www.lanindex.com/twitter-snowflake- END -4113425fa694b826ec3b3c7dfa9d0cfe.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/332472.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

冀教版五年级计算机教学计划,冀教版五年级上册教学计划资料

现状分析:五年级两个班现有学生四十余人,基本为我校四年级直升学生.已学习了北师大版《语文》五年级上册和冀教版《语文》五年级下册.目前普遍情况是听、写的能力尚可,读和说的能力有待提高.一班学生基础知识较为扎实,但缺乏变通性,学习较为用功,但成绩不佳&#xff1b;二班学生…

spring缓存_有关Spring缓存性能的更多信息

spring缓存这是我们最后一篇关于Spring的缓存抽象的文章的后续文章 。 作为工程师&#xff0c;您可以通过了解所使用的某些工具的内部知识来获得宝贵的经验。 了解工具的行为有助于您在做出设计选择时变得更加成熟。 在本文中&#xff0c;我们描述了基准测试实验和结果&#x…

提交构件到Maven的远程中央仓

参见&#xff1a;https://central.sonatype.org/publish/publish-guide/

ctrl导致开机弹出计算机,Win7系统开机黑屏提示Press Ctrl+Alt+Del to restart如何解决...

在使用win7系统的时候&#xff0c;难免会遇到各种各样的问题&#xff0c;比如最近有雨林木风win7旗舰版系统用户反映说开机黑屏&#xff0c;并提示Press CtrlAltDelto restart&#xff0c;该怎么解决这样的问题呢&#xff0c;现在给大家分享一下Win7系统开机黑屏提示PressCtrlA…

rxjava背压怎样使用_使用MicroProfile应用隔板和背压

rxjava背压怎样使用我录制了一段视频&#xff0c;介绍如何使用MicroProfile Fault Tolerance实现隔板和背压。 隔板后面的想法是将应用程序分成几个隔离功能的执行单元。 在企业Java应用程序中&#xff0c;这通常意味着定义多个线程池。 向客户端施加背压会导致向客户端添加有…

单片机蜂鸣器编程音乐_基于单片机的智能鱼缸温控系统设计

曹益豪聊城大学东昌学院机电工程系山东 聊城 252000摘 要&#xff1a;为满足热带鱼的饲养要求&#xff0c;设计了一种基于AT89C52单片机的小型智能温控鱼缸系统。该系统利用DS18B20传感器检测水温&#xff0c;并通过测量温度与设定温度的对比控制加热装置的通断。现详细介绍了…

计算机快捷键 还原默认值,CAD默认快捷键如何恢复?教你还原CAD默认配置的方法...

小编告诉大家AutoCAD的操作命令和各种设置分为默认和自定义。 可以根据每个人的喜好进行设置。 不必完全遵循该软件的默认操作&#xff0c;只是因为此快捷键是可变的&#xff0c;所以我仍然不习惯更改我的个人操作&#xff0c;并且想要恢复为默认设置&#xff0c;我们如何还原它…

java开发指南_Java 12新功能完整指南

java开发指南六个月飞得如此之快&#xff0c;是时候再次仔细研究一下即将发布的新JDK版本。 让我们满足Java 12及其向开发人员介绍的功能。 自Oracle推出加速六个月的发布节奏以来已经有一段时间了&#xff0c;要跟上每个版本及其添加到表中的功能越来越难了。 从好的方面来看…

python语句join_Python中的join()函数的用法

函数&#xff1a;string.join() Python中有join()和os.path.join()两个函数&#xff0c;具体作用如下&#xff1a; join()&#xff1a;连接字符串数组。将字符串、元组、列表中的元素以指定的字符(分隔符)连接生成一个新的字符串 os.path.join()&#xff1a;将多个路径组合后返…

怎么升级计算机内存容量,如何升级电脑内存?给电脑内存升级的操作步骤

电脑内存是用来作为临时存储数据设备使用的&#xff0c;如果内存占用率高&#xff0c;会让电脑运行负荷。喜欢玩游戏的人对电脑内存要求都比较高。很多玩家在购机时内存选择并不大&#xff0c;一般都是标配8G及以下&#xff0c;那么如何升级电脑内存&#xff1f;下面就和大家一…

搜索Maven依赖资源/搜索Maven工件/搜索Maven构件/搜索依赖/搜索构件/搜索工件/下载依赖/依赖下载

文章目录依赖索引索引文件更新存放在本地的远程仓库的资源索引以及本地仓库的资源索引macOS 下索引文件的路径在 pom.xml 页面内按快捷键 ⌘N 搜索构件在资源库管理中&#xff0c;创建资源库时&#xff0c;选择 From Maven 来搜索构件通过 Maven 构件资源网站搜索依赖索引 如果…

在Selenium中按TagName定位元素

Selenium定位器是处理网页上的元素时的关键。 从ID&#xff0c;名称&#xff0c;类&#xff0c;标记名&#xff0c;XPath&#xff0c;CSS选择器等定位器列表中&#xff0c;可以根据需要选择其中任何一种&#xff0c;然后在网页上找到Web元素。 由于与tagName或linktext相比&…

python open读取_python,一读取文件open()

在实际操作中&#xff0c;我们经常会读取文件&#xff0c;这个时候python为我们提供了一个open()的方法&#xff0c;供我们读取文件&#xff0c;通过help(open)&#xff0c;我们可以获取open的方法 f.close()关闭读取 f.read(size-1)读取文件size个字符&#xff0c;但未给size赋…

笔记本计算机风扇声音大怎么办,笔记本电脑噪音大怎么办 全解决方法

笔记本电脑在使用一段时间后&#xff0c;或多或少会产生令人厌烦的噪音。其中大的噪音多数来源于笔记本散热风扇。那么&#xff0c;笔记本电脑噪音大怎么办呢?下面我们来看具体解决方法吧。一、电脑噪音大怎么办之风扇原因在不少人看来&#xff0c;散热和噪音是一对不可调和的…

Maven的resources插件配置详解(含过滤器的配置详解)

文章目录指定配置文件所在的目录使用 excludes 元素可以排除指定的配置文件使用 includes 元素可以指定要处理的文件处理测试资源过滤器配置resources 插件&#xff0c;负责将配置文件复制到编译目录中。Maven Java Web 项目默认的编译目录 target/classes。两种配置文件 src/…

latex如何使节标题居左_为使节构建控制平面的指南第3部分-特定于域的配置API...

latex如何使节标题居左这是探索为Envoy Proxy构建控制平面的系列文章的第3部分。 在本博客系列中&#xff0c;我们将研究以下领域&#xff1a; 采用一种机制来动态更新Envoy的路由&#xff0c;服务发现和其他配置 确定哪些组件构成了控制平面&#xff0c;包括后备存储&#…

微信支付api的服务器上,服务器微信支付接口笔记(与app端对接)

到这里&#xff0c;准备工作就算完成了。支付流程步骤详解&#xff1a;步骤1&#xff1a;用户在商户APP中选择商品&#xff0c;提交订单&#xff0c;选择微信支付。这一步&#xff0c;app将相关订单信息提交给商户步骤2&#xff1a;商户后台收到用户支付单&#xff0c;调用微信…

python写sql语句_Python操作文件模拟SQL语句功能

一、需求 当然此表你在文件存储时可以这样表示 1,Alex Li,22,13651054608,IT,2013-04-01 现需要对这个员工信息文件&#xff0c;实现增删改查操作 1. 可进行模糊查询&#xff0c;语法至少支持下面3种: 1. select name,age from staff_table where age > 22 2. select * from…