您好,欢迎来到东饰资讯网。
搜索
您的当前位置:首页正文

mysql从库Retrieved_Gtid_Set事务数比Executed_Gtid_Set事务数少的异常情况

2023-11-10 来源:东饰资讯网

    今天从库crash重启后出现很有趣的现象:

    技术分享

    可以发现:

    Retrieved_Gtid_Set值显示从库没有接收到部分事务,丢失了部分事务。但是从Executed_Gtid_Set显示从库没有丢失事务。

    错误日志:

    2017-03-08 10:41:12 118393 [ERROR] /usr/local/mysql/bin/mysqld: Sort aborted: Query execution was interrupted170308 10:55:38 mysqld_safe Number of processes running now: 0170308 10:55:38 mysqld_safe mysqld restarted2017-03-08 10:55:39 0 [Note] /usr/local/mysql/bin/mysqld (mysqld 5.6.29-log) starting as process 131069 ...

    从中可以看到,mysql crash后由mysqld_safe重新拉起来,应该是IO彪增导致数据库crash重启。CPU和剩余内存并无瓶颈现象。

技术分享

    

技术分享

技术分享

    为了确认从库是否真的因为少接收到事务而漏了部分数据,特意去解析了从库的binlog日志。

    技术分享

    可以发现,其实从库后续有接收到事务号:77d12988-29c1-11e6-a323-fa163ea5bbe1:334314693的事务,只是事务号次序被打乱,没有依次递增的情况。

    这是主库的binlog日志记录:

    技术分享

    注意:由于mysql的主从数据一致是以从库必须严格同主库按照相同sql执行次序为前提的,这种情况虽然从库也接收所有的事务并执行完成,但是主从库的执行次序并不一致。谨慎来说,从库仍然存在数据不一致的风险。需要使用pt工具包对主从库的数据做数据校验为好!

本文出自 “厚积薄发” 博客,请务必保留此出处http://1057212.blog.51cto.com/1047212/1904320

mysql从库Retrieved_Gtid_Set事务数比Executed_Gtid_Set事务数少的异常情况

标签:mysql从库 executed_gtid_set retrieved_gtid_set

小编还为您整理了以下内容,可能对您也有帮助:

故障分析 | MySQL 从机故障重启后主从同步报错案例分析

MySQL 从库所在主机故障重启后,sql_thread 线程报错:

通过报错信息可知,worker 线程在回放事务 '471c2974-f9bb-11eb-afb1-52540010fb89:88313207' 时,由于要插入的记录主键冲突报错。

主机重启前,主从同步正常,主机重启后,主从同步由于主键冲突报错,对比了冲突主键所在行记

录在主从库是一致的,初步分析事务 '471c2974-f9bb-11eb-afb1-52540010fb89:88313207' 在主机故

障前已经在从库进行了回放,那为何事务会重复回放呢?

在开启gtid模式下,如果指定 master_auto_position=1,start slave 时,从库会把

Retrieved_Gtid_Set 和 Executed_Gtid_Set 的并集发送给主库,主库将收到的并集和自己的

gtid_executed 比较,把从库 gtid 集合里缺失的事务全都发送给从库。

主机重启后,事务重复回放,表明 Retrieved_Gtid_Set 和 Executed_Gtid_Set 的并集中有 GTID 事务

丢失,导致重复获取事务执行引发主键冲突错误。Retrieved_Gtid_Set 和 Executed_Gtid_Set 均为内存变

量,MySQL 重启后,Retrieved_Gtid_Set 初始化为空值,从而推断出 Executed_Gtid_Set 有 GTID 事务丢

失。

Executed_Gtid_Set 来源于 gtid_executed 变量,gtid_executed 变量持久化介质有

mysql.gtid_executed 表和 binlog ,其中 mysql.gtid_executed 表是 MySQL 5.7 后引入的,在 MySQL 5.6 中,从库要使用 GTID ,必须要先设置 log_bin=on,log_slave_updates=on ,因为从库执行过的 GTID 只保留在 binlog 中。

gtid_executed 变量值陈旧,推断出 binlog 未实时持久化,我们看一下参数 sync_binlog :

通过以上分析,此次故障来龙去脉就清楚了:

Worker 线程报 1062 主键冲突错误 --> gtid_executed 信息陈旧 --> binlog 未实时持久化

