简介这份资源面向金融数据分析、风控开发及数据库运维人员提供2020年4月25日版银行卡BIN号数据集合用于识别发卡行、卡片类型与归属机构支撑交易验证、反欺诈与客户分析等场景。压缩包共7个文件以5个xls表格和2个sql脚本为主xls覆盖农民工卡、跨行转账卡、非标卡、单位结算卡等分类卡表sql脚本可直接导入MySQL或PostgreSQL包体约1.4MB轻量易用。已有859人学习下载适合需要快速搭建BIN号查询表或做数据比对的初中级开发者。拿到资源后可参照卡表字段在Excel中完成排序筛选与统计也可将sql导入数据库后编写查询按银行、卡种维度分析卡片分布并据此建立定期更新机制为风控与运营提供基础数据支撑。1. 银行卡 BIN 号资源怎么落地从 Excel 到 MySQL、PG 的完整链路做支付风控、清结算或者收单系统的同行大概率都遇到过同一个需求拿到一串卡号要快速判断它属于哪家银行、什么卡种、是不是借记卡。这个判断的核心依据就是银行卡 BIN 号也就是卡号前 6 到 8 位所对应的发卡行标识。最近拿到一份银行卡 BIN 号资源包里面同时提供了 Excel 原始表和 MySQL、PostgreSQL 两种数据库的导入文件这个组合其实挺实用——Excel 方便人工核对和二次加工SQL 文件可以直接灌库上线查询。它解决的是「卡号归属行识别」这个高频但容易被低估的问题适合做支付网关、风控规则引擎、对账系统的后端和数据分析同学。下面把我拆包、建表、导数据、写查询的完整过程捋一遍顺带把几个容易翻车的地方标出来。2. 资源结构拆解Excel、MySQL、PG 三份文件各管什么2.1 三份文件的定位与字段对照拿到资源包后先别急着导库把三份文件的关系理清楚能省很多返工。Excel 通常是「母版」字段最全包含 BIN 号、发卡行名称、卡种借记卡/信用卡、卡组织银联、Visa、Mastercard 等以及可能的卡号长度规则。MySQL 和 PG 的 SQL 文件则是从母版派生出来的导入脚本字段会做一定精简但核心的 BIN 和归属行信息一定在。我一般会先用 Excel 打开母版确认几个关键列是否存在字段名含义是否必需bin卡号前 6-8 位必需bank_name发卡行名称必需card_type借记卡/信用卡/预付卡建议有card_org卡组织建议有card_length卡号总长度可选如果 Excel 里 bin 列存在前导零被吃掉的情况比如「012345」变成「12345」那导入数据库后查询会直接失效。这是最常见的第一个坑后面避坑章节会细说。2.2 为什么同时给 MySQL 和 PG 两份有人会问直接给一份 CSV 不就行了为什么还要分 MySQL 和 PG实际用起来就明白了MySQL 和 PG 在数据类型、索引策略、批量插入语法上差异不小。比如 MySQL 的LOAD DATA INFILE和 PG 的COPY命令完全是两套写法字符集处理也不一样。资源包直接提供两份 SQL意味着你不需要自己写转换脚本省掉了「CSV 转 SQL」这个容易出错的中间环节。从选型角度看如果你的系统已经在用 MySQL直接用对应的 SQL 文件如果是 PG 技术栈就用 PG 版本。两边都用的混合架构也不少见那就两份都导保持数据同步即可。需要注意的是两份文件的数据版本要一致别一个是最新的一个还是旧的这个在导入前用行数比对一下就能确认。2.3 导入前的环境确认在动手之前确认几件事数据库版本MySQL 5.7 和 8.0 在字符集默认值上有差异PG 10 和 14 在COPY语法上基本兼容但分区表支持不同、目标库的字符集建议 utf8mb4、以及是否有写权限。这些看起来是废话但我确实见过因为目标库字符集是 latin1 导致银行名称中文乱码的情况排查了半天以为是数据问题。# 确认 MySQL 版本和字符集 mysql -u root -p -e SELECT VERSION(); SHOW VARIABLES LIKE character_set_database; # 确认 PG 版本和编码 psql -U postgres -c SELECT version(); -c SHOW server_encoding;这两条命令输出确认无误后再往下走。如果字符集不是 utf8mb4MySQL或 UTF8PG建议先建库时指定别等导完再改改起来更麻烦。3. MySQL 侧实操建表、导入与查询验证3.1 建表语句与索引设计资源包里的 SQL 文件通常包含建表和插入两部分。但直接执行之前我建议先看一眼建表语句的索引设计。BIN 查询的特点是「精确匹配前 6-8 位」所以 bin 列上必须要有索引而且考虑到查询频率用主键或者唯一索引都行。如果资源包没有自带建表语句或者你想自己控制表结构可以用下面这个CREATE TABLE bank_bin ( id int NOT NULL AUTO_INCREMENT, bin varchar(8) NOT NULL COMMENT 卡号前6-8位, bank_name varchar(100) NOT NULL COMMENT 发卡行名称, card_type varchar(20) DEFAULT NULL COMMENT 卡种, card_org varchar(20) DEFAULT NULL COMMENT 卡组织, card_length tinyint DEFAULT NULL COMMENT 卡号长度, PRIMARY KEY (id), UNIQUE KEY uk_bin (bin), KEY idx_bank_name (bank_name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT银行卡BIN号表;这里 bin 用 varchar 而不是 int就是为了防止前导零丢失。unique key 保证不会重复插入同一条 BIN。bank_name 上建普通索引是为了支持「按银行名反查有哪些 BIN」这种需求比如做银行维度的统计时用得上。3.2 导入 SQL 文件与批量插入优化如果资源包提供的是.sql文件直接 source 进去就行mysql -u root -p your_database bank_bin_mysql.sql但如果数据量比较大几万条以上直接 source 可能会比较慢因为默认是每条 insert 一个事务。可以在导入前临时调整几个参数SET autocommit 0; SET unique_checks 0; SET foreign_key_checks 0; -- 执行导入 COMMIT; SET autocommit 1; SET unique_checks 1; SET foreign_key_checks 1;关掉唯一性检查和外键检查能明显加快导入速度代价是如果数据本身有重复或冲突导入时不会报错需要导完后自己校验。我一般会在导入后跑一遍SELECT bin, COUNT(*) FROM bank_bin GROUP BY bin HAVING COUNT(*) 1;来确认没有重复。3.3 查询验证从卡号反查归属行数据进去之后最核心的查询就是给一个卡号返回归属行信息。标准做法是取前 6 位或前 8 位去匹配-- 取前6位匹配 SELECT bank_name, card_type, card_org FROM bank_bin WHERE bin LEFT(6222021234567890, 6); -- 如果6位没匹配到尝试8位 SELECT bank_name, card_type, card_org FROM bank_bin WHERE bin LEFT(6222021234567890, 8);实际业务中BIN 号有 6 位和 8 位两种规格一般优先用 6 位匹配匹配不到再用 8 位。有些资源包会把 6 位和 8 位混在一张表里这时候查询要按长度排序取最优匹配SELECT bank_name, card_type, card_org FROM bank_bin WHERE bin LEFT(6222021234567890, LENGTH(bin)) ORDER BY LENGTH(bin) DESC LIMIT 1;这个写法的逻辑是用卡号前缀去匹配表中所有可能的 BIN 长度然后取最长的那个匹配结果。因为 8 位比 6 位更精确优先返回 8 位的结果更合理。4. PostgreSQL 侧实操COPY 导入与性能对比4.1 PG 建表与 COPY 命令导入PG 这边的建表逻辑和 MySQL 类似但语法细节有差异。比如自增主键用serial或generated always as identity字符集在建库时指定CREATE TABLE bank_bin ( id serial PRIMARY KEY, bin varchar(8) NOT NULL, bank_name varchar(100) NOT NULL, card_type varchar(20), card_org varchar(20), card_length smallint, CONSTRAINT uk_bin UNIQUE (bin) ); CREATE INDEX idx_bank_name ON bank_bin (bank_name);导入时如果资源包给的是 CSV 或 TSV用COPY命令比逐条 insert 快一个数量级COPY bank_bin(bin, bank_name, card_type, card_org, card_length) FROM /path/to/bank_bin.csv WITH (FORMAT csv, HEADER true, ENCODING UTF8);注意COPY是服务端命令文件路径必须是数据库服务器能访问到的路径。如果文件在本地而数据库在远程用\copy客户端命令代替psql -U postgres -d your_db -c \copy bank_bin(bin, bank_name, card_type, card_org, card_length) FROM bank_bin.csv WITH (FORMAT csv, HEADER true)4.2 查询写法与 MySQL 的差异PG 的字符串函数和 MySQL 有些不同LEFT函数两边都有但LENGTH在 PG 里返回的是字符数MySQL 里默认也是字符数除非用LENGTH的字节版本OCTET_LENGTH。所以前面的查询逻辑在 PG 里基本可以直接用SELECT bank_name, card_type, card_org FROM bank_bin WHERE bin LEFT(6222021234567890, LENGTH(bin)) ORDER BY LENGTH(bin) DESC LIMIT 1;但 PG 有个更优雅的写法是用LIKE或者starts_withSELECT bank_name, card_type, card_org FROM bank_bin WHERE 6222021234567890 LIKE bin || % ORDER BY LENGTH(bin) DESC LIMIT 1;这个写法利用了 BIN 表作为前缀匹配的模式逻辑上更直观。不过性能上不如等值查询因为LIKE无法直接用 BIN 列的索引。数据量小的时候无所谓数据量大了还是建议用等值匹配。4.3 两种数据库的选型建议如果单纯从「BIN 查询」这个场景出发MySQL 和 PG 都能胜任差异不大。选哪个更多取决于你现有技术栈。但如果考虑到后续扩展比如要做 BIN 数据的版本管理、增量更新、或者和风控规则引擎做联动PG 在 JSON 字段、窗口函数、分区表方面的能力会更有优势。MySQL 的优势在于生态成熟、运维人才多、云服务商支持更广泛。我个人的习惯是如果 BIN 表只是作为一个静态字典用MySQL 足够了如果 BIN 数据需要频繁更新、和业务表做复杂关联查询PG 会更顺手。资源包同时提供两份正好可以根据实际情况选。5. 避坑与排查BIN 数据落地时最容易翻车的五个点5.1 前导零丢失导致查询失效现象导入后查询某些卡号返回空结果但 Excel 里明明有这条记录。原因Excel 默认把「012345」这样的数字当成数值处理前导零被自动去掉。导出 CSV 或 SQL 时如果没做处理入库后就变成了「12345」和实际卡号前缀对不上。解决在 Excel 里把 bin 列格式设为「文本」再导出或者导入时用LPAD(bin, 6, 0)补零。更稳妥的做法是导入后跑一遍校验SELECT COUNT(*) FROM bank_bin WHERE LENGTH(bin) 6;如果有结果就说明有短 BIN 需要处理。5.2 字符集不匹配导致银行名称乱码现象查询结果里银行名称显示为问号或乱码。原因源文件编码是 UTF-8但数据库或表的字符集是 latin1 或 GBK导入时没有做转换。解决建库建表时统一用 utf8mb4MySQL或 UTF8PG。如果已经建好了用ALTER TABLE bank_bin CONVERT TO CHARACTER SET utf8mb4;修改。导入时 MySQL 可以指定--default-character-setutf8mb4PG 在COPY里加ENCODING UTF8。5.3 BIN 长度混用导致匹配优先级混乱现象同一个卡号查出来两个不同的银行或者 8 位 BIN 被 6 位 BIN 覆盖。原因表里同时存在 6 位和 8 位的 BIN查询时没有按长度排序数据库返回了先匹配到的那条。解决查询时强制按 BIN 长度降序排列取第一条。或者在建表时就把 6 位和 8 位分两张表存查询时先查 8 位表再查 6 位表。我一般倾向于后者逻辑更清晰也避免了排序开销。5.4 批量导入时事务过大导致锁表现象导入过程中其他查询变慢甚至超时或者导入直接失败报锁等待超时。原因一次性插入几万条数据事务太大占用了大量锁资源和 undo log。解决分批导入每批 1000 到 5000 条。MySQL 可以用LIMIT配合循环PG 可以用COPY的FREEZE选项或者把数据拆成多个文件分批COPY。另外导入时尽量避开业务高峰期。5.5 数据版本过旧导致新卡号识别不了现象新发行的卡号查不到归属行。原因BIN 数据是静态的但银行会不断发行新卡、调整 BIN 段。如果资源包更新不及时新卡号就会漏查。解决建立定期更新机制比如每季度重新导入一次最新数据。更新时用INSERT ... ON DUPLICATE KEY UPDATEMySQL或INSERT ... ON CONFLICT DO UPDATEPG做增量合并避免全量替换导致的服务中断。6. 进阶技巧用 BIN 表做卡号校验与风控规则6.1 Luhn 算法与 BIN 查询的组合校验BIN 查询只能判断归属行但卡号本身是否合法还需要 Luhn 算法校验。把两者结合起来可以在入库前就过滤掉大部分无效卡号。Luhn 算法的逻辑是从卡号最后一位往前偶数位乘 2如果乘后大于 9 则减 9然后所有位相加总和能被 10 整除即为合法。def luhn_check(card_number): Luhn算法校验卡号合法性 digits [int(d) for d in str(card_number)] # 从右往左偶数位索引为奇数乘2 for i in range(len(digits) - 2, -1, -2): digits[i] * 2 if digits[i] 9: digits[i] - 9 return sum(digits) % 10 0 # 结合BIN查询使用 def validate_and_lookup(card_number, cursor): if not luhn_check(card_number): return {valid: False, reason: Luhn校验失败} bin_code card_number[:6] cursor.execute( SELECT bank_name, card_type FROM bank_bin WHERE bin %s, (bin_code,) ) row cursor.fetchone() if row: return {valid: True, bank_name: row[0], card_type: row[1]} return {valid: False, reason: BIN未匹配}这个组合校验在实际风控场景里很常用。Luhn 先过滤掉格式错误的卡号BIN 查询再判断归属行两步下来能挡掉大部分无效请求。注意 Luhn 算法不能判断卡号是否真实存在只能判断格式是否合法别把它当成万能校验。6.2 用 BIN 表做路由和费率匹配在支付路由场景里BIN 表还能用来做通道路由。比如某些银行走 A 通道费率更低某些卡组织走 B 通道成功率更高。这时候可以在 BIN 表里加一列route_channel查询时直接返回推荐通道SELECT b.bank_name, b.card_type, b.card_org, r.channel_name FROM bank_bin b LEFT JOIN route_config r ON b.card_org r.card_org AND b.bank_name r.bank_name WHERE b.bin LEFT(6222021234567890, LENGTH(b.bin)) ORDER BY LENGTH(b.bin) DESC LIMIT 1;这个查询把 BIN 信息和路由配置关联起来一次查询就能拿到归属行和推荐通道。路由配置表可以单独维护根据费率、成功率、限额等维度动态调整不用改 BIN 表本身。6.3 定期更新与版本管理BIN 数据不是一成不变的建议建立一个简单的版本管理机制。我一般会在表里加两列data_version数据版本号和updated_at更新时间。每次导入新数据时先查一下当前最大版本号新数据用新版本号插入旧版本数据保留但标记为失效。查询时只查有效版本SELECT bank_name, card_type FROM bank_bin WHERE bin LEFT(6222021234567890, LENGTH(bin)) AND is_active 1 ORDER BY LENGTH(bin) DESC LIMIT 1;这样即使新数据有问题也能快速回滚到旧版本。从那以后我每次更新 BIN 数据都强制走一遍「备份旧表 → 导入新表 → 抽样比对 → 切换生效」的流程再也没出现过更新后线上查询大面积失败的情况。希望帮到你。本文还有配套的精品资源点击获取