首页
关于
Search
1
给你10个市场数据调研报告的免费下载网站!以后竞品数据就从这里找!
182 阅读
2
php接口优化 使用curl_multi_init批量请求
144 阅读
3
《从菜鸟到大师之路 ElasticSearch 篇》
107 阅读
4
2024年备考系统架构设计师
104 阅读
5
PHP 文件I/O
92 阅读
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
登录
Search
标签搜索
php函数
php语法
性能优化
安全
错误和异常处理
问题
vue
Composer
Session
缓存
框架
Swoole
api
并发
异步
正则表达式
php-fpm
mysql 索引
开发规范
协程
dafenqi
累计撰写
786
篇文章
累计收到
28
条评论
首页
栏目
php
thinkphp
laravel
工具
开源
mysql
数据结构
总结
思维逻辑
令人感动的创富故事
读书笔记
前端
vue
js
css
书籍
开源之旅
架构
消息队列
docker
教程
代码片段
副业
redis
服务器
nginx
linux
科普
java
c
ElasticSearch
测试
php进阶
php基础
页面
关于
搜索到
100
篇与
的结果
2023-08-12
一张图彻底搞懂 MySQL 的锁机制
一张图彻底搞懂 MySQL 的锁机制锁在 MySQL 中是非常重要的一部分,锁对 MySQL 的数据访问并发有着举足轻重的影响。锁涉及到的知识篇幅也很多,所以要啃完并消化到自己的肚子里,是需要静下心好好反反复复几遍地细细品味。本文是对锁的一个大概的整理,一些相关深入的细节,还是需要找到相关书籍来继续夯实。锁的认识1.1 锁的解释计算机协调多个进程或线程并发访问某一资源的机制。1.2 锁的重要性在数据库中,除传统计算资源(CPU、RAM、I\O等)的争抢,数据也是一种供多用户共享的资源。如何保证数据并发访问的一致性,有效性,是所有数据库必须要解决的问题。锁冲突也是影响数据库并发访问性能的一个重要因素,因此锁对数据库尤其重要。1.3 锁的缺点加锁是消耗资源的,锁的各种操作,包括获得锁、检测锁是否已解除、释放锁等 ,都会增加系统的开销。1.4 简单的例子现如今网购已经特别普遍了,比如淘宝双十一活动,当天的人流量是千万及亿级别的,但商家的库存是有限的。系统为了保证商家的商品库存不发生超卖现象,会对商品的库存进行锁控制。当有用户正在下单某款商品最后一件时,系统会立马对该件商品进行锁定,防止其他用户也重复下单,直到支付动作完成才会释放(支付成功则立即减库存售罄,支付失败则立即释放)。锁的类型2.1 表锁种类读锁(read lock),也叫共享锁(shared lock)针对同一份数据,多个读操作可以同时进行而不会互相影响(select)写锁(write lock),也叫排他锁(exclusive lock)当前操作没完成之前,会阻塞其它读和写操作(update、insert、delete)存储引擎默认锁MyISAM特点对整张表加锁开销小加锁快无死锁锁粒度大,发生锁冲突概率大,并发性低结论读锁会阻塞写操作,不会阻塞读操作写锁会阻塞读和写操作建议MyISAM的读写锁调度是写优先,这也是MyISAM不适合做写为主表的引擎,因为写锁以后,其它线程不能做任何操作,大量的更新使查询很难得到锁,从而造成永远阻塞。2.2 行锁种类读锁(read lock),也叫共享锁(shared lock)允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁写锁(write lock),也叫排他锁(exclusive lock)允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享锁和排他锁意向共享锁(IS)一个事务给一个数据行加共享锁时,必须先获得表的IS锁意向排它锁(IX)一个事务给一个数据行加排他锁时,必须先获得该表的IX锁存储引擎默认锁InnoDB特点对一行数据加锁开销大加锁慢会出现死锁锁粒度小,发生锁冲突概率最低,并发性高事务并发带来的问题更新丢失解决:让事务变成串行操作,而不是并发的操作,即对每个事务开始---对读取记录加排他锁脏读解决:隔离级别为Read uncommitted不可重读解决:使用Next-Key Lock算法来避免幻读解决:间隙锁(Gap Lock)2.3 页锁开销、加锁时间和锁粒度介于表锁和行锁之间,会出现死锁,并发处理能力一般(此锁不做多介绍)如何上锁?3.1 表锁隐式上锁(默认,自动加锁自动释放)select //上读锁insert、update、delete //上写锁显式上锁(手动)lock table tableName read;//读锁lock table tableName write;//写锁解锁(手动)unlock tables;//所有锁表session01 session02lock table teacher read;// 上读锁select from teacher; // 可以正常读取 select from teacher;// 可以正常读取update teacher set name = 3 where id =2;// 报错因被上读锁不能写操作 update teacher set name = 3 where id =2;// 被阻塞unlock tables;// 解锁update teacher set name = 3 where id =2;// 更新操作成功session01 session02lock table teacher write;// 上写锁select from teacher; // 可以正常读取 select from teacher;// 被阻塞update teacher set name = 3 where id =2;// 可以正常更新操作 update teacher set name = 4 where id =2;// 被阻塞unlock tables;// 解锁select * from teacher;// 读取成功update teacher set name = 4 where id =2;// 更新操作成功3.2 行锁隐式上锁(默认,自动加锁自动释放)select //不会上锁insert、update、delete //上写锁显式上锁(手动)select * from tableName lock in share mode;//读锁select * from tableName for update;//写锁解锁(手动)提交事务(commit)回滚事务(rollback)kill 阻塞进程session01 session02begin;select * from teacher where id = 2 lock in share mode;// 上读锁select * from teacher where id = 2;// 可以正常读取update teacher set name = 3 where id =2;// 可以更新操作 update teacher set name = 5 where id =2;// 被阻塞commit;update teacher set name = 5 where id =2;// 更新操作成功session01 session02begin;select * from teacher where id = 2 for update;// 上写锁select * from teacher where id = 2;// 可以正常读取update teacher set name = 3 where id =2;// 可以更新操作 update teacher set name = 5 where id =2;// 被阻塞rollback;update teacher set name = 5 where id =2;// 更新操作成功为什么上了写锁,别的事务还可以读操作?因为InnoDB有MVCC机制(多版本并发控制),可以使用快照读,而不会被阻塞。行锁的实现算法4.1 Record Lock 锁单个行记录上的锁Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表建立的时候没有设置任何一个索引,这时InnoDB存储引擎会使用隐式的主键来进行锁定4.2 Gap Lock 锁当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引加锁,对于键值在条件范围内但并不存在的记录。优点:解决了事务并发的幻读问题不足:因为query执行过程中通过范围查找的话,他会锁定争个范围内所有的索引键值,即使这个键值并不存在。间隙锁有一个致命的弱点,就是当锁定一个范围键值之后,即使某些不存在的键值也会被无辜的锁定,而造成锁定的时候无法插入锁定键值范围内任何数据。在某些场景下这可能会对性能造成很大的危害。4.3 Next-key Lock 锁同时锁住数据+间隙锁在Repeatable Read隔离级别下,Next-key Lock 算法是默认的行记录锁定算法。4.4 行锁的注意点只有通过索引条件检索数据时,InnoDB才会使用行级锁,否则会使用表级锁(索引失效,行锁变表锁)即使是访问不同行的记录,如果使用的是相同的索引键,会发生锁冲突如果数据表建有多个索引时,可以通过不同的索引锁定不同的行如何排查锁?5.1 表锁查看表锁情况show open tables;表锁分析show status like 'table%';table_locks_waited出现表级锁定争用而发生等待的次数(不能立即获取锁的次数,每等待一次值加1),此值高说明存在着较严重的表级锁争用情况table_locks_immediate产生表级锁定次数,不是可以立即获取锁的查询次数,每立即获取锁加15.2 行锁行锁分析show status like 'innodb_row_lock%';innodb_row_lock_current_waits //当前正在等待锁定的数量innodb_row_lock_time //从系统启动到现在锁定总时间长度innodb_row_lock_time_avg //每次等待所花平均时间innodb_row_lock_time_max //从系统启动到现在等待最长的一次所花时间innodb_row_lock_waits //系统启动后到现在总共等待的次数information_schema 库innodb_lock_waits表innodb_locks表innodb_trx表优化建议尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁合理设计索引,尽量缩小锁的范围尽可能较少检索条件,避免间隙锁尽量控制事务大小,减少锁定资源量和时间长度尽可能低级别事务隔离死锁6.1 解释指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象6.2 产生的条件互斥条件:一个资源每次只能被一个进程使用请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放不剥夺条件:进程已获得的资源,在没有使用完之前,不能强行剥夺循环等待条件:多个进程之间形成的一种互相循环等待的资源的关系6.1 解决查看死锁:show engine innodb status \G自动检测机制,超时自动回滚代价较小的事务(innodb_lock_wait_timeout 默认50s)人为解决,kill阻塞进程(show processlist)wait for graph 等待图(主动检测)6.1 如何避免加锁顺序一致,尽可能一次性锁定所需的数据行尽量基于primary(主键)或unique key更新数据单次操作数据量不宜过多,涉及表尽量少减少表上索引,减少锁定资源尽量使用较低的隔离级别尽量使用相同条件访问数据,这样可以避免间隙锁对并发的插入影响精心设计索引,尽量使用索引访问数据借助相关工具:pt-deadlock-logger乐观锁与悲观锁7.1 悲观锁解释假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作实现机制表锁、行锁等实现层面数据库本身适用场景并发量大7.2 乐观锁解释假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性实现机制提交更新时检查版本号或者时间戳是否符合实现层面业务代码适用场景并发量小
2023年08月12日
16 阅读
0 评论
0 点赞
2023-08-12
MYSQL[快问快答系列]
[MYSQL[快问快答系列]](https://learnku.com/articles/75347)4 核 16GB 内存 1TB 固态硬盘,请给我 mysql8.0 的最优配置 my.cnf[mysqld]基本设置port = 3306user = mysqldefault_storage_engine = InnoDBsocket = /var/lib/mysql/mysql.sockpid-file = /var/lib/mysql/mysql.pid日志文件设置log-error = /var/log/mysql/error.logslow_query_log = 1slow_query_log_file = /var/log/mysql/mysql-slow.loglong_query_time = 2log_queries_not_using_indexes = 1缓存设置thread_cache_size = 128table_open_cache = 2048table_definition_cache = 2048table_open_cache_instances = 64query_cache_type = 1query_cache_size = 64Mquery_cache_limit = 4MInnoDB存储引擎设置innodb_buffer_pool_size = 12G //缓冲池大小innodb_log_file_size = 2G //事务日志文件大小innodb_flush_log_at_trx_commit = 1 //2为异步,有风险;1为同步,性能较差innodb_file_per_table = 1 //每个InnoDB表创建单独的数据文件。innodb_flush_method = O_DIRECT //指定InnoDB使用的磁盘刷新方法。O_DIRECT参数表示数据被直接写入磁盘而不是被缓存到操作系统缓存中。innodb_io_capacity = 2000 //指定InnoDB磁盘IO容量的预估值。innodb_io_capacity_max = 4000 //指定InnoDB磁盘IO容量的上限值。线程和连接设置max_connections = 500 //允许的最大客户端连接数max_user_connections = 1000 //每个mysql用户允许的最大连接数。如果没有单独设置,则使用全局max_connections值。thread_stack = 512K //线程分配的堆栈空间thread_cache_size = 128 //MySQL线程缓存的大小。wait_timeout = 600 //连接在没有活动操作时被关闭之前的等待时间。interactive_timeout = 600connect_timeout = 60其他设置character_set_server = utf8mb4collation_server = utf8mb4_unicode_ci执行一条 select 语句,期间发生了什么?Mysql 客户端与 Mysql 服务端通过三次握手建立 TCP 连接验证账号密码查询缓存,如果命中直接返回 (MySQL 8.0 版本直接将查询缓存删掉了)解析器阶段,进行词法分析,生成语法树预处理阶段,检查 SQL 语句中的表或者字段是否存在,将 select 中的 符号扩展为表上的所有列优化器阶段,基于查询成本的考虑,决定使用什么索引执行器阶段,开始与存储引擎交互,从存储引擎读取记录,返回给客户端如何查看 MySQL 服务被多少个客户端连接了?show processlist;空闲连接会一直占用着吗?MySQL 定义了空闲连接的最大空闲时长,由 wait_timeout 参数控制的,默认值是 8 小时(28880 秒),如果空闲连接超过了这个时间,连接器就会自动将它断开。mysql> show variables like 'wait_timeout';Variable_nameValuewait_timeout28800MySQL 的连接数有限制吗?最大连接数由 max_connections 参数控制,超过这个值,系统就会拒绝接下来的连接请求,并报错提示 “Too many connections”。mysql> show variables like 'max_connections';Variable_nameValuemax_connections500怎么解决长连接占用内存的问题?定期断开长连接客户端主动重置连接explain 命令possible_keys 字段表示可能用到的索引key 字段表示实际用的索引,如果这一项为 NULL,说明没有使用索引;key_len 表示索引的长度,根据 key_len 长度来分析联合索引使用了几个rows 表示扫描的数据行数。type 表示数据扫描类型extra 额外信息type 执行效率从低到高的顺序为:All 全表扫描index 全索引扫描 比全表扫描好一些,好处是不用再排序range 索引范围扫描 尽量让 SQL 查询可以使用到 range 这一级别及以上的 type 访问方式ref 非唯一索引扫描eq_ref 主键或唯一索引扫描,通常使用在多表联查中const 结果只有一条的主键或唯一索引扫描extra:覆盖索引 Extra:Using index 效率不错索引下推 Extra:Using index condition文件排序 Extra:Using filesort 常见于 group by 操作 效率低临时表 Extra:Using temporary 常见于 group by/order by 操作 效率低查询一次数据最多需要多少次 IOB+Tree 存储千万级的数据只需要 3-4 层高度就可以满足,即使在数据量很大的情况,查询一个数据的磁盘 I/O 依然维持在 3-4 次什么是回表先检二级索引中的 B+Tree 的索引值,找到对应的叶子节点,然后获取主键值,然后再通过主键索引中的 B+Tree 树查询到对应的叶子节点,然后获取整行数据。这个过程叫回表,也就是说要查两个 B+Tree 才能查到数据。什么是覆盖索引在二级索引的 B+Tree 就能查询到结果的过程就叫作覆盖索引,不需要回表。什么是索引下推MySQL 5.6 引入的索引下推优化, 可以在联合索引遍历过程中,对联合索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。字符集 utf8,utf8mb4,ascii 一个字符分别占用多少字节?utf8 3 个utf8mb4 4 个 允许存储 emoji 表情ascii 1 个怎么提高这条 SQL 效率select * from order where status = 1 order by create_time asc建立 status,create_time 联合索引,避免文件排序什么时候使用索引字段有唯一性限制的经常用于 WHERE 查询条件的字段经常用于 GROUP BY 和 ORDER BY 的字段什么时候不使用索引WHERE 条件,GROUP BY,ORDER BY 里用不到的字段字段中存在大量重复数据,不需要创建索引表数据太少的时候,不需要创建索引经常更新的字段不用创建索引索引列为什么最好设置为 Not Null索引列存在 NULL 就会导致优化器在做索引选择的时候更加复杂,更加难以优化NULL 值是一个没意义的值,但是它会占用物理空间,至少每行都会用 1 字节空间存储 NULL 值列表索引什么时候失效like % xx 或者 like % xx%对索引列做了计算、函数、类型转换操作没有按照最左优先的方式进行索引的匹配在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列Innodb 为什么选择 B+treeHashHash 在做等值查询的时候效率贼快,搜索复杂度为 O (1)。但是 Hash 表不适合做范围查询,它更适合做等值的查询二叉树数据量越大,二叉树高越高,磁盘 IO 越多,B+Tree 再千万级别的数据量下只用 3-4 层B Tree相同数据量时,B+Tree 树高比 BTree 低,磁盘 IO 次数更少B+Tree 叶子节点用双向链表串起来,适合范围查询,BTree 无法做到这点如何添加测试数据CREATE TABLE person(id int NOT NULL AUTO_INCREMENT PRIMARY KEY comment '主键', person_id tinyint not null comment '用户id', person_name VARCHAR(200) comment '用户名称', gmt_create datetime comment '创建时间', gmt_modified datetime comment '修改时间') comment '人员信息表';添加一条数据insert into person values(1, 1,'user_1', NOW(), now());以下 sql 执行 20 次 约等于 100 万条数据执行 24 次 1600 万条数据INSERT INTO person ( person_id, person_name, gmt_create, gmt_modified ) SELECT LEFT( rand()* 10, 10 ) AS person_id,concat( 'user_', LEFT ( rand()* 10, 10 ) % 2048 ),date_add( gmt_create, INTERVAL + @i cast( rand() 100 AS signed ) SECOND ),date_add(date_add( gmt_modified, INTERVAL + @i * cast( rand()* 100 AS signed ) SECOND ), INTERVAL + cast( rand()* 1000000 AS signed ) SECOND ) FROMperson;MySQL 单表为什么最好不要超过 2000W超过了这个值可能会导致 B + 树层级更高,影响查询性能。MySQL 使用 like “% x“,索引一定会失效吗?不一定,如果扫描了二级索引就能得到结果,就不用回表,直接返回数据,这个时候 explain 的 type 是 indexcount (*) 和 count (1) 有什么区别?哪个性能最好?count() 执行过程跟 count (1) 执行过程基本一样的,性能没有什么差异。而且 MySQL 会对 count () 和 count (1) 有个优化,如果有多个二级索引的时候,优化器会使用 key_len 最小的二级索引进行扫描。只有当没有二级索引的时候,才会采用主键索引来进行统计。如何优化 count (*)?近似值explain select count(*) from t_order会返回一个近似值 rows额外表保存计数值插入一条记录的同时,将计数表字段 + 1, 新增和删除的时候需要额外维护这个计数器,存在 redis 也行事务有哪些特性?原子性(Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成一致性(Consistency):事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。隔离性(Isolation):每个事务都有一个完整的数据空间,对其他并发事务是隔离的。持久性(Durability):事务处理结束后,对数据的修改就是永久的InnoDB 引擎通过什么技术来保证事务的这四个特性的呢?持久性是通过 redo log (重做日志)来保证的;原子性是通过 undo log(回滚日志) 来保证的;隔离性是通过 MVCC(多版本并发控制) 或锁机制来保证的;一致性则是通过持久性 + 原子性 + 隔离性来保证;事务的隔离级别有哪些?读未提交(read uncommitted),指一个事务还没提交时,它做的变更就能被其他事务看到;读提交(read committed),指一个事务提交之后,它做的变更才能被其他事务看到;可重复读(repeatable read),指一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,MySQL InnoDB 引擎的默认隔离级别;串行化(serializable );会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行;MySQL InnoDB 怎么避免幻读现象?针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。针对当前读(select … for update 等语句),是通过 next-key lock(记录锁 + 间隙锁)方式解决了幻读,因为当执行 select … for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。快照读和当前读有什么区别?MySQL 里除了普通查询是快照读,其他都是当前读,比如 update、insert、delete,这些语句执行前都会查询最新版本的数据。另外,select ... for update 这种查询语句是当前读,每次执行的时候都是读取最新的数据。既然备份数据库数据的时候,使用全局锁会影响业务,那有什么其他方式可以避免?在使用 mysqldump 时加上 –single-transaction 参数的时候,就会在备份数据库之前先开启事务。因为在可重复读的隔离级别下,即使其他事务更新了表的数据,也不会影响备份数据库时的 Read View,这就是事务四大特性中的隔离性,这样备份期间备份的数据一直是在开启事务时的数据。MySQL 死锁了,怎么办?设置事务等待锁的超时时间,当一个事务的等待时间超过该值后,就对这个事务进行回滚,于是锁就释放了,另一个事务就可以继续执行了。在 InnoDB 中,参数 innodb_lock_wait_timeout 是用来设置超时时间的,默认值时 50 秒。开启主动死锁检测。主动死锁检测在发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑,默认就开启。分析产生死锁的业务逻辑,考虑怎么去掉 select …for update 导致产生的死锁为什么需要 undo log?实现事务回滚,保障事务的原子性。。实现 MVCC(多版本并发控制)关键因素之一。为什么需要 缓冲池 Buffer Pool?当读取数据时,如果数据存在于 Buffer Pool 中,客户端就会直接读取 Buffer Pool 中的数据,否则再去磁盘中读取。当修改数据时,如果数据存在于 Buffer Pool 中,那直接修改 Buffer Pool 中数据所在的页,然后将其页设置为脏页,为了减少磁盘 I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘Buffer Pool 除了缓存「索引页」和「数据页」,还包括了 Undo 页,插入缓存、自适应哈希索引、锁信息等等。查询一条记录,就只需要缓冲一条记录吗?不对,当我们查询一条记录时,InnoDB 是会把整个页的数据加载到 Buffer Pool 中为什么需要 redo log ?为了防止断电导致数据丢失的问题,当有一条记录需要更新的时候,InnoDB 引擎就会先更新内存(同时标记为脏页),然后将本次对这个页的修改以 redo log 的形式记录下来当系统崩溃时,虽然脏页数据没有持久化,但是 redo log 已经持久化,接着 MySQL 重启后,可以根据 redo log 的内容,将所有数据恢复到最新的状态。redo log 和 undo log 区别在哪?redo log 记录了此次事务「完成后」的数据状态,记录的是更新之后的值;undo log 记录了此次事务「开始前」的数据状态,记录的是更新之前的值;事务提交之前发生了崩溃,重启后会通过 undo log 回滚事务,事务提交之后发生了崩溃,重启后会通过 redo log 恢复事务,如下图:redo log 什么时候刷盘?MySQL 正常关闭时;当 redo log buffer 中记录的写入量大于 redo log buffer 内存空间的一半时,会触发落盘;InnoDB 的后台线程每隔 1 秒,将 redo log buffer 持久化到磁盘。每次事务提交时都将缓存在 redo log buffer 里的 redo log 直接持久化到磁盘(这个策略可由 innodb_flush_log_at_trx_commit 参数控制,下面会说)。为什么需要 binlog ?binlog 文件是记录了所有数据库表结构变更和表数据修改的日志,不会记录查询类的操作,比如 SELECT 和 SHOW 操作。redo log 和 binlog 有什么区别?binlog 是 MySQL 的 Server 层实现的日志,所有存储引擎都可以使用,用于备份恢复、主从复制;redo log 是 Innodb 存储引擎实现的日志,用于掉电等故障恢复。如果不小心整个数据库的数据被删除了,能使用 redo log 文件恢复数据吗?不可以使用 redo log 文件恢复,只能使用 binlog 文件恢复。主从复制是怎么实现?写入 Binlog:主库写 binlog 日志,提交事务,并更新本地存储数据。同步 Binlog:把 binlog 复制到所有从库上,每个从库把 binlog 写到暂存日志中。回放 Binlog:回放 binlog,并更新存储引擎中的数据。从库是不是越多越好?不是的。因为从库数量增加,从库连接上来的 I/O 线程也比较多,主库也要创建同样多的 log dump 线程来处理复制的请求,对主库资源消耗比较高,同时还受限于主库的网络带宽。所以在实际使用中,一个主库一般跟 2~3 个从库(1 套数据库,1 主 2 从 1 备主),这就是一主多从的 MySQL 集群结构。为什么需要两阶段提交?MySQL 会同时维护 binlog 日志与 InnoDB 的 redo log,为了保证这两个日志的一致性,MySQL 使用了内部 XA 事务MySQL 磁盘 I/O 很高,有什么优化的方法?设置组提交的两个参数: binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数,延迟 binlog 刷盘的时机,从而减少 binlog 的刷盘次数。这个方法是基于 “额外的故意等待” 来实现的,因此可能会增加语句的响应时间,但即使 MySQL 进程中途挂了,也没有丢失数据的风险,因为 binlog 早被写入到 page cache 了,只要系统没有宕机,缓存在 page cache 里的 binlog 就会被持久化到磁盘。将 sync_binlog 设置为大于 1 的值(比较常见是 100~1000),表示每次提交事务都 write,但累积 N 个事务后才 fsync,相当于延迟了 binlog 刷盘的时机。但是这样做的风险是,主机掉电时会丢 N 个事务的 binlog 日志。将 innodb_flush_log_at_trx_commit 设置为 2。表示每次事务提交时,都只是缓存在 redo log buffer 里的 redo log 写到 redo log 文件,注意写入到「 redo log 文件」并不意味着写入到了磁盘,因为操作系统的文件系统中有个 Page Cache,专门用来缓存文件数据的,所以写入「 redo log 文件」意味着写入到了操作系统的文件缓存,然后交由操作系统控制持久化到磁盘的时机。但是这样做的风险是,主机掉电的时候会丢数据。你知道 mysql 主从延迟吗?有些时候延迟的时间还会很长。遇到这种情况咋么办?多线程复制确保使用了多线程复制,并且已经按照手册查看了如何调整各种选项以从中获得最佳效率。调整 binlog 格式调整为 statement 减小 binlog 容量临时降低持久化要求可以临时设置sync_binlog=0,innodb_flush_log_at_trx_commit=0以提高复制速度。怎么定位运行中的 MySQL 使用哪个配置文件which mysqld/usr/sbin/mysqld/usr/sbin/mysqld --verbose --help | grep -A 1 'Default options'Default options are read from the following files in the given order:/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf 为什么不建议字段格式为 null如果查询中包含可为 NULL 的列,对 MySQL 来说更难优化,因为可为 NULL 的列使得索引、索引统计和值比较都更复杂。可为 NULL 的列会使用更多的存储空间,在 MySQL 里也需要特殊处理。储存财务数据,BIGINT 还是 DECIMAL假设要存储财务数据并精确到万分之一分,则可以把所有金额乘以一百万然后将结果存储在 BIGINT 里,这样可以同时避免浮点存储计算不精确和 DECIMAL 精确计算代价高的问题。VARCHAR 和 CHAR 的区别VARCHARVARCHAR 需要额外使用 1 或 2 字节在每行数据前记录字符串的长度:如果最大长度 <=255 字节,则只使用 1 字节表示,否则使用 2 字节。VARCHAR 节省了存储空间,所以对性能也有帮助。但是,由于行是可变长度的,在更新时可能会增长,这会导致 InnoDB 需要分割页面来容纳行,InnoDB 将过长的 VARCHAR 值存储为 BLOBCHAR当存储 CHAR 值时,MySQL 删除所有尾随空格。如果需要进行比较,值会用空格填充。CHAR 适合存储用户密码的 MD5 值。对于经常修改的数据,CHAR 也比 VARCHAR 更好,因为固定长度的行不容易出现碎片。对于非常短的列,CHAR 也比 VARCHAR 更高效;设计为只保存 Y 和 N 的值的 CHAR(1)在单字节字符集 [1] 中只使用 1 字节,但 VARCHAR(1)需要 2 字节如何查询各个数据表储存大小mysql> select table_schema as '数据库', table_name as '表名', table_rows as '记录数', truncate(data_length/1024/1024, 2) as '数据容量(MB)', truncate(index_length/1024/1024, 2) as '索引容量(MB)' from information_schema.tables where table_schema='test' order by data_length desc, index_length desc;数据库表名记录数数据容量(MB)索引容量(MB)testwd_admin_log3830.200.01DATETIME 还是 TIMESTAMPDATETIME可以保存大范围的数值,从 1000 年到 9999 年,精度为 1 微秒。它以 YYYYMMDDHHMMSS 格式存储压缩成整数的日期和时间,且与时区无关。这需要 8 字节的存储空间。TIMESTAMP存储自 1970 年 1 月 1 日格林尼治标准时间(GMT)午夜以来经过的秒数,只使用 4 字节的存储空间,只能表示从 1970 年到 2038 年 1 月 19 日,MySQL 提供 FROM_UNIXTIME () 函数来将 UNIX 时间戳转换为日期,并提供了 UNIX_TIMESTAMP () 函数将日期转换为 UNIX 时间戳。结论使用带符号的 32 位 INT,可以表达直到 2038 年的时间。使用无符号的 32 位 INT,可以表达直到 2106 年的时间。如果使用 64 位,还可以超出这些范围。这一做法越来越流行。可以对保存 md5 (),sha1 (),uuid () 的列做索引吗?这些函数生成的新值会任意分布在很大的空间内,这会减慢INSERT 和某些类型的 SELECT 查询的速度因为插入的值会写到索引的随机位置,所以会使得 INSERT 查询变慢。这会导致页分裂、磁盘随机访问,以及对于聚簇存储引擎产生聚簇索引碎片。SELECT 查询也会变慢,因为逻辑上相邻的行会广泛分布在磁盘和内存中。IPV4 地址用什么数据类型?用过函数转成 int 然后用 unsigned int 存储MySQL schema 设计中的有什么骚操作 (错误示范)?太多的列MySQL 的存储引擎 API 通过在服务器和存储引擎之间以行缓冲区格式复制行来工作,InnoDB 的行格式总是需要转换的。这种转换的成本取决于列数。太多的联接MySQL 限制每个联接有 61 个表全能的枚举如何不停机不锁表地更新表结构?pt-online-schema-change: 原理是使用触发器,对已经使用触发器的表不太兼容gh-ost: 原理是使用 binlog, 对使用外键的表不太兼容怎么选择如果你正在运行许多已存在外键的旧数据库,并且删除外键是很困难的,那么会发现 pt-online-schema-change 已经尝试对外键提供更广泛的支持,但你必须承担为数据完整性和正常运行时间选择最安全选项的认知负担。此外,gh-ost 利用 binlog 来完成其工作,因此如果由于某种原因无法访问这些日志,pt-online-schema-change 仍然是一个可行的选项。INNODB 自适应哈希索引特性?当 InnoDB 发现某些索引值被非常频繁地被访问时,它会在原有的 B-tree 索引之上,在内存中再构建一个哈希索引。这就可以实现非常快速的哈希查找。这个过程是完全自动化的,用户可以通过参数关闭这个特性。如何减少索引和数据的碎片通过执行 OPTIMIZE TABLE 或者导出再导入的方式来重新整理数据。联接查询怎么优化?确保 ON 或者 USING 子句中的列上有索引。确保任何 GROUP BY 和 ORDER BY 中的表达式只涉及一个表中的列,这样 MySQL 才有可能使用索引来优化这个过程。怎么优化大表 limit offset错误示范select id,description from film order by title limit 50, 5正确示范select id,description from film inner join (select id from film order by title limit 50,5) as lim using (id)复制如何工作主库把数据更改记录到 binlog从库启动线程 1 把主库的 binlog 复制到自己的中继日志从库启动线程 2 重放中继日志binlog_format 有多少种日志格式?STATEMENT表示在 binlog 文件中只记录 SQL 语句。这是默认值,优点是简单紧凑容量小,其最大的弊端则在于会遇到某些具有 “不确定性” 的 SQL 语句问题ROW表示在 binlog 文件中只记录受影响的行。在这种格式下,MySQL 会将对数据的更改记录为行级别的操作。这种格式可以更准确地记录数据更改,但会导致 binlog 文件更大。MIXED表示在 binlog 文件中既记录 SQL 语句,又记录受影响的行。MySQL 会自动选择适当的格式来记录数据更改。这是最常用的格式,因为它可以平衡可读性和性能。结论我们 (高性能 MYSQL 第四版) 建议坚持使用基于行的复制 ROW,除非某些场景下明确需要临时使用基于语句的复制。基于行的复制提供了最安全的数据复制方法。怎么保证崩溃后的复制安全保证每个undo log都被同步地写到磁盘,增加磁盘写入操作频次,确保数据持久性innodb_flush_log_at_trx_commit=1每次事务执行的时候都会把二进制日志同步写入磁盘。增加磁盘写入操作频次,防止丢失事务sync_binlog=1使用 InnoDB 存储引擎存储中继日志信息,提供更好的可靠性和性能。relay_log_info_repository=TABLE在复制过程中发生错误时自动尝试使用中继日志进行恢复relay_log_recovery=ON延迟复制有什么用想象一下这样的场景,你正在处理大量数据,突然意外地做了一些变更:删除了一个表。从备份中恢复可能需要几个小时。如果使用了延迟复制的副本,则可以找到 DROP TABLE 语句对应的 GTID,使副本服务器的复制运行到表被删除之前的时间点,这会大大减少修复时间。什么是多线程复制?最新的 MySQL 版本则提供了多线程复制能力,可以在副本端运行多个 SQL 线程,从而加快本地中继日志的应用。什么是半同步复制?在半同步复制中,主服务器会等待至少一个从服务器将更新成功复制后,才会返回客户端。确保至少一个从服务器成功复制了更新,降低故障恢复的成本,缺点是会影响主服务器的性能
2023年08月12日
12 阅读
0 评论
0 点赞
2023-08-12
Redis[快问快答系列]
[Redis[快问快答系列]](https://learnku.com/articles/75382)什么是 Redis?Redis 是一种基于内存的数据库,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景。Redis 提供了多种数据类型来支持不同的业务场景,比如 String (字符串)、Hash (哈希)、 List (列表)、Set (集合)、Zset (有序集合)、Bitmaps(位图)、HyperLogLog(基数统计)、GEO(地理信息)、Stream(流),并且对数据类型的操作都是原子性的,因为执行命令由单线程负责的,不存在并发竞争的问题。除此之外,Redis 还支持事务 、持久化、Lua 脚本、多种集群方案(主从复制模式、哨兵模式、切片机群模式)、发布 / 订阅模式,内存淘汰机制、过期删除机制等等。Redis 和 Memcached 有什么区别?Memcached 只支持最简单的 key-value 数据类型Redis 支持数据的持久化,Memcached 重启或者挂掉后,数据就没了Redis 原生支持集群模式,Memcached 没有原生的集群模式Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持为什么用 Redis 作为 MySQL 的缓存?Redis 具备高性能,高并发,Redis 单机的 QPS 能轻松破 10w,而 MySQL 单机的 QPS 很难破 1w。Redis 数据类型以及使用场景分别是什么?String:缓存对象:SET user:1 '{"name":"xiaolin", "age":18}'计数器:INCR count:1001分布式锁:SET lock_key unique_value NX PX 10000共享 session: 适用分布式系统List:消息队列:消息保序:使用 LPUSH + RPOP;阻塞读取:使用 BRPOP;重复消息处理:生产者自行实现全局唯一 ID;消息的可靠性:使用 BRPOPLPUSHHash:缓存对象一般对象用 String + Json 存储,对象中某些频繁变化的属性可以考虑抽出来用 Hash 类型存储。存储一个哈希表uid:1的键值HMSET uid:1 name Tom age 152存储一个哈希表uid:2的键值HMSET uid:2 name Jerry age 132获取哈希表用户id为1中所有的键值HGETALL uid:11) "name"2) "Tom"3) "age"4) "15"购物车添加商品:HSET cart:{用户id} {商品id} 1添加数量:HINCRBY cart:{用户id} {商品id} 1商品总数:HLEN cart:{用户id}删除商品:HDEL cart:{用户id} {商品id}获取购物车所有商品:HGETALL cart:{用户id}Set:聚合计算场景主从集群中,为了避免主库因为 Set 做聚合计算(交集、差集、并集)时导致主库被阻塞,我们可以选择一个从库完成聚合统计点赞uid:1 用户对文章 article:1 点赞SADD article:1 uid:1uid:1 取消了对 article:1 文章点赞。SREM article:1 uid:1获取 article:1 文章所有点赞用户 :SMEMBERS article:11) "uid:3"2) "uid:2"获取 article:1 文章的点赞用户数量:SCARD article:1(integer) 2判断用户 uid:1 是否对文章 article:1 点赞了:SISMEMBER article:1 uid:1(integer) 0 # 返回0说明没点赞,返回1则说明点赞了共同关注Set 类型支持交集运算,所以可以用来计算共同关注的好友、公众号等。uid:1 用户关注公众号 id 为 5、6、7、8、9SADD uid:1 5 6 7 8 9uid:2 用户关注公众号 id 为 7、8、9、10、11SADD uid:2 7 8 9 10 11获取共同关注SINTER uid:1 uid:21) "7"2) "8"3) "9"给 uid:2 推荐 uid:1 关注的公众号:SDIFF uid:1 uid:21) "5"2) "6"验证某个公众号是否同时被 uid:1 或 uid:2 关注:SISMEMBER uid:1 5(integer) 1 # 返回0,说明关注了SISMEMBER uid:2 5(integer) 0 # 返回0,说明没关注抽奖活动存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次。key 为抽奖活动名,value 为员工名称,把所有员工名称放入抽奖箱 :SADD lucky Tom Jerry John Sean Marry Lindy Sary Mark(integer) 5如果允许重复中奖,可以使用 SRANDMEMBER 命令。抽取 1 个一等奖:SRANDMEMBER lucky 11) "Tom"抽取 2 个二等奖:SRANDMEMBER lucky 21) "Mark"2) "Jerry"抽取 3 个三等奖:SRANDMEMBER lucky 31) "Sary"2) "Tom"3) "Jerry"如果不允许重复中奖,可以使用 SPOP 命令。抽取一等奖1个SPOP lucky 11) "Sary"抽取二等奖2个SPOP lucky 21) "Jerry"2) "Mark"抽取三等奖3个SPOP lucky 31) "John"2) "Sean"3) "Lindy"Zset:排行榜arcticle:1 文章获得了200个赞ZADD user:xiaolin:ranking 200 arcticle:1文章 arcticle:1 新增一个赞ZINCRBY user:xiaolin:ranking 1 arcticle:1查看某篇文章的赞数ZSCORE user:xiaolin:ranking arcticle:4获取文章赞数最多的 3 篇文章ZREVRANGE user:xiaolin:ranking 0 2 WITHSCORES获取100赞到200 赞的文章ZRANGEBYSCORE user:xiaolin:ranking 100 200 WITHSCORES电话和姓名排序使用有序集合的 ZRANGEBYLEX 或 ZREVRANGEBYLEX 可以帮助我们实现电话号码或姓名的排序BitMap:签到第一步,执行下面的命令,记录该用户 6 月 3 号已签到。SETBIT uid:sign:100:202206 2 1第二步,检查该用户 6 月 3 日是否签到。GETBIT uid:sign:100:202206 2 第三步,统计该用户在 6 月份的签到次数。BITCOUNT uid:sign:100:202206用户登录状态第一步,执行以下指令,表示用户已登录。SETBIT login_status 10086 1第二步,检查该用户是否登陆,返回值 1 表示已登录。GETBIT login_status 10086第三步,登出,将 offset 对应的 value 设置成 0。SETBIT login_status 10086 0布隆过滤器HyperLogLog:只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数,统计结果是有一定误差的,标准误算率是 0.81%。百万计网页 UV 计数在统计 UV 时,你可以用 PFADD 命令把访问页面的每个用户都添加到 HyperLogLog 中。PFADD page1:uv user1 PFADD page1:uv user2用 PFCOUNT 命令直接获得 page1 的 UV 值了PFCOUNT page1:uvGEO:GEO 本身并没有设计新的底层数据结构,而是直接使用了 Sorted Set 集合类型。查找用户附近的网约车把 ID 号为 33 的车辆的当前经纬度位置存入 GEO 集合中:GEOADD cars:locations 116.034579 39.030452 33当用户想要寻找自己经纬度(116.054579,39.030452 )为中心的 5 公里内的车辆信息GEORADIUS cars:locations 116.054579 39.030452 5 km ASC COUNT 10Stream:消息队列 比 list 高级Redis 线程模型Redis 单线程指的是「接收客户端请求 -> 解析请求 -> 进行数据读写等操作 -> 发送数据给客户端」这个过程是由一个线程(主线程)来完成的,这也是我们常说 Redis 是单线程的原因。Redis 程序不是单线程的,后台还会有三个线程处理关闭文件,AOF 刷盘,释放内存Redis 采用单线程为什么还这么快?Redis 采用单线程模型可以避免了多线程之间的竞争,省去了多线程切换带来的时间和性能上的开销,而且也不会导致死锁问题。Redis 采用了 I/O Epoll 多路复用机制处理大量的客户端 Socket 请求Redis 6.0 之后为什么引入了多线程?在 Redis 6.0 版本之后,也采用了多个 I/O 线程来处理网络请求,** 但是对于命令的执行,Redis 仍然使用单线程来处理,Redis 6.0 版本引入的多线程 I/O 特性对性能提升至少是一倍以上。Redis 如何实现数据不丢失?AOF 日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里;RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘;混合持久化方式:Redis 4.0 新增的方式,集成了 AOF 和 RBD 的优点;AOF 日志是如何实现的?Redis 在执行完一条写操作命令后,就会把该命令以追加的方式写入到一个文件里,然后 Redis 重启时,会读取该文件记录的命令,然后逐一执行命令的方式来进行数据恢复。Redis 提供了 3 种 AOF 写回硬盘的策略,在 Redis.conf 配置文件中的 appendfsync 配置项Always,每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘;Everysec,每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;No,意味着不由 Redis 控制写回硬盘的时机,由操作系统决定何时将缓冲区内容写回硬盘。AOF 日志过大,会触发压缩机制 bgrewriteaofRDB 做快照时会阻塞线程吗?执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,会阻塞主线程;执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞;Redis 还可以通过配置文件的选项来实现每隔一段时间自动执行一次 bgsave 命令save 900 1 //900 秒之内,对数据库进行了至少 1 次修改;save 300 10 //300 秒之内,对数据库进行了至少 10 次修改;save 60 10000 // 60 秒之内,对数据库进行了至少 10000 次修改。RDB 在执行快照的时候,数据能修改吗?可以的,执行 bgsave 过程中,Redis 依然可以继续处理操作命令的,也就是数据是能被修改的,关键的技术就在于多进程的写时复制技术(Copy-On-Write, COW)。为什么会有混合持久化?Redis 4.0 提出了混合持久化,既保证了 Redis 重启速度,又降低数据丢失风险。Redis 如何实现服务高可用?主从复制:一主多从的模式,且主从服务器之间采用的是「读写分离」的方式。哨兵模式:主从服务器出现故障宕机时,需要手动进行恢复。所以 Redis 增加了哨兵模式,因为哨兵模式做到了可以监控主从服务器,并且提供主从节点故障转移的功能。Redis Cluster: 分布式集群,采用哈希槽,来处理数据和节点之间的映射关系Redis 使用的过期删除策略是什么?Redis 使用的过期删除策略是「惰性删除 + 定期删除」这两种策略配和使用。惰性删除策略的做法是,不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。定期删除策略的做法是,每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期 key。Redis 主从模式中,对过期键会如何处理?主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库Redis 内存满了,会发生什么?在 Redis 的运行内存达到了配置项设置的 maxmemory,就会触发内存淘汰机制Redis 内存淘汰策略有哪些?noeviction: 默认的内存淘汰策略,不淘汰任何数据,而是不再提供服务,直接返回错误。在设置了过期时间的数据中进行淘汰volatile-random:随机淘汰设置了过期时间的任意键值;volatile-ttl:优先淘汰更早过期的键值。volatile-lru:淘汰所有设置了过期时间的键值中,最久未使用的键值;volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值;在所有数据范围内进行淘汰:allkeys-random:随机淘汰任意键值;allkeys-lru:淘汰整个键值中最久未使用的键值;allkeys-lfu:淘汰整个键值中最少使用的键值。如何避免缓存雪崩?将缓存失效时间随机打散,在原有的失效时间基础上增加一个随机值设置缓存不过期,通过业务逻辑来更新缓存数据如何避免缓存击穿互斥锁方案(Redis 中使用 SET EX NX)不给热点数据设置过期时间,由后台异步更新缓存如何避免缓存穿透判断求请求参数是否合理,请求参数是否含有非法值、请求字段是否存在,如果判断出是恶意请求就直接返回错误可以针对查询的数据,在缓存中设置一个空值或者默认值返回给应用,而不会继续查询数据库。使用布隆过滤器快速判断数据是否存在,避免通过查询数据库来判断数据是否存在Redis 如何实现延迟队列?在 Redis 可以使用有序集合(ZSet)的方式来实现延迟消息队列的,ZSet 有一个 Score 属性可以用来存储延迟执行的时间。zadd score1 value1 命令就可以一直往内存中生产消息。 zrangebyscore 查询符合条件的所有待处理的任务, 通过循环执行队列任务即可。Redis 的大 key 如何处理?一般而言,下面这两种情况被称为大 key:String 类型的值大于 10 KB;Hash、List、Set、ZSet 类型的元素的个数超过 5000 个;//最好选择在从节点上执行该命令。因为主节点上执行时,会阻塞主节点,只能返回每种类型中最大的那个 bigkey,无法得到大小排在前 N 位的 bigkey,对于集合类型来说,只统计集合元素个数的多少,而不是实际占用的内存量。redis-cli -h 127.0.0.1 -p6379 -a "password" -- bigkeysscan命令,配合key类型再用对应的命令计算内存//使用 RdbTools 第三方开源工具,可以用来解析 Redis 快照(RDB)文件,找到其中的大 key。rdb dump.rdb -c memory --bytes 10240 -f redis.csv如何删除大 key?分批次删除异步删除(Redis 4.0 版本以上)推荐使用从 Redis 4.0 版本开始,可以采用异步删除法,用 unlink 命令代替 del 来删除。这样 Redis 会将这个 key 放入到一个异步线程中进行删除,这样不会阻塞主线程。我们还可以通过配置参数,达到某些条件的时候自动进行异步删除。Redis 管道有什么用?把多条命令拼接到一起,当成一次请求发出去,结果也是拼接到一起发回来,免去了每条命令执行后都要等待的情况,从而有效地提高了程序的执行效率。Redis 事务支持回滚吗?Redis 中并没有提供回滚机制如何用 Redis 实现分布式锁的?SET lock_key unique_value NX PX 10000 lock_key 就是 key 键;unique_value 是客户端生成的唯一的标识,区分来自不同客户端的锁操作;NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。解锁需要 Lua 脚本保证原子性// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])elsereturn 0end基于 Redis 实现分布式锁有什么缺点?超时时间不好设置。集群情况下的不可靠性。Redis 如何解决集群情况下分布式锁的可靠性?为了保证集群环境下分布式锁的可靠性,Redis 官方已经设计了一个分布式锁算法 Redlock(红锁)。它是基于多个 Redis 节点的分布式锁,官方推荐是至少部署 5 个 Redis 节点,而且都是主节点为什么用跳表而不用平衡树?从内存占用上来比较,跳表比平衡树更灵活一些。在做范围查找的时候,跳表比平衡树操作要简单从算法实现难度上来比较,跳表比平衡树要简单得多如何保证缓存和数据库数据的一致性?更新数据库 + 更新缓存如果我们的业务对缓存命中率有很高的要求,我们可以采用「更新数据库 + 更新缓存」的方案,但是在两个更新请求并发执行的时候,会出现数据不一致的问题所以我们得增加一些手段来解决这个问题,这里提供两种做法:在更新缓存前先加个分布式锁,保证同一时间只运行一个请求更新缓存,当然对于写入的性能就会带来影响。在更新完缓存时,给缓存加上较短的过期时间,缓存的数据也会很快过期,先删除缓存 + 更新数据库延迟双删删除缓存redis.delKey(X)更新数据库db.update(X)睡眠Thread.sleep(N)再删除缓存redis.delKey(X)
2023年08月12日
13 阅读
0 评论
0 点赞
2023-08-12
Composer命令全解析
Composer中文文档Composer命令全解析常用命令镜像配置中国镜像composer config -g repo.packagist composer https://packagist.phpcomposer.com配置其他厂商镜像阿里云 (好像说停用了)composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/腾讯composer config -g repo.packagist composer https://mirrors.cloud.tencent.com/composer/华为composer config -g repo.packagist composer https://repo.huaweicloud.com/repository/php/解除镜像composer config -g --unset repos.packagist项目搜索项目composer search ThinkPHP创建项目composer create-project topthink/ThinkPHP=5.1.* ./tp5安装扩展composer require laravel/laravel ">=5.5"移除扩展composer remove laravel/laravel生产最佳实践转换 PSR-0/4 autoloading 到 classmap 获得更快的载入速度,禁用开发者模式composer dump-autoload -o --no-dev 自动加载 autoload目前支持四种自动加载方式:PSR-0PSR-0 规范是 PHP5.2 之前的一种命名空间映射规范,它规定命名空间与文件路径的对应关系如下命名空间中的每个下划线字符(_)都会被转换成目录分隔符(/);命名空间中的每个命名空间分隔符(\)都会被转换成目录分隔符(/);命名空间中的首字母和下划线字符都会被转换成目录名和文件名中的小写字母;每个类的文件名必须与类名完全一致,包括大小写。"psr-0" : { "Foo\\" : "psr0src/", # "Foo_Bar_" : "psr0src/" },composer install/update 之后,PSR-0 引用全部合并到 vendor/composer/autoload_namespaces.phpPSR-4PSR-4 规范是较新的一种命名空间映射规范,它与 PSR-0 规范的区别在于:命名空间中的下划线字符不再被特殊处理,只有命名空间分隔符(\)会被转换成目录分隔符(/);命名空间中的首字母和下划线字符不再被强制转换成小写字母;类的文件名与类名可以不完全一致,但必须满足相对文件路径和类名的对应关系"psr-4": { #查找Afishpapa\Httptool\Http类时的路径为src/Http.php "Afishpapa\\Httptool\\": "src/" #可以在src/和lib/ 下面找 Monolog命名空间下的类 "psr-4": { "Monolog\\": ["src/", "lib/"] } #所有命名空间都来src/目录下找 "" : "src/" }composer install/update 之后,PSR-4 引用全部合并到 vendor/composer/automoad_psr4.php 中。return array( 'Afishpapa\\Httptool\\' => array($baseDir . '/src'), );Classmapcomposer install/update 之后,PSR-4 引用全部合并到 vendor/composer/autoload_classmap.php 中。你可以用 classmap 生成支持支持自定义加载的不遵循 PSR-0/4 规范的类库。Files通常作为函数库的载入方式(而非类库)。{ "autoload": { "files": ["src/MyLibrary/functions.php"] } }全局配置-v : 增加消息的详细程度,正常输出-vv : 增加消息的详细程度,更加详细得输出-vvv : 增加消息的详细程度,debug用-h : 显示帮助-q : 不要显示任何信息-n : 不要问任何交互问题-d : 设置工作目录--ansi: 强制输出 ANSI 编码--no-ansi: 禁用 ANSI 编码--version (-V): 展示所有应用版本--profile: 展示时间和内存信息常用配置--prefer-install: 默认值为distdist: 检查本地缓存压缩包,如果有直接复制到vendor目录,如果本地缓存没有,则去远程仓库下载压缩包,如果远程仓库没有提供压缩包,则尝试从github中安装包,并且删除.git版本信息,总之,dist能够快速地下载并安装依赖包,适用于大部分生产环境。 source: 直接从github中下载源码,保留.git信息,如果您需要对包进行自定义修改或者需要对其进行特殊的构建过程,则应该使用 source。 auto:2.1版本后已弃用-o: 生成自动加载器文件的优化版本,以加快类加载速度。-a: 默认情况下,Composer 会根据 composer.json 中的 PSR-4 和 PSR-0 配置来生成自动加载器, 使用 --classmap-authoritative 选项可以让 Composer 忽略 PSR-4 和 PSR-0 配置,而直接根据类文件生成一个类映射表(class map),并将其作为自动加载器的唯一来源。需要注意的是,使用 --classmap-authoritative 选项可能会导致一些问题,比如在添加新的类文件时需要重新生成类映射表,否则新添加的类无法被自动加载器加载。因此,建议在开发环境中使用这个选项来提高性能,但在生产环境中不要使用,以免出现问题。--dry-run: 执行安装过程的模拟运行,不会实际下载或安装任何软件包。--dev: 安装开发依赖项,包括测试框架和调试工具等。composer init以交互方式初始化 composer--name: 包名,格式为作者/名称,比如monolog/monolog--description: 简短描述--author: 作者名--type: 包的安装类型,默认librarylibrary : 它会简单的将文件复制到 vendor 目录 project : 当前包是一个项目,而不是一个库 metapackage : 一个空的包,包含依赖并且需要触发依赖的安装,这将不会对系统写入额外的文件。 composer-plugin : 它有一个自定义安装类型,可以为其它包提供一个 installler。--homepage: 官网首页--require: 引入包,格式为 包:版本 foo/bar:1.0.0--require-dev: 开发用的组件-s : 最小稳定性值 dev stable --license (-l): 许可证--repository: 指定一个或多个 Composer 仓库-a : 添加一个autoload.psr-4的对象到composer.jsoncomposer install如果存在 composer.lock 文件,它会从此文件读取依赖版本,这确保了该库的每个使用者都能得到相同的依赖版本。如果不存在 composer.lock 文件,它会从 composer.json 文件读取依赖版本,并把其安装到 vendor 目录下。如果没有 composer.lock 文件,composer 将在处理完依赖关系后创建它。composer require添加一个包到 composer.json 文件,如果没有 composer.json 就创建一个# 该命令会安装两个不同的软件包 # vendor/package:2.* : 版本号以 2. 开头,后面跟着任何版本号。 # vendor/package2:dev-master : 使用 dev-master 分支。这意味着它将安装该分支的最新版本,通常是开发版本,不属于正式发布。 composer require "vendor/package:2.*" vendor/package2:dev-mastercomposer update获取依赖的最新版本# 只更新这两个包vendor/package vendor/package2 composer update vendor/package vendor/package2 # 更新符合正则匹配的包 composer update "vendor/*" # 更新依赖包到指定版本,需符合composer.json的约束 composer update --with vendor/package:2.0.1composer remove移除依赖包# 移除这两个包 composer remove vendor/package vendor/package2 composer reinstall 重装包,如果不小心改了包文件,可以使用重装命令恢复 # 重装两个包 composer reinstall acme/foo acme/bar # 重装正则匹配的包 composer.phar reinstall "acme/*"composer check-platform-reqs用于检查您的 PHP 和扩展版本是否符合已安装包的平台要求--lock: 仅从锁定文件中检查要求,而不是从已安装的包中检查要求。 --no-dev: 不检查 require-dev 包要求。 -f: 格式> composer check-platform-reqs Checking platform requirements for packages in the vendor dir ext-json 1.7.0 success ext-libxml 7.3.4 success ext-mbstring 7.3.4 success ext-openssl 7.3.4 success ext-simplexml 7.3.4 success php 7.3.4 successcomposer globalglobal 允许您全局运行其他命令,如 install、remove、require,updatecomposer search monolog搜索依赖包-N : 只搜包名-O : 只搜作者-t : 搜全称λ composer search monolog monolog/monolog Sends your logs to files, sockets, inboxes, databases and various web services symfony/monolog-bundle Symfony MonologBundle symfony/monolog-bridge Provides integration for Monolog with various Symfony components ...composer show / composer info列出所有可用的包信息λ composer show defuse/php-encryption v2.3.1 Secure PHP Encryption Library laminas/laminas-diactoros 2.5.0 PSR HTTP Message implementations laminas/laminas-zendframework-bridge 1.4.1 Alias legacy ZF class names to Laminas Project equivalents. ...列出单个包的详情λ composer show slim/slim name : slim/slim descrip. : Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs keywords : api, framework, micro, router versions : * 3.12.4 type : library license : MIT License (MIT) (OSI approved) https://spdx.org/licenses/MIT.html#licenseText homepage : https://slimframework.com source : [git] https://github.com/slimphp/Slim.git ce3cb65a06325fc9fe3d0223f2ae23113a767304 dist : [zip] https://api.github.com/repos/slimphp/Slim/zipball/ce3cb65a06325fc9fe3d0223f2ae23113a767304 ce3cb65a06325fc9fe3d0223f2ae23113a767304 path : D:\workspace\oauth\examples\vendor\slim\slim names : slim/slim, psr/http-message-implementation support issues : https://github.com/slimphp/Slim/issues source : https://github.com/slimphp/Slim/tree/3.12.4 autoload psr-4 Slim\ => Slim requires ext-json * ext-libxml * ext-simplexml * nikic/fast-route ^1.0 php >=5.5.0 pimple/pimple ^3.0 psr/container ^1.0 psr/http-message ^1.0 requires (dev) phpunit/phpunit ^4.0 squizlabs/php_codesniffer ^3.6.0 provides psr/http-message-implementation 1.0composer outdated列出所有安装包是否可以更新λ composer outdated Direct dependencies required in composer.json: laminas/laminas-diactoros 2.5.0 2.14.0 PSR HTTP Message implementations league/event 2.2.0 3.0.1 Event package slim/slim 3.12.4 4.9.0 Slim is a PHP micro framework that helps you quickly write simple yet po... Transitive dependencies not required in composer.json: psr/container 1.1.1 2.0.1 Common Container Interface (PHP FIG PSR-11)composer browse / composer home浏览器直接打开这个包的 github 仓库# 打开这个包的官网 composer browser -H slim/slim # 打印slim的github仓库链接 composer browser -s slim/slimcomposer suggests给你人生路上一点建议λ composer suggests lcobucci/jwt suggests: - lcobucci/clock: * 1 additional suggestions by transitive dependencies can be shown with --all composer fund给出你所用的包的捐赠链接composer depends / whycomposer depends 命令用于显示一个包的依赖关系树。如果你要删除一个包之前,可以用这个命令先看看它上面是不是有人> composer depends psr/log -t psr/log 1.1.4 Common interface for logging libraries ├──composer/composer 2.4.x-dev (requires psr/log ^1.0 || ^2.0 || ^3.0) ├──composer/composer dev-main (requires psr/log ^1.0 || ^2.0 || ^3.0) ├──composer/xdebug-handler 3.0.3 (requires psr/log ^1 || ^2 || ^3) │ ├──composer/composer 2.4.x-dev (requires composer/xdebug-handler ^2.0.2 || ^3.0.3) │ └──composer/composer dev-main (requires composer/xdebug-handler ^2.0.2 || ^3.0.3) └──symfony/console v5.4.11 (conflicts psr/log >=3) (circular dependency aborted here)composer prohibits / why-not告诉您哪些包在阻止你想安装的包,并给出理由composer validate如果你手动修改 composer.json, 当准备提交 composer.json 文件之前,最好执行这条命令检查一下composer status如果你手动修改过一个包,这个包安装来源是 source, 则可以用这个命令看你本地修改记录,相当于 git statuscomposer self-update / selfupdatecomposer 程序自更新# 更新到指定版本 composer self-update 2.4.0-RC1composer config修改当前项目或者全局的配置-g : 修改全局配置文件--unset: 移除配置-l: 展示全部配置信息,如果加上-g,就显示全局的--absolute: *-dir的配置返回绝对路径--append: 追加一个镜像时,设置比较低的优先级--source: 展示config从哪里加载的# 添加一个测试foo到repositories composer config repo.foo vcs https://github.com/foo/bar # 添加一个阿里云镜像到repositories(阿里云镜像凉了) composer config repo.packagist composer https://mirrors.aliyun.com/composer/ #效果如下 "repositories": { "packagist": { "type": "composer", "url": "https://mirrors.aliyun.com/composer/" }, "foo": { "type": "vcs", "url": "https://github.com/foo/bar" } } # 修改extra配置 composer config extra.foo.bar value composer config --json extra.foo.bar '{"baz": true, "qux": []}'composer create-project创建项目 / 包,相当于 git clone + composer installcomposer create-project doctrine/orm path "2.2.*"composer dump-autoload当修改了包里面的类名,或者增加删除文件之后,需要执行这个命令# -o 选项是为了生产环境中的性能优化, composer dump-autoload -o # -a 选项则是为了开发环境中的重新生成类映射。 composer dump-autoload -acomposer clear-cache / clearcache / cc清理本地包缓存archive从远程下载一个包,并打包成 zip/tar 压缩包php composer.phar archive vendor/package 2.0.21 --format=ziprun-script / run你可以运行此命令来手动执行,只需要指定脚本的名称,可选的 --no-dev 参数允许你禁用开发者模式。{ "scripts": { "post-update-cmd": "MyVendor\\MyClass::postUpdate", "post-package-install": [ "MyVendor\\MyClass::postPackageInstall" ], "post-install-cmd": [ "MyVendor\\MyClass::warmCache", "phpunit -c app/" ] } } # 将会运行所有 post-install-cmd 事件下定义的脚本。 `composer run-script post-install-cmd` diagnose可以用来检查当前 Composer 环境是否符合最佳实践,包括 PHP 环境、Composer 配置等。audit检查当前项目的依赖项是否存在已知的安全漏洞。help使用 help 可以获取指定命令的帮助信息。php composer.phar help install
2023年08月12日
20 阅读
0 评论
0 点赞
2023-08-12
使用workerman加速任意项目
使用workerman加速任意项目众所周知,workerman是基于php cli的,由于php cli模式下无法使用php自带的header、sesion、cookie等函数,这导致将传统的php项目无法直接在workerman容器下直接运行。我一度以为让传统业务在workerman中运行,就必须更改框架甚至业务代码以适配workerman,直到joanhey发了一个issue,打破了我的认知。他们发布了一个名叫AdapterMan的项目,它可以做到不更改传统框架代码的情况下让你的传统php项目放到workerman中正常运行,并且他们公司已经在生产环境用了2年。注意,是零代码改动直接让laravel、lumen、Slim等框架的项目在workerman上运行。目前他们已经在laravel、lumen、Slim、Symfony、CakePHP、Yii2、KumbiaPHP 等做了初步压力测试,性能有很大的提升。以下是压测结果Laravel 8Fw Plaintext Json Single query Multiple query Updates FortunesLaravel 14,799 14,770 9,263 3,247 1,452 8,354Laravel Roadrunner 482 478 474 375 359 472Laravel Swoole 38,824 37,439 21,687 3,958 1,588 16,035Laravel Laravel s 54,617 49,372 23,677 2,917 1,255 16,696Laravel Workerman 103,004 99,891 46,001 5,828 1,666 27,158Laravel with Workerman % gain 596.02% 576.31% 396.61% 79.489% 14.738% 225.09%截图Symfony 6截图Fw Plaintext Json Single query Multiple query Updates FortunesSymfony 38,231 37,557 12,578 10,741 3,420 10,741Symfony Workerman 210,796 197,059 107,050 13,401 4,062 71,092Lumen 9Fw Plaintext Json Single query Multiple query Updates FortunesLumen 18,998 18,616 10,791 3,496 1,461 9,223Lumen Swoole 44,861 43,598 24,255 4,178 1,599 16,854Lumen Laravel s 93,335 82,745 31,567 3,030 1,282 21,130Lumen Workerman 185,126 177,667 58,729 5,857 1,662 31,430Slim with WorkermanWithout ORMFramework JSON 1-query 20-query Fortunes Updates PlaintextSlim 4 38,305 34,272 12,579 32,634 2,097 35,251Slim 4 Workerman 129,393 81,889 15,803 73,212 2,456 134,531Slim 4 Workerman pgsql * 102,926 19,637 92,752 14,875Lumen v9截图接入代码类似<?php require_once __DIR__ . '/vendor/autoload.php'; use AdaptermanAdapterman; use WorkermanWorker; Adapterman::init(); $http_worker = new Worker('http://0.0.0.0:8080'); $http_worker->count = 8; $http_worker->name = 'AdapterMan'; $http_worker->onWorkerStart = static function () { //init(); require __DIR__.'/start.php'; }; $http_worker->onMessage = static function ($connection, $request) { $connection->send(run()); }; Worker::runAll();项目地址:https://github.com/joanhey/AdapterMan 强烈建议大家为其点赞(点星星)相关链接:https://github.com/walkor/workerman/issues/824
2023年08月12日
29 阅读
0 评论
0 点赞
1
...
9
10
11
...
20