搭建一主一从测试环境,通过 sysbench 模拟主库并发插入,从库主机暴力关机后,故障复现:

既然错误原因是事务重复执行,那跳过错误就好了,有如下两种方式,根据需要选取其中一种方式执行:

如果最新 binglog 丢失的 GTID 较多,手工执行比较繁琐,需要不断试错。可写一个存储过程批量执行:

待主从同步正常后,再取消参数 slave_skip_errors 设置重启 MySQL 。

用XtraBackup备份时报错又一个错误,这是什么原因

如题。在xtrabackup的参数里好像没有找到像innobackupex那样指定备份某一个数据库的参数。有人知道,是否可以指定只备份某一个库?另外,因为xtrabackup不能备份表结构 e tid=998这个帖子,尝试用innobackupex来做全备,用xtrbackup来做增量备份。为什么在做增量备份的时候总是出现这样的错误:xtrabackup: Error: cannot open ~/backup/base/2011-08-09_06-38-53/xtrabackup_checkpointsxtrabackup: error: failed to re

用XtraBackup备份时报错又一个错误,这是什么原因

如题。在xtrabackup的参数里好像没有找到像innobackupex那样指定备份某一个数据库的参数。有人知道,是否可以指定只备份某一个库?另外,因为xtrabackup不能备份表结构 e tid=998这个帖子,尝试用innobackupex来做全备,用xtrbackup来做增量备份。为什么在做增量备份的时候总是出现这样的错误:xtrabackup: Error: cannot open ~/backup/base/2011-08-09_06-38-53/xtrabackup_checkpointsxtrabackup: error: failed to re

装MySQL数据库时出现一个错误这怎么解决

如果从库上表 t 数据与主库不一致,导致复制错误,整个库的数据量很大,重做从库很慢,如何单独恢复这张表的数据?通常认为是不能修复单表数据的,因为涉及到各表状态不一致的问题。下面就列举备份单表恢复到从库会面临的问题以及解决办法:

场景 1

如果复制报错后,没有使用跳过错误、复制过滤等方法修复主从复制。主库数据一直在更新,从库数据停滞在报错状态(假设 GTID 为 aaaa:1-100)。

修复步骤:

    在主库上备份表 t (假设备份快照 GTID 为 aaaa:1-10000);

    恢复到从库;

    启动复制。

    这里的问题是复制起始位点是 aaaa:101,从库上表 t 的数据状态是领先其他表的。aaaa:101-10000 这些事务中只要有修改表 t 数据的事务,就会导致复制报错 ,比如主键冲突、记录不存在(而 aaaa:101 这个之前复制报错的事务必定是修改表 t 的事务)

    解决办法:启动复制时跳过 aaaa:101-10000 这些事务中修改表 t 的事务。

    正确的修复步骤:

    1. 在主库上备份表 t (假设备份快照 GTID 为 aaaa:1-10000),恢复到从库;

    2. 设置复制过滤,过滤表 t:

    CHANGE REPLICATION FILTER REPLICATE_WILD_IGNORE_TABLE = ('db_name.t');

    3. 启动复制,回放到 aaaa:10000 时停止复制(此时从库上所有表的数据都在同一状态,是一致的);

    START SLAVE UNTIL SQL_AFTER_GTIDS = 'aaaa:10000';

    4. 删除复制过滤,正常启动复制。

    注意事项:这里要用 mysqlmp --single-transaction --master-data=2,记录备份快照对应的 GTID

    场景 2

    如果复制报错后,使用跳过错误、复制过滤等办法修复了主从复制。主、从库数据一直在更新。

    修复步骤:

    在主库上备份表 t (假设备份快照 GTID为 aaaa:1-10000);

    停止从库复制,GTID为 aaaa:1-20000;

    恢复表 t 到从库;

    启动复制。

    这里的问题是复制起始位点是 aaaa:20001,aaaa:10000-20000 这些事务将不会在从库上回放,如果这里面有修改表 t 数据的事务,从库上将丢失这部分数据。

    解决办法:从备份开始到启动复制,锁定表 t,保证 aaaa:10000-20000 中没有修改表 t 的事务。

    正确修复步骤:

    对表 t 加读锁;

    在主库上备份表 t;

    停止从库复制,恢复表 t;

    启动复制;

    解锁表 t。

    如果是大表,这里可以用可传输表空间方式备份、恢复表,减少锁表时间。

