Pep Guardiola's side were in danger of gifting second-placed Liverpool a title lifeline after West Ham's Jarrod Bowen struck twice in the first half.
But City's stirring recovery showed the character that has already brought them three titles in the last four seasons.
Jack Grealish got one back soon after the interval and Vladimir Coufal's own goal completed City's escape act.
It should have been even better for City, but Mahrez's penalty was saved by Lukasz Fabianski with five minutes left.
While that was a bitter blow to Guardiola, who stood ashen-faced on the touchline, in the circumstances City will be relieved to have moved four points clear of Liverpool with a goal difference seven better than their rivals.
They will retain the title if Jurgen Klopp's side lose their game in hand at Southampton on Tuesday.
If Liverpool beat Southampton to close the gap to one point, Guardiola's men can still clinch the trophy with a victory against Aston Villa at the Etihad Stadium on 22 May.
A title race for the ages could well go down to the last day of the season, when Liverpool host Wolves, as it did when City pipped the Reds to first place in 2019.
Guardiola said this week he wants players with a "special mentality" as he looks to bolster his squad for next season, personalities who, like him, wake up every day obsessed with winning.
City's enthralling revival to extend their unbeaten league run to 11 matches showed Guardiola already has plenty of fighters in his squad, although he might not put Mahrez on penalty duties again for a while.
Aymeric Laporte and Fernandinho were passed fit to start in City's defence after suffering injuries in Wednesday's win at Wolves.
But City were already without injured defenders Ruben Dias, John Stones and Kyle Walker and they didn't look comfortable at the back from the opening moments.