装MySQL数据库时出现一个错误这怎么解决

如果从库上表 t 数据与主库不一致,导致复制错误,整个库的数据量很大,重做从库很慢,如何单独恢复这张表的数据?通常认为是不能修复单表数据的,因为涉及到各表状态不一致的问题。下面就列举备份单表恢复到从库会面临的问题以及解决办法:

场景 1

如果复制报错后,没有使用跳过错误、复制过滤等方法修复主从复制。主库数据一直在更新,从库数据停滞在报错状态(假设 GTID 为 aaaa:1-100)。

修复步骤:

    在主库上备份表 t (假设备份快照 GTID 为 aaaa:1-10000);

    恢复到从库;

    启动复制。

    这里的问题是复制起始位点是 aaaa:101,从库上表 t 的数据状态是领先其他表的。aaaa:101-10000 这些事务中只要有修改表 t 数据的事务,就会导致复制报错 ,比如主键冲突、记录不存在(而 aaaa:101 这个之前复制报错的事务必定是修改表 t 的事务)

    解决办法:启动复制时跳过 aaaa:101-10000 这些事务中修改表 t 的事务。

    正确的修复步骤:

    1. 在主库上备份表 t (假设备份快照 GTID 为 aaaa:1-10000),恢复到从库;

    2. 设置复制过滤,过滤表 t:

    CHANGE REPLICATION FILTER REPLICATE_WILD_IGNORE_TABLE = ('db_name.t');

    3. 启动复制,回放到 aaaa:10000 时停止复制(此时从库上所有表的数据都在同一状态,是一致的);

    START SLAVE UNTIL SQL_AFTER_GTIDS = 'aaaa:10000';

    4. 删除复制过滤,正常启动复制。

    注意事项:这里要用 mysqlmp --single-transaction --master-data=2,记录备份快照对应的 GTID

    场景 2

    如果复制报错后,使用跳过错误、复制过滤等办法修复了主从复制。主、从库数据一直在更新。

    修复步骤:

    在主库上备份表 t (假设备份快照 GTID为 aaaa:1-10000);

    停止从库复制,GTID为 aaaa:1-20000;

    恢复表 t 到从库;

    启动复制。

    这里的问题是复制起始位点是 aaaa:20001,aaaa:10000-20000 这些事务将不会在从库上回放,如果这里面有修改表 t 数据的事务,从库上将丢失这部分数据。

    解决办法:从备份开始到启动复制,锁定表 t,保证 aaaa:10000-20000 中没有修改表 t 的事务。

    正确修复步骤:

    对表 t 加读锁;

    在主库上备份表 t;

    停止从库复制,恢复表 t;

    启动复制;

    解锁表 t。

    如果是大表,这里可以用可传输表空间方式备份、恢复表,减少锁表时间。

腾讯云数据库团队:浅谈如何对MySQL内核进行深度优化

MYSQL数据库适用场景广泛,相较于Oracle、DB2性价比更高,Web网站、日志系统、数据仓库等场景都有MYSQL用武之地,但是也存在对于事务性支持不太好(MySQL 5.5版本开始默认引擎才是InnoDB事务型)、存在多个分支、读写效率瓶颈等问题。

所以如何用好MYSQL变得至关重要,一方面需要通过MYSQL优化找出系统读写瓶颈,提高数据库性能;另一方面需要合理涉及数据结构、调整参数,以提高用户操作响应;同时还有尽可能节省系统资源,以便系统可以提供更大负荷的服务。本文将为大家介绍腾讯云团队是如何对Mysql进行内核级优化的思路和经验。

早期的CDB主要基于开源的Oracle MySQL分支,侧重于优化运维和运营的OSS系统。在腾讯云,因为用户数的不断增加,对CDB for MySQL提出越来越高的要求,腾讯云CDB团队针对用户的需求和业界发展的技术趋势,对CDB for MySQL分支进行深度的定制优化。优化重点围绕内核性能、内核功能和外围OSS系统三个维度展开,具体的做法如下:

一.内核性能的优化

由于腾讯云上的DB基本都需要跨园区灾备的特性,因此CDB for MySQL的优化主要针对主从DB部署在跨园区网络拓扑的前提下,重点去解决真实部署环境下的性能难题。经过分析和调研,我们将优化的思路归纳为:“消除冗余I/O、缩短I/O路径和避免大锁竞争”。以下是内核性能的部分案例:

1.主备DB间的复制优化

问题分析

如上图所示,在原生MySQL的复制架构中,Master侧通过Dump线程不断发送Binlog事件给Slave的I/O线程,Slave的I/O线程在接受到Binlog事件后,有两个主要的动作:

写入到Relay Log中,这个过程会和Slave SQL线程争抢保护Relay Log的锁。

更新复制元数据(包含Master的位置等信息)。

优化方法

经过分析,我们的优化策略是:

Slave I/O线程和Slave SQL线程是典型的单写单读生产者-消费者模型,是可以做到无锁设计的;因此实现思路就是Slave I/O线程在每次写完数据后,原子更新Relay Log的长度信息,Slave SQL线程读取Relay Log的时以长度信息为边界。这样就将原本竞争激烈的Relay Log锁化解为无锁;

由于Binlog事件中的GTID(Global Transaction Identifier)和DB事务是一一对应的关系,所以Relay Log中的数据本身已经包含了所需要的复制元数据,所以我们可以不写Master info文件,消除了冗余的文件I/O;

于DB都是以事务为更新粒度的,因为在Relay Log文件I/O上,我们通过合并离散小I/O为事务粒度的大I/O等手段,使磁盘I/O得以大幅提升。

优化效果

如上图所示,经过优化:左图35.79%的锁竞争(futex)已经被完全消除;同压测压力下,56.15%的文件I/O开销被优化到19.16%,Slave I/O线程被优化为预期的I/O密集型线程。

2.主库事务线程和Dump线程间的优化

问题分析

如上图所示,在原生MySQL中多个事务提交线程TrxN和多个Dump线程之间会同时竞争Binlog文件资源的保护锁,多个事务提交线程对Binlog执行写入,多个Dump线程从Binlog文件读取数据并发送给Slave。所有的线程之间是串行执行的!

优化方法

经过分析,我们的优化策略是:

将读写分离开来,多个写入的线程还是在锁保护下串行执行,每一个写入线程写入完成后更新当前Binlog的长度信息,多个Dump线程以Binlog文件的长度信息为读取边界,多个Dump线程之间并行执行。以这种方式来让复制拓扑中的Dump线程发送得更快!

效果

优化后的示意图如下:

经过测试,优化后的内核,不仅提升了事务提交线程的性能,在Dump线程较多的情况下,对主从复制性能有较大提升。

二.主备库交互流程优化

问题分析

如上图所示,在原生MySQL中主备库之间的数据发送和ACK回应是简单的串行执行,在上一个事件ACK回应到达之前,不允许继续发送下一个事件;这个行为在跨园区(RTT 2-3ms)的情况性能非常差,而且也不能很好地利用带宽优势。

优化方法

经过分析,我们的优化策略是:

将发送和ACK回应的接收到不同的线程中,由于发送和接收都是基于TCP流的传输,所以时序性是有保障的;这样发送线程可以在未收ACK之前继续发送,接受线程收到ACK后唤醒等待的线程执行相应的任务。

效果

根据实际用例测试,优化后的TPS提升为15%左右。

三.内核功能的优化

1. 预留运维帐号连接数配额

在腾讯云上,不时遇到用户APP异常或者BUG从而占满DB的最大连接,这是CDB OSS帐号无法登录以进行紧急的运维操作。针对这个现状,我们在MySQL内核单独开辟了一个可配置的连接数配额,即便在上述场景下,运维帐号仍然可以连接到DB进行紧急的运维操作。极大地降低了异常情况下DB无状态的风险。该帐号仅有数据库运维管理权限,无法获取用户数据,也保证了用户数据的安全性。

2. 主备强同步

针对一些应用对数据的一致性要求非常高,CDB在MySQL原生半同步的基础上进行了深度优化,确保一个事务在主库上提交之前一定已经复制到至少一个备库上。确保主库宕机时数据的一致性。

四.外围系统的优化

除了以上提到的MySQL内核侧的部分优化,我们也在外围OSS平台进行了多处优化。例如使用异步MySQL ping协议实现大量实例的监控、通过分布式技术来加固原有系统的HA/服务发现和自动扩容等功能、在数据安全/故障切换和快速恢复方面也进行了多处优化。

相关推荐

腾讯云数据库CDB for MySQL产品相关文档

MySQL数据库设计总结

此文已由作者授权腾讯云技术社区发布,转载请注明文章出处,获取更多云计算技术干货,可请前往腾讯云技术社区

微信公众号:腾讯云技术社区( QcloudCommunity)

腾讯云数据库团队:浅谈如何对MySQL内核进行深度优化

标签:更新环境情况web传输信息安全文件技术分享

基于GTID的MySQL Replication的主键冲突

主健冲突或数据不一致的情况:
(1)停止slave进程
STOP SLAVE;

(2)设置事务号,事务号从Retrieved_Gtid_Set获取

SET @@SESSION.GTID_NEXT= 'xxxxxxxxxxx'

(3)设置空事务

BEGIN; COMMIT;

(4)恢复事务号

SET SESSION GTID_NEXT = AUTOMATIC;

(5)启动slave进程

START SLAVE;

这些是基本的操作步骤,不知道你的还有什么其他的错误,如果有的话加我的群问我: 371561513

基于GTID的MySQL Replication的主键冲突

主健冲突或数据不一致的情况:
(1)停止slave进程
STOP SLAVE;

(2)设置事务号,事务号从Retrieved_Gtid_Set获取

SET @@SESSION.GTID_NEXT= 'xxxxxxxxxxx'

(3)设置空事务

BEGIN; COMMIT;

(4)恢复事务号

SET SESSION GTID_NEXT = AUTOMATIC;

(5)启动slave进程

START SLAVE;

这些是基本的操作步骤,不知道你的还有什么其他的错误,如果有的话加我的群问我: 371561513

求助下utf8mb4的问题

整理 MySQL 8.0 文档时发现一个变更:
默认字符集由 latin1 变为 utf8mb4。想起以前整理过字符集转换文档,升级到 MySQL 8.0 后大概率会有字符集转换的需求,在此正好分享一下。
当时的需求背景是:
部分系统使用的字符集是 utf8,但 utf8 最多只能存 3 字节长度的字符,不能存放 4 字节的生僻字或者表情符号,因此打算迁移到 utf8mb4。
迁移方案一1. 准备新的数据库实例,修改以下参数:[mysqld]## Character Settingsinit_connect='SET NAMES utf8mb4'#连接建立时执行设置的语句,对super权限用户无效character-set-server = utf8mb4collation-server = utf8mb4_general_ci#设置服务端校验规则,如果字符串需要区分大小写,设置为utf8mb4_binskip-character-set-client-handshake#忽略应用连接自己设置的字符编码,保持与全局设置一致## Innodb Settingsinnodb_file_format = Barracudainnodb_file_format_max = Barracudainnodb_file_per_table = 1innodb_large_prefix = ON#允许索引的最大字节数为3072(不开启则最大为767字节,对于类似varchar(255)字段的索引会有问题,因为255*4大于767)

2. 停止应用,观察,确认不再有数据写入
可通过 show master status 观察 GTID 或者 binlog position,没有变化则没有写入。
3. 导出数据
先导出表结构:mysqlmp -u -p --no-data --default-character-set=utf8mb4 --single-transaction --set-gtid-purged=OFF --databases testdb > /backup/testdb.sql
后导出数据:mysqlmp -u -p --no-create-info --master-data=2 --flush-logs --routines --events --triggers --default-character-set=utf8mb4 --single-transaction --set-gtid-purged=OFF --database testdb > /backup/testdata.sql

4. 修改建表语句
修改导出的表结构文件,将表、列定义中的 utf8 改为 utf8mb4
5. 导入数据
先导入表结构:mysql -u -p testdb < /backup/testdb.sql
后导入数据:mysql -u -p testdb < /backup/testdata.sql

6. 建用户
查出旧环境的数据库用户,在新数据库中创建
7. 修改新数据库端口,启动应用进行测试
关闭旧数据库,修改新数据库端口重启,启动应用

求助下utf8mb4的问题

整理 MySQL 8.0 文档时发现一个变更:
默认字符集由 latin1 变为 utf8mb4。想起以前整理过字符集转换文档,升级到 MySQL 8.0 后大概率会有字符集转换的需求,在此正好分享一下。
当时的需求背景是:
部分系统使用的字符集是 utf8,但 utf8 最多只能存 3 字节长度的字符,不能存放 4 字节的生僻字或者表情符号,因此打算迁移到 utf8mb4。
迁移方案一1. 准备新的数据库实例,修改以下参数:[mysqld]## Character Settingsinit_connect='SET NAMES utf8mb4'#连接建立时执行设置的语句,对super权限用户无效character-set-server = utf8mb4collation-server = utf8mb4_general_ci#设置服务端校验规则,如果字符串需要区分大小写,设置为utf8mb4_binskip-character-set-client-handshake#忽略应用连接自己设置的字符编码,保持与全局设置一致## Innodb Settingsinnodb_file_format = Barracudainnodb_file_format_max = Barracudainnodb_file_per_table = 1innodb_large_prefix = ON#允许索引的最大字节数为3072(不开启则最大为767字节,对于类似varchar(255)字段的索引会有问题,因为255*4大于767)

2. 停止应用,观察,确认不再有数据写入
可通过 show master status 观察 GTID 或者 binlog position,没有变化则没有写入。
3. 导出数据
先导出表结构:mysqlmp -u -p --no-data --default-character-set=utf8mb4 --single-transaction --set-gtid-purged=OFF --databases testdb > /backup/testdb.sql
后导出数据:mysqlmp -u -p --no-create-info --master-data=2 --flush-logs --routines --events --triggers --default-character-set=utf8mb4 --single-transaction --set-gtid-purged=OFF --database testdb > /backup/testdata.sql

4. 修改建表语句
修改导出的表结构文件,将表、列定义中的 utf8 改为 utf8mb4
5. 导入数据
先导入表结构:mysql -u -p testdb < /backup/testdb.sql
后导入数据:mysql -u -p testdb < /backup/testdata.sql

6. 建用户
查出旧环境的数据库用户,在新数据库中创建
7. 修改新数据库端口,启动应用进行测试
关闭旧数据库,修改新数据库端口重启,启动应用

如何有效地提高 MySQL 的备份和恢复速度

你好,

一 加速备份

1、 加了single-transaction参数 备份时 需要先flush table with read lock 这个过程中会有一个锁表的过程,如果有事务或语句正在执行,没有结束,那么备份进程会一直等待,并且阻塞别的事务,那么也会影响业务。所以要先确认备份的时候没有大的事务在运行。具体 single-transaction的加锁可以参考 我的博客:mysqlmp备份时加single-transaction会不会加锁2 、mysqlmp是单进程的,没有办法并行,但现在机器的瓶颈多是出现在IO方面,可以使用更了的IO设备加快速度3 、mysqlmp时如果空间够的话,不要边压缩边备份二 加速恢复

1 关闭binlog:不写入Binlog会大大的加快数据导入的速度2 innodb_flush_log_at_trx_commit=0

3 更好的配置

建议:

如果非要使用逻辑备份,可以考虑mysqlmper, mysqlpump(5.7)这两个工具去备份,这两个在备份的时候支持并行操作,mysqlmper还可以对单表进行恢复,在只需要恢复单表的情况下,恢复速度会大大加快使用物理备份 xtrabackup (open source),MEB(oracle提供,收费): 他们的备份原理是基于mysql crash recover, 备份速度 是和逻辑备份的相差不太大。但是恢复速度却有很大的提升。

逻辑备份 备出来的是sql语句文件,恢复时需要一条一条的执行sql,所以恢复很慢。

而物理备份和还原的速度 相当于直接copy文件,所以恢复的时候性能有很大的提升并且这两个软件还支持并行,效果更好。

逻辑备份最大的优点是 备份好的文件经压缩后占用空间较小,最大缺点恢复太慢物理备份可以很快的恢复,但是备份好的文件压缩后占用空间比逻辑备份要大

linux编译安装mysql5.6出错?麻烦看看什么原因?

1、这是因为你的某些依赖文件没有安装或者版本不够新,看看configure的输出或者mkmf.log文件的内容,或者传上来;

2、上次make的垃圾文件存在导致再次make失败,在make之前先执行make clean

Copyright © 2019- huatuoyibo.cn 版权所有

